2002-04-09 12:14:34 -07:00
/*
* mos7720 . c
* Controls the Moschip 7720 usb to dual port serial convertor
*
* Copyright 2006 Moschip Semiconductor Tech . Ltd .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , version 2 of the License .
*
* Developed by :
2007-06-25 01:08:01 -07:00
* Vijaya Kumar < vijaykumar . gn @ gmail . com >
* Ajay Kumar < naanuajay @ yahoo . com >
* Gurudeva < ngurudeva @ yahoo . com >
2002-04-09 12:14:34 -07: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/init.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 11:16:21 +01:00
# include <linux/uaccess.h>
2010-04-15 17:01:33 -04:00
# include <linux/parport.h>
2002-04-09 12:14:34 -07:00
/*
* Version Information
*/
2010-04-15 17:02:09 -04:00
# define DRIVER_VERSION "2.1"
2002-04-09 12:14:34 -07:00
# define DRIVER_AUTHOR "Aspire Communications pvt Ltd."
# define DRIVER_DESC "Moschip USB Serial Driver"
/* default urb timeout */
# define MOS_WDR_TIMEOUT (HZ * 5)
# define MOS_MAX_PORT 0x02
# define MOS_WRITE 0x0E
# define MOS_READ 0x0D
/* Interrupt Rotinue Defines */
# 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-15 17:01:33 -04:00
/* This structure holds all of the local serial port information */
2008-07-22 11:16:21 +01:00
struct moschip_port {
2002-04-09 12:14:34 -07:00
__u8 shadowLCR ; /* last LCR value received */
__u8 shadowMCR ; /* last MCR value received */
__u8 shadowMSR ; /* last MSR value received */
char open ;
struct async_icount icount ;
struct usb_serial_port * port ; /* loop back to the owner */
struct urb * write_urb_pool [ NUM_URBS ] ;
} ;
static int debug ;
2010-01-26 12:12:12 -05:00
static struct usb_serial_driver moschip7720_2port_driver ;
2002-04-09 12:14:34 -07:00
# define USB_VENDOR_ID_MOSCHIP 0x9710
# define MOSCHIP_DEVICE_ID_7720 0x7720
# define MOSCHIP_DEVICE_ID_7715 0x7715
2010-01-10 15:34:24 +01:00
static const struct usb_device_id moschip_port_id_table [ ] = {
2008-07-22 11:16:21 +01:00
{ USB_DEVICE ( USB_VENDOR_ID_MOSCHIP , MOSCHIP_DEVICE_ID_7720 ) } ,
2010-01-26 12:12:12 -05:00
{ USB_DEVICE ( USB_VENDOR_ID_MOSCHIP , MOSCHIP_DEVICE_ID_7715 ) } ,
2002-04-09 12:14:34 -07:00
{ } /* terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , moschip_port_id_table ) ;
2010-04-15 17: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 ;
} ;
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-15 17:02:09 -04:00
# endif /* CONFIG_USB_SERIAL_MOS7715_PARPORT */
static const unsigned int dummy ; /* for clarity in register access fns */
2010-04-15 17:01:33 -04:00
enum mos_regs {
THR , /* serial port regs */
RHR ,
IER ,
FCR ,
ISR ,
LCR ,
MCR ,
LSR ,
MSR ,
SPR ,
DLL ,
DLM ,
DPR , /* parallel port regs */
DSR ,
DCR ,
ECR ,
SP1_REG , /* device control regs */
SP2_REG , /* serial port 2 (7720 only) */
PP_REG ,
SP_CONTROL_REG ,
} ;
/*
* 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 [ ] = {
0x00 , /* THR */
0x00 , /* RHR */
0x01 , /* IER */
0x02 , /* FCR */
0x02 , /* ISR */
0x03 , /* LCR */
0x04 , /* MCR */
0x05 , /* LSR */
0x06 , /* MSR */
0x07 , /* SPR */
0x00 , /* DLL */
0x01 , /* DLM */
0x00 , /* DPR */
0x01 , /* DSR */
0x02 , /* DCR */
0x0a , /* ECR */
0x01 , /* SP1_REG */
0x02 , /* SP2_REG (7720 only) */
0x04 , /* PP_REG (7715 only) */
0x08 , /* SP_CONTROL_REG */
} ;
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-15 17:02:09 -04:00
static inline __u16 get_reg_value ( enum mos_regs reg ,
unsigned int serial_portnum )
2010-04-15 17:01:33 -04:00
{
if ( reg > = SP1_REG ) /* control reg */
return 0x0000 ;
2010-04-15 17:02:09 -04:00
else if ( reg > = DPR ) /* parallel port reg (7715 only) */
2010-04-15 17:01:33 -04:00
return 0x0100 ;
2010-04-15 17:02:09 -04:00
else /* serial port reg */
return ( serial_portnum + 2 ) < < 8 ;
2010-04-15 17:01:33 -04:00
}
/*
* Write data byte to the specified device register . The data is embedded in
2010-04-15 17: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-15 17:01:33 -04:00
*/
2010-04-15 17: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-15 17: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-15 17: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-15 17:01:33 -04:00
if ( status < 0 )
dev_err ( & usbdev - > dev ,
" mos7720: usb_control_msg() failed: %d " , status ) ;
return status ;
}
/*
* Read data byte from the specified device register . The data returned by the
2010-04-15 17: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-15 17:01:33 -04:00
*/
2010-04-15 17: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-15 17:01:33 -04:00
{
2010-04-15 17:02:09 -04:00
struct usb_device * usbdev = serial - > dev ;
2010-04-15 17: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-15 17:02:09 -04:00
__u16 value = get_reg_value ( reg , serial_portnum ) ;
2010-04-15 17:01:33 -04:00
int status = usb_control_msg ( usbdev , pipe , request , requesttype , value ,
2010-04-15 17:02:09 -04:00
index , data , 1 , MOS_WDR_TIMEOUT ) ;
2010-04-15 17:01:33 -04:00
if ( status < 0 )
dev_err ( & usbdev - > dev ,
" mos7720: usb_control_msg() failed: %d " , status ) ;
return status ;
}
2010-04-15 17:02:09 -04:00
# ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
2010-04-15 17:01:33 -04:00
static inline int mos7715_change_mode ( struct mos7715_parport * mos_parport ,
enum mos7715_pp_modes mode )
{
mos_parport - > shadowECR = mode ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( mos_parport - > serial , dummy , ECR , mos_parport - > shadowECR ) ;
2010-04-15 17: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 ) ;
dbg ( " %s called " , __func__ ) ;
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 ;
dbg ( " %s called " , __func__ ) ;
usb_free_urb ( urbtrack - > urb ) ;
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 ;
struct urbtracker * urbtrack ;
struct list_head * cursor , * next ;
dbg ( " %s called " , __func__ ) ;
/* if release function ran, game over */
if ( unlikely ( mos_parport - > serial = = NULL ) )
return ;
/* try again to get the mutex */
if ( ! mutex_trylock ( & mos_parport - > serial - > disc_mutex ) ) {
dbg ( " %s: rescheduling tasklet " , __func__ ) ;
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 ) ;
dbg ( " %s: deferred_urbs list empty " , __func__ ) ;
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 ) ;
list_for_each_entry ( urbtrack , & mos_parport - > active_urbs ,
urblist_entry ) {
ret_val = usb_submit_urb ( urbtrack - > urb , GFP_ATOMIC ) ;
dbg ( " %s: urb submitted " , __func__ ) ;
if ( ret_val ) {
dev_err ( & mos_parport - > serial - > dev - > dev ,
" usb_submit_urb() failed: %d " , ret_val ) ;
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 ;
dbg ( " %s called " , __func__ ) ;
if ( unlikely ( status ) )
dbg ( " %s - nonzero urb status received: %d " , __func__ , status ) ;
/* remove the urbtracker from the active_urbs list */
spin_lock ( & urbtrack - > mos_parport - > listlock ) ;
list_del ( & urbtrack - > urblist_entry ) ;
spin_unlock ( & urbtrack - > mos_parport - > listlock ) ;
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_ctrlrequest setup ;
struct usb_serial * serial = mos_parport - > serial ;
struct usb_device * usbdev = serial - > dev ;
dbg ( " %s called " , __func__ ) ;
/* create and initialize the control urb and containing urbtracker */
urbtrack = kmalloc ( sizeof ( struct urbtracker ) , GFP_ATOMIC ) ;
if ( urbtrack = = NULL ) {
dev_err ( & usbdev - > dev , " out of memory " ) ;
return - ENOMEM ;
}
kref_get ( & mos_parport - > ref_count ) ;
urbtrack - > mos_parport = mos_parport ;
urbtrack - > urb = usb_alloc_urb ( 0 , GFP_ATOMIC ) ;
if ( urbtrack - > urb = = NULL ) {
dev_err ( & usbdev - > dev , " out of urbs " ) ;
kfree ( urbtrack ) ;
return - ENOMEM ;
}
setup . bRequestType = ( __u8 ) 0x40 ;
setup . bRequest = ( __u8 ) 0x0e ;
2010-04-15 17:02:09 -04:00
setup . wValue = get_reg_value ( reg , dummy ) ;
2010-04-15 17:01:33 -04:00
setup . wIndex = get_reg_index ( reg ) ;
setup . wLength = 0 ;
usb_fill_control_urb ( urbtrack - > urb , usbdev ,
usb_sndctrlpipe ( usbdev , 0 ) ,
( unsigned char * ) & setup ,
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 ) ;
dbg ( " tasklet scheduled " ) ;
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 ,
" %s: submit_urb() failed: %d " , __func__ , ret_val ) ;
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
* flag to ensure that all synchronous usb messgage calls have completed before
* 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 */
INIT_COMPLETION ( mos_parport - > syncmsg_compl ) ;
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 ;
}
/*
* This is the the common bottom part of all parallel port functions that send
* 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 ;
dbg ( " %s called: %2.2x " , __func__ , d ) ;
if ( parport_prologue ( pp ) < 0 )
return ;
mos7715_change_mode ( mos_parport , SPP ) ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( mos_parport - > serial , dummy , DPR , ( __u8 ) d ) ;
2010-04-15 17: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 ;
dbg ( " %s called " , __func__ ) ;
if ( parport_prologue ( pp ) < 0 )
return 0 ;
2010-04-15 17:02:09 -04:00
read_mos_reg ( mos_parport - > serial , dummy , DPR , & d ) ;
2010-04-15 17: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 ;
dbg ( " %s called: %2.2x " , __func__ , d ) ;
if ( parport_prologue ( pp ) < 0 )
return ;
data = ( ( __u8 ) d & 0x0f ) | ( mos_parport - > shadowDCR & 0xf0 ) ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( mos_parport - > serial , dummy , DCR , data ) ;
2010-04-15 17:01:33 -04:00
mos_parport - > shadowDCR = data ;
parport_epilogue ( pp ) ;
}
static unsigned char parport_mos7715_read_control ( struct parport * pp )
{
struct mos7715_parport * mos_parport = pp - > private_data ;
__u8 dcr ;
dbg ( " %s called " , __func__ ) ;
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 ;
dbg ( " %s called " , __func__ ) ;
mask & = 0x0f ;
val & = 0x0f ;
if ( parport_prologue ( pp ) < 0 )
return 0 ;
mos_parport - > shadowDCR = ( mos_parport - > shadowDCR & ( ~ mask ) ) ^ val ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( mos_parport - > serial , dummy , DCR , mos_parport - > shadowDCR ) ;
2010-04-15 17: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 ;
struct mos7715_parport * mos_parport = pp - > private_data ;
dbg ( " %s called " , __func__ ) ;
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 )
{
dbg ( " %s called " , __func__ ) ;
}
static void parport_mos7715_disable_irq ( struct parport * pp )
{
dbg ( " %s called " , __func__ ) ;
}
static void parport_mos7715_data_forward ( struct parport * pp )
{
struct mos7715_parport * mos_parport = pp - > private_data ;
dbg ( " %s called " , __func__ ) ;
if ( parport_prologue ( pp ) < 0 )
return ;
mos7715_change_mode ( mos_parport , PS2 ) ;
mos_parport - > shadowDCR & = ~ 0x20 ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( mos_parport - > serial , dummy , DCR , mos_parport - > shadowDCR ) ;
2010-04-15 17:01:33 -04:00
parport_epilogue ( pp ) ;
}
static void parport_mos7715_data_reverse ( struct parport * pp )
{
struct mos7715_parport * mos_parport = pp - > private_data ;
dbg ( " %s called " , __func__ ) ;
if ( parport_prologue ( pp ) < 0 )
return ;
mos7715_change_mode ( mos_parport , PS2 ) ;
mos_parport - > shadowDCR | = 0x20 ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( mos_parport - > serial , dummy , DCR , mos_parport - > shadowDCR ) ;
2010-04-15 17:01:33 -04:00
parport_epilogue ( pp ) ;
}
static void parport_mos7715_init_state ( struct pardevice * dev ,
struct parport_state * s )
{
dbg ( " %s called " , __func__ ) ;
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 ;
dbg ( " %s called " , __func__ ) ;
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 ;
dbg ( " %s called " , __func__ ) ;
spin_lock ( & release_lock ) ;
mos_parport = pp - > private_data ;
if ( unlikely ( mos_parport = = NULL ) ) { /* release called */
spin_unlock ( & release_lock ) ;
return ;
}
write_parport_reg_nonblock ( mos_parport , DCR , mos_parport - > shadowDCR ) ;
write_parport_reg_nonblock ( mos_parport , ECR , mos_parport - > shadowECR ) ;
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 ;
dbg ( " %s called: %u chars " , __func__ , ( unsigned int ) len ) ;
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 ,
" mos7720: usb_bulk_msg() failed: %d " , retval ) ;
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 ) ;
if ( mos_parport = = NULL ) {
dbg ( " mos7715_parport_init: kzalloc failed " ) ;
return - ENOMEM ;
}
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 */
2010-04-15 17:02:09 -04:00
write_mos_reg ( mos_parport - > serial , dummy , PP_REG , ( __u8 ) 0x80 ) ;
write_mos_reg ( mos_parport - > serial , dummy , PP_REG , ( __u8 ) 0x00 ) ;
2010-04-15 17:01:33 -04:00
/* initialize device registers */
mos_parport - > shadowDCR = DCR_INIT_VAL ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( mos_parport - > serial , dummy , DCR , mos_parport - > shadowDCR ) ;
2010-04-15 17:01:33 -04:00
mos_parport - > shadowECR = ECR_INIT_VAL ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( mos_parport - > serial , dummy , ECR , mos_parport - > shadowECR ) ;
2010-04-15 17: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 12:14:34 -07: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-15 15:44:13 -07:00
int status = urb - > status ;
2007-03-19 13:58:29 +01:00
__u8 * data ;
2002-04-09 12:14:34 -07:00
__u8 sp1 ;
__u8 sp2 ;
2007-06-15 15:44:13 -07:00
switch ( status ) {
2002-04-09 12:14:34 -07:00
case 0 :
/* success */
break ;
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
/* this urb is terminated, clean up */
2008-03-03 16:08:34 -08:00
dbg ( " %s - urb shutting down with status: %d " , __func__ ,
2007-06-15 15:44:13 -07:00
status ) ;
2002-04-09 12:14:34 -07:00
return ;
default :
2008-03-03 16:08:34 -08:00
dbg ( " %s - nonzero urb status received: %d " , __func__ ,
2007-06-15 15:44:13 -07:00
status ) ;
2002-04-09 12:14:34 -07: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 13:58:29 +01:00
/* the above description is inverted
* oneukum 2007 - 03 - 14 */
if ( unlikely ( length ! = 4 ) ) {
2002-04-09 12:14:34 -07:00
dbg ( " Wrong data !!! " ) ;
return ;
}
2007-03-19 13:58:29 +01:00
sp1 = data [ 3 ] ;
sp2 = data [ 2 ] ;
2002-04-09 12:14:34 -07:00
2007-03-19 13:58:29 +01:00
if ( ( sp1 | sp2 ) & 0x01 ) {
2002-04-09 12:14:34 -07:00
/* No Interrupt Pending in both the ports */
dbg ( " No Interrupt !!! " ) ;
} else {
switch ( sp1 & 0x0f ) {
case SERIAL_IIR_RLS :
dbg ( " Serial Port 1: Receiver status error or address "
" bit detected in 9-bit mode \n " ) ;
break ;
case SERIAL_IIR_CTI :
dbg ( " Serial Port 1: Receiver time out " ) ;
break ;
case SERIAL_IIR_MS :
2010-04-15 17:01:33 -04:00
/* dbg("Serial Port 1: Modem status change"); */
2002-04-09 12:14:34 -07:00
break ;
}
switch ( sp2 & 0x0f ) {
case SERIAL_IIR_RLS :
dbg ( " Serial Port 2: Receiver status error or address "
" bit detected in 9-bit mode " ) ;
break ;
case SERIAL_IIR_CTI :
dbg ( " Serial Port 2: Receiver time out " ) ;
break ;
case SERIAL_IIR_MS :
2010-04-15 17:01:33 -04:00
/* dbg("Serial Port 2: Modem status change"); */
2002-04-09 12:14:34 -07:00
break ;
}
}
exit :
result = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( result )
dev_err ( & urb - > dev - > dev ,
" %s - Error %d submitting control urb \n " ,
2008-03-03 16:08:34 -08:00
__func__ , result ) ;
2002-04-09 12:14:34 -07:00
return ;
}
2010-01-26 12:12:12 -05: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 ;
__u8 * data ;
__u8 iir ;
switch ( status ) {
case 0 :
/* success */
break ;
case - ECONNRESET :
case - ENOENT :
case - ESHUTDOWN :
2010-04-15 17:01:33 -04:00
case - ENODEV :
2010-01-26 12:12:12 -05:00
/* this urb is terminated, clean up */
dbg ( " %s - urb shutting down with status: %d " , __func__ ,
status ) ;
return ;
default :
dbg ( " %s - nonzero urb status received: %d " , __func__ ,
status ) ;
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 ) ) {
dbg ( " Wrong data !!! " ) ;
return ;
}
iir = data [ 0 ] ;
if ( ! ( iir & 0x01 ) ) { /* serial port interrupt pending */
switch ( iir & 0x0f ) {
case SERIAL_IIR_RLS :
dbg ( " Serial Port: Receiver status error or address "
" bit detected in 9-bit mode \n " ) ;
break ;
case SERIAL_IIR_CTI :
dbg ( " Serial Port: Receiver time out " ) ;
break ;
case SERIAL_IIR_MS :
2010-04-15 17:01:33 -04:00
/* dbg("Serial Port: Modem status change"); */
2010-01-26 12:12:12 -05:00
break ;
}
}
2010-04-15 17: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 12:12:12 -05:00
exit :
result = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( result )
dev_err ( & urb - > dev - > dev ,
" %s - Error %d submitting control urb \n " ,
__func__ , result ) ;
return ;
}
2002-04-09 12:14:34 -07: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-15 15:44:13 -07:00
int retval ;
2002-04-09 12:14:34 -07:00
unsigned char * data ;
struct usb_serial_port * port ;
struct tty_struct * tty ;
2007-06-15 15:44:13 -07:00
int status = urb - > status ;
2002-04-09 12:14:34 -07:00
2007-06-15 15:44:13 -07:00
if ( status ) {
dbg ( " nonzero read bulk status received: %d " , status ) ;
2002-04-09 12:14:34 -07:00
return ;
}
2010-04-15 17:01:33 -04:00
port = urb - > context ;
2002-04-09 12:14:34 -07:00
2008-03-03 16:08:34 -08:00
dbg ( " Entering...%s " , __func__ ) ;
2002-04-09 12:14:34 -07:00
data = urb - > transfer_buffer ;
2008-10-13 10:39:46 +01:00
tty = tty_port_tty_get ( & port - > port ) ;
2002-04-09 12:14:34 -07:00
if ( tty & & urb - > actual_length ) {
tty_insert_flip_string ( tty , data , urb - > actual_length ) ;
tty_flip_buffer_push ( tty ) ;
}
2008-10-13 10:39:46 +01:00
tty_kref_put ( tty ) ;
2002-04-09 12:14:34 -07:00
if ( ! port - > read_urb ) {
dbg ( " URB KILLED !!! " ) ;
return ;
}
if ( port - > read_urb - > status ! = - EINPROGRESS ) {
port - > read_urb - > dev = port - > serial - > dev ;
2007-06-15 15:44:13 -07:00
retval = usb_submit_urb ( port - > read_urb , GFP_ATOMIC ) ;
if ( retval )
dbg ( " usb_submit_urb(read bulk) failed, retval = %d " ,
retval ) ;
2002-04-09 12:14:34 -07: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 ;
struct tty_struct * tty ;
2007-06-15 15:44:13 -07:00
int status = urb - > status ;
2002-04-09 12:14:34 -07:00
2007-06-15 15:44:13 -07:00
if ( status ) {
dbg ( " nonzero write bulk status received:%d " , status ) ;
2002-04-09 12:14:34 -07:00
return ;
}
mos7720_port = urb - > context ;
if ( ! mos7720_port ) {
dbg ( " NULL mos7720_port pointer " ) ;
return ;
}
2008-10-13 10:39:46 +01:00
tty = tty_port_tty_get ( & mos7720_port - > port - > port ) ;
2002-04-09 12:14:34 -07:00
2007-02-10 01:44:55 -08:00
if ( tty & & mos7720_port - > open )
tty_wakeup ( tty ) ;
2008-10-13 10:39:46 +01:00
tty_kref_put ( tty ) ;
2002-04-09 12:14:34 -07:00
}
2010-01-26 12:12:12 -05:00
/*
* mos77xx_probe
* this function installs the appropriate read interrupt endpoint callback
* depending on whether the device is a 7720 or 7715 , thus avoiding costly
* run - time checks in the high - frequency callback routine itself .
*/
static int mos77xx_probe ( struct usb_serial * serial ,
const struct usb_device_id * id )
{
if ( id - > idProduct = = MOSCHIP_DEVICE_ID_7715 )
moschip7720_2port_driver . read_int_callback =
mos7715_interrupt_callback ;
else
moschip7720_2port_driver . read_int_callback =
mos7720_interrupt_callback ;
return 0 ;
}
static int mos77xx_calc_num_ports ( struct usb_serial * serial )
{
u16 product = le16_to_cpu ( serial - > dev - > descriptor . idProduct ) ;
if ( product = = MOSCHIP_DEVICE_ID_7715 )
return 1 ;
return 2 ;
}
2009-09-19 13:13:26 -07:00
static int mos7720_open ( struct tty_struct * tty , struct usb_serial_port * port )
2002-04-09 12:14:34 -07:00
{
struct usb_serial * serial ;
struct usb_serial_port * port0 ;
struct urb * urb ;
struct moschip_port * mos7720_port ;
int response ;
int port_number ;
2010-04-15 17:02:09 -04:00
__u8 data ;
2007-03-14 15:22:25 +01:00
int allocated_urbs = 0 ;
2002-04-09 12:14:34 -07:00
int j ;
serial = port - > serial ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL )
return - ENODEV ;
port0 = serial - > port [ 0 ] ;
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 11:16:21 +01:00
urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
2002-04-09 12:14:34 -07:00
mos7720_port - > write_urb_pool [ j ] = urb ;
if ( urb = = NULL ) {
2008-08-20 16:56:34 -07:00
dev_err ( & port - > dev , " No more urbs??? \n " ) ;
2002-04-09 12:14:34 -07:00
continue ;
}
urb - > transfer_buffer = kmalloc ( URB_TRANSFER_BUFFER_SIZE ,
GFP_KERNEL ) ;
if ( ! urb - > transfer_buffer ) {
2008-08-20 16:56:34 -07:00
dev_err ( & port - > dev ,
" %s-out of memory for urb buffers. \n " ,
__func__ ) ;
2007-03-14 15:22:25 +01:00
usb_free_urb ( mos7720_port - > write_urb_pool [ j ] ) ;
mos7720_port - > write_urb_pool [ j ] = NULL ;
2002-04-09 12:14:34 -07:00
continue ;
}
2007-03-14 15:22:25 +01:00
allocated_urbs + + ;
2002-04-09 12:14:34 -07:00
}
2007-03-14 15:22:25 +01:00
if ( ! allocated_urbs )
return - ENOMEM ;
2002-04-09 12:14:34 -07:00
/* Initialize MCS7720 -- Write Init values to corresponding Registers
*
* Register Index
2009-09-19 13:13:18 -07:00
* 0 : THR / RHR
2002-04-09 12:14:34 -07:00
* 1 : IER
* 2 : FCR
* 3 : LCR
* 4 : MCR
2009-09-19 13:13:18 -07:00
* 5 : LSR
* 6 : MSR
* 7 : SPR
2002-04-09 12:14:34 -07:00
*
* 0x08 : SP1 / 2 Control Reg
*/
port_number = port - > number - port - > serial - > minor ;
2010-04-15 17:02:09 -04:00
read_mos_reg ( serial , port_number , LSR , & data ) ;
2010-02-05 16:50:08 -08:00
dbg ( " SS::%p LSR:%x " , mos7720_port , data ) ;
2002-04-09 12:14:34 -07:00
dbg ( " Check:Sending Command .......... " ) ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( serial , dummy , SP1_REG , 0x02 ) ;
write_mos_reg ( serial , dummy , SP2_REG , 0x02 ) ;
2002-04-09 12:14:34 -07:00
2010-04-15 17:02:09 -04:00
write_mos_reg ( serial , port_number , IER , 0x00 ) ;
write_mos_reg ( serial , port_number , FCR , 0x00 ) ;
2002-04-09 12:14:34 -07:00
2010-04-15 17:02:09 -04:00
write_mos_reg ( serial , port_number , FCR , 0xcf ) ;
mos7720_port - > shadowLCR = 0x03 ;
write_mos_reg ( serial , port_number , LCR , mos7720_port - > shadowLCR ) ;
mos7720_port - > shadowMCR = 0x0b ;
write_mos_reg ( serial , port_number , MCR , mos7720_port - > shadowMCR ) ;
2002-04-09 12:14:34 -07:00
2010-04-15 17:02:09 -04:00
write_mos_reg ( serial , port_number , SP_CONTROL_REG , 0x00 ) ;
read_mos_reg ( serial , dummy , SP_CONTROL_REG , & data ) ;
2002-04-09 12:14:34 -07:00
data = data | ( port - > number - port - > serial - > minor + 1 ) ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( serial , dummy , SP_CONTROL_REG , data ) ;
mos7720_port - > shadowLCR = 0x83 ;
write_mos_reg ( serial , port_number , LCR , mos7720_port - > shadowLCR ) ;
write_mos_reg ( serial , port_number , THR , 0x0c ) ;
write_mos_reg ( serial , port_number , IER , 0x00 ) ;
mos7720_port - > shadowLCR = 0x03 ;
write_mos_reg ( serial , port_number , LCR , mos7720_port - > shadowLCR ) ;
write_mos_reg ( serial , port_number , IER , 0x0c ) ;
2002-04-09 12:14:34 -07:00
response = usb_submit_urb ( port - > read_urb , GFP_KERNEL ) ;
if ( response )
2008-07-22 11:16:21 +01:00
dev_err ( & port - > dev , " %s - Error %d submitting read urb \n " ,
__func__ , response ) ;
2002-04-09 12:14:34 -07:00
/* initialize our icount structure */
memset ( & ( mos7720_port - > icount ) , 0x00 , sizeof ( mos7720_port - > icount ) ) ;
/* 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 11:09:07 +01:00
static int mos7720_chars_in_buffer ( struct tty_struct * tty )
2002-04-09 12:14:34 -07:00
{
2008-07-22 11:09:07 +01:00
struct usb_serial_port * port = tty - > driver_data ;
2002-04-09 12:14:34 -07:00
int i ;
int chars = 0 ;
struct moschip_port * mos7720_port ;
2008-03-03 16:08:34 -08:00
dbg ( " %s:entering ........... " , __func__ ) ;
2002-04-09 12:14:34 -07:00
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL ) {
2008-03-03 16:08:34 -08:00
dbg ( " %s:leaving ........... " , __func__ ) ;
2009-07-20 16:05:27 +01:00
return 0 ;
2002-04-09 12:14:34 -07:00
}
for ( i = 0 ; i < NUM_URBS ; + + i ) {
2008-07-22 11:16:21 +01:00
if ( mos7720_port - > write_urb_pool [ i ] & &
mos7720_port - > write_urb_pool [ i ] - > status = = - EINPROGRESS )
2002-04-09 12:14:34 -07:00
chars + = URB_TRANSFER_BUFFER_SIZE ;
}
2008-03-03 16:08:34 -08:00
dbg ( " %s - returns %d " , __func__ , chars ) ;
2002-04-09 12:14:34 -07:00
return chars ;
}
2009-06-11 12:26:29 +01:00
static void mos7720_close ( struct usb_serial_port * port )
2002-04-09 12:14:34 -07:00
{
struct usb_serial * serial ;
struct moschip_port * mos7720_port ;
int j ;
dbg ( " mos7720_close:entering... " ) ;
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 17:18:52 +01:00
* and interrupt read if they exists , otherwise nop */
dbg ( " Shutdown bulk write " ) ;
usb_kill_urb ( port - > write_urb ) ;
dbg ( " Shutdown bulk read " ) ;
usb_kill_urb ( port - > read_urb ) ;
mutex_lock ( & serial - > disc_mutex ) ;
/* these commands must not be issued if the device has
* been disconnected */
if ( ! serial - > disconnected ) {
2010-04-15 17:02:09 -04:00
write_mos_reg ( serial , port - > number - port - > serial - > minor ,
MCR , 0x00 ) ;
write_mos_reg ( serial , port - > number - port - > serial - > minor ,
IER , 0x00 ) ;
2002-04-09 12:14:34 -07:00
}
2008-01-16 17:18:52 +01:00
mutex_unlock ( & serial - > disc_mutex ) ;
2002-04-09 12:14:34 -07:00
mos7720_port - > open = 0 ;
2008-03-03 16:08:34 -08:00
dbg ( " Leaving %s " , __func__ ) ;
2002-04-09 12:14:34 -07:00
}
2008-07-22 11:09:07 +01:00
static void mos7720_break ( struct tty_struct * tty , int break_state )
2002-04-09 12:14:34 -07:00
{
2008-07-22 11:09:07 +01:00
struct usb_serial_port * port = tty - > driver_data ;
2008-07-22 11:16:21 +01:00
unsigned char data ;
2002-04-09 12:14:34 -07:00
struct usb_serial * serial ;
struct moschip_port * mos7720_port ;
2008-03-03 16:08:34 -08:00
dbg ( " Entering %s " , __func__ ) ;
2002-04-09 12:14:34 -07:00
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 ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( serial , port - > number - port - > serial - > minor ,
LCR , mos7720_port - > shadowLCR ) ;
2002-04-09 12:14:34 -07:00
return ;
}
/*
* 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 11:09:07 +01:00
static int mos7720_write_room ( struct tty_struct * tty )
2002-04-09 12:14:34 -07:00
{
2008-07-22 11:09:07 +01:00
struct usb_serial_port * port = tty - > driver_data ;
2002-04-09 12:14:34 -07:00
struct moschip_port * mos7720_port ;
int room = 0 ;
int i ;
2008-03-03 16:08:34 -08:00
dbg ( " %s:entering ........... " , __func__ ) ;
2002-04-09 12:14:34 -07:00
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL ) {
2008-03-03 16:08:34 -08:00
dbg ( " %s:leaving ........... " , __func__ ) ;
2002-04-09 12:14:34 -07:00
return - ENODEV ;
}
2008-04-08 17:16:06 +01:00
/* FIXME: Locking */
2002-04-09 12:14:34 -07:00
for ( i = 0 ; i < NUM_URBS ; + + i ) {
2008-07-22 11:16:21 +01:00
if ( mos7720_port - > write_urb_pool [ i ] & &
mos7720_port - > write_urb_pool [ i ] - > status ! = - EINPROGRESS )
2002-04-09 12:14:34 -07:00
room + = URB_TRANSFER_BUFFER_SIZE ;
}
2008-03-03 16:08:34 -08:00
dbg ( " %s - returns %d " , __func__ , room ) ;
2002-04-09 12:14:34 -07:00
return room ;
}
2008-07-22 11:09:07 +01:00
static int mos7720_write ( struct tty_struct * tty , struct usb_serial_port * port ,
const unsigned char * data , int count )
2002-04-09 12:14:34 -07: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 ;
2008-03-03 16:08:34 -08:00
dbg ( " %s:entering ........... " , __func__ ) ;
2002-04-09 12:14:34 -07:00
serial = port - > serial ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL ) {
dbg ( " mos7720_port is NULL " ) ;
return - ENODEV ;
}
/* try to find a free urb in the list */
urb = NULL ;
for ( i = 0 ; i < NUM_URBS ; + + i ) {
2008-07-22 11:16:21 +01:00
if ( mos7720_port - > write_urb_pool [ i ] & &
mos7720_port - > write_urb_pool [ i ] - > status ! = - EINPROGRESS ) {
2002-04-09 12:14:34 -07:00
urb = mos7720_port - > write_urb_pool [ i ] ;
2008-07-22 11:16:21 +01:00
dbg ( " URB:%d " , i ) ;
2002-04-09 12:14:34 -07:00
break ;
}
}
if ( urb = = NULL ) {
2008-03-03 16:08:34 -08:00
dbg ( " %s - no more free urbs " , __func__ ) ;
2002-04-09 12:14:34 -07:00
goto exit ;
}
if ( urb - > transfer_buffer = = NULL ) {
urb - > transfer_buffer = kmalloc ( URB_TRANSFER_BUFFER_SIZE ,
GFP_KERNEL ) ;
if ( urb - > transfer_buffer = = NULL ) {
2008-08-20 16:56:34 -07:00
dev_err ( & port - > dev , " %s no more kernel memory... \n " ,
__func__ ) ;
2002-04-09 12:14:34 -07:00
goto exit ;
}
}
2008-07-22 11:16:21 +01:00
transfer_size = min ( count , URB_TRANSFER_BUFFER_SIZE ) ;
2002-04-09 12:14:34 -07:00
memcpy ( urb - > transfer_buffer , current_position , transfer_size ) ;
2008-03-03 16:08:34 -08:00
usb_serial_debug_data ( debug , & port - > dev , __func__ , transfer_size ,
2002-04-09 12:14:34 -07:00
urb - > transfer_buffer ) ;
/* fill urb with data and submit */
usb_fill_bulk_urb ( urb , serial - > dev ,
usb_sndbulkpipe ( serial - > dev ,
2008-07-22 11:16:21 +01:00
port - > bulk_out_endpointAddress ) ,
2002-04-09 12:14:34 -07:00
urb - > transfer_buffer , transfer_size ,
mos7720_bulk_out_data_callback , mos7720_port ) ;
/* send it down the pipe */
2008-07-22 11:16:21 +01:00
status = usb_submit_urb ( urb , GFP_ATOMIC ) ;
2002-04-09 12:14:34 -07:00
if ( status ) {
2008-08-20 16:56:34 -07:00
dev_err ( & port - > dev , " %s - usb_submit_urb(write bulk) failed "
" with status = %d \n " , __func__ , status ) ;
2002-04-09 12:14:34 -07:00
bytes_sent = status ;
goto exit ;
}
bytes_sent = transfer_size ;
exit :
return bytes_sent ;
}
2008-07-22 11:09:07 +01:00
static void mos7720_throttle ( struct tty_struct * tty )
2002-04-09 12:14:34 -07:00
{
2008-07-22 11:09:07 +01:00
struct usb_serial_port * port = tty - > driver_data ;
2002-04-09 12:14:34 -07:00
struct moschip_port * mos7720_port ;
int status ;
2010-02-05 16:50:08 -08:00
dbg ( " %s- port %d " , __func__ , port - > number ) ;
2002-04-09 12:14:34 -07:00
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL )
return ;
if ( ! mos7720_port - > open ) {
dbg ( " port not opened " ) ;
return ;
}
2008-03-03 16:08:34 -08:00
dbg ( " %s: Entering .......... " , __func__ ) ;
2002-04-09 12:14:34 -07:00
/* if we are implementing XON/XOFF, send the stop character */
if ( I_IXOFF ( tty ) ) {
unsigned char stop_char = STOP_CHAR ( tty ) ;
2008-07-22 11:09:07 +01:00
status = mos7720_write ( tty , port , & stop_char , 1 ) ;
2002-04-09 12:14:34 -07:00
if ( status < = 0 )
return ;
}
/* if we are implementing RTS/CTS, toggle that line */
if ( tty - > termios - > c_cflag & CRTSCTS ) {
mos7720_port - > shadowMCR & = ~ UART_MCR_RTS ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( port - > serial , port - > number - port - > serial - > minor ,
MCR , mos7720_port - > shadowMCR ) ;
2002-04-09 12:14:34 -07:00
if ( status ! = 0 )
return ;
}
}
2008-07-22 11:09:07 +01:00
static void mos7720_unthrottle ( struct tty_struct * tty )
2002-04-09 12:14:34 -07:00
{
2008-07-22 11:09:07 +01:00
struct usb_serial_port * port = tty - > driver_data ;
2002-04-09 12:14:34 -07:00
struct moschip_port * mos7720_port = usb_get_serial_port_data ( port ) ;
2008-07-22 11:09:07 +01:00
int status ;
2002-04-09 12:14:34 -07:00
if ( mos7720_port = = NULL )
return ;
if ( ! mos7720_port - > open ) {
2008-03-03 16:08:34 -08:00
dbg ( " %s - port not opened " , __func__ ) ;
2002-04-09 12:14:34 -07:00
return ;
}
2008-03-03 16:08:34 -08:00
dbg ( " %s: Entering .......... " , __func__ ) ;
2002-04-09 12:14:34 -07:00
/* if we are implementing XON/XOFF, send the start character */
if ( I_IXOFF ( tty ) ) {
unsigned char start_char = START_CHAR ( tty ) ;
2008-07-22 11:09:07 +01:00
status = mos7720_write ( tty , port , & start_char , 1 ) ;
2002-04-09 12:14:34 -07:00
if ( status < = 0 )
return ;
}
/* if we are implementing RTS/CTS, toggle that line */
if ( tty - > termios - > c_cflag & CRTSCTS ) {
mos7720_port - > shadowMCR | = UART_MCR_RTS ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( port - > serial , port - > number - port - > serial - > minor ,
MCR , mos7720_port - > shadowMCR ) ;
2002-04-09 12:14:34 -07:00
if ( status ! = 0 )
return ;
}
}
2010-04-15 17:01:33 -04:00
/* FIXME: this function does not work */
2002-04-09 12:14:34 -07: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-15 17:02:09 -04:00
enum mos_regs sp_reg ;
2002-04-09 12:14:34 -07:00
if ( mos7720_port = = NULL )
return - EINVAL ;
port = mos7720_port - > port ;
serial = port - > serial ;
2008-07-22 11:16:21 +01:00
/***********************************************
* Init Sequence for higher rates
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2002-04-09 12:14:34 -07:00
dbg ( " Sending Setting Commands .......... " ) ;
port_number = port - > number - port - > serial - > minor ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( serial , port_number , IER , 0x00 ) ;
write_mos_reg ( serial , port_number , FCR , 0x00 ) ;
write_mos_reg ( serial , port_number , FCR , 0xcf ) ;
mos7720_port - > shadowMCR = 0x0b ;
write_mos_reg ( serial , port_number , MCR , mos7720_port - > shadowMCR ) ;
write_mos_reg ( serial , dummy , SP_CONTROL_REG , 0x00 ) ;
2002-04-09 12:14:34 -07:00
2008-07-22 11:16:21 +01:00
/***********************************************
* Set for higher rates *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-04-15 17:01:33 -04:00
/* writing baud rate verbatum into uart clock field clearly not right */
2010-04-15 17:02:09 -04:00
if ( port_number = = 0 )
sp_reg = SP1_REG ;
else
sp_reg = SP2_REG ;
write_mos_reg ( serial , dummy , sp_reg , baud * 0x10 ) ;
write_mos_reg ( serial , dummy , SP_CONTROL_REG , 0x03 ) ;
mos7720_port - > shadowMCR = 0x2b ;
write_mos_reg ( serial , port_number , MCR , mos7720_port - > shadowMCR ) ;
2002-04-09 12:14:34 -07:00
2008-07-22 11:16:21 +01:00
/***********************************************
* Set DLL / DLM
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-04-15 17:02:09 -04:00
mos7720_port - > shadowLCR = mos7720_port - > shadowLCR | UART_LCR_DLAB ;
write_mos_reg ( serial , port_number , LCR , mos7720_port - > shadowLCR ) ;
write_mos_reg ( serial , port_number , DLL , 0x01 ) ;
write_mos_reg ( serial , port_number , DLM , 0x00 ) ;
mos7720_port - > shadowLCR = mos7720_port - > shadowLCR & ~ UART_LCR_DLAB ;
write_mos_reg ( serial , port_number , LCR , mos7720_port - > shadowLCR ) ;
2002-04-09 12:14:34 -07:00
return 0 ;
}
/* baud rate information */
2008-07-22 11:16:21 +01:00
struct divisor_table_entry {
2002-04-09 12:14:34 -07: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. */
static struct divisor_table_entry divisor_table [ ] = {
{ 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 .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int calc_baud_rate_divisor ( int baudrate , int * divisor )
{
int i ;
__u16 custom ;
__u16 round1 ;
__u16 round ;
2008-03-03 16:08:34 -08:00
dbg ( " %s - %d " , __func__ , baudrate ) ;
2002-04-09 12:14:34 -07: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 11:16:21 +01:00
/* After trying for all the standard baud rates *
* Try calculating the divisor for this baud rate */
2002-04-09 12:14:34 -07: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 ;
2008-07-22 11:16:21 +01:00
dbg ( " Baud %d = %d " , baudrate , custom ) ;
2002-04-09 12:14:34 -07:00
return 0 ;
}
dbg ( " Baud calculation Failed... " ) ;
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 ;
2008-03-03 16:08:34 -08:00
dbg ( " %s: Entering .......... " , __func__ ) ;
2002-04-09 12:14:34 -07:00
number = port - > number - port - > serial - > minor ;
2008-03-03 16:08:34 -08:00
dbg ( " %s - port = %d, baud = %d " , __func__ , port - > number , baudrate ) ;
2002-04-09 12:14:34 -07:00
2008-07-22 11:16:21 +01:00
/* Calculate the Divisor */
2002-04-09 12:14:34 -07:00
status = calc_baud_rate_divisor ( baudrate , & divisor ) ;
if ( status ) {
2008-08-20 16:56:34 -07:00
dev_err ( & port - > dev , " %s - bad baud rate \n " , __func__ ) ;
2002-04-09 12:14:34 -07:00
return status ;
}
2008-07-22 11:16:21 +01:00
/* Enable access to divisor latch */
2010-04-15 17:02:09 -04:00
mos7720_port - > shadowLCR = mos7720_port - > shadowLCR | UART_LCR_DLAB ;
write_mos_reg ( serial , number , LCR , mos7720_port - > shadowLCR ) ;
2002-04-09 12:14:34 -07:00
/* Write the divisor */
2010-04-15 17:02:09 -04:00
write_mos_reg ( serial , number , DLL , ( __u8 ) ( divisor & 0xff ) ) ;
write_mos_reg ( serial , number , DLM , ( __u8 ) ( ( divisor & 0xff00 ) > > 8 ) ) ;
2002-04-09 12:14:34 -07:00
2008-07-22 11:16:21 +01:00
/* Disable access to divisor latch */
2010-04-15 17:02:09 -04:00
mos7720_port - > shadowLCR = mos7720_port - > shadowLCR & ~ UART_LCR_DLAB ;
write_mos_reg ( serial , number , LCR , mos7720_port - > shadowLCR ) ;
2002-04-09 12:14:34 -07: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 11:09:07 +01:00
static void change_port_settings ( struct tty_struct * tty ,
struct moschip_port * mos7720_port ,
2006-12-08 02:38:45 -08:00
struct ktermios * old_termios )
2002-04-09 12:14:34 -07:00
{
struct usb_serial_port * port ;
struct usb_serial * serial ;
int baud ;
unsigned cflag ;
unsigned iflag ;
__u8 mask = 0xff ;
__u8 lData ;
__u8 lParity ;
__u8 lStop ;
int status ;
int port_number ;
if ( mos7720_port = = NULL )
return ;
port = mos7720_port - > port ;
serial = port - > serial ;
port_number = port - > number - port - > serial - > minor ;
2008-03-03 16:08:34 -08:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2002-04-09 12:14:34 -07:00
if ( ! mos7720_port - > open ) {
2008-03-03 16:08:34 -08:00
dbg ( " %s - port not opened " , __func__ ) ;
2002-04-09 12:14:34 -07:00
return ;
}
2008-03-03 16:08:34 -08:00
dbg ( " %s: Entering .......... " , __func__ ) ;
2002-04-09 12:14:34 -07:00
lData = UART_LCR_WLEN8 ;
lStop = 0x00 ; /* 1 stop bit */
lParity = 0x00 ; /* No parity */
cflag = tty - > termios - > c_cflag ;
iflag = tty - > termios - > c_iflag ;
/* Change the number of bits */
switch ( cflag & CSIZE ) {
case CS5 :
lData = UART_LCR_WLEN5 ;
mask = 0x1f ;
break ;
case CS6 :
lData = UART_LCR_WLEN6 ;
mask = 0x3f ;
break ;
case CS7 :
lData = UART_LCR_WLEN7 ;
mask = 0x7f ;
break ;
default :
case CS8 :
lData = UART_LCR_WLEN8 ;
break ;
}
/* Change the Parity bit */
if ( cflag & PARENB ) {
if ( cflag & PARODD ) {
lParity = UART_LCR_PARITY ;
2008-03-03 16:08:34 -08:00
dbg ( " %s - parity = odd " , __func__ ) ;
2002-04-09 12:14:34 -07:00
} else {
lParity = ( UART_LCR_EPAR | UART_LCR_PARITY ) ;
2008-03-03 16:08:34 -08:00
dbg ( " %s - parity = even " , __func__ ) ;
2002-04-09 12:14:34 -07:00
}
} else {
2008-03-03 16:08:34 -08:00
dbg ( " %s - parity = none " , __func__ ) ;
2002-04-09 12:14:34 -07:00
}
if ( cflag & CMSPAR )
lParity = lParity | 0x20 ;
/* Change the Stop bit */
if ( cflag & CSTOPB ) {
lStop = UART_LCR_STOP ;
2008-03-03 16:08:34 -08:00
dbg ( " %s - stop bits = 2 " , __func__ ) ;
2002-04-09 12:14:34 -07:00
} else {
lStop = 0x00 ;
2008-03-03 16:08:34 -08:00
dbg ( " %s - stop bits = 1 " , __func__ ) ;
2002-04-09 12:14:34 -07: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 11:16:21 +01:00
mos7720_port - > shadowLCR & =
2010-04-15 17:02:09 -04:00
~ ( LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK ) ;
2002-04-09 12:14:34 -07:00
mos7720_port - > shadowLCR | = ( lData | lParity | lStop ) ;
/* Disable Interrupts */
2010-04-15 17:02:09 -04:00
write_mos_reg ( serial , port_number , IER , 0x00 ) ;
write_mos_reg ( serial , port_number , FCR , 0x00 ) ;
write_mos_reg ( serial , port_number , FCR , 0xcf ) ;
2002-04-09 12:14:34 -07:00
/* Send the updated LCR value to the mos7720 */
2010-04-15 17:02:09 -04:00
write_mos_reg ( serial , port_number , LCR , mos7720_port - > shadowLCR ) ;
mos7720_port - > shadowMCR = 0x0b ;
write_mos_reg ( serial , port_number , MCR , mos7720_port - > shadowMCR ) ;
2002-04-09 12:14:34 -07: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 11:16:21 +01:00
/* To set hardware flow control to the specified *
* serial port , in SP1 / 2 _CONTROL_REG */
2010-04-15 17:02:09 -04:00
if ( port - > number )
write_mos_reg ( serial , dummy , SP_CONTROL_REG , 0x01 ) ;
else
write_mos_reg ( serial , dummy , SP_CONTROL_REG , 0x02 ) ;
} else
2002-04-09 12:14:34 -07:00
mos7720_port - > shadowMCR & = ~ ( UART_MCR_XONANY ) ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( serial , port_number , MCR , mos7720_port - > shadowMCR ) ;
2002-04-09 12:14:34 -07:00
/* Determine divisor based on baud rate */
baud = tty_get_baud_rate ( tty ) ;
if ( ! baud ) {
/* pick a default, any default... */
dbg ( " Picked default baud... " ) ;
baud = 9600 ;
}
if ( baud > = 230400 ) {
set_higher_rates ( mos7720_port , baud ) ;
/* Enable Interrupts */
2010-04-15 17:02:09 -04:00
write_mos_reg ( serial , port_number , IER , 0x0c ) ;
2002-04-09 12:14:34 -07:00
return ;
}
2008-03-03 16:08:34 -08:00
dbg ( " %s - baud rate = %d " , __func__ , baud ) ;
2002-04-09 12:14:34 -07:00
status = send_cmd_write_baud_rate ( mos7720_port , baud ) ;
2008-01-03 17:01:18 +00: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 12:14:34 -07:00
/* Enable Interrupts */
2010-04-15 17:02:09 -04:00
write_mos_reg ( serial , port_number , IER , 0x0c ) ;
2002-04-09 12:14:34 -07:00
if ( port - > read_urb - > status ! = - EINPROGRESS ) {
port - > read_urb - > dev = serial - > dev ;
status = usb_submit_urb ( port - > read_urb , GFP_ATOMIC ) ;
if ( status )
dbg ( " usb_submit_urb(read bulk) failed, status = %d " ,
status ) ;
}
return ;
}
/*
* mos7720_set_termios
* this function is called by the tty driver when it wants to change the
* termios structure .
*/
2008-07-22 11:09:07 +01:00
static void mos7720_set_termios ( struct tty_struct * tty ,
struct usb_serial_port * port , struct ktermios * old_termios )
2002-04-09 12:14:34 -07:00
{
int status ;
unsigned int cflag ;
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 ( ! mos7720_port - > open ) {
2008-03-03 16:08:34 -08:00
dbg ( " %s - port not opened " , __func__ ) ;
2002-04-09 12:14:34 -07:00
return ;
}
2010-04-15 17:01:33 -04:00
dbg ( " %s \n " , " setting termios - ASPIRE " ) ;
2002-04-09 12:14:34 -07:00
cflag = tty - > termios - > c_cflag ;
2008-03-03 16:08:34 -08:00
dbg ( " %s - cflag %08x iflag %08x " , __func__ ,
2002-04-09 12:14:34 -07:00
tty - > termios - > c_cflag ,
RELEVANT_IFLAG ( tty - > termios - > c_iflag ) ) ;
2008-03-03 16:08:34 -08:00
dbg ( " %s - old cflag %08x old iflag %08x " , __func__ ,
2008-01-03 17:01:18 +00:00
old_termios - > c_cflag ,
RELEVANT_IFLAG ( old_termios - > c_iflag ) ) ;
2002-04-09 12:14:34 -07:00
2008-03-03 16:08:34 -08:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2002-04-09 12:14:34 -07:00
/* change the port settings to the new ones specified */
2008-07-22 11:09:07 +01:00
change_port_settings ( tty , mos7720_port , old_termios ) ;
2002-04-09 12:14:34 -07:00
2008-07-22 11:16:21 +01:00
if ( ! port - > read_urb ) {
2010-04-15 17:01:33 -04:00
dbg ( " %s " , " URB KILLED !!!!! " ) ;
2002-04-09 12:14:34 -07:00
return ;
}
2008-07-22 11:16:21 +01:00
if ( port - > read_urb - > status ! = - EINPROGRESS ) {
2002-04-09 12:14:34 -07:00
port - > read_urb - > dev = serial - > dev ;
status = usb_submit_urb ( port - > read_urb , GFP_ATOMIC ) ;
if ( status )
dbg ( " usb_submit_urb(read bulk) failed, status = %d " ,
status ) ;
}
return ;
}
/*
* 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 11:16:21 +01:00
static int get_lsr_info ( struct tty_struct * tty ,
struct moschip_port * mos7720_port , unsigned int __user * value )
2002-04-09 12:14:34 -07:00
{
2009-09-19 13:13:18 -07:00
struct usb_serial_port * port = tty - > driver_data ;
2002-04-09 12:14:34 -07:00
unsigned int result = 0 ;
2009-09-19 13:13:18 -07:00
unsigned char data = 0 ;
int port_number = port - > number - port - > serial - > minor ;
int count ;
2002-04-09 12:14:34 -07:00
2008-07-22 11:09:07 +01:00
count = mos7720_chars_in_buffer ( tty ) ;
2002-04-09 12:14:34 -07:00
if ( count = = 0 ) {
2010-04-15 17:02:09 -04:00
read_mos_reg ( port - > serial , port_number , LSR , & data ) ;
2009-09-19 13:13:18 -07:00
if ( ( data & ( UART_LSR_TEMT | UART_LSR_THRE ) )
= = ( UART_LSR_TEMT | UART_LSR_THRE ) ) {
dbg ( " %s -- Empty " , __func__ ) ;
result = TIOCSER_TEMT ;
}
2002-04-09 12:14:34 -07:00
}
if ( copy_to_user ( value , & result , sizeof ( int ) ) )
return - EFAULT ;
return 0 ;
}
2009-09-19 13:13:18 -07:00
static int mos7720_tiocmget ( struct tty_struct * tty , struct file * file )
{
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 ;
dbg ( " %s - port %d " , __func__ , port - > number ) ;
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 */
dbg ( " %s -- %x " , __func__ , result ) ;
return result ;
}
static int mos7720_tiocmset ( struct tty_struct * tty , struct file * file ,
2010-04-15 17:02:09 -04:00
unsigned int set , unsigned int clear )
2009-09-19 13:13:18 -07:00
{
struct usb_serial_port * port = tty - > driver_data ;
struct moschip_port * mos7720_port = usb_get_serial_port_data ( port ) ;
unsigned int mcr ;
dbg ( " %s - port %d " , __func__ , port - > number ) ;
dbg ( " he was at tiocmget " ) ;
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 ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( port - > serial , port - > number - port - > serial - > minor ,
MCR , mos7720_port - > shadowMCR ) ;
2009-09-19 13:13:18 -07:00
return 0 ;
}
2002-04-09 12:14:34 -07:00
static int set_modem_info ( struct moschip_port * mos7720_port , unsigned int cmd ,
unsigned int __user * value )
{
unsigned int mcr ;
unsigned int arg ;
struct usb_serial_port * port ;
if ( mos7720_port = = NULL )
return - 1 ;
2008-07-22 11:16:21 +01:00
port = ( struct usb_serial_port * ) mos7720_port - > port ;
2002-04-09 12:14:34 -07:00
mcr = mos7720_port - > shadowMCR ;
if ( copy_from_user ( & arg , value , sizeof ( int ) ) )
return - EFAULT ;
switch ( cmd ) {
case TIOCMBIS :
if ( arg & TIOCM_RTS )
mcr | = UART_MCR_RTS ;
if ( arg & TIOCM_DTR )
mcr | = UART_MCR_RTS ;
if ( arg & TIOCM_LOOP )
mcr | = UART_MCR_LOOP ;
break ;
case TIOCMBIC :
if ( arg & TIOCM_RTS )
mcr & = ~ UART_MCR_RTS ;
if ( arg & TIOCM_DTR )
mcr & = ~ UART_MCR_RTS ;
if ( arg & TIOCM_LOOP )
mcr & = ~ UART_MCR_LOOP ;
break ;
}
mos7720_port - > shadowMCR = mcr ;
2010-04-15 17:02:09 -04:00
write_mos_reg ( port - > serial , port - > number - port - > serial - > minor ,
MCR , mos7720_port - > shadowMCR ) ;
2002-04-09 12:14:34 -07:00
return 0 ;
}
static int get_serial_info ( struct moschip_port * mos7720_port ,
struct serial_struct __user * retinfo )
{
struct serial_struct tmp ;
if ( ! retinfo )
return - EFAULT ;
memset ( & tmp , 0 , sizeof ( tmp ) ) ;
tmp . type = PORT_16550A ;
tmp . line = mos7720_port - > port - > serial - > minor ;
tmp . port = mos7720_port - > port - > number ;
tmp . irq = 0 ;
tmp . flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ ;
2008-07-22 11:16:21 +01:00
tmp . xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE ;
2002-04-09 12:14:34 -07:00
tmp . baud_base = 9600 ;
tmp . close_delay = 5 * HZ ;
tmp . closing_wait = 30 * HZ ;
if ( copy_to_user ( retinfo , & tmp , sizeof ( * retinfo ) ) )
return - EFAULT ;
return 0 ;
}
2008-07-22 11:09:07 +01:00
static int mos7720_ioctl ( struct tty_struct * tty , struct file * file ,
2002-04-09 12:14:34 -07:00
unsigned int cmd , unsigned long arg )
{
2008-07-22 11:09:07 +01:00
struct usb_serial_port * port = tty - > driver_data ;
2002-04-09 12:14:34 -07:00
struct moschip_port * mos7720_port ;
struct async_icount cnow ;
struct async_icount cprev ;
struct serial_icounter_struct icount ;
mos7720_port = usb_get_serial_port_data ( port ) ;
if ( mos7720_port = = NULL )
return - ENODEV ;
2008-03-03 16:08:34 -08:00
dbg ( " %s - port %d, cmd = 0x%x " , __func__ , port - > number , cmd ) ;
2002-04-09 12:14:34 -07:00
switch ( cmd ) {
case TIOCSERGETLSR :
2008-03-03 16:08:34 -08:00
dbg ( " %s (%d) TIOCSERGETLSR " , __func__ , port - > number ) ;
2008-07-22 11:16:21 +01:00
return get_lsr_info ( tty , mos7720_port ,
( unsigned int __user * ) arg ) ;
2002-04-09 12:14:34 -07:00
return 0 ;
2008-07-22 11:09:07 +01:00
/* FIXME: These should be using the mode methods */
2002-04-09 12:14:34 -07:00
case TIOCMBIS :
case TIOCMBIC :
2008-07-22 11:16:21 +01:00
dbg ( " %s (%d) TIOCMSET/TIOCMBIC/TIOCMSET " ,
__func__ , port - > number ) ;
2002-04-09 12:14:34 -07:00
return set_modem_info ( mos7720_port , cmd ,
( unsigned int __user * ) arg ) ;
case TIOCGSERIAL :
2008-03-03 16:08:34 -08:00
dbg ( " %s (%d) TIOCGSERIAL " , __func__ , port - > number ) ;
2002-04-09 12:14:34 -07:00
return get_serial_info ( mos7720_port ,
( struct serial_struct __user * ) arg ) ;
case TIOCMIWAIT :
2008-03-03 16:08:34 -08:00
dbg ( " %s (%d) TIOCMIWAIT " , __func__ , port - > number ) ;
2002-04-09 12:14:34 -07:00
cprev = mos7720_port - > icount ;
while ( 1 ) {
if ( signal_pending ( current ) )
return - ERESTARTSYS ;
cnow = mos7720_port - > icount ;
if ( cnow . rng = = cprev . rng & & cnow . dsr = = cprev . dsr & &
cnow . dcd = = cprev . dcd & & cnow . cts = = cprev . cts )
return - EIO ; /* no change => error */
if ( ( ( arg & TIOCM_RNG ) & & ( cnow . rng ! = cprev . rng ) ) | |
( ( arg & TIOCM_DSR ) & & ( cnow . dsr ! = cprev . dsr ) ) | |
( ( arg & TIOCM_CD ) & & ( cnow . dcd ! = cprev . dcd ) ) | |
2008-07-22 11:16:21 +01:00
( ( arg & TIOCM_CTS ) & & ( cnow . cts ! = cprev . cts ) ) ) {
2002-04-09 12:14:34 -07:00
return 0 ;
}
cprev = cnow ;
}
/* NOTREACHED */
break ;
case TIOCGICOUNT :
cnow = mos7720_port - > icount ;
2010-09-15 17:44:16 -04:00
memset ( & icount , 0 , sizeof ( struct serial_icounter_struct ) ) ;
2002-04-09 12:14:34 -07:00
icount . cts = cnow . cts ;
icount . dsr = cnow . dsr ;
icount . rng = cnow . rng ;
icount . dcd = cnow . dcd ;
icount . rx = cnow . rx ;
icount . tx = cnow . tx ;
icount . frame = cnow . frame ;
icount . overrun = cnow . overrun ;
icount . parity = cnow . parity ;
icount . brk = cnow . brk ;
icount . buf_overrun = cnow . buf_overrun ;
2008-03-03 16:08:34 -08:00
dbg ( " %s (%d) TIOCGICOUNT RX=%d, TX=%d " , __func__ ,
2008-07-22 11:16:21 +01:00
port - > number , icount . rx , icount . tx ) ;
2002-04-09 12:14:34 -07:00
if ( copy_to_user ( ( void __user * ) arg , & icount , sizeof ( icount ) ) )
return - EFAULT ;
return 0 ;
}
return - ENOIOCTLCMD ;
}
static int mos7720_startup ( struct usb_serial * serial )
{
struct moschip_port * mos7720_port ;
struct usb_device * dev ;
int i ;
char data ;
2010-01-26 12:12:12 -05:00
u16 product = le16_to_cpu ( serial - > dev - > descriptor . idProduct ) ;
2010-04-15 17:01:33 -04:00
int ret_val ;
2002-04-09 12:14:34 -07:00
2008-03-03 16:08:34 -08:00
dbg ( " %s: Entering .......... " , __func__ ) ;
2002-04-09 12:14:34 -07:00
if ( ! serial ) {
dbg ( " Invalid Handler " ) ;
return - ENODEV ;
}
dev = serial - > dev ;
2010-01-26 12:12:12 -05:00
/*
* The 7715 uses the first bulk in / out endpoint pair for the parallel
* port , and the second for the serial port . Because the usbserial core
* assumes both pairs are serial ports , we must engage in a bit of
* subterfuge and swap the pointers for ports 0 and 1 in order to make
* port 0 point to the serial port . However , both moschip devices use a
* single interrupt - in endpoint for both ports ( as mentioned a little
* further down ) , and this endpoint was assigned to port 0. So after
* the swap , we must copy the interrupt endpoint elements from port 1
* ( as newly assigned ) to port 0 , and null out port 1 pointers .
*/
if ( product = = MOSCHIP_DEVICE_ID_7715 ) {
struct usb_serial_port * tmp = serial - > port [ 0 ] ;
serial - > port [ 0 ] = serial - > port [ 1 ] ;
serial - > port [ 1 ] = tmp ;
serial - > port [ 0 ] - > interrupt_in_urb = tmp - > interrupt_in_urb ;
serial - > port [ 0 ] - > interrupt_in_buffer = tmp - > interrupt_in_buffer ;
serial - > port [ 0 ] - > interrupt_in_endpointAddress =
tmp - > interrupt_in_endpointAddress ;
serial - > port [ 1 ] - > interrupt_in_urb = NULL ;
serial - > port [ 1 ] - > interrupt_in_buffer = NULL ;
}
2002-04-09 12:14:34 -07:00
2010-04-15 17:01:33 -04:00
/* set up serial port private structures */
2002-04-09 12:14:34 -07:00
for ( i = 0 ; i < serial - > num_ports ; + + i ) {
mos7720_port = kzalloc ( sizeof ( struct moschip_port ) , GFP_KERNEL ) ;
if ( mos7720_port = = NULL ) {
2008-08-20 16:56:34 -07:00
dev_err ( & dev - > dev , " %s - Out of memory \n " , __func__ ) ;
2002-04-09 12:14:34 -07:00
return - ENOMEM ;
}
/* Initialize all port interrupt end point to port 0 int
* endpoint . Our device has only one interrupt endpoint
2010-01-26 12:12:12 -05:00
* common to all ports */
2008-07-22 11:16:21 +01:00
serial - > port [ i ] - > interrupt_in_endpointAddress =
serial - > port [ 0 ] - > interrupt_in_endpointAddress ;
2002-04-09 12:14:34 -07:00
mos7720_port - > port = serial - > port [ i ] ;
usb_set_serial_port_data ( serial - > port [ i ] , mos7720_port ) ;
dbg ( " port number is %d " , serial - > port [ i ] - > number ) ;
dbg ( " serial number is %d " , serial - > minor ) ;
}
/* setting configuration feature to one */
usb_control_msg ( serial - > dev , usb_sndctrlpipe ( serial - > dev , 0 ) ,
2008-07-22 11:16:21 +01:00
( __u8 ) 0x03 , 0x00 , 0x01 , 0x00 , NULL , 0x00 , 5 * HZ ) ;
2002-04-09 12:14:34 -07:00
2010-04-15 17:01:33 -04: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 ,
" %s - Error %d submitting control urb \n " ,
__func__ , ret_val ) ;
# ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
if ( product = = MOSCHIP_DEVICE_ID_7715 ) {
ret_val = mos7715_parport_init ( serial ) ;
if ( ret_val < 0 )
return ret_val ;
}
# endif
2008-07-22 11:16:21 +01:00
/* LSR For Port 1 */
2010-04-15 17:02:09 -04:00
read_mos_reg ( serial , 0 , LSR , & data ) ;
2008-07-22 11:16:21 +01:00
dbg ( " LSR:%x " , data ) ;
2002-04-09 12:14:34 -07:00
return 0 ;
}
2009-06-02 11:53:55 -04:00
static void mos7720_release ( struct usb_serial * serial )
2002-04-09 12:14:34 -07:00
{
int i ;
2010-04-15 17: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 ,
MOS_WDR_TIMEOUT ) ;
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 ) ;
kref_put ( & mos_parport - > ref_count , destroy_mos_parport ) ;
}
# endif
2002-04-09 12:14:34 -07:00
/* free private structure allocated for serial port */
2009-06-02 11:53:55 -04:00
for ( i = 0 ; i < serial - > num_ports ; + + i )
2002-04-09 12:14:34 -07:00
kfree ( usb_get_serial_port_data ( serial - > port [ i ] ) ) ;
}
2006-12-17 21:50:24 +01:00
static struct usb_driver usb_driver = {
. name = " moschip7720 " ,
. probe = usb_serial_probe ,
. disconnect = usb_serial_disconnect ,
. id_table = moschip_port_id_table ,
. no_dynamic_id = 1 ,
} ;
2002-04-09 12:14:34 -07:00
static struct usb_serial_driver moschip7720_2port_driver = {
. driver = {
. owner = THIS_MODULE ,
. name = " moschip7720 " ,
} ,
. description = " Moschip 2 port adapter " ,
2006-12-17 21:50:24 +01:00
. usb_driver = & usb_driver ,
2002-04-09 12:14:34 -07:00
. id_table = moschip_port_id_table ,
2010-01-26 12:12:12 -05:00
. calc_num_ports = mos77xx_calc_num_ports ,
2002-04-09 12:14:34 -07:00
. open = mos7720_open ,
. close = mos7720_close ,
. throttle = mos7720_throttle ,
. unthrottle = mos7720_unthrottle ,
2010-01-26 12:12:12 -05:00
. probe = mos77xx_probe ,
2002-04-09 12:14:34 -07:00
. attach = mos7720_startup ,
2009-06-02 11:53:55 -04:00
. release = mos7720_release ,
2002-04-09 12:14:34 -07:00
. ioctl = mos7720_ioctl ,
2009-09-19 13:13:18 -07:00
. tiocmget = mos7720_tiocmget ,
. tiocmset = mos7720_tiocmset ,
2002-04-09 12:14:34 -07: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 ,
2010-01-26 12:12:12 -05:00
. read_int_callback = NULL /* dynamically assigned in probe() */
2002-04-09 12:14:34 -07:00
} ;
static int __init moschip7720_init ( void )
{
int retval ;
2008-03-03 16:08:34 -08:00
dbg ( " %s: Entering .......... " , __func__ ) ;
2002-04-09 12:14:34 -07:00
/* Register with the usb serial */
retval = usb_serial_register ( & moschip7720_2port_driver ) ;
if ( retval )
goto failed_port_device_register ;
2008-08-18 13:21:04 -07:00
printk ( KERN_INFO KBUILD_MODNAME " : " DRIVER_VERSION " : "
DRIVER_DESC " \n " ) ;
2002-04-09 12:14:34 -07:00
/* Register with the usb */
retval = usb_register ( & usb_driver ) ;
if ( retval )
goto failed_usb_register ;
return 0 ;
failed_usb_register :
usb_serial_deregister ( & moschip7720_2port_driver ) ;
failed_port_device_register :
return retval ;
}
static void __exit moschip7720_exit ( void )
{
usb_deregister ( & usb_driver ) ;
usb_serial_deregister ( & moschip7720_2port_driver ) ;
}
module_init ( moschip7720_init ) ;
module_exit ( moschip7720_exit ) ;
/* Module information */
2008-07-22 11:16:21 +01:00
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
2002-04-09 12:14:34 -07:00
MODULE_LICENSE ( " GPL " ) ;
module_param ( debug , bool , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( debug , " Debug enabled or not " ) ;