2017-11-03 13:28:30 +03:00
// SPDX-License-Identifier: GPL-2.0
2002-04-09 23:14:34 +04:00
/*
* mos7720 . c
2014-01-02 17:59:24 +04:00
* Controls the Moschip 7720 usb to dual port serial converter
2002-04-09 23:14:34 +04:00
*
* Copyright 2006 Moschip Semiconductor Tech . Ltd .
*
* Developed by :
2007-06-25 12:08:01 +04:00
* Vijaya Kumar < vijaykumar . gn @ gmail . com >
* Ajay Kumar < naanuajay @ yahoo . com >
* Gurudeva < ngurudeva @ yahoo . com >
2002-04-09 23:14:34 +04:00
*
* Cleaned up from the original by :
* Greg Kroah - Hartman < gregkh @ suse . de >
*
* Originally based on drivers / usb / serial / io_edgeport . c which is :
* Copyright ( C ) 2000 Inside Out Networks , All rights reserved .
* Copyright ( C ) 2001 - 2002 Greg Kroah - Hartman < greg @ kroah . com >
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/tty.h>
# include <linux/tty_driver.h>
# include <linux/tty_flip.h>
# include <linux/module.h>
# include <linux/spinlock.h>
# include <linux/serial.h>
# include <linux/serial_reg.h>
# include <linux/usb.h>
# include <linux/usb/serial.h>
2008-07-22 14:16:21 +04:00
# include <linux/uaccess.h>
2010-04-16 01:01:33 +04:00
# include <linux/parport.h>
2002-04-09 23:14:34 +04:00
# define DRIVER_AUTHOR "Aspire Communications pvt Ltd."
# define DRIVER_DESC "Moschip USB Serial Driver"
/* default urb timeout */
2013-05-27 16:44:43 +04:00
# define MOS_WDR_TIMEOUT 5000
2002-04-09 23:14:34 +04:00
# define MOS_MAX_PORT 0x02
# define MOS_WRITE 0x0E
# define MOS_READ 0x0D
2014-01-02 17:59:24 +04:00
/* Interrupt Routines Defines */
2002-04-09 23:14:34 +04:00
# define SERIAL_IIR_RLS 0x06
# define SERIAL_IIR_RDA 0x04
# define SERIAL_IIR_CTI 0x0c
# define SERIAL_IIR_THR 0x02
# define SERIAL_IIR_MS 0x00
# define NUM_URBS 16 /* URB Count */
# define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */
2010-04-16 01:01:33 +04:00
/* This structure holds all of the local serial port information */
2008-07-22 14:16:21 +04:00
struct moschip_port {
2002-04-09 23:14:34 +04:00
__u8 shadowLCR ; /* last LCR value received */
__u8 shadowMCR ; /* last MCR value received */
__u8 shadowMSR ; /* last MSR value received */
char open ;
struct usb_serial_port * port ; /* loop back to the owner */
struct urb * write_urb_pool [ NUM_URBS ] ;
} ;
# define USB_VENDOR_ID_MOSCHIP 0x9710
# define MOSCHIP_DEVICE_ID_7720 0x7720
# define MOSCHIP_DEVICE_ID_7715 0x7715
2012-05-09 02:46:14 +04:00
static const struct usb_device_id id_table [ ] = {
2008-07-22 14:16:21 +04:00
{ USB_DEVICE ( USB_VENDOR_ID_MOSCHIP , MOSCHIP_DEVICE_ID_7720 ) } ,
2010-01-26 20:12:12 +03:00
{ USB_DEVICE ( USB_VENDOR_ID_MOSCHIP , MOSCHIP_DEVICE_ID_7715 ) } ,
2002-04-09 23:14:34 +04:00
{ } /* terminating entry */
} ;
2012-05-09 02:46:14 +04:00
MODULE_DEVICE_TABLE ( usb , id_table ) ;
2002-04-09 23:14:34 +04:00
2010-04-16 01:01:33 +04:00
# ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
/* initial values for parport regs */
# define DCR_INIT_VAL 0x0c /* SLCTIN, nINIT */
# define ECR_INIT_VAL 0x00 /* SPP mode */
struct urbtracker {
struct mos7715_parport * mos_parport ;
struct list_head urblist_entry ;
struct kref ref_count ;
struct urb * urb ;
2013-08-13 15:27:34 +04:00
struct usb_ctrlrequest * setup ;
2010-04-16 01:01:33 +04:00
} ;
enum mos7715_pp_modes {
SPP = 0 < < 5 ,
PS2 = 1 < < 5 , /* moschip calls this 'NIBBLE' mode */
PPF = 2 < < 5 , /* moschip calls this 'CB-FIFO mode */
} ;
struct mos7715_parport {
struct parport * pp ; /* back to containing struct */
struct kref ref_count ; /* to instance of this struct */
struct list_head deferred_urbs ; /* list deferred async urbs */
struct list_head active_urbs ; /* list async urbs in flight */
spinlock_t listlock ; /* protects list access */
bool msg_pending ; /* usb sync call pending */
struct completion syncmsg_compl ; /* usb sync call completed */
struct tasklet_struct urb_tasklet ; /* for sending deferred urbs */
struct usb_serial * serial ; /* back to containing struct */
__u8 shadowECR ; /* parallel port regs... */
__u8 shadowDCR ;
atomic_t shadowDSR ; /* updated in int-in callback */
} ;
/* lock guards against dereferencing NULL ptr in parport ops callbacks */
static DEFINE_SPINLOCK ( release_lock ) ;
2010-04-16 01:02:09 +04:00
# endif /* CONFIG_USB_SERIAL_MOS7715_PARPORT */
static const unsigned int dummy ; /* for clarity in register access fns */
2010-04-16 01:01:33 +04:00
enum mos_regs {
2015-06-23 16:29:40 +03:00
MOS7720_THR , /* serial port regs */
MOS7720_RHR ,
MOS7720_IER ,
MOS7720_FCR ,
MOS7720_ISR ,
MOS7720_LCR ,
MOS7720_MCR ,
MOS7720_LSR ,
MOS7720_MSR ,
MOS7720_SPR ,
MOS7720_DLL ,
MOS7720_DLM ,
MOS7720_DPR , /* parallel port regs */
MOS7720_DSR ,
MOS7720_DCR ,
MOS7720_ECR ,
MOS7720_SP1_REG , /* device control regs */
MOS7720_SP2_REG , /* serial port 2 (7720 only) */
MOS7720_PP_REG ,
MOS7720_SP_CONTROL_REG ,
2010-04-16 01:01:33 +04:00
} ;
/*
* Return the correct value for the Windex field of the setup packet
* for a control endpoint message . See the 7715 datasheet .
*/
static inline __u16 get_reg_index ( enum mos_regs reg )
{
static const __u16 mos7715_index_lookup_table [ ] = {
2015-06-23 16:29:40 +03:00
0x00 , /* MOS7720_THR */
0x00 , /* MOS7720_RHR */
0x01 , /* MOS7720_IER */
0x02 , /* MOS7720_FCR */
0x02 , /* MOS7720_ISR */
0x03 , /* MOS7720_LCR */
0x04 , /* MOS7720_MCR */
0x05 , /* MOS7720_LSR */
0x06 , /* MOS7720_MSR */
0x07 , /* MOS7720_SPR */
0x00 , /* MOS7720_DLL */
0x01 , /* MOS7720_DLM */
0x00 , /* MOS7720_DPR */
0x01 , /* MOS7720_DSR */
0x02 , /* MOS7720_DCR */
0x0a , /* MOS7720_ECR */
0x01 , /* MOS7720_SP1_REG */
0x02 , /* MOS7720_SP2_REG (7720 only) */
0x04 , /* MOS7720_PP_REG (7715 only) */
0x08 , /* MOS7720_SP_CONTROL_REG */
2010-04-16 01:01:33 +04:00
} ;
return mos7715_index_lookup_table [ reg ] ;
}
/*
* Return the correct value for the upper byte of the Wvalue field of
* the setup packet for a control endpoint message .
*/
2010-04-16 01:02:09 +04:00
static inline __u16 get_reg_value ( enum mos_regs reg ,
unsigned int serial_portnum )
2010-04-16 01:01:33 +04:00
{
2015-06-23 16:29:40 +03:00
if ( reg > = MOS7720_SP1_REG ) /* control reg */
2010-04-16 01:01:33 +04:00
return 0x0000 ;
2010-04-16 01:02:09 +04:00
2015-06-23 16:29:40 +03:00
else if ( reg > = MOS7720_DPR ) /* parallel port reg (7715 only) */
2010-04-16 01:01:33 +04:00
return 0x0100 ;
2010-04-16 01:02:09 +04:00
else /* serial port reg */
return ( serial_portnum + 2 ) < < 8 ;
2010-04-16 01:01:33 +04:00
}
/*
* Write data byte to the specified device register . The data is embedded in
2010-04-16 01:02:09 +04:00
* the value field of the setup packet . serial_portnum is ignored for registers
* not specific to a particular serial port .
2010-04-16 01:01:33 +04:00
*/
2010-04-16 01:02:09 +04:00
static int write_mos_reg ( struct usb_serial * serial , unsigned int serial_portnum ,
enum mos_regs reg , __u8 data )
2010-04-16 01:01:33 +04:00
{
struct usb_device * usbdev = serial - > dev ;
unsigned int pipe = usb_sndctrlpipe ( usbdev , 0 ) ;
__u8 request = ( __u8 ) 0x0e ;
__u8 requesttype = ( __u8 ) 0x40 ;
__u16 index = get_reg_index ( reg ) ;
2010-04-16 01:02:09 +04:00
__u16 value = get_reg_value ( reg , serial_portnum ) + data ;
int status = usb_control_msg ( usbdev , pipe , request , requesttype , value ,
index , NULL , 0 , MOS_WDR_TIMEOUT ) ;
2010-04-16 01:01:33 +04:00
if ( status < 0 )
dev_err ( & usbdev - > dev ,
2014-03-12 22:09:42 +04:00
" mos7720: usb_control_msg() failed: %d \n " , status ) ;
2010-04-16 01:01:33 +04:00
return status ;
}
/*
* Read data byte from the specified device register . The data returned by the
2010-04-16 01:02:09 +04:00
* device is embedded in the value field of the setup packet . serial_portnum is
* ignored for registers that are not specific to a particular serial port .
2010-04-16 01:01:33 +04:00
*/
2010-04-16 01:02:09 +04:00
static int read_mos_reg ( struct usb_serial * serial , unsigned int serial_portnum ,
enum mos_regs reg , __u8 * data )
2010-04-16 01:01:33 +04:00
{
2010-04-16 01:02:09 +04:00
struct usb_device * usbdev = serial - > dev ;
2010-04-16 01:01:33 +04:00
unsigned int pipe = usb_rcvctrlpipe ( usbdev , 0 ) ;
__u8 request = ( __u8 ) 0x0d ;
__u8 requesttype = ( __u8 ) 0xc0 ;
__u16 index = get_reg_index ( reg ) ;
2010-04-16 01:02:09 +04:00
__u16 value = get_reg_value ( reg , serial_portnum ) ;
2013-05-27 16:44:39 +04:00
u8 * buf ;
int status ;
buf = kmalloc ( 1 , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
status = usb_control_msg ( usbdev , pipe , request , requesttype , value ,
index , buf , 1 , MOS_WDR_TIMEOUT ) ;
2017-01-12 16:56:17 +03:00
if ( status = = 1 ) {
2013-05-27 16:44:39 +04:00
* data = * buf ;
2017-01-12 16:56:17 +03:00
} else {
2010-04-16 01:01:33 +04:00
dev_err ( & usbdev - > dev ,
2014-03-12 22:09:42 +04:00
" mos7720: usb_control_msg() failed: %d \n " , status ) ;
2017-01-12 16:56:17 +03:00
if ( status > = 0 )
status = - EIO ;
* data = 0 ;
}
2013-05-27 16:44:39 +04:00
kfree ( buf ) ;
2010-04-16 01:01:33 +04:00
return status ;
}
2010-04-16 01:02:09 +04:00
# ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
2010-04-16 01:01:33 +04:00
static inline int mos7715_change_mode ( struct mos7715_parport * mos_parport ,
enum mos7715_pp_modes mode )
{
mos_parport - > shadowECR = mode ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( mos_parport - > serial , dummy , MOS7720_ECR ,
mos_parport - > shadowECR ) ;
2010-04-16 01:01:33 +04:00
return 0 ;
}
static void destroy_mos_parport ( struct kref * kref )
{
struct mos7715_parport * mos_parport =
container_of ( kref , struct mos7715_parport , ref_count ) ;
kfree ( mos_parport ) ;
}
static void destroy_urbtracker ( struct kref * kref )
{
struct urbtracker * urbtrack =
container_of ( kref , struct urbtracker , ref_count ) ;
struct mos7715_parport * mos_parport = urbtrack - > mos_parport ;
2012-05-04 03:44:31 +04:00
2010-04-16 01:01:33 +04:00
usb_free_urb ( urbtrack - > urb ) ;
2013-08-13 15:27:34 +04:00
kfree ( urbtrack - > setup ) ;
2010-04-16 01:01:33 +04:00
kfree ( urbtrack ) ;
kref_put ( & mos_parport - > ref_count , destroy_mos_parport ) ;
}
/*
* This runs as a tasklet when sending an urb in a non - blocking parallel
* port callback had to be deferred because the disconnect mutex could not be
* obtained at the time .
*/
static void send_deferred_urbs ( unsigned long _mos_parport )
{
int ret_val ;
unsigned long flags ;
struct mos7715_parport * mos_parport = ( void * ) _mos_parport ;
2012-08-21 07:28:45 +04:00
struct urbtracker * urbtrack , * tmp ;
2010-04-16 01:01:33 +04:00
struct list_head * cursor , * next ;
2012-09-15 02:08:33 +04:00
struct device * dev ;
2010-04-16 01:01:33 +04:00
/* if release function ran, game over */
if ( unlikely ( mos_parport - > serial = = NULL ) )
return ;
2012-09-15 02:08:33 +04:00
dev = & mos_parport - > serial - > dev - > dev ;
2010-04-16 01:01:33 +04:00
/* try again to get the mutex */
if ( ! mutex_trylock ( & mos_parport - > serial - > disc_mutex ) ) {
2012-09-15 02:08:33 +04:00
dev_dbg ( dev , " %s: rescheduling tasklet \n " , __func__ ) ;
2010-04-16 01:01:33 +04:00
tasklet_schedule ( & mos_parport - > urb_tasklet ) ;
return ;
}
/* if device disconnected, game over */
if ( unlikely ( mos_parport - > serial - > disconnected ) ) {
mutex_unlock ( & mos_parport - > serial - > disc_mutex ) ;
return ;
}
spin_lock_irqsave ( & mos_parport - > listlock , flags ) ;
if ( list_empty ( & mos_parport - > deferred_urbs ) ) {
spin_unlock_irqrestore ( & mos_parport - > listlock , flags ) ;
mutex_unlock ( & mos_parport - > serial - > disc_mutex ) ;
2012-09-15 02:08:33 +04:00
dev_dbg ( dev , " %s: deferred_urbs list empty \n " , __func__ ) ;
2010-04-16 01:01:33 +04:00
return ;
}
/* move contents of deferred_urbs list to active_urbs list and submit */
list_for_each_safe ( cursor , next , & mos_parport - > deferred_urbs )
list_move_tail ( cursor , & mos_parport - > active_urbs ) ;
2012-08-21 07:28:45 +04:00
list_for_each_entry_safe ( urbtrack , tmp , & mos_parport - > active_urbs ,
2010-04-16 01:01:33 +04:00
urblist_entry ) {
ret_val = usb_submit_urb ( urbtrack - > urb , GFP_ATOMIC ) ;
2012-09-15 02:08:33 +04:00
dev_dbg ( dev , " %s: urb submitted \n " , __func__ ) ;
2010-04-16 01:01:33 +04:00
if ( ret_val ) {
2012-09-15 02:08:33 +04:00
dev_err ( dev , " usb_submit_urb() failed: %d \n " , ret_val ) ;
2010-04-16 01:01:33 +04:00
list_del ( & urbtrack - > urblist_entry ) ;
kref_put ( & urbtrack - > ref_count , destroy_urbtracker ) ;
}
}
spin_unlock_irqrestore ( & mos_parport - > listlock , flags ) ;
mutex_unlock ( & mos_parport - > serial - > disc_mutex ) ;
}
/* callback for parallel port control urbs submitted asynchronously */
static void async_complete ( struct urb * urb )
{
struct urbtracker * urbtrack = urb - > context ;
int status = urb - > status ;
2018-06-24 01:32:08 +03:00
unsigned long flags ;
2012-05-04 03:44:31 +04:00
2010-04-16 01:01:33 +04:00
if ( unlikely ( status ) )
2012-09-15 02:08:33 +04:00
dev_dbg ( & urb - > dev - > dev , " %s - nonzero urb status received: %d \n " , __func__ , status ) ;
2010-04-16 01:01:33 +04:00
/* remove the urbtracker from the active_urbs list */
2018-06-24 01:32:08 +03:00
spin_lock_irqsave ( & urbtrack - > mos_parport - > listlock , flags ) ;
2010-04-16 01:01:33 +04:00
list_del ( & urbtrack - > urblist_entry ) ;
2018-06-24 01:32:08 +03:00
spin_unlock_irqrestore ( & urbtrack - > mos_parport - > listlock , flags ) ;
2010-04-16 01:01:33 +04:00
kref_put ( & urbtrack - > ref_count , destroy_urbtracker ) ;
}
static int write_parport_reg_nonblock ( struct mos7715_parport * mos_parport ,
enum mos_regs reg , __u8 data )
{
struct urbtracker * urbtrack ;
int ret_val ;
unsigned long flags ;
struct usb_serial * serial = mos_parport - > serial ;
struct usb_device * usbdev = serial - > dev ;
/* create and initialize the control urb and containing urbtracker */
urbtrack = kmalloc ( sizeof ( struct urbtracker ) , GFP_ATOMIC ) ;
2013-12-29 22:22:56 +04:00
if ( ! urbtrack )
2010-04-16 01:01:33 +04:00
return - ENOMEM ;
2013-12-29 22:22:56 +04:00
2010-04-16 01:01:33 +04:00
kref_get ( & mos_parport - > ref_count ) ;
urbtrack - > mos_parport = mos_parport ;
urbtrack - > urb = usb_alloc_urb ( 0 , GFP_ATOMIC ) ;
2013-12-29 22:22:56 +04:00
if ( ! urbtrack - > urb ) {
2010-04-16 01:01:33 +04:00
kfree ( urbtrack ) ;
return - ENOMEM ;
}
2013-08-16 11:16:59 +04:00
urbtrack - > setup = kmalloc ( sizeof ( * urbtrack - > setup ) , GFP_ATOMIC ) ;
2013-08-13 15:27:34 +04:00
if ( ! urbtrack - > setup ) {
usb_free_urb ( urbtrack - > urb ) ;
kfree ( urbtrack ) ;
return - ENOMEM ;
}
urbtrack - > setup - > bRequestType = ( __u8 ) 0x40 ;
urbtrack - > setup - > bRequest = ( __u8 ) 0x0e ;
2013-08-19 15:05:45 +04:00
urbtrack - > setup - > wValue = cpu_to_le16 ( get_reg_value ( reg , dummy ) ) ;
urbtrack - > setup - > wIndex = cpu_to_le16 ( get_reg_index ( reg ) ) ;
2013-08-13 15:27:34 +04:00
urbtrack - > setup - > wLength = 0 ;
2010-04-16 01:01:33 +04:00
usb_fill_control_urb ( urbtrack - > urb , usbdev ,
usb_sndctrlpipe ( usbdev , 0 ) ,
2013-08-13 15:27:34 +04:00
( unsigned char * ) urbtrack - > setup ,
2010-04-16 01:01:33 +04:00
NULL , 0 , async_complete , urbtrack ) ;
kref_init ( & urbtrack - > ref_count ) ;
INIT_LIST_HEAD ( & urbtrack - > urblist_entry ) ;
/*
* get the disconnect mutex , or add tracker to the deferred_urbs list
* and schedule a tasklet to try again later
*/
if ( ! mutex_trylock ( & serial - > disc_mutex ) ) {
spin_lock_irqsave ( & mos_parport - > listlock , flags ) ;
list_add_tail ( & urbtrack - > urblist_entry ,
& mos_parport - > deferred_urbs ) ;
spin_unlock_irqrestore ( & mos_parport - > listlock , flags ) ;
tasklet_schedule ( & mos_parport - > urb_tasklet ) ;
2014-03-12 22:09:42 +04:00
dev_dbg ( & usbdev - > dev , " tasklet scheduled \n " ) ;
2010-04-16 01:01:33 +04:00
return 0 ;
}
/* bail if device disconnected */
if ( serial - > disconnected ) {
kref_put ( & urbtrack - > ref_count , destroy_urbtracker ) ;
mutex_unlock ( & serial - > disc_mutex ) ;
return - ENODEV ;
}
/* add the tracker to the active_urbs list and submit */
spin_lock_irqsave ( & mos_parport - > listlock , flags ) ;
list_add_tail ( & urbtrack - > urblist_entry , & mos_parport - > active_urbs ) ;
spin_unlock_irqrestore ( & mos_parport - > listlock , flags ) ;
ret_val = usb_submit_urb ( urbtrack - > urb , GFP_ATOMIC ) ;
mutex_unlock ( & serial - > disc_mutex ) ;
if ( ret_val ) {
dev_err ( & usbdev - > dev ,
2014-03-12 22:09:42 +04:00
" %s: submit_urb() failed: %d \n " , __func__ , ret_val ) ;
2010-04-16 01:01:33 +04:00
spin_lock_irqsave ( & mos_parport - > listlock , flags ) ;
list_del ( & urbtrack - > urblist_entry ) ;
spin_unlock_irqrestore ( & mos_parport - > listlock , flags ) ;
kref_put ( & urbtrack - > ref_count , destroy_urbtracker ) ;
return ret_val ;
}
return 0 ;
}
/*
* This is the the common top part of all parallel port callback operations that
* send synchronous messages to the device . This implements convoluted locking
* that avoids two scenarios : ( 1 ) a port operation is called after usbserial
* has called our release function , at which point struct mos7715_parport has
* been destroyed , and ( 2 ) the device has been disconnected , but usbserial has
* not called the release function yet because someone has a serial port open .
* The shared release_lock prevents the first , and the mutex and disconnected
* flag maintained by usbserial covers the second . We also use the msg_pending
2014-01-02 17:59:24 +04:00
* flag to ensure that all synchronous usb message calls have completed before
2010-04-16 01:01:33 +04:00
* our release function can return .
*/
static int parport_prologue ( struct parport * pp )
{
struct mos7715_parport * mos_parport ;
spin_lock ( & release_lock ) ;
mos_parport = pp - > private_data ;
if ( unlikely ( mos_parport = = NULL ) ) {
/* release fn called, port struct destroyed */
spin_unlock ( & release_lock ) ;
return - 1 ;
}
mos_parport - > msg_pending = true ; /* synch usb call pending */
2013-11-15 02:32:02 +04:00
reinit_completion ( & mos_parport - > syncmsg_compl ) ;
2010-04-16 01:01:33 +04:00
spin_unlock ( & release_lock ) ;
mutex_lock ( & mos_parport - > serial - > disc_mutex ) ;
if ( mos_parport - > serial - > disconnected ) {
/* device disconnected */
mutex_unlock ( & mos_parport - > serial - > disc_mutex ) ;
mos_parport - > msg_pending = false ;
complete ( & mos_parport - > syncmsg_compl ) ;
return - 1 ;
}
return 0 ;
}
/*
2014-01-02 17:59:24 +04:00
* This is the common bottom part of all parallel port functions that send
2010-04-16 01:01:33 +04:00
* synchronous messages to the device .
*/
static inline void parport_epilogue ( struct parport * pp )
{
struct mos7715_parport * mos_parport = pp - > private_data ;
mutex_unlock ( & mos_parport - > serial - > disc_mutex ) ;
mos_parport - > msg_pending = false ;
complete ( & mos_parport - > syncmsg_compl ) ;
}
static void parport_mos7715_write_data ( struct parport * pp , unsigned char d )
{
struct mos7715_parport * mos_parport = pp - > private_data ;
2012-05-04 03:44:31 +04:00
2010-04-16 01:01:33 +04:00
if ( parport_prologue ( pp ) < 0 )
return ;
mos7715_change_mode ( mos_parport , SPP ) ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( mos_parport - > serial , dummy , MOS7720_DPR , ( __u8 ) d ) ;
2010-04-16 01:01:33 +04:00
parport_epilogue ( pp ) ;
}
static unsigned char parport_mos7715_read_data ( struct parport * pp )
{
struct mos7715_parport * mos_parport = pp - > private_data ;
unsigned char d ;
2012-05-04 03:44:31 +04:00
2010-04-16 01:01:33 +04:00
if ( parport_prologue ( pp ) < 0 )
return 0 ;
2015-06-23 16:29:40 +03:00
read_mos_reg ( mos_parport - > serial , dummy , MOS7720_DPR , & d ) ;
2010-04-16 01:01:33 +04:00
parport_epilogue ( pp ) ;
return d ;
}
static void parport_mos7715_write_control ( struct parport * pp , unsigned char d )
{
struct mos7715_parport * mos_parport = pp - > private_data ;
__u8 data ;
2012-05-04 03:44:31 +04:00
2010-04-16 01:01:33 +04:00
if ( parport_prologue ( pp ) < 0 )
return ;
data = ( ( __u8 ) d & 0x0f ) | ( mos_parport - > shadowDCR & 0xf0 ) ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( mos_parport - > serial , dummy , MOS7720_DCR , data ) ;
2010-04-16 01:01:33 +04:00
mos_parport - > shadowDCR = data ;
parport_epilogue ( pp ) ;
}
static unsigned char parport_mos7715_read_control ( struct parport * pp )
{
2018-01-17 14:54:27 +03:00
struct mos7715_parport * mos_parport ;
2010-04-16 01:01:33 +04:00
__u8 dcr ;
2012-05-04 03:44:31 +04:00
2010-04-16 01:01:33 +04:00
spin_lock ( & release_lock ) ;
mos_parport = pp - > private_data ;
if ( unlikely ( mos_parport = = NULL ) ) {
spin_unlock ( & release_lock ) ;
return 0 ;
}
dcr = mos_parport - > shadowDCR & 0x0f ;
spin_unlock ( & release_lock ) ;
return dcr ;
}
static unsigned char parport_mos7715_frob_control ( struct parport * pp ,
unsigned char mask ,
unsigned char val )
{
struct mos7715_parport * mos_parport = pp - > private_data ;
__u8 dcr ;
2012-05-04 03:44:31 +04:00
2010-04-16 01:01:33 +04:00
mask & = 0x0f ;
val & = 0x0f ;
if ( parport_prologue ( pp ) < 0 )
return 0 ;
mos_parport - > shadowDCR = ( mos_parport - > shadowDCR & ( ~ mask ) ) ^ val ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( mos_parport - > serial , dummy , MOS7720_DCR ,
mos_parport - > shadowDCR ) ;
2010-04-16 01:01:33 +04:00
dcr = mos_parport - > shadowDCR & 0x0f ;
parport_epilogue ( pp ) ;
return dcr ;
}
static unsigned char parport_mos7715_read_status ( struct parport * pp )
{
unsigned char status ;
2018-01-17 14:54:27 +03:00
struct mos7715_parport * mos_parport ;
2012-05-04 03:44:31 +04:00
2010-04-16 01:01:33 +04:00
spin_lock ( & release_lock ) ;
mos_parport = pp - > private_data ;
if ( unlikely ( mos_parport = = NULL ) ) { /* release called */
spin_unlock ( & release_lock ) ;
return 0 ;
}
status = atomic_read ( & mos_parport - > shadowDSR ) & 0xf8 ;
spin_unlock ( & release_lock ) ;
return status ;
}
static void parport_mos7715_enable_irq ( struct parport * pp )
{
}
2012-05-04 03:44:31 +04:00
2010-04-16 01:01:33 +04:00
static void parport_mos7715_disable_irq ( struct parport * pp )
{
}
static void parport_mos7715_data_forward ( struct parport * pp )
{
struct mos7715_parport * mos_parport = pp - > private_data ;
2012-05-04 03:44:31 +04:00
2010-04-16 01:01:33 +04:00
if ( parport_prologue ( pp ) < 0 )
return ;
mos7715_change_mode ( mos_parport , PS2 ) ;
mos_parport - > shadowDCR & = ~ 0x20 ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( mos_parport - > serial , dummy , MOS7720_DCR ,
mos_parport - > shadowDCR ) ;
2010-04-16 01:01:33 +04:00
parport_epilogue ( pp ) ;
}
static void parport_mos7715_data_reverse ( struct parport * pp )
{
struct mos7715_parport * mos_parport = pp - > private_data ;
2012-05-04 03:44:31 +04:00
2010-04-16 01:01:33 +04:00
if ( parport_prologue ( pp ) < 0 )
return ;
mos7715_change_mode ( mos_parport , PS2 ) ;
mos_parport - > shadowDCR | = 0x20 ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( mos_parport - > serial , dummy , MOS7720_DCR ,
mos_parport - > shadowDCR ) ;
2010-04-16 01:01:33 +04:00
parport_epilogue ( pp ) ;
}
static void parport_mos7715_init_state ( struct pardevice * dev ,
struct parport_state * s )
{
s - > u . pc . ctr = DCR_INIT_VAL ;
s - > u . pc . ecr = ECR_INIT_VAL ;
}
/* N.B. Parport core code requires that this function not block */
static void parport_mos7715_save_state ( struct parport * pp ,
struct parport_state * s )
{
struct mos7715_parport * mos_parport ;
2012-05-04 03:44:31 +04:00
2010-04-16 01:01:33 +04:00
spin_lock ( & release_lock ) ;
mos_parport = pp - > private_data ;
if ( unlikely ( mos_parport = = NULL ) ) { /* release called */
spin_unlock ( & release_lock ) ;
return ;
}
s - > u . pc . ctr = mos_parport - > shadowDCR ;
s - > u . pc . ecr = mos_parport - > shadowECR ;
spin_unlock ( & release_lock ) ;
}
/* N.B. Parport core code requires that this function not block */
static void parport_mos7715_restore_state ( struct parport * pp ,
struct parport_state * s )
{
struct mos7715_parport * mos_parport ;
2012-05-04 03:44:31 +04:00
2010-04-16 01:01:33 +04:00
spin_lock ( & release_lock ) ;
mos_parport = pp - > private_data ;
if ( unlikely ( mos_parport = = NULL ) ) { /* release called */
spin_unlock ( & release_lock ) ;
return ;
}
2015-06-23 16:29:40 +03:00
write_parport_reg_nonblock ( mos_parport , MOS7720_DCR ,
mos_parport - > shadowDCR ) ;
write_parport_reg_nonblock ( mos_parport , MOS7720_ECR ,
mos_parport - > shadowECR ) ;
2010-04-16 01:01:33 +04:00
spin_unlock ( & release_lock ) ;
}
static size_t parport_mos7715_write_compat ( struct parport * pp ,
const void * buffer ,
size_t len , int flags )
{
int retval ;
struct mos7715_parport * mos_parport = pp - > private_data ;
int actual_len ;
2012-05-04 03:44:31 +04:00
2010-04-16 01:01:33 +04:00
if ( parport_prologue ( pp ) < 0 )
return 0 ;
mos7715_change_mode ( mos_parport , PPF ) ;
retval = usb_bulk_msg ( mos_parport - > serial - > dev ,
usb_sndbulkpipe ( mos_parport - > serial - > dev , 2 ) ,
( void * ) buffer , len , & actual_len ,
MOS_WDR_TIMEOUT ) ;
parport_epilogue ( pp ) ;
if ( retval ) {
dev_err ( & mos_parport - > serial - > dev - > dev ,
2014-03-12 22:09:42 +04:00
" mos7720: usb_bulk_msg() failed: %d \n " , retval ) ;
2010-04-16 01:01:33 +04:00
return 0 ;
}
return actual_len ;
}
static struct parport_operations parport_mos7715_ops = {
. owner = THIS_MODULE ,
. write_data = parport_mos7715_write_data ,
. read_data = parport_mos7715_read_data ,
. write_control = parport_mos7715_write_control ,
. read_control = parport_mos7715_read_control ,
. frob_control = parport_mos7715_frob_control ,
. read_status = parport_mos7715_read_status ,
. enable_irq = parport_mos7715_enable_irq ,
. disable_irq = parport_mos7715_disable_irq ,
. data_forward = parport_mos7715_data_forward ,
. data_reverse = parport_mos7715_data_reverse ,
. init_state = parport_mos7715_init_state ,
. save_state = parport_mos7715_save_state ,
. restore_state = parport_mos7715_restore_state ,
. compat_write_data = parport_mos7715_write_compat ,
. nibble_read_data = parport_ieee1284_read_nibble ,
. byte_read_data = parport_ieee1284_read_byte ,
} ;
/*
* Allocate and initialize parallel port control struct , initialize
* the parallel port hardware device , and register with the parport subsystem .
*/
static int mos7715_parport_init ( struct usb_serial * serial )
{
struct mos7715_parport * mos_parport ;
/* allocate and initialize parallel port control struct */
mos_parport = kzalloc ( sizeof ( struct mos7715_parport ) , GFP_KERNEL ) ;
2013-12-29 22:22:56 +04:00
if ( ! mos_parport )
2010-04-16 01:01:33 +04:00
return - ENOMEM ;
2013-12-29 22:22:56 +04:00
2010-04-16 01:01:33 +04:00
mos_parport - > msg_pending = false ;
kref_init ( & mos_parport - > ref_count ) ;
spin_lock_init ( & mos_parport - > listlock ) ;
INIT_LIST_HEAD ( & mos_parport - > active_urbs ) ;
INIT_LIST_HEAD ( & mos_parport - > deferred_urbs ) ;
usb_set_serial_data ( serial , mos_parport ) ; /* hijack private pointer */
mos_parport - > serial = serial ;
tasklet_init ( & mos_parport - > urb_tasklet , send_deferred_urbs ,
( unsigned long ) mos_parport ) ;
init_completion ( & mos_parport - > syncmsg_compl ) ;
/* cycle parallel port reset bit */
2015-06-23 16:29:40 +03:00
write_mos_reg ( mos_parport - > serial , dummy , MOS7720_PP_REG , ( __u8 ) 0x80 ) ;
write_mos_reg ( mos_parport - > serial , dummy , MOS7720_PP_REG , ( __u8 ) 0x00 ) ;
2010-04-16 01:01:33 +04:00
/* initialize device registers */
mos_parport - > shadowDCR = DCR_INIT_VAL ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( mos_parport - > serial , dummy , MOS7720_DCR ,
mos_parport - > shadowDCR ) ;
2010-04-16 01:01:33 +04:00
mos_parport - > shadowECR = ECR_INIT_VAL ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( mos_parport - > serial , dummy , MOS7720_ECR ,
mos_parport - > shadowECR ) ;
2010-04-16 01:01:33 +04:00
/* register with parport core */
mos_parport - > pp = parport_register_port ( 0 , PARPORT_IRQ_NONE ,
PARPORT_DMA_NONE ,
& parport_mos7715_ops ) ;
if ( mos_parport - > pp = = NULL ) {
dev_err ( & serial - > interface - > dev ,
" Could not register parport \n " ) ;
kref_put ( & mos_parport - > ref_count , destroy_mos_parport ) ;
return - EIO ;
}
mos_parport - > pp - > private_data = mos_parport ;
mos_parport - > pp - > modes = PARPORT_MODE_COMPAT | PARPORT_MODE_PCSPP ;
mos_parport - > pp - > dev = & serial - > interface - > dev ;
parport_announce_port ( mos_parport - > pp ) ;
return 0 ;
}
# endif /* CONFIG_USB_SERIAL_MOS7715_PARPORT */
2002-04-09 23:14:34 +04:00
/*
* mos7720_interrupt_callback
* this is the callback function for when we have received data on the
* interrupt endpoint .
*/
static void mos7720_interrupt_callback ( struct urb * urb )
{
int result ;
int length ;
2007-06-16 02:44:13 +04:00
int status = urb - > status ;
2012-09-15 02:08:33 +04:00
struct device * dev = & urb - > dev - > dev ;
2007-03-19 15:58:29 +03:00
__u8 * data ;
2002-04-09 23:14:34 +04:00
__u8 sp1 ;
__u8 sp2 ;
2007-06-16 02:44:13 +04:00
switch ( status ) {
2002-04-09 23:14:34 +04:00
case 0 :
/* success */
break ;
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
/* this urb is terminated, clean up */
2012-09-15 02:08:33 +04:00
dev_dbg ( dev , " %s - urb shutting down with status: %d \n " , __func__ , status ) ;
2002-04-09 23:14:34 +04:00
return ;
default :
2012-09-15 02:08:33 +04:00
dev_dbg ( dev , " %s - nonzero urb status received: %d \n " , __func__ , status ) ;
2002-04-09 23:14:34 +04:00
goto exit ;
}
length = urb - > actual_length ;
data = urb - > transfer_buffer ;
/* Moschip get 4 bytes
* Byte 1 IIR Port 1 ( port . number is 0 )
* Byte 2 IIR Port 2 ( port . number is 1 )
* Byte 3 - - - - - - - - - - - - - -
* Byte 4 FIFO status for both */
2007-03-19 15:58:29 +03:00
/* the above description is inverted
* oneukum 2007 - 03 - 14 */
if ( unlikely ( length ! = 4 ) ) {
2012-09-15 02:08:33 +04:00
dev_dbg ( dev , " Wrong data !!! \n " ) ;
2002-04-09 23:14:34 +04:00
return ;
}
2007-03-19 15:58:29 +03:00
sp1 = data [ 3 ] ;
sp2 = data [ 2 ] ;
2002-04-09 23:14:34 +04:00
2007-03-19 15:58:29 +03:00
if ( ( sp1 | sp2 ) & 0x01 ) {
2002-04-09 23:14:34 +04:00
/* No Interrupt Pending in both the ports */
2012-09-15 02:08:33 +04:00
dev_dbg ( dev , " No Interrupt !!! \n " ) ;
2002-04-09 23:14:34 +04:00
} else {
switch ( sp1 & 0x0f ) {
case SERIAL_IIR_RLS :
2012-09-15 02:08:33 +04:00
dev_dbg ( dev , " Serial Port 1: Receiver status error or address bit detected in 9-bit mode \n " ) ;
2002-04-09 23:14:34 +04:00
break ;
case SERIAL_IIR_CTI :
2012-09-15 02:08:33 +04:00
dev_dbg ( dev , " Serial Port 1: Receiver time out \n " ) ;
2002-04-09 23:14:34 +04:00
break ;
case SERIAL_IIR_MS :
2012-09-15 02:08:33 +04:00
/* dev_dbg(dev, "Serial Port 1: Modem status change\n"); */
2002-04-09 23:14:34 +04:00
break ;
}
switch ( sp2 & 0x0f ) {
case SERIAL_IIR_RLS :
2012-09-15 02:08:33 +04:00
dev_dbg ( dev , " Serial Port 2: Receiver status error or address bit detected in 9-bit mode \n " ) ;
2002-04-09 23:14:34 +04:00
break ;
case SERIAL_IIR_CTI :
2012-09-15 02:08:33 +04:00
dev_dbg ( dev , " Serial Port 2: Receiver time out \n " ) ;
2002-04-09 23:14:34 +04:00
break ;
case SERIAL_IIR_MS :
2012-09-15 02:08:33 +04:00
/* dev_dbg(dev, "Serial Port 2: Modem status change\n"); */
2002-04-09 23:14:34 +04:00
break ;
}
}
exit :
result = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( result )
2012-09-15 02:08:33 +04:00
dev_err ( dev , " %s - Error %d submitting control urb \n " , __func__ , result ) ;
2002-04-09 23:14:34 +04:00
}
2010-01-26 20:12:12 +03:00
/*
* mos7715_interrupt_callback
* this is the 7715 ' s callback function for when we have received data on
* the interrupt endpoint .
*/
static void mos7715_interrupt_callback ( struct urb * urb )
{
int result ;
int length ;
int status = urb - > status ;
2012-09-15 02:08:33 +04:00
struct device * dev = & urb - > dev - > dev ;
2010-01-26 20:12:12 +03:00
__u8 * data ;
__u8 iir ;
switch ( status ) {
case 0 :
/* success */
break ;
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
2010-04-16 01:01:33 +04:00
case - ENODEV :
2010-01-26 20:12:12 +03:00
/* this urb is terminated, clean up */
2012-09-15 02:08:33 +04:00
dev_dbg ( dev , " %s - urb shutting down with status: %d \n " , __func__ , status ) ;
2010-01-26 20:12:12 +03:00
return ;
default :
2012-09-15 02:08:33 +04:00
dev_dbg ( dev , " %s - nonzero urb status received: %d \n " , __func__ , status ) ;
2010-01-26 20:12:12 +03:00
goto exit ;
}
length = urb - > actual_length ;
data = urb - > transfer_buffer ;
/* Structure of data from 7715 device:
* Byte 1 : IIR serial Port
* Byte 2 : unused
* Byte 2 : DSR parallel port
* Byte 4 : FIFO status for both */
if ( unlikely ( length ! = 4 ) ) {
2012-09-15 02:08:33 +04:00
dev_dbg ( dev , " Wrong data !!! \n " ) ;
2010-01-26 20:12:12 +03:00
return ;
}
iir = data [ 0 ] ;
if ( ! ( iir & 0x01 ) ) { /* serial port interrupt pending */
switch ( iir & 0x0f ) {
case SERIAL_IIR_RLS :
2014-03-12 22:09:42 +04:00
dev_dbg ( dev , " Serial Port: Receiver status error or address bit detected in 9-bit mode \n " ) ;
2010-01-26 20:12:12 +03:00
break ;
case SERIAL_IIR_CTI :
2012-09-15 02:08:33 +04:00
dev_dbg ( dev , " Serial Port: Receiver time out \n " ) ;
2010-01-26 20:12:12 +03:00
break ;
case SERIAL_IIR_MS :
2012-09-15 02:08:33 +04:00
/* dev_dbg(dev, "Serial Port: Modem status change\n"); */
2010-01-26 20:12:12 +03:00
break ;
}
}
2010-04-16 01:01:33 +04:00
# ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
{ /* update local copy of DSR reg */
struct usb_serial_port * port = urb - > context ;
struct mos7715_parport * mos_parport = port - > serial - > private ;
if ( unlikely ( mos_parport = = NULL ) )
return ;
atomic_set ( & mos_parport - > shadowDSR , data [ 2 ] ) ;
}
# endif
2010-01-26 20:12:12 +03:00
exit :
result = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( result )
2012-09-15 02:08:33 +04:00
dev_err ( dev , " %s - Error %d submitting control urb \n " , __func__ , result ) ;
2010-01-26 20:12:12 +03:00
}
2002-04-09 23:14:34 +04:00
/*
* mos7720_bulk_in_callback
* this is the callback function for when we have received data on the
* bulk in endpoint .
*/
static void mos7720_bulk_in_callback ( struct urb * urb )
{
2007-06-16 02:44:13 +04:00
int retval ;
2002-04-09 23:14:34 +04:00
unsigned char * data ;
struct usb_serial_port * port ;
2007-06-16 02:44:13 +04:00
int status = urb - > status ;
2002-04-09 23:14:34 +04:00
2007-06-16 02:44:13 +04:00
if ( status ) {
2012-09-15 02:08:33 +04:00
dev_dbg ( & urb - > dev - > dev , " nonzero read bulk status received: %d \n " , status ) ;
2002-04-09 23:14:34 +04:00
return ;
}
2010-04-16 01:01:33 +04:00
port = urb - > context ;
2002-04-09 23:14:34 +04:00
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " Entering...%s \n " , __func__ ) ;
2002-04-09 23:14:34 +04:00
data = urb - > transfer_buffer ;
2013-01-03 18:53:06 +04:00
if ( urb - > actual_length ) {
2013-01-03 18:53:04 +04:00
tty_insert_flip_string ( & port - > port , data , urb - > actual_length ) ;
2013-01-03 18:53:06 +04:00
tty_flip_buffer_push ( & port - > port ) ;
2002-04-09 23:14:34 +04:00
}
if ( port - > read_urb - > status ! = - EINPROGRESS ) {
2007-06-16 02:44:13 +04:00
retval = usb_submit_urb ( port - > read_urb , GFP_ATOMIC ) ;
if ( retval )
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " usb_submit_urb(read bulk) failed, retval = %d \n " , retval ) ;
2002-04-09 23:14:34 +04:00
}
}
/*
* mos7720_bulk_out_data_callback
* this is the callback function for when we have finished sending serial
* data on the bulk out endpoint .
*/
static void mos7720_bulk_out_data_callback ( struct urb * urb )
{
struct moschip_port * mos7720_port ;
2007-06-16 02:44:13 +04:00
int status = urb - > status ;
2002-04-09 23:14:34 +04:00
2007-06-16 02:44:13 +04:00
if ( status ) {
2012-09-15 02:08:33 +04:00
dev_dbg ( & urb - > dev - > dev , " nonzero write bulk status received:%d \n " , status ) ;
2002-04-09 23:14:34 +04:00
return ;
}
mos7720_port = urb - > context ;
if ( ! mos7720_port ) {
2012-09-15 02:08:33 +04:00
dev_dbg ( & urb - > dev - > dev , " NULL mos7720_port pointer \n " ) ;
2002-04-09 23:14:34 +04:00
return ;
}
2013-03-07 16:12:29 +04:00
if ( mos7720_port - > open )
tty_port_tty_wakeup ( & mos7720_port - > port - > port ) ;
2002-04-09 23:14:34 +04:00
}
2017-03-16 19:13:30 +03:00
static int mos77xx_calc_num_ports ( struct usb_serial * serial ,
struct usb_serial_endpoints * epds )
2010-01-26 20:12:12 +03:00
{
u16 product = le16_to_cpu ( serial - > dev - > descriptor . idProduct ) ;
2017-03-16 19:13:45 +03:00
if ( product = = MOSCHIP_DEVICE_ID_7715 ) {
/*
* The 7715 uses the first bulk in / out endpoint pair for the
* parallel port , and the second for the serial port . We swap
* the endpoint descriptors here so that the the first and
* only registered port structure uses the serial - port
* endpoints .
*/
swap ( epds - > bulk_in [ 0 ] , epds - > bulk_in [ 1 ] ) ;
swap ( epds - > bulk_out [ 0 ] , epds - > bulk_out [ 1 ] ) ;
2010-01-26 20:12:12 +03:00
return 1 ;
2017-03-16 19:13:45 +03:00
}
2010-01-26 20:12:12 +03:00
return 2 ;
}
2009-09-20 00:13:26 +04:00
static int mos7720_open ( struct tty_struct * tty , struct usb_serial_port * port )
2002-04-09 23:14:34 +04:00
{
struct usb_serial * serial ;
struct urb * urb ;
struct moschip_port * mos7720_port ;
int response ;
int port_number ;
2010-04-16 01:02:09 +04:00
__u8 data ;
2007-03-14 17:22:25 +03:00
int allocated_urbs = 0 ;
2002-04-09 23:14:34 +04:00
int j ;
serial = port - > serial ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL )
return - ENODEV ;
usb_clear_halt ( serial - > dev , port - > write_urb - > pipe ) ;
usb_clear_halt ( serial - > dev , port - > read_urb - > pipe ) ;
/* Initialising the write urb pool */
for ( j = 0 ; j < NUM_URBS ; + + j ) {
2008-07-22 14:16:21 +04:00
urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
2002-04-09 23:14:34 +04:00
mos7720_port - > write_urb_pool [ j ] = urb ;
2013-12-29 22:22:56 +04:00
if ( ! urb )
2002-04-09 23:14:34 +04:00
continue ;
urb - > transfer_buffer = kmalloc ( URB_TRANSFER_BUFFER_SIZE ,
GFP_KERNEL ) ;
if ( ! urb - > transfer_buffer ) {
2007-03-14 17:22:25 +03:00
usb_free_urb ( mos7720_port - > write_urb_pool [ j ] ) ;
mos7720_port - > write_urb_pool [ j ] = NULL ;
2002-04-09 23:14:34 +04:00
continue ;
}
2007-03-14 17:22:25 +03:00
allocated_urbs + + ;
2002-04-09 23:14:34 +04:00
}
2007-03-14 17:22:25 +03:00
if ( ! allocated_urbs )
return - ENOMEM ;
2002-04-09 23:14:34 +04:00
/* Initialize MCS7720 -- Write Init values to corresponding Registers
*
* Register Index
2015-06-23 16:29:40 +03:00
* 0 : MOS7720_THR / MOS7720_RHR
* 1 : MOS7720_IER
* 2 : MOS7720_FCR
* 3 : MOS7720_LCR
* 4 : MOS7720_MCR
* 5 : MOS7720_LSR
* 6 : MOS7720_MSR
* 7 : MOS7720_SPR
2002-04-09 23:14:34 +04:00
*
* 0x08 : SP1 / 2 Control Reg
*/
2013-06-06 21:32:00 +04:00
port_number = port - > port_number ;
2015-06-23 16:29:40 +03:00
read_mos_reg ( serial , port_number , MOS7720_LSR , & data ) ;
2010-04-16 01:02:09 +04:00
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " SS::%p LSR:%x \n " , mos7720_port , data ) ;
2002-04-09 23:14:34 +04:00
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , dummy , MOS7720_SP1_REG , 0x02 ) ;
write_mos_reg ( serial , dummy , MOS7720_SP2_REG , 0x02 ) ;
2002-04-09 23:14:34 +04:00
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_IER , 0x00 ) ;
write_mos_reg ( serial , port_number , MOS7720_FCR , 0x00 ) ;
2002-04-09 23:14:34 +04:00
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_FCR , 0xcf ) ;
2010-04-16 01:02:09 +04:00
mos7720_port - > shadowLCR = 0x03 ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_LCR ,
mos7720_port - > shadowLCR ) ;
2010-04-16 01:02:09 +04:00
mos7720_port - > shadowMCR = 0x0b ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_MCR ,
mos7720_port - > shadowMCR ) ;
2002-04-09 23:14:34 +04:00
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_SP_CONTROL_REG , 0x00 ) ;
read_mos_reg ( serial , dummy , MOS7720_SP_CONTROL_REG , & data ) ;
2013-06-06 21:32:00 +04:00
data = data | ( port - > port_number + 1 ) ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , dummy , MOS7720_SP_CONTROL_REG , data ) ;
2010-04-16 01:02:09 +04:00
mos7720_port - > shadowLCR = 0x83 ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_LCR ,
mos7720_port - > shadowLCR ) ;
write_mos_reg ( serial , port_number , MOS7720_THR , 0x0c ) ;
write_mos_reg ( serial , port_number , MOS7720_IER , 0x00 ) ;
2010-04-16 01:02:09 +04:00
mos7720_port - > shadowLCR = 0x03 ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_LCR ,
mos7720_port - > shadowLCR ) ;
write_mos_reg ( serial , port_number , MOS7720_IER , 0x0c ) ;
2002-04-09 23:14:34 +04:00
response = usb_submit_urb ( port - > read_urb , GFP_KERNEL ) ;
if ( response )
2008-07-22 14:16:21 +04:00
dev_err ( & port - > dev , " %s - Error %d submitting read urb \n " ,
__func__ , response ) ;
2002-04-09 23:14:34 +04:00
/* initialize our port settings */
mos7720_port - > shadowMCR = UART_MCR_OUT2 ; /* Must set to enable ints! */
/* send a open port command */
mos7720_port - > open = 1 ;
return 0 ;
}
/*
* mos7720_chars_in_buffer
* this function is called by the tty driver when it wants to know how many
* bytes of data we currently have outstanding in the port ( data that has
* been written , but hasn ' t made it out the port yet )
* If successful , we return the number of bytes left to be written in the
* system ,
* Otherwise we return a negative error number .
*/
2008-07-22 14:09:07 +04:00
static int mos7720_chars_in_buffer ( struct tty_struct * tty )
2002-04-09 23:14:34 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2002-04-09 23:14:34 +04:00
int i ;
int chars = 0 ;
struct moschip_port * mos7720_port ;
mos7720_port = usb_get_serial_port_data ( port ) ;
2012-09-15 02:08:33 +04:00
if ( mos7720_port = = NULL )
2009-07-20 19:05:27 +04:00
return 0 ;
2002-04-09 23:14:34 +04:00
for ( i = 0 ; i < NUM_URBS ; + + i ) {
2008-07-22 14:16:21 +04:00
if ( mos7720_port - > write_urb_pool [ i ] & &
mos7720_port - > write_urb_pool [ i ] - > status = = - EINPROGRESS )
2002-04-09 23:14:34 +04:00
chars + = URB_TRANSFER_BUFFER_SIZE ;
}
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s - returns %d \n " , __func__ , chars ) ;
2002-04-09 23:14:34 +04:00
return chars ;
}
2009-06-11 15:26:29 +04:00
static void mos7720_close ( struct usb_serial_port * port )
2002-04-09 23:14:34 +04:00
{
struct usb_serial * serial ;
struct moschip_port * mos7720_port ;
int j ;
serial = port - > serial ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL )
return ;
for ( j = 0 ; j < NUM_URBS ; + + j )
usb_kill_urb ( mos7720_port - > write_urb_pool [ j ] ) ;
/* Freeing Write URBs */
for ( j = 0 ; j < NUM_URBS ; + + j ) {
if ( mos7720_port - > write_urb_pool [ j ] ) {
kfree ( mos7720_port - > write_urb_pool [ j ] - > transfer_buffer ) ;
usb_free_urb ( mos7720_port - > write_urb_pool [ j ] ) ;
}
}
/* While closing port, shutdown all bulk read, write *
2008-01-16 19:18:52 +03:00
* and interrupt read if they exists , otherwise nop */
usb_kill_urb ( port - > write_urb ) ;
usb_kill_urb ( port - > read_urb ) ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port - > port_number , MOS7720_MCR , 0x00 ) ;
write_mos_reg ( serial , port - > port_number , MOS7720_IER , 0x00 ) ;
2013-03-21 15:37:41 +04:00
2002-04-09 23:14:34 +04:00
mos7720_port - > open = 0 ;
}
2008-07-22 14:09:07 +04:00
static void mos7720_break ( struct tty_struct * tty , int break_state )
2002-04-09 23:14:34 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2008-07-22 14:16:21 +04:00
unsigned char data ;
2002-04-09 23:14:34 +04:00
struct usb_serial * serial ;
struct moschip_port * mos7720_port ;
serial = port - > serial ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL )
return ;
if ( break_state = = - 1 )
data = mos7720_port - > shadowLCR | UART_LCR_SBC ;
else
data = mos7720_port - > shadowLCR & ~ UART_LCR_SBC ;
mos7720_port - > shadowLCR = data ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port - > port_number , MOS7720_LCR ,
mos7720_port - > shadowLCR ) ;
2002-04-09 23:14:34 +04:00
}
/*
* mos7720_write_room
* this function is called by the tty driver when it wants to know how many
* bytes of data we can accept for a specific port .
* If successful , we return the amount of room that we have for this port
* Otherwise we return a negative error number .
*/
2008-07-22 14:09:07 +04:00
static int mos7720_write_room ( struct tty_struct * tty )
2002-04-09 23:14:34 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2002-04-09 23:14:34 +04:00
struct moschip_port * mos7720_port ;
int room = 0 ;
int i ;
mos7720_port = usb_get_serial_port_data ( port ) ;
2012-09-15 02:08:33 +04:00
if ( mos7720_port = = NULL )
2002-04-09 23:14:34 +04:00
return - ENODEV ;
2008-04-08 20:16:06 +04:00
/* FIXME: Locking */
2002-04-09 23:14:34 +04:00
for ( i = 0 ; i < NUM_URBS ; + + i ) {
2008-07-22 14:16:21 +04:00
if ( mos7720_port - > write_urb_pool [ i ] & &
mos7720_port - > write_urb_pool [ i ] - > status ! = - EINPROGRESS )
2002-04-09 23:14:34 +04:00
room + = URB_TRANSFER_BUFFER_SIZE ;
}
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s - returns %d \n " , __func__ , room ) ;
2002-04-09 23:14:34 +04:00
return room ;
}
2008-07-22 14:09:07 +04:00
static int mos7720_write ( struct tty_struct * tty , struct usb_serial_port * port ,
const unsigned char * data , int count )
2002-04-09 23:14:34 +04:00
{
int status ;
int i ;
int bytes_sent = 0 ;
int transfer_size ;
struct moschip_port * mos7720_port ;
struct usb_serial * serial ;
struct urb * urb ;
const unsigned char * current_position = data ;
serial = port - > serial ;
mos7720_port = usb_get_serial_port_data ( port ) ;
2012-09-15 02:08:33 +04:00
if ( mos7720_port = = NULL )
2002-04-09 23:14:34 +04:00
return - ENODEV ;
/* try to find a free urb in the list */
urb = NULL ;
for ( i = 0 ; i < NUM_URBS ; + + i ) {
2008-07-22 14:16:21 +04:00
if ( mos7720_port - > write_urb_pool [ i ] & &
mos7720_port - > write_urb_pool [ i ] - > status ! = - EINPROGRESS ) {
2002-04-09 23:14:34 +04:00
urb = mos7720_port - > write_urb_pool [ i ] ;
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " URB:%d \n " , i ) ;
2002-04-09 23:14:34 +04:00
break ;
}
}
if ( urb = = NULL ) {
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s - no more free urbs \n " , __func__ ) ;
2002-04-09 23:14:34 +04:00
goto exit ;
}
if ( urb - > transfer_buffer = = NULL ) {
urb - > transfer_buffer = kmalloc ( URB_TRANSFER_BUFFER_SIZE ,
2016-08-12 01:05:08 +03:00
GFP_ATOMIC ) ;
2013-12-29 22:22:56 +04:00
if ( ! urb - > transfer_buffer )
2002-04-09 23:14:34 +04:00
goto exit ;
}
2008-07-22 14:16:21 +04:00
transfer_size = min ( count , URB_TRANSFER_BUFFER_SIZE ) ;
2002-04-09 23:14:34 +04:00
memcpy ( urb - > transfer_buffer , current_position , transfer_size ) ;
2012-09-18 12:58:57 +04:00
usb_serial_debug_data ( & port - > dev , __func__ , transfer_size ,
2002-04-09 23:14:34 +04:00
urb - > transfer_buffer ) ;
/* fill urb with data and submit */
usb_fill_bulk_urb ( urb , serial - > dev ,
usb_sndbulkpipe ( serial - > dev ,
2008-07-22 14:16:21 +04:00
port - > bulk_out_endpointAddress ) ,
2002-04-09 23:14:34 +04:00
urb - > transfer_buffer , transfer_size ,
mos7720_bulk_out_data_callback , mos7720_port ) ;
/* send it down the pipe */
2008-07-22 14:16:21 +04:00
status = usb_submit_urb ( urb , GFP_ATOMIC ) ;
2002-04-09 23:14:34 +04:00
if ( status ) {
2012-02-10 16:20:51 +04:00
dev_err_console ( port , " %s - usb_submit_urb(write bulk) failed "
2008-08-21 03:56:34 +04:00
" with status = %d \n " , __func__ , status ) ;
2002-04-09 23:14:34 +04:00
bytes_sent = status ;
goto exit ;
}
bytes_sent = transfer_size ;
exit :
return bytes_sent ;
}
2008-07-22 14:09:07 +04:00
static void mos7720_throttle ( struct tty_struct * tty )
2002-04-09 23:14:34 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2002-04-09 23:14:34 +04:00
struct moschip_port * mos7720_port ;
int status ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL )
return ;
if ( ! mos7720_port - > open ) {
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s - port not opened \n " , __func__ ) ;
2002-04-09 23:14:34 +04:00
return ;
}
/* if we are implementing XON/XOFF, send the stop character */
if ( I_IXOFF ( tty ) ) {
unsigned char stop_char = STOP_CHAR ( tty ) ;
2008-07-22 14:09:07 +04:00
status = mos7720_write ( tty , port , & stop_char , 1 ) ;
2002-04-09 23:14:34 +04:00
if ( status < = 0 )
return ;
}
/* if we are implementing RTS/CTS, toggle that line */
2016-01-11 07:36:15 +03:00
if ( C_CRTSCTS ( tty ) ) {
2002-04-09 23:14:34 +04:00
mos7720_port - > shadowMCR & = ~ UART_MCR_RTS ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( port - > serial , port - > port_number , MOS7720_MCR ,
2013-06-06 21:32:00 +04:00
mos7720_port - > shadowMCR ) ;
2002-04-09 23:14:34 +04:00
}
}
2008-07-22 14:09:07 +04:00
static void mos7720_unthrottle ( struct tty_struct * tty )
2002-04-09 23:14:34 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2002-04-09 23:14:34 +04:00
struct moschip_port * mos7720_port = usb_get_serial_port_data ( port ) ;
2008-07-22 14:09:07 +04:00
int status ;
2002-04-09 23:14:34 +04:00
if ( mos7720_port = = NULL )
return ;
if ( ! mos7720_port - > open ) {
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s - port not opened \n " , __func__ ) ;
2002-04-09 23:14:34 +04:00
return ;
}
/* if we are implementing XON/XOFF, send the start character */
if ( I_IXOFF ( tty ) ) {
unsigned char start_char = START_CHAR ( tty ) ;
2008-07-22 14:09:07 +04:00
status = mos7720_write ( tty , port , & start_char , 1 ) ;
2002-04-09 23:14:34 +04:00
if ( status < = 0 )
return ;
}
/* if we are implementing RTS/CTS, toggle that line */
2016-01-11 07:36:15 +03:00
if ( C_CRTSCTS ( tty ) ) {
2002-04-09 23:14:34 +04:00
mos7720_port - > shadowMCR | = UART_MCR_RTS ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( port - > serial , port - > port_number , MOS7720_MCR ,
2013-06-06 21:32:00 +04:00
mos7720_port - > shadowMCR ) ;
2002-04-09 23:14:34 +04:00
}
}
2010-04-16 01:01:33 +04:00
/* FIXME: this function does not work */
2002-04-09 23:14:34 +04:00
static int set_higher_rates ( struct moschip_port * mos7720_port ,
unsigned int baud )
{
struct usb_serial_port * port ;
struct usb_serial * serial ;
int port_number ;
2010-04-16 01:02:09 +04:00
enum mos_regs sp_reg ;
2002-04-09 23:14:34 +04:00
if ( mos7720_port = = NULL )
return - EINVAL ;
port = mos7720_port - > port ;
serial = port - > serial ;
2008-07-22 14:16:21 +04:00
/***********************************************
* Init Sequence for higher rates
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " Sending Setting Commands .......... \n " ) ;
2013-06-06 21:32:00 +04:00
port_number = port - > port_number ;
2002-04-09 23:14:34 +04:00
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_IER , 0x00 ) ;
write_mos_reg ( serial , port_number , MOS7720_FCR , 0x00 ) ;
write_mos_reg ( serial , port_number , MOS7720_FCR , 0xcf ) ;
2010-04-16 01:02:09 +04:00
mos7720_port - > shadowMCR = 0x0b ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_MCR ,
mos7720_port - > shadowMCR ) ;
write_mos_reg ( serial , dummy , MOS7720_SP_CONTROL_REG , 0x00 ) ;
2002-04-09 23:14:34 +04:00
2008-07-22 14:16:21 +04:00
/***********************************************
* Set for higher rates *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-04-16 01:01:33 +04:00
/* writing baud rate verbatum into uart clock field clearly not right */
2010-04-16 01:02:09 +04:00
if ( port_number = = 0 )
2015-06-23 16:29:40 +03:00
sp_reg = MOS7720_SP1_REG ;
2010-04-16 01:02:09 +04:00
else
2015-06-23 16:29:40 +03:00
sp_reg = MOS7720_SP2_REG ;
2010-04-16 01:02:09 +04:00
write_mos_reg ( serial , dummy , sp_reg , baud * 0x10 ) ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , dummy , MOS7720_SP_CONTROL_REG , 0x03 ) ;
2010-04-16 01:02:09 +04:00
mos7720_port - > shadowMCR = 0x2b ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_MCR ,
mos7720_port - > shadowMCR ) ;
2002-04-09 23:14:34 +04:00
2008-07-22 14:16:21 +04:00
/***********************************************
* Set DLL / DLM
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-04-16 01:02:09 +04:00
mos7720_port - > shadowLCR = mos7720_port - > shadowLCR | UART_LCR_DLAB ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_LCR ,
mos7720_port - > shadowLCR ) ;
write_mos_reg ( serial , port_number , MOS7720_DLL , 0x01 ) ;
write_mos_reg ( serial , port_number , MOS7720_DLM , 0x00 ) ;
2010-04-16 01:02:09 +04:00
mos7720_port - > shadowLCR = mos7720_port - > shadowLCR & ~ UART_LCR_DLAB ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_LCR ,
mos7720_port - > shadowLCR ) ;
2002-04-09 23:14:34 +04:00
return 0 ;
}
/* baud rate information */
2008-07-22 14:16:21 +04:00
struct divisor_table_entry {
2002-04-09 23:14:34 +04:00
__u32 baudrate ;
__u16 divisor ;
} ;
/* Define table of divisors for moschip 7720 hardware *
* These assume a 3.6864 MHz crystal , the standard / 16 , and *
* MCR .7 = 0. */
2017-04-18 15:42:28 +03:00
static const struct divisor_table_entry divisor_table [ ] = {
2002-04-09 23:14:34 +04:00
{ 50 , 2304 } ,
{ 110 , 1047 } , /* 2094.545455 => 230450 => .0217 % over */
{ 134 , 857 } , /* 1713.011152 => 230398.5 => .00065% under */
{ 150 , 768 } ,
{ 300 , 384 } ,
{ 600 , 192 } ,
{ 1200 , 96 } ,
{ 1800 , 64 } ,
{ 2400 , 48 } ,
{ 4800 , 24 } ,
{ 7200 , 16 } ,
{ 9600 , 12 } ,
{ 19200 , 6 } ,
{ 38400 , 3 } ,
{ 57600 , 2 } ,
{ 115200 , 1 } ,
} ;
/*****************************************************************************
* calc_baud_rate_divisor
* this function calculates the proper baud rate divisor for the specified
* baud rate .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-09-15 02:08:33 +04:00
static int calc_baud_rate_divisor ( struct usb_serial_port * port , int baudrate , int * divisor )
2002-04-09 23:14:34 +04:00
{
int i ;
__u16 custom ;
__u16 round1 ;
__u16 round ;
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s - %d \n " , __func__ , baudrate ) ;
2002-04-09 23:14:34 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( divisor_table ) ; i + + ) {
if ( divisor_table [ i ] . baudrate = = baudrate ) {
* divisor = divisor_table [ i ] . divisor ;
return 0 ;
}
}
2008-07-22 14:16:21 +04:00
/* After trying for all the standard baud rates *
* Try calculating the divisor for this baud rate */
2002-04-09 23:14:34 +04:00
if ( baudrate > 75 & & baudrate < 230400 ) {
/* get the divisor */
custom = ( __u16 ) ( 230400L / baudrate ) ;
/* Check for round off */
round1 = ( __u16 ) ( 2304000L / baudrate ) ;
round = ( __u16 ) ( round1 - ( custom * 10 ) ) ;
if ( round > 4 )
custom + + ;
* divisor = custom ;
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " Baud %d = %d \n " , baudrate , custom ) ;
2002-04-09 23:14:34 +04:00
return 0 ;
}
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " Baud calculation Failed... \n " ) ;
2002-04-09 23:14:34 +04:00
return - EINVAL ;
}
/*
* send_cmd_write_baud_rate
* this function sends the proper command to change the baud rate of the
* specified port .
*/
static int send_cmd_write_baud_rate ( struct moschip_port * mos7720_port ,
int baudrate )
{
struct usb_serial_port * port ;
struct usb_serial * serial ;
int divisor ;
int status ;
unsigned char number ;
if ( mos7720_port = = NULL )
return - 1 ;
port = mos7720_port - > port ;
serial = port - > serial ;
2013-06-06 21:32:00 +04:00
number = port - > port_number ;
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s - baud = %d \n " , __func__ , baudrate ) ;
2002-04-09 23:14:34 +04:00
2008-07-22 14:16:21 +04:00
/* Calculate the Divisor */
2012-09-15 02:08:33 +04:00
status = calc_baud_rate_divisor ( port , baudrate , & divisor ) ;
2002-04-09 23:14:34 +04:00
if ( status ) {
2008-08-21 03:56:34 +04:00
dev_err ( & port - > dev , " %s - bad baud rate \n " , __func__ ) ;
2002-04-09 23:14:34 +04:00
return status ;
}
2008-07-22 14:16:21 +04:00
/* Enable access to divisor latch */
2010-04-16 01:02:09 +04:00
mos7720_port - > shadowLCR = mos7720_port - > shadowLCR | UART_LCR_DLAB ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , number , MOS7720_LCR , mos7720_port - > shadowLCR ) ;
2002-04-09 23:14:34 +04:00
/* Write the divisor */
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , number , MOS7720_DLL , ( __u8 ) ( divisor & 0xff ) ) ;
write_mos_reg ( serial , number , MOS7720_DLM ,
( __u8 ) ( ( divisor & 0xff00 ) > > 8 ) ) ;
2002-04-09 23:14:34 +04:00
2008-07-22 14:16:21 +04:00
/* Disable access to divisor latch */
2010-04-16 01:02:09 +04:00
mos7720_port - > shadowLCR = mos7720_port - > shadowLCR & ~ UART_LCR_DLAB ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , number , MOS7720_LCR , mos7720_port - > shadowLCR ) ;
2002-04-09 23:14:34 +04:00
return status ;
}
/*
* change_port_settings
* This routine is called to set the UART on the device to match
* the specified new settings .
*/
2008-07-22 14:09:07 +04:00
static void change_port_settings ( struct tty_struct * tty ,
struct moschip_port * mos7720_port ,
2006-12-08 13:38:45 +03:00
struct ktermios * old_termios )
2002-04-09 23:14:34 +04:00
{
struct usb_serial_port * port ;
struct usb_serial * serial ;
int baud ;
unsigned cflag ;
__u8 lData ;
__u8 lParity ;
__u8 lStop ;
int status ;
int port_number ;
if ( mos7720_port = = NULL )
return ;
port = mos7720_port - > port ;
serial = port - > serial ;
2013-06-06 21:32:00 +04:00
port_number = port - > port_number ;
2002-04-09 23:14:34 +04:00
if ( ! mos7720_port - > open ) {
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s - port not opened \n " , __func__ ) ;
2002-04-09 23:14:34 +04:00
return ;
}
lData = UART_LCR_WLEN8 ;
lStop = 0x00 ; /* 1 stop bit */
lParity = 0x00 ; /* No parity */
2012-07-14 18:31:47 +04:00
cflag = tty - > termios . c_cflag ;
2002-04-09 23:14:34 +04:00
/* Change the number of bits */
switch ( cflag & CSIZE ) {
case CS5 :
lData = UART_LCR_WLEN5 ;
break ;
case CS6 :
lData = UART_LCR_WLEN6 ;
break ;
case CS7 :
lData = UART_LCR_WLEN7 ;
break ;
default :
case CS8 :
lData = UART_LCR_WLEN8 ;
break ;
}
/* Change the Parity bit */
if ( cflag & PARENB ) {
if ( cflag & PARODD ) {
lParity = UART_LCR_PARITY ;
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s - parity = odd \n " , __func__ ) ;
2002-04-09 23:14:34 +04:00
} else {
lParity = ( UART_LCR_EPAR | UART_LCR_PARITY ) ;
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s - parity = even \n " , __func__ ) ;
2002-04-09 23:14:34 +04:00
}
} else {
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s - parity = none \n " , __func__ ) ;
2002-04-09 23:14:34 +04:00
}
if ( cflag & CMSPAR )
lParity = lParity | 0x20 ;
/* Change the Stop bit */
if ( cflag & CSTOPB ) {
lStop = UART_LCR_STOP ;
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s - stop bits = 2 \n " , __func__ ) ;
2002-04-09 23:14:34 +04:00
} else {
lStop = 0x00 ;
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s - stop bits = 1 \n " , __func__ ) ;
2002-04-09 23:14:34 +04:00
}
# define LCR_BITS_MASK 0x03 /* Mask for bits/char field */
# define LCR_STOP_MASK 0x04 /* Mask for stop bits field */
# define LCR_PAR_MASK 0x38 /* Mask for parity field */
/* Update the LCR with the correct value */
2008-07-22 14:16:21 +04:00
mos7720_port - > shadowLCR & =
2010-04-16 01:02:09 +04:00
~ ( LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK ) ;
2002-04-09 23:14:34 +04:00
mos7720_port - > shadowLCR | = ( lData | lParity | lStop ) ;
/* Disable Interrupts */
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_IER , 0x00 ) ;
write_mos_reg ( serial , port_number , MOS7720_FCR , 0x00 ) ;
write_mos_reg ( serial , port_number , MOS7720_FCR , 0xcf ) ;
2002-04-09 23:14:34 +04:00
/* Send the updated LCR value to the mos7720 */
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_LCR ,
mos7720_port - > shadowLCR ) ;
2010-04-16 01:02:09 +04:00
mos7720_port - > shadowMCR = 0x0b ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_MCR ,
mos7720_port - > shadowMCR ) ;
2002-04-09 23:14:34 +04:00
/* set up the MCR register and send it to the mos7720 */
mos7720_port - > shadowMCR = UART_MCR_OUT2 ;
if ( cflag & CBAUD )
mos7720_port - > shadowMCR | = ( UART_MCR_DTR | UART_MCR_RTS ) ;
if ( cflag & CRTSCTS ) {
mos7720_port - > shadowMCR | = ( UART_MCR_XONANY ) ;
2008-07-22 14:16:21 +04:00
/* To set hardware flow control to the specified *
* serial port , in SP1 / 2 _CONTROL_REG */
2013-06-04 20:50:31 +04:00
if ( port_number )
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , dummy , MOS7720_SP_CONTROL_REG ,
0x01 ) ;
2010-04-16 01:02:09 +04:00
else
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , dummy , MOS7720_SP_CONTROL_REG ,
0x02 ) ;
2010-04-16 01:02:09 +04:00
} else
2002-04-09 23:14:34 +04:00
mos7720_port - > shadowMCR & = ~ ( UART_MCR_XONANY ) ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_MCR ,
mos7720_port - > shadowMCR ) ;
2002-04-09 23:14:34 +04:00
/* Determine divisor based on baud rate */
baud = tty_get_baud_rate ( tty ) ;
if ( ! baud ) {
/* pick a default, any default... */
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " Picked default baud... \n " ) ;
2002-04-09 23:14:34 +04:00
baud = 9600 ;
}
if ( baud > = 230400 ) {
set_higher_rates ( mos7720_port , baud ) ;
/* Enable Interrupts */
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_IER , 0x0c ) ;
2002-04-09 23:14:34 +04:00
return ;
}
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s - baud rate = %d \n " , __func__ , baud ) ;
2002-04-09 23:14:34 +04:00
status = send_cmd_write_baud_rate ( mos7720_port , baud ) ;
2008-01-03 20:01:18 +03:00
/* FIXME: needs to write actual resulting baud back not just
blindly do so */
if ( cflag & CBAUD )
tty_encode_baud_rate ( tty , baud , baud ) ;
2002-04-09 23:14:34 +04:00
/* Enable Interrupts */
2015-06-23 16:29:40 +03:00
write_mos_reg ( serial , port_number , MOS7720_IER , 0x0c ) ;
2002-04-09 23:14:34 +04:00
if ( port - > read_urb - > status ! = - EINPROGRESS ) {
2014-10-29 11:07:33 +03:00
status = usb_submit_urb ( port - > read_urb , GFP_KERNEL ) ;
2002-04-09 23:14:34 +04:00
if ( status )
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " usb_submit_urb(read bulk) failed, status = %d \n " , status ) ;
2002-04-09 23:14:34 +04:00
}
}
/*
* mos7720_set_termios
* this function is called by the tty driver when it wants to change the
* termios structure .
*/
2008-07-22 14:09:07 +04:00
static void mos7720_set_termios ( struct tty_struct * tty ,
struct usb_serial_port * port , struct ktermios * old_termios )
2002-04-09 23:14:34 +04:00
{
int status ;
struct moschip_port * mos7720_port ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL )
return ;
if ( ! mos7720_port - > open ) {
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s - port not opened \n " , __func__ ) ;
2002-04-09 23:14:34 +04:00
return ;
}
/* change the port settings to the new ones specified */
2008-07-22 14:09:07 +04:00
change_port_settings ( tty , mos7720_port , old_termios ) ;
2002-04-09 23:14:34 +04:00
2008-07-22 14:16:21 +04:00
if ( port - > read_urb - > status ! = - EINPROGRESS ) {
2014-10-29 11:07:33 +03:00
status = usb_submit_urb ( port - > read_urb , GFP_KERNEL ) ;
2002-04-09 23:14:34 +04:00
if ( status )
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " usb_submit_urb(read bulk) failed, status = %d \n " , status ) ;
2002-04-09 23:14:34 +04:00
}
}
/*
* get_lsr_info - get line status register info
*
* Purpose : Let user call ioctl ( ) to get info when the UART physically
* is emptied . On bus types like RS485 , the transmitter must
* release the bus after transmitting . This must be done when
* the transmit shift register is empty , not be done when the
* transmit holding register is empty . This functionality
* allows an RS485 driver to be written in user space .
*/
2008-07-22 14:16:21 +04:00
static int get_lsr_info ( struct tty_struct * tty ,
struct moschip_port * mos7720_port , unsigned int __user * value )
2002-04-09 23:14:34 +04:00
{
2009-09-20 00:13:18 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2002-04-09 23:14:34 +04:00
unsigned int result = 0 ;
2009-09-20 00:13:18 +04:00
unsigned char data = 0 ;
2013-06-06 21:32:00 +04:00
int port_number = port - > port_number ;
2009-09-20 00:13:18 +04:00
int count ;
2002-04-09 23:14:34 +04:00
2008-07-22 14:09:07 +04:00
count = mos7720_chars_in_buffer ( tty ) ;
2002-04-09 23:14:34 +04:00
if ( count = = 0 ) {
2015-06-23 16:29:40 +03:00
read_mos_reg ( port - > serial , port_number , MOS7720_LSR , & data ) ;
2009-09-20 00:13:18 +04:00
if ( ( data & ( UART_LSR_TEMT | UART_LSR_THRE ) )
= = ( UART_LSR_TEMT | UART_LSR_THRE ) ) {
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s -- Empty \n " , __func__ ) ;
2009-09-20 00:13:18 +04:00
result = TIOCSER_TEMT ;
}
2002-04-09 23:14:34 +04:00
}
if ( copy_to_user ( value , & result , sizeof ( int ) ) )
return - EFAULT ;
return 0 ;
}
2011-02-14 19:26:14 +03:00
static int mos7720_tiocmget ( struct tty_struct * tty )
2009-09-20 00:13:18 +04:00
{
struct usb_serial_port * port = tty - > driver_data ;
struct moschip_port * mos7720_port = usb_get_serial_port_data ( port ) ;
unsigned int result = 0 ;
unsigned int mcr ;
unsigned int msr ;
mcr = mos7720_port - > shadowMCR ;
msr = mos7720_port - > shadowMSR ;
result = ( ( mcr & UART_MCR_DTR ) ? TIOCM_DTR : 0 ) /* 0x002 */
| ( ( mcr & UART_MCR_RTS ) ? TIOCM_RTS : 0 ) /* 0x004 */
| ( ( msr & UART_MSR_CTS ) ? TIOCM_CTS : 0 ) /* 0x020 */
| ( ( msr & UART_MSR_DCD ) ? TIOCM_CAR : 0 ) /* 0x040 */
| ( ( msr & UART_MSR_RI ) ? TIOCM_RI : 0 ) /* 0x080 */
| ( ( msr & UART_MSR_DSR ) ? TIOCM_DSR : 0 ) ; /* 0x100 */
return result ;
}
2011-02-14 19:26:50 +03:00
static int mos7720_tiocmset ( struct tty_struct * tty ,
2010-04-16 01:02:09 +04:00
unsigned int set , unsigned int clear )
2009-09-20 00:13:18 +04:00
{
struct usb_serial_port * port = tty - > driver_data ;
struct moschip_port * mos7720_port = usb_get_serial_port_data ( port ) ;
unsigned int mcr ;
mcr = mos7720_port - > shadowMCR ;
if ( set & TIOCM_RTS )
mcr | = UART_MCR_RTS ;
if ( set & TIOCM_DTR )
mcr | = UART_MCR_DTR ;
if ( set & TIOCM_LOOP )
mcr | = UART_MCR_LOOP ;
if ( clear & TIOCM_RTS )
mcr & = ~ UART_MCR_RTS ;
if ( clear & TIOCM_DTR )
mcr & = ~ UART_MCR_DTR ;
if ( clear & TIOCM_LOOP )
mcr & = ~ UART_MCR_LOOP ;
mos7720_port - > shadowMCR = mcr ;
2015-06-23 16:29:40 +03:00
write_mos_reg ( port - > serial , port - > port_number , MOS7720_MCR ,
2013-06-06 21:32:00 +04:00
mos7720_port - > shadowMCR ) ;
2009-09-20 00:13:18 +04:00
return 0 ;
}
2018-09-12 07:20:17 +03:00
static int get_serial_info ( struct tty_struct * tty ,
struct serial_struct * ss )
2002-04-09 23:14:34 +04:00
{
2018-09-12 07:20:17 +03:00
struct usb_serial_port * port = tty - > driver_data ;
struct moschip_port * mos7720_port = usb_get_serial_port_data ( port ) ;
2002-04-09 23:14:34 +04:00
2018-09-12 07:20:17 +03:00
ss - > type = PORT_16550A ;
ss - > line = mos7720_port - > port - > minor ;
ss - > port = mos7720_port - > port - > port_number ;
ss - > irq = 0 ;
ss - > xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE ;
ss - > baud_base = 9600 ;
ss - > close_delay = 5 * HZ ;
ss - > closing_wait = 30 * HZ ;
2002-04-09 23:14:34 +04:00
return 0 ;
}
2011-02-14 19:27:06 +03:00
static int mos7720_ioctl ( struct tty_struct * tty ,
2002-04-09 23:14:34 +04:00
unsigned int cmd , unsigned long arg )
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2002-04-09 23:14:34 +04:00
struct moschip_port * mos7720_port ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL )
return - ENODEV ;
switch ( cmd ) {
case TIOCSERGETLSR :
2012-09-15 02:08:33 +04:00
dev_dbg ( & port - > dev , " %s TIOCSERGETLSR \n " , __func__ ) ;
2008-07-22 14:16:21 +04:00
return get_lsr_info ( tty , mos7720_port ,
( unsigned int __user * ) arg ) ;
2002-04-09 23:14:34 +04:00
}
return - ENOIOCTLCMD ;
}
static int mos7720_startup ( struct usb_serial * serial )
{
struct usb_device * dev ;
char data ;
2011-02-21 10:28:45 +03:00
u16 product ;
2010-04-16 01:01:33 +04:00
int ret_val ;
2002-04-09 23:14:34 +04:00
2011-02-21 10:28:45 +03:00
product = le16_to_cpu ( serial - > dev - > descriptor . idProduct ) ;
2002-04-09 23:14:34 +04:00
dev = serial - > dev ;
/* setting configuration feature to one */
usb_control_msg ( serial - > dev , usb_sndctrlpipe ( serial - > dev , 0 ) ,
2013-05-27 16:44:43 +04:00
( __u8 ) 0x03 , 0x00 , 0x01 , 0x00 , NULL , 0x00 , 5000 ) ;
2002-04-09 23:14:34 +04:00
2010-04-16 01:01:33 +04:00
if ( product = = MOSCHIP_DEVICE_ID_7715 ) {
2017-03-16 19:13:45 +03:00
struct urb * urb = serial - > port [ 0 ] - > interrupt_in_urb ;
2017-03-16 19:13:46 +03:00
urb - > complete = mos7715_interrupt_callback ;
2017-03-16 19:13:45 +03:00
# ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
2010-04-16 01:01:33 +04:00
ret_val = mos7715_parport_init ( serial ) ;
2017-01-03 18:39:52 +03:00
if ( ret_val < 0 )
2010-04-16 01:01:33 +04:00
return ret_val ;
# endif
2017-03-16 19:13:45 +03:00
}
2017-01-03 18:39:52 +03:00
/* start the interrupt urb */
ret_val = usb_submit_urb ( serial - > port [ 0 ] - > interrupt_in_urb , GFP_KERNEL ) ;
if ( ret_val ) {
dev_err ( & dev - > dev , " failed to submit interrupt urb: %d \n " ,
ret_val ) ;
}
2008-07-22 14:16:21 +04:00
/* LSR For Port 1 */
2015-06-23 16:29:40 +03:00
read_mos_reg ( serial , 0 , MOS7720_LSR , & data ) ;
2012-09-15 02:08:33 +04:00
dev_dbg ( & dev - > dev , " LSR:%x \n " , data ) ;
2002-04-09 23:14:34 +04:00
return 0 ;
}
2009-06-02 19:53:55 +04:00
static void mos7720_release ( struct usb_serial * serial )
2002-04-09 23:14:34 +04:00
{
2017-01-03 18:39:51 +03:00
usb_kill_urb ( serial - > port [ 0 ] - > interrupt_in_urb ) ;
2010-04-16 01:01:33 +04:00
# ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
/* close the parallel port */
if ( le16_to_cpu ( serial - > dev - > descriptor . idProduct )
= = MOSCHIP_DEVICE_ID_7715 ) {
struct urbtracker * urbtrack ;
unsigned long flags ;
struct mos7715_parport * mos_parport =
usb_get_serial_data ( serial ) ;
/* prevent NULL ptr dereference in port callbacks */
spin_lock ( & release_lock ) ;
mos_parport - > pp - > private_data = NULL ;
spin_unlock ( & release_lock ) ;
/* wait for synchronous usb calls to return */
if ( mos_parport - > msg_pending )
wait_for_completion_timeout ( & mos_parport - > syncmsg_compl ,
2013-05-27 16:44:43 +04:00
msecs_to_jiffies ( MOS_WDR_TIMEOUT ) ) ;
2010-04-16 01:01:33 +04:00
parport_remove_port ( mos_parport - > pp ) ;
usb_set_serial_data ( serial , NULL ) ;
mos_parport - > serial = NULL ;
/* if tasklet currently scheduled, wait for it to complete */
tasklet_kill ( & mos_parport - > urb_tasklet ) ;
/* unlink any urbs sent by the tasklet */
spin_lock_irqsave ( & mos_parport - > listlock , flags ) ;
list_for_each_entry ( urbtrack ,
& mos_parport - > active_urbs ,
urblist_entry )
usb_unlink_urb ( urbtrack - > urb ) ;
spin_unlock_irqrestore ( & mos_parport - > listlock , flags ) ;
2016-05-30 16:46:33 +03:00
parport_del_port ( mos_parport - > pp ) ;
2010-04-16 01:01:33 +04:00
kref_put ( & mos_parport - > ref_count , destroy_mos_parport ) ;
}
# endif
2012-10-25 12:29:05 +04:00
}
static int mos7720_port_probe ( struct usb_serial_port * port )
{
struct moschip_port * mos7720_port ;
mos7720_port = kzalloc ( sizeof ( * mos7720_port ) , GFP_KERNEL ) ;
if ( ! mos7720_port )
return - ENOMEM ;
mos7720_port - > port = port ;
usb_set_serial_port_data ( port , mos7720_port ) ;
return 0 ;
}
static int mos7720_port_remove ( struct usb_serial_port * port )
{
struct moschip_port * mos7720_port ;
mos7720_port = usb_get_serial_port_data ( port ) ;
kfree ( mos7720_port ) ;
return 0 ;
2002-04-09 23:14:34 +04:00
}
static struct usb_serial_driver moschip7720_2port_driver = {
. driver = {
. owner = THIS_MODULE ,
. name = " moschip7720 " ,
} ,
. description = " Moschip 2 port adapter " ,
2012-05-09 02:46:14 +04:00
. id_table = id_table ,
2017-03-02 14:51:27 +03:00
. num_bulk_in = 2 ,
. num_bulk_out = 2 ,
2017-03-16 19:13:46 +03:00
. num_interrupt_in = 1 ,
2010-01-26 20:12:12 +03:00
. calc_num_ports = mos77xx_calc_num_ports ,
2002-04-09 23:14:34 +04:00
. open = mos7720_open ,
. close = mos7720_close ,
. throttle = mos7720_throttle ,
. unthrottle = mos7720_unthrottle ,
. attach = mos7720_startup ,
2009-06-02 19:53:55 +04:00
. release = mos7720_release ,
2012-10-25 12:29:05 +04:00
. port_probe = mos7720_port_probe ,
. port_remove = mos7720_port_remove ,
2002-04-09 23:14:34 +04:00
. ioctl = mos7720_ioctl ,
2009-09-20 00:13:18 +04:00
. tiocmget = mos7720_tiocmget ,
. tiocmset = mos7720_tiocmset ,
2018-09-12 07:20:17 +03:00
. get_serial = get_serial_info ,
2002-04-09 23:14:34 +04:00
. set_termios = mos7720_set_termios ,
. write = mos7720_write ,
. write_room = mos7720_write_room ,
. chars_in_buffer = mos7720_chars_in_buffer ,
. break_ctl = mos7720_break ,
. read_bulk_callback = mos7720_bulk_in_callback ,
2017-01-03 18:39:53 +03:00
. read_int_callback = mos7720_interrupt_callback ,
2002-04-09 23:14:34 +04:00
} ;
2012-02-23 23:57:09 +04:00
static struct usb_serial_driver * const serial_drivers [ ] = {
& moschip7720_2port_driver , NULL
} ;
2012-05-09 02:46:14 +04:00
module_usb_serial_driver ( serial_drivers , id_table ) ;
2002-04-09 23:14:34 +04:00
2008-07-22 14:16:21 +04:00
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
2017-11-03 20:12:08 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;