/*
USB Driver for Sierra Wireless
Copyright ( C ) 2006 Kevin Lloyd < linux @ sierrawireless . com >
IMPORTANT DISCLAIMER : This driver is not commercially supported by
Sierra Wireless . Use at your own risk .
This driver is free software ; you can redistribute it and / or modify
it under the terms of Version 2 of the GNU General Public License as
published by the Free Software Foundation .
Portions based on the option driver by Matthias Urlichs < smurf @ smurf . noris . de >
Whom based his on the Keyspan driver by Hugh Blemings < hugh @ blemings . org >
History :
*/
# define DRIVER_VERSION "v.1.0.5"
# define DRIVER_AUTHOR "Kevin Lloyd <linux@sierrawireless.com>"
# define DRIVER_DESC "USB Driver for Sierra Wireless USB modems"
# include <linux/kernel.h>
# include <linux/jiffies.h>
# include <linux/errno.h>
# include <linux/tty.h>
# include <linux/tty_flip.h>
# include <linux/module.h>
# include <linux/usb.h>
# include <linux/usb/serial.h>
static struct usb_device_id id_table [ ] = {
{ USB_DEVICE ( 0x1199 , 0x0018 ) } , /* Sierra Wireless MC5720 */
{ USB_DEVICE ( 0x1199 , 0x0020 ) } , /* Sierra Wireless MC5725 */
{ USB_DEVICE ( 0x1199 , 0x0017 ) } , /* Sierra Wireless EM5625 */
{ USB_DEVICE ( 0x1199 , 0x0019 ) } , /* Sierra Wireless AirCard 595 */
{ USB_DEVICE ( 0x1199 , 0x0218 ) } , /* Sierra Wireless MC5720 */
{ USB_DEVICE ( 0x1199 , 0x6802 ) } , /* Sierra Wireless MC8755 */
{ USB_DEVICE ( 0x1199 , 0x6803 ) } , /* Sierra Wireless MC8765 */
{ USB_DEVICE ( 0x1199 , 0x6804 ) } , /* Sierra Wireless MC8755 for Europe */
{ USB_DEVICE ( 0x1199 , 0x6812 ) } , /* Sierra Wireless MC8775 */
{ USB_DEVICE ( 0x1199 , 0x6820 ) } , /* Sierra Wireless AirCard 875 */
{ USB_DEVICE ( 0x1199 , 0x0112 ) } , /* Sierra Wireless AirCard 580 */
{ USB_DEVICE ( 0x0F3D , 0x0112 ) } , /* AirPrime/Sierra PC 5220 */
{ }
} ;
MODULE_DEVICE_TABLE ( usb , id_table ) ;
static struct usb_device_id id_table_1port [ ] = {
{ USB_DEVICE ( 0x1199 , 0x0112 ) } , /* Sierra Wireless AirCard 580 */
{ USB_DEVICE ( 0x0F3D , 0x0112 ) } , /* AirPrime/Sierra PC 5220 */
{ }
} ;
static struct usb_device_id id_table_3port [ ] = {
{ USB_DEVICE ( 0x1199 , 0x0018 ) } , /* Sierra Wireless MC5720 */
{ USB_DEVICE ( 0x1199 , 0x0020 ) } , /* Sierra Wireless MC5725 */
{ USB_DEVICE ( 0x1199 , 0x0017 ) } , /* Sierra Wireless EM5625 */
{ USB_DEVICE ( 0x1199 , 0x0019 ) } , /* Sierra Wireless AirCard 595 */
{ USB_DEVICE ( 0x1199 , 0x0218 ) } , /* Sierra Wireless MC5720 */
{ USB_DEVICE ( 0x1199 , 0x6802 ) } , /* Sierra Wireless MC8755 */
{ USB_DEVICE ( 0x1199 , 0x6803 ) } , /* Sierra Wireless MC8765 */
{ USB_DEVICE ( 0x1199 , 0x6804 ) } , /* Sierra Wireless MC8755 for Europe */
{ USB_DEVICE ( 0x1199 , 0x6812 ) } , /* Sierra Wireless MC8775 */
{ USB_DEVICE ( 0x1199 , 0x6820 ) } , /* Sierra Wireless AirCard 875 */
{ }
} ;
static struct usb_driver sierra_driver = {
. name = " sierra " ,
. probe = usb_serial_probe ,
. disconnect = usb_serial_disconnect ,
. id_table = id_table ,
. no_dynamic_id = 1 ,
} ;
static int debug ;
/* per port private data */
# define N_IN_URB 4
# define N_OUT_URB 1
# define IN_BUFLEN 4096
# define OUT_BUFLEN 128
struct sierra_port_private {
/* Input endpoints and buffer for this port */
struct urb * in_urbs [ N_IN_URB ] ;
char in_buffer [ N_IN_URB ] [ IN_BUFLEN ] ;
/* Output endpoints and buffer for this port */
struct urb * out_urbs [ N_OUT_URB ] ;
char out_buffer [ N_OUT_URB ] [ OUT_BUFLEN ] ;
/* Settings for the port */
int rts_state ; /* Handshaking pins (outputs) */
int dtr_state ;
int cts_state ; /* Handshaking pins (inputs) */
int dsr_state ;
int dcd_state ;
int ri_state ;
unsigned long tx_start_time [ N_OUT_URB ] ;
} ;
static int sierra_send_setup ( struct usb_serial_port * port )
{
struct usb_serial * serial = port - > serial ;
struct sierra_port_private * portdata ;
dbg ( " %s " , __FUNCTION__ ) ;
portdata = usb_get_serial_port_data ( port ) ;
if ( port - > tty ) {
int val = 0 ;
if ( portdata - > dtr_state )
val | = 0x01 ;
if ( portdata - > rts_state )
val | = 0x02 ;
return usb_control_msg ( serial - > dev ,
usb_rcvctrlpipe ( serial - > dev , 0 ) ,
0x22 , 0x21 , val , 0 , NULL , 0 , USB_CTRL_SET_TIMEOUT ) ;
}
return 0 ;
}
static void sierra_rx_throttle ( struct usb_serial_port * port )
{
dbg ( " %s " , __FUNCTION__ ) ;
}
static void sierra_rx_unthrottle ( struct usb_serial_port * port )
{
dbg ( " %s " , __FUNCTION__ ) ;
}
static void sierra_break_ctl ( struct usb_serial_port * port , int break_state )
{
/* Unfortunately, I don't know how to send a break */
dbg ( " %s " , __FUNCTION__ ) ;
}
static void sierra_set_termios ( struct usb_serial_port * port ,
struct ktermios * old_termios )
{
dbg ( " %s " , __FUNCTION__ ) ;
sierra_send_setup ( port ) ;
}
static int sierra_tiocmget ( struct usb_serial_port * port , struct file * file )
{
unsigned int value ;
struct sierra_port_private * portdata ;
portdata = usb_get_serial_port_data ( port ) ;
value = ( ( portdata - > rts_state ) ? TIOCM_RTS : 0 ) |
( ( portdata - > dtr_state ) ? TIOCM_DTR : 0 ) |
( ( portdata - > cts_state ) ? TIOCM_CTS : 0 ) |
( ( portdata - > dsr_state ) ? TIOCM_DSR : 0 ) |
( ( portdata - > dcd_state ) ? TIOCM_CAR : 0 ) |
( ( portdata - > ri_state ) ? TIOCM_RNG : 0 ) ;
return value ;
}
static int sierra_tiocmset ( struct usb_serial_port * port , struct file * file ,
unsigned int set , unsigned int clear )
{
struct sierra_port_private * portdata ;
portdata = usb_get_serial_port_data ( port ) ;
if ( set & TIOCM_RTS )
portdata - > rts_state = 1 ;
if ( set & TIOCM_DTR )
portdata - > dtr_state = 1 ;
if ( clear & TIOCM_RTS )
portdata - > rts_state = 0 ;
if ( clear & TIOCM_DTR )
portdata - > dtr_state = 0 ;
return sierra_send_setup ( port ) ;
}
static int sierra_ioctl ( struct usb_serial_port * port , struct file * file ,
unsigned int cmd , unsigned long arg )
{
return - ENOIOCTLCMD ;
}
/* Write */
static int sierra_write ( struct usb_serial_port * port ,
const unsigned char * buf , int count )
{
struct sierra_port_private * portdata ;
int i ;
int left , todo ;
struct urb * this_urb = NULL ; /* spurious */
int err ;
portdata = usb_get_serial_port_data ( port ) ;
dbg ( " %s: write (%d chars) " , __FUNCTION__ , count ) ;
i = 0 ;
left = count ;
for ( i = 0 ; left > 0 & & i < N_OUT_URB ; i + + ) {
todo = left ;
if ( todo > OUT_BUFLEN )
todo = OUT_BUFLEN ;
this_urb = portdata - > out_urbs [ i ] ;
if ( this_urb - > status = = - EINPROGRESS ) {
if ( time_before ( jiffies ,
portdata - > tx_start_time [ i ] + 10 * HZ ) )
continue ;
usb_unlink_urb ( this_urb ) ;
continue ;
}
if ( this_urb - > status ! = 0 )
dbg ( " usb_write %p failed (err=%d) " ,
this_urb , this_urb - > status ) ;
dbg ( " %s: endpoint %d buf %d " , __FUNCTION__ ,
usb_pipeendpoint ( this_urb - > pipe ) , i ) ;
/* send the data */
memcpy ( this_urb - > transfer_buffer , buf , todo ) ;
this_urb - > transfer_buffer_length = todo ;
this_urb - > dev = port - > serial - > dev ;
err = usb_submit_urb ( this_urb , GFP_ATOMIC ) ;
if ( err ) {
dbg ( " usb_submit_urb %p (write bulk) failed "
" (%d, has %d) " , this_urb ,
err , this_urb - > status ) ;
continue ;
}
portdata - > tx_start_time [ i ] = jiffies ;
buf + = todo ;
left - = todo ;
}
count - = left ;
dbg ( " %s: wrote (did %d) " , __FUNCTION__ , count ) ;
return count ;
}
static void sierra_indat_callback ( struct urb * urb )
{
int err ;
int endpoint ;
struct usb_serial_port * port ;
struct tty_struct * tty ;
unsigned char * data = urb - > transfer_buffer ;
dbg ( " %s: %p " , __FUNCTION__ , urb ) ;
endpoint = usb_pipeendpoint ( urb - > pipe ) ;
port = ( struct usb_serial_port * ) urb - > context ;
if ( urb - > status ) {
dbg ( " %s: nonzero status: %d on endpoint %02x. " ,
__FUNCTION__ , urb - > status , endpoint ) ;
} else {
tty = port - > tty ;
if ( urb - > actual_length ) {
tty_buffer_request_room ( tty , urb - > actual_length ) ;
tty_insert_flip_string ( tty , data , urb - > actual_length ) ;
tty_flip_buffer_push ( tty ) ;
} else {
dbg ( " %s: empty read urb received " , __FUNCTION__ ) ;
}
/* Resubmit urb so we continue receiving */
if ( port - > open_count & & urb - > status ! = - ESHUTDOWN ) {
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err )
printk ( KERN_ERR " %s: resubmit read urb failed. "
" (%d) " , __FUNCTION__ , err ) ;
}
}
return ;
}
static void sierra_outdat_callback ( struct urb * urb )
{
struct usb_serial_port * port ;
dbg ( " %s " , __FUNCTION__ ) ;
port = ( struct usb_serial_port * ) urb - > context ;
usb_serial_port_softint ( port ) ;
}
static void sierra_instat_callback ( struct urb * urb )
{
int err ;
struct usb_serial_port * port = ( struct usb_serial_port * ) urb - > context ;
struct sierra_port_private * portdata = usb_get_serial_port_data ( port ) ;
struct usb_serial * serial = port - > serial ;
dbg ( " %s " , __FUNCTION__ ) ;
dbg ( " %s: urb %p port %p has data %p " , __FUNCTION__ , urb , port , portdata ) ;
if ( urb - > status = = 0 ) {
struct usb_ctrlrequest * req_pkt =
( struct usb_ctrlrequest * ) urb - > transfer_buffer ;
if ( ! req_pkt ) {
dbg ( " %s: NULL req_pkt \n " , __FUNCTION__ ) ;
return ;
}
if ( ( req_pkt - > bRequestType = = 0xA1 ) & &
( req_pkt - > bRequest = = 0x20 ) ) {
int old_dcd_state ;
unsigned char signals = * ( ( unsigned char * )
urb - > transfer_buffer +
sizeof ( struct usb_ctrlrequest ) ) ;
dbg ( " %s: signal x%x " , __FUNCTION__ , signals ) ;
old_dcd_state = portdata - > dcd_state ;
portdata - > cts_state = 1 ;
portdata - > dcd_state = ( ( signals & 0x01 ) ? 1 : 0 ) ;
portdata - > dsr_state = ( ( signals & 0x02 ) ? 1 : 0 ) ;
portdata - > ri_state = ( ( signals & 0x08 ) ? 1 : 0 ) ;
if ( port - > tty & & ! C_CLOCAL ( port - > tty ) & &
old_dcd_state & & ! portdata - > dcd_state )
tty_hangup ( port - > tty ) ;
} else {
dbg ( " %s: type %x req %x " , __FUNCTION__ ,
req_pkt - > bRequestType , req_pkt - > bRequest ) ;
}
} else
dbg ( " %s: error %d " , __FUNCTION__ , urb - > status ) ;
/* Resubmit urb so we continue receiving IRQ data */
if ( urb - > status ! = - ESHUTDOWN ) {
urb - > dev = serial - > dev ;
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err )
dbg ( " %s: resubmit intr urb failed. (%d) " ,
__FUNCTION__ , err ) ;
}
}
static int sierra_write_room ( struct usb_serial_port * port )
{
struct sierra_port_private * portdata ;
int i ;
int data_len = 0 ;
struct urb * this_urb ;
portdata = usb_get_serial_port_data ( port ) ;
for ( i = 0 ; i < N_OUT_URB ; i + + ) {
this_urb = portdata - > out_urbs [ i ] ;
if ( this_urb & & this_urb - > status ! = - EINPROGRESS )
data_len + = OUT_BUFLEN ;
}
dbg ( " %s: %d " , __FUNCTION__ , data_len ) ;
return data_len ;
}
static int sierra_chars_in_buffer ( struct usb_serial_port * port )
{
struct sierra_port_private * portdata ;
int i ;
int data_len = 0 ;
struct urb * this_urb ;
portdata = usb_get_serial_port_data ( port ) ;
for ( i = 0 ; i < N_OUT_URB ; i + + ) {
this_urb = portdata - > out_urbs [ i ] ;
if ( this_urb & & this_urb - > status = = - EINPROGRESS )
data_len + = this_urb - > transfer_buffer_length ;
}
dbg ( " %s: %d " , __FUNCTION__ , data_len ) ;
return data_len ;
}
static int sierra_open ( struct usb_serial_port * port , struct file * filp )
{
struct sierra_port_private * portdata ;
struct usb_serial * serial = port - > serial ;
int i , err ;
struct urb * urb ;
portdata = usb_get_serial_port_data ( port ) ;
dbg ( " %s " , __FUNCTION__ ) ;
/* Set some sane defaults */
portdata - > rts_state = 1 ;
portdata - > dtr_state = 1 ;
/* Reset low level data toggle and start reading from endpoints */
for ( i = 0 ; i < N_IN_URB ; i + + ) {
urb = portdata - > in_urbs [ i ] ;
if ( ! urb )
continue ;
if ( urb - > dev ! = serial - > dev ) {
dbg ( " %s: dev %p != %p " , __FUNCTION__ ,
urb - > dev , serial - > dev ) ;
continue ;
}
/*
* make sure endpoint data toggle is synchronized with the
* device
*/
usb_clear_halt ( urb - > dev , urb - > pipe ) ;
err = usb_submit_urb ( urb , GFP_KERNEL ) ;
if ( err ) {
dbg ( " %s: submit urb %d failed (%d) %d " ,
__FUNCTION__ , i , err ,
urb - > transfer_buffer_length ) ;
}
}
/* Reset low level data toggle on out endpoints */
for ( i = 0 ; i < N_OUT_URB ; i + + ) {
urb = portdata - > out_urbs [ i ] ;
if ( ! urb )
continue ;
urb - > dev = serial - > dev ;
/* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout ( urb - > pipe ) , 0 ) ; */
}
port - > tty - > low_latency = 1 ;
sierra_send_setup ( port ) ;
return ( 0 ) ;
}
static inline void stop_urb ( struct urb * urb )
{
if ( urb & & urb - > status = = - EINPROGRESS )
usb_kill_urb ( urb ) ;
}
static void sierra_close ( struct usb_serial_port * port , struct file * filp )
{
int i ;
struct usb_serial * serial = port - > serial ;
struct sierra_port_private * portdata ;
dbg ( " %s " , __FUNCTION__ ) ;
portdata = usb_get_serial_port_data ( port ) ;
portdata - > rts_state = 0 ;
portdata - > dtr_state = 0 ;
if ( serial - > dev ) {
sierra_send_setup ( port ) ;
/* Stop reading/writing urbs */
for ( i = 0 ; i < N_IN_URB ; i + + )
stop_urb ( portdata - > in_urbs [ i ] ) ;
for ( i = 0 ; i < N_OUT_URB ; i + + )
stop_urb ( portdata - > out_urbs [ i ] ) ;
}
port - > tty = NULL ;
}
/* Helper functions used by sierra_setup_urbs */
static struct urb * sierra_setup_urb ( struct usb_serial * serial , int endpoint ,
int dir , void * ctx , char * buf , int len ,
usb_complete_t callback )
{
struct urb * urb ;
if ( endpoint = = - 1 )
return NULL ; /* endpoint not needed */
urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ; /* No ISO */
if ( urb = = NULL ) {
dbg ( " %s: alloc for endpoint %d failed. " , __FUNCTION__ , endpoint ) ;
return NULL ;
}
/* Fill URB using supplied data. */
usb_fill_bulk_urb ( urb , serial - > dev ,
usb_sndbulkpipe ( serial - > dev , endpoint ) | dir ,
buf , len , callback , ctx ) ;
return urb ;
}
/* Setup urbs */
static void sierra_setup_urbs ( struct usb_serial * serial )
{
int i , j ;
struct usb_serial_port * port ;
struct sierra_port_private * portdata ;
dbg ( " %s " , __FUNCTION__ ) ;
for ( i = 0 ; i < serial - > num_ports ; i + + ) {
port = serial - > port [ i ] ;
portdata = usb_get_serial_port_data ( port ) ;
/* Do indat endpoints first */
for ( j = 0 ; j < N_IN_URB ; + + j ) {
portdata - > in_urbs [ j ] = sierra_setup_urb ( serial ,
port - > bulk_in_endpointAddress , USB_DIR_IN , port ,
portdata - > in_buffer [ j ] , IN_BUFLEN , sierra_indat_callback ) ;
}
/* outdat endpoints */
for ( j = 0 ; j < N_OUT_URB ; + + j ) {
portdata - > out_urbs [ j ] = sierra_setup_urb ( serial ,
port - > bulk_out_endpointAddress , USB_DIR_OUT , port ,
portdata - > out_buffer [ j ] , OUT_BUFLEN , sierra_outdat_callback ) ;
}
}
}
static int sierra_startup ( struct usb_serial * serial )
{
int i , err ;
struct usb_serial_port * port ;
struct sierra_port_private * portdata ;
dbg ( " %s " , __FUNCTION__ ) ;
/* Now setup per port private data */
for ( i = 0 ; i < serial - > num_ports ; i + + ) {
port = serial - > port [ i ] ;
portdata = kzalloc ( sizeof ( * portdata ) , GFP_KERNEL ) ;
if ( ! portdata ) {
dbg ( " %s: kmalloc for sierra_port_private (%d) failed!. " ,
__FUNCTION__ , i ) ;
return ( 1 ) ;
}
usb_set_serial_port_data ( port , portdata ) ;
if ( ! port - > interrupt_in_urb )
continue ;
err = usb_submit_urb ( port - > interrupt_in_urb , GFP_KERNEL ) ;
if ( err )
dbg ( " %s: submit irq_in urb failed %d " ,
__FUNCTION__ , err ) ;
}
sierra_setup_urbs ( serial ) ;
return ( 0 ) ;
}
static void sierra_shutdown ( struct usb_serial * serial )
{
int i , j ;
struct usb_serial_port * port ;
struct sierra_port_private * portdata ;
dbg ( " %s " , __FUNCTION__ ) ;
/* Stop reading/writing urbs */
for ( i = 0 ; i < serial - > num_ports ; + + i ) {
port = serial - > port [ i ] ;
portdata = usb_get_serial_port_data ( port ) ;
for ( j = 0 ; j < N_IN_URB ; j + + )
stop_urb ( portdata - > in_urbs [ j ] ) ;
for ( j = 0 ; j < N_OUT_URB ; j + + )
stop_urb ( portdata - > out_urbs [ j ] ) ;
}
/* Now free them */
for ( i = 0 ; i < serial - > num_ports ; + + i ) {
port = serial - > port [ i ] ;
portdata = usb_get_serial_port_data ( port ) ;
for ( j = 0 ; j < N_IN_URB ; j + + ) {
if ( portdata - > in_urbs [ j ] ) {
usb_free_urb ( portdata - > in_urbs [ j ] ) ;
portdata - > in_urbs [ j ] = NULL ;
}
}
for ( j = 0 ; j < N_OUT_URB ; j + + ) {
if ( portdata - > out_urbs [ j ] ) {
usb_free_urb ( portdata - > out_urbs [ j ] ) ;
portdata - > out_urbs [ j ] = NULL ;
}
}
}
/* Now free per port private data */
for ( i = 0 ; i < serial - > num_ports ; i + + ) {
port = serial - > port [ i ] ;
kfree ( usb_get_serial_port_data ( port ) ) ;
}
}
static struct usb_serial_driver sierra_1port_device = {
. driver = {
. owner = THIS_MODULE ,
. name = " sierra1 " ,
} ,
. description = " Sierra USB modem (1 port) " ,
. id_table = id_table_1port ,
. num_interrupt_in = NUM_DONT_CARE ,
. num_bulk_in = 1 ,
. num_bulk_out = 1 ,
. num_ports = 1 ,
. open = sierra_open ,
. close = sierra_close ,
. write = sierra_write ,
. write_room = sierra_write_room ,
. chars_in_buffer = sierra_chars_in_buffer ,
. throttle = sierra_rx_throttle ,
. unthrottle = sierra_rx_unthrottle ,
. ioctl = sierra_ioctl ,
. set_termios = sierra_set_termios ,
. break_ctl = sierra_break_ctl ,
. tiocmget = sierra_tiocmget ,
. tiocmset = sierra_tiocmset ,
. attach = sierra_startup ,
. shutdown = sierra_shutdown ,
. read_int_callback = sierra_instat_callback ,
} ;
static struct usb_serial_driver sierra_3port_device = {
. driver = {
. owner = THIS_MODULE ,
. name = " sierra3 " ,
} ,
. description = " Sierra USB modem (3 port) " ,
. id_table = id_table_3port ,
. num_interrupt_in = NUM_DONT_CARE ,
. num_bulk_in = 3 ,
. num_bulk_out = 3 ,
. num_ports = 3 ,
. open = sierra_open ,
. close = sierra_close ,
. write = sierra_write ,
. write_room = sierra_write_room ,
. chars_in_buffer = sierra_chars_in_buffer ,
. throttle = sierra_rx_throttle ,
. unthrottle = sierra_rx_unthrottle ,
. ioctl = sierra_ioctl ,
. set_termios = sierra_set_termios ,
. break_ctl = sierra_break_ctl ,
. tiocmget = sierra_tiocmget ,
. tiocmset = sierra_tiocmset ,
. attach = sierra_startup ,
. shutdown = sierra_shutdown ,
. read_int_callback = sierra_instat_callback ,
} ;
/* Functions used by new usb-serial code. */
static int __init sierra_init ( void )
{
int retval ;
retval = usb_serial_register ( & sierra_1port_device ) ;
if ( retval )
goto failed_1port_device_register ;
retval = usb_serial_register ( & sierra_3port_device ) ;
if ( retval )
goto failed_3port_device_register ;
retval = usb_register ( & sierra_driver ) ;
if ( retval )
goto failed_driver_register ;
info ( DRIVER_DESC " : " DRIVER_VERSION ) ;
return 0 ;
failed_driver_register :
usb_serial_deregister ( & sierra_3port_device ) ;
failed_3port_device_register :
usb_serial_deregister ( & sierra_1port_device ) ;
failed_1port_device_register :
return retval ;
}
static void __exit sierra_exit ( void )
{
usb_deregister ( & sierra_driver ) ;
usb_serial_deregister ( & sierra_1port_device ) ;
usb_serial_deregister ( & sierra_3port_device ) ;
}
module_init ( sierra_init ) ;
module_exit ( sierra_exit ) ;
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_VERSION ( DRIVER_VERSION ) ;
MODULE_LICENSE ( " GPL " ) ;
# ifdef CONFIG_USB_DEBUG
module_param ( debug , bool , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( debug , " Debug messages " ) ;
# endif