2006-06-30 22:17:55 +04:00
/*
2006-10-14 10:53:21 +04:00
USB Driver for Sierra Wireless
2008-03-28 20:05:08 +03:00
Copyright ( C ) 2006 , 2007 , 2008 Kevin Lloyd < klloyd @ sierrawireless . com >
2006-10-14 10:53:21 +04:00
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 >
*/
2008-08-26 06:20:40 +04:00
# define DRIVER_VERSION "v.1.3.2"
2008-03-28 20:05:08 +03:00
# define DRIVER_AUTHOR "Kevin Lloyd <klloyd@sierrawireless.com>"
2006-10-14 10:53:21 +04:00
# define DRIVER_DESC "USB Driver for Sierra Wireless USB modems"
2006-06-30 22:17:55 +04:00
# include <linux/kernel.h>
2006-10-14 10:53:21 +04:00
# include <linux/jiffies.h>
# include <linux/errno.h>
2006-06-30 22:17:55 +04:00
# include <linux/tty.h>
2006-10-14 10:53:21 +04:00
# include <linux/tty_flip.h>
2006-06-30 22:17:55 +04:00
# include <linux/module.h>
# include <linux/usb.h>
2006-07-12 08:22:58 +04:00
# include <linux/usb/serial.h>
2008-01-10 22:11:04 +03:00
# include <linux/usb/ch9.h>
2006-06-30 22:17:55 +04:00
2008-01-10 22:11:04 +03:00
# define SWIMS_USB_REQUEST_SetPower 0x00
# define SWIMS_USB_REQUEST_SetNmea 0x07
2007-07-17 00:49:27 +04:00
/* per port private data */
# define N_IN_URB 4
# define N_OUT_URB 4
# define IN_BUFLEN 4096
static int debug ;
2008-01-10 22:11:04 +03:00
static int nmea ;
2007-07-17 00:49:27 +04:00
2007-07-29 18:59:02 +04:00
static int sierra_set_power_state ( struct usb_device * udev , __u16 swiState )
2007-07-17 00:49:27 +04:00
{
int result ;
2008-07-11 01:14:47 +04:00
dev_dbg ( & udev - > dev , " %s " , __func__ ) ;
2007-07-17 00:49:27 +04:00
result = usb_control_msg ( udev , usb_sndctrlpipe ( udev , 0 ) ,
2008-01-10 22:11:04 +03:00
SWIMS_USB_REQUEST_SetPower , /* __u8 request */
2008-03-28 20:05:08 +03:00
USB_TYPE_VENDOR , /* __u8 request type */
2008-01-10 22:11:04 +03:00
swiState , /* __u16 value */
0 , /* __u16 index */
NULL , /* void *data */
0 , /* __u16 size */
USB_CTRL_SET_TIMEOUT ) ; /* int timeout */
2007-07-17 00:49:27 +04:00
return result ;
}
2008-01-10 22:11:04 +03:00
static int sierra_vsc_set_nmea ( struct usb_device * udev , __u16 enable )
2007-07-17 00:49:27 +04:00
{
int result ;
2008-07-11 01:14:47 +04:00
dev_dbg ( & udev - > dev , " %s " , __func__ ) ;
2008-01-10 22:11:04 +03:00
result = usb_control_msg ( udev , usb_sndctrlpipe ( udev , 0 ) ,
SWIMS_USB_REQUEST_SetNmea , /* __u8 request */
2008-03-28 20:05:08 +03:00
USB_TYPE_VENDOR , /* __u8 request type */
2008-01-10 22:11:04 +03:00
enable , /* __u16 value */
0x0000 , /* __u16 index */
NULL , /* void *data */
0 , /* __u16 size */
USB_CTRL_SET_TIMEOUT ) ; /* int timeout */
return result ;
}
static int sierra_calc_num_ports ( struct usb_serial * serial )
{
int result ;
int * num_ports = usb_get_serial_data ( serial ) ;
2008-07-11 01:14:47 +04:00
dev_dbg ( & serial - > dev - > dev , " %s " , __func__ ) ;
2008-01-10 22:11:04 +03:00
result = * num_ports ;
if ( result ) {
kfree ( num_ports ) ;
usb_set_serial_data ( serial , NULL ) ;
}
return result ;
}
2008-04-02 22:24:56 +04:00
static int sierra_calc_interface ( struct usb_serial * serial )
{
2008-07-11 01:14:47 +04:00
int interface ;
struct usb_interface * p_interface ;
struct usb_host_interface * p_host_interface ;
dev_dbg ( & serial - > dev - > dev , " %s " , __func__ ) ;
2008-04-02 22:24:56 +04:00
2008-07-11 01:14:47 +04:00
/* Get the interface structure pointer from the serial struct */
p_interface = serial - > interface ;
2008-04-02 22:24:56 +04:00
2008-07-11 01:14:47 +04:00
/* Get a pointer to the host interface structure */
p_host_interface = p_interface - > cur_altsetting ;
2008-04-02 22:24:56 +04:00
2008-07-11 01:14:47 +04:00
/* read the interface descriptor for this active altsetting
* to find out the interface number we are on
*/
interface = p_host_interface - > desc . bInterfaceNumber ;
2008-04-02 22:24:56 +04:00
2008-07-11 01:14:47 +04:00
return interface ;
2008-04-02 22:24:56 +04:00
}
2008-01-10 22:11:04 +03:00
static int sierra_probe ( struct usb_serial * serial ,
const struct usb_device_id * id )
{
int result = 0 ;
2007-07-17 00:49:27 +04:00
struct usb_device * udev ;
2008-01-10 22:11:04 +03:00
int * num_ports ;
u8 ifnum ;
2008-07-11 01:14:51 +04:00
u8 numendpoints ;
2008-07-11 01:14:47 +04:00
dev_dbg ( & serial - > dev - > dev , " %s " , __func__ ) ;
2007-07-17 00:49:27 +04:00
2008-01-10 22:11:04 +03:00
num_ports = kmalloc ( sizeof ( * num_ports ) , GFP_KERNEL ) ;
if ( ! num_ports )
return - ENOMEM ;
ifnum = serial - > interface - > cur_altsetting - > desc . bInterfaceNumber ;
2008-07-11 01:14:51 +04:00
numendpoints = serial - > interface - > cur_altsetting - > desc . bNumEndpoints ;
2008-01-10 22:11:04 +03:00
udev = serial - > dev ;
2007-07-17 00:49:27 +04:00
2008-07-11 01:14:51 +04:00
/* Figure out the interface number from the serial structure */
ifnum = sierra_calc_interface ( serial ) ;
2008-04-02 22:24:56 +04:00
2008-07-11 01:14:51 +04:00
/*
* If this interface supports more than 1 alternate
* select the 2 nd one
*/
if ( serial - > interface - > num_altsetting = = 2 ) {
dev_dbg ( & udev - > dev , " Selecting alt setting for interface %d \n " ,
ifnum ) ;
/* We know the alternate setting is 1 for the MC8785 */
usb_set_interface ( udev , ifnum , 1 ) ;
}
2008-04-02 22:24:56 +04:00
2008-07-11 01:14:51 +04:00
/* Dummy interface present on some SKUs should be ignored */
2008-07-11 01:14:54 +04:00
if ( ifnum = = 0x99 )
2008-01-10 22:11:04 +03:00
* num_ports = 0 ;
2008-07-11 01:14:51 +04:00
else if ( numendpoints < = 3 )
* num_ports = 1 ;
2008-01-10 22:11:04 +03:00
else
2008-07-11 01:14:51 +04:00
* num_ports = ( numendpoints - 1 ) / 2 ;
2008-01-10 22:11:04 +03:00
/*
* save off our num_ports info so that we can use it in the
* calc_num_ports callback
*/
usb_set_serial_data ( serial , ( void * ) num_ports ) ;
2007-07-17 00:49:27 +04:00
2008-01-10 22:11:04 +03:00
return result ;
2007-07-17 00:49:27 +04:00
}
2006-10-14 10:53:21 +04:00
2006-06-30 22:17:55 +04:00
static struct usb_device_id id_table [ ] = {
2007-01-18 03:04:18 +03:00
{ USB_DEVICE ( 0x1199 , 0x0017 ) } , /* Sierra Wireless EM5625 */
2006-06-30 22:17:55 +04:00
{ USB_DEVICE ( 0x1199 , 0x0018 ) } , /* Sierra Wireless MC5720 */
2007-01-18 03:04:18 +03:00
{ USB_DEVICE ( 0x1199 , 0x0218 ) } , /* Sierra Wireless MC5720 */
2008-08-23 02:30:44 +04:00
{ USB_DEVICE ( 0x03f0 , 0x1b1d ) } , /* HP ev2200 a.k.a MC5720 */
2006-06-30 22:17:55 +04:00
{ USB_DEVICE ( 0x1199 , 0x0020 ) } , /* Sierra Wireless MC5725 */
2008-07-11 01:14:47 +04:00
{ USB_DEVICE ( 0x1199 , 0x0024 ) } , /* Sierra Wireless MC5727 */
2007-12-04 21:37:12 +03:00
{ USB_DEVICE ( 0x1199 , 0x0220 ) } , /* Sierra Wireless MC5725 */
2006-06-30 22:17:55 +04:00
{ USB_DEVICE ( 0x1199 , 0x0019 ) } , /* Sierra Wireless AirCard 595 */
2007-01-18 03:04:18 +03:00
{ USB_DEVICE ( 0x1199 , 0x0021 ) } , /* Sierra Wireless AirCard 597E */
2007-07-17 00:49:29 +04:00
{ USB_DEVICE ( 0x1199 , 0x0120 ) } , /* Sierra Wireless USB Dongle 595U */
2008-07-11 01:14:47 +04:00
/* Sierra Wireless C597 */
{ USB_DEVICE_AND_INTERFACE_INFO ( 0x1199 , 0x0023 , 0xFF , 0xFF , 0xFF ) } ,
2008-07-11 01:14:51 +04:00
/* Sierra Wireless Device */
{ USB_DEVICE_AND_INTERFACE_INFO ( 0x1199 , 0x0025 , 0xFF , 0xFF , 0xFF ) } ,
{ USB_DEVICE ( 0x1199 , 0x0026 ) } , /* Sierra Wireless Device */
2008-08-26 06:20:40 +04:00
{ USB_DEVICE ( 0x1199 , 0x0027 ) } , /* Sierra Wireless Device */
{ USB_DEVICE ( 0x1199 , 0x0028 ) } , /* Sierra Wireless Device */
2007-07-17 00:49:29 +04:00
2006-06-30 22:17:55 +04:00
{ USB_DEVICE ( 0x1199 , 0x6802 ) } , /* Sierra Wireless MC8755 */
2007-01-18 03:04:18 +03:00
{ USB_DEVICE ( 0x1199 , 0x6804 ) } , /* Sierra Wireless MC8755 */
2006-06-30 22:17:55 +04:00
{ USB_DEVICE ( 0x1199 , 0x6803 ) } , /* Sierra Wireless MC8765 */
2007-07-17 00:49:29 +04:00
{ USB_DEVICE ( 0x1199 , 0x6812 ) } , /* Sierra Wireless MC8775 & AC 875U */
2008-07-11 01:14:47 +04:00
{ USB_DEVICE ( 0x1199 , 0x6813 ) } , /* Sierra Wireless MC8775 (Lenovo) */
2008-03-14 10:53:24 +03:00
{ USB_DEVICE ( 0x1199 , 0x6815 ) } , /* Sierra Wireless MC8775 */
2008-04-17 09:47:34 +04:00
{ USB_DEVICE ( 0x03f0 , 0x1e1d ) } , /* HP hs2300 a.k.a MC8775 */
2006-06-30 22:17:55 +04:00
{ USB_DEVICE ( 0x1199 , 0x6820 ) } , /* Sierra Wireless AirCard 875 */
2008-04-02 22:24:56 +04:00
{ USB_DEVICE ( 0x1199 , 0x6821 ) } , /* Sierra Wireless AirCard 875U */
2008-07-11 01:14:47 +04:00
{ USB_DEVICE ( 0x1199 , 0x6832 ) } , /* Sierra Wireless MC8780 */
{ USB_DEVICE ( 0x1199 , 0x6833 ) } , /* Sierra Wireless MC8781 */
2008-09-17 20:03:38 +04:00
{ USB_DEVICE ( 0x1199 , 0x683A ) } , /* Sierra Wireless MC8785 */
2008-07-11 01:14:51 +04:00
{ USB_DEVICE ( 0x1199 , 0x683B ) } , /* Sierra Wireless MC8785 Composite */
{ USB_DEVICE ( 0x1199 , 0x683C ) } , /* Sierra Wireless MC8790 */
{ USB_DEVICE ( 0x1199 , 0x683D ) } , /* Sierra Wireless MC8790 */
{ USB_DEVICE ( 0x1199 , 0x683E ) } , /* Sierra Wireless MC8790 */
2007-07-17 00:49:29 +04:00
{ USB_DEVICE ( 0x1199 , 0x6850 ) } , /* Sierra Wireless AirCard 880 */
{ USB_DEVICE ( 0x1199 , 0x6851 ) } , /* Sierra Wireless AirCard 881 */
{ USB_DEVICE ( 0x1199 , 0x6852 ) } , /* Sierra Wireless AirCard 880 E */
{ USB_DEVICE ( 0x1199 , 0x6853 ) } , /* Sierra Wireless AirCard 881 E */
2008-01-10 22:11:04 +03:00
{ USB_DEVICE ( 0x1199 , 0x6855 ) } , /* Sierra Wireless AirCard 880 U */
2007-12-31 05:11:35 +03:00
{ USB_DEVICE ( 0x1199 , 0x6856 ) } , /* Sierra Wireless AirCard 881 U */
2008-07-11 01:14:51 +04:00
{ USB_DEVICE ( 0x1199 , 0x6859 ) } , /* Sierra Wireless AirCard 885 E */
{ USB_DEVICE ( 0x1199 , 0x685A ) } , /* Sierra Wireless AirCard 885 E */
/* Sierra Wireless C885 */
{ USB_DEVICE_AND_INTERFACE_INFO ( 0x1199 , 0x6880 , 0xFF , 0xFF , 0xFF ) } ,
/* Sierra Wireless Device */
{ USB_DEVICE_AND_INTERFACE_INFO ( 0x1199 , 0x6890 , 0xFF , 0xFF , 0xFF ) } ,
/* Sierra Wireless Device */
2008-08-26 06:20:40 +04:00
{ USB_DEVICE_AND_INTERFACE_INFO ( 0x1199 , 0x6891 , 0xFF , 0xFF , 0xFF ) } ,
/* Sierra Wireless Device */
2008-07-11 01:14:51 +04:00
{ USB_DEVICE_AND_INTERFACE_INFO ( 0x1199 , 0x6892 , 0xFF , 0xFF , 0xFF ) } ,
{ USB_DEVICE ( 0x1199 , 0x0112 ) } , /* Sierra Wireless AirCard 580 */
{ USB_DEVICE ( 0x0F3D , 0x0112 ) } , /* Airprime/Sierra PC 5220 */
2007-07-17 00:49:27 +04:00
2006-10-14 10:53:21 +04:00
{ }
} ;
2006-10-17 21:17:58 +04:00
MODULE_DEVICE_TABLE ( usb , id_table ) ;
2006-10-14 10:53:21 +04:00
2006-06-30 22:17:55 +04:00
static struct usb_driver sierra_driver = {
2006-10-14 10:53:21 +04:00
. name = " sierra " ,
2008-01-10 22:11:04 +03:00
. probe = usb_serial_probe ,
2006-10-14 10:53:21 +04:00
. disconnect = usb_serial_disconnect ,
. id_table = id_table ,
2006-10-17 21:17:58 +04:00
. no_dynamic_id = 1 ,
2006-10-14 10:53:21 +04:00
} ;
struct sierra_port_private {
2007-06-20 09:22:23 +04:00
spinlock_t lock ; /* lock the structure */
int outstanding_urbs ; /* number of out urbs in flight */
2008-03-14 10:53:24 +03:00
/* Input endpoints and buffers for this port */
2006-10-14 10:53:21 +04:00
struct urb * in_urbs [ N_IN_URB ] ;
2008-03-14 10:53:24 +03:00
char * in_buffer [ N_IN_URB ] ;
2006-10-14 10:53:21 +04:00
/* 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 ;
} ;
2008-07-22 14:09:07 +04:00
static int sierra_send_setup ( struct tty_struct * tty ,
struct usb_serial_port * port )
2006-06-30 22:17:55 +04:00
{
2006-10-17 21:17:58 +04:00
struct usb_serial * serial = port - > serial ;
struct sierra_port_private * portdata ;
2008-01-10 22:11:04 +03:00
__u16 interface = 0 ;
2006-10-14 10:53:21 +04:00
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s " , __func__ ) ;
2006-10-14 10:53:21 +04:00
2006-10-17 21:17:58 +04:00
portdata = usb_get_serial_port_data ( port ) ;
2006-10-14 10:53:21 +04:00
2008-07-22 14:09:07 +04:00
if ( tty ) {
2006-10-17 21:17:58 +04:00
int val = 0 ;
if ( portdata - > dtr_state )
val | = 0x01 ;
if ( portdata - > rts_state )
val | = 0x02 ;
2006-10-14 10:53:21 +04:00
2008-07-11 01:14:51 +04:00
/* If composite device then properly report interface */
if ( serial - > num_ports = = 1 )
interface = sierra_calc_interface ( serial ) ;
/* Otherwise the need to do non-composite mapping */
else {
if ( port - > bulk_out_endpointAddress = = 2 )
interface = 0 ;
else if ( port - > bulk_out_endpointAddress = = 4 )
interface = 1 ;
else if ( port - > bulk_out_endpointAddress = = 5 )
interface = 2 ;
}
2008-01-10 22:11:04 +03:00
2006-10-17 21:17:58 +04:00
return usb_control_msg ( serial - > dev ,
usb_rcvctrlpipe ( serial - > dev , 0 ) ,
2008-01-10 22:11:04 +03:00
0x22 , 0x21 , val , interface ,
NULL , 0 , USB_CTRL_SET_TIMEOUT ) ;
2006-10-17 21:17:58 +04:00
}
2006-06-30 22:17:55 +04:00
2006-10-17 21:17:58 +04:00
return 0 ;
2006-06-30 22:17:55 +04:00
}
2008-07-22 14:09:07 +04:00
static void sierra_set_termios ( struct tty_struct * tty ,
struct usb_serial_port * port , struct ktermios * old_termios )
2006-10-14 10:53:21 +04:00
{
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s " , __func__ ) ;
2008-07-22 14:09:07 +04:00
tty_termios_copy_hw ( tty - > termios , old_termios ) ;
sierra_send_setup ( tty , port ) ;
2006-10-14 10:53:21 +04:00
}
2008-07-22 14:09:07 +04:00
static int sierra_tiocmget ( struct tty_struct * tty , struct file * file )
2006-10-14 10:53:21 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2006-10-14 10:53:21 +04:00
unsigned int value ;
struct sierra_port_private * portdata ;
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s " , __func__ ) ;
2006-10-14 10:53:21 +04:00
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 ;
}
2008-07-22 14:09:07 +04:00
static int sierra_tiocmset ( struct tty_struct * tty , struct file * file ,
2006-10-14 10:53:21 +04:00
unsigned int set , unsigned int clear )
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2006-10-14 10:53:21 +04:00
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 ;
2008-07-22 14:09:07 +04:00
return sierra_send_setup ( tty , port ) ;
2006-10-14 10:53:21 +04:00
}
2007-06-20 09:22:23 +04:00
static void sierra_outdat_callback ( struct urb * urb )
{
struct usb_serial_port * port = urb - > context ;
struct sierra_port_private * portdata = usb_get_serial_port_data ( port ) ;
int status = urb - > status ;
unsigned long flags ;
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s - port %d " , __func__ , port - > number ) ;
2007-06-20 09:22:23 +04:00
/* free up the transfer buffer, as usb_free_urb() does not do this */
kfree ( urb - > transfer_buffer ) ;
if ( status )
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s - nonzero write bulk status "
" received: %d " , __func__ , status ) ;
2007-06-20 09:22:23 +04:00
spin_lock_irqsave ( & portdata - > lock , flags ) ;
- - portdata - > outstanding_urbs ;
spin_unlock_irqrestore ( & portdata - > lock , flags ) ;
usb_serial_port_softint ( port ) ;
}
2006-10-14 10:53:21 +04:00
/* Write */
2008-07-22 14:09:07 +04:00
static int sierra_write ( struct tty_struct * tty , struct usb_serial_port * port ,
const unsigned char * buf , int count )
2006-10-14 10:53:21 +04:00
{
2007-06-20 09:22:23 +04:00
struct sierra_port_private * portdata = usb_get_serial_port_data ( port ) ;
struct usb_serial * serial = port - > serial ;
unsigned long flags ;
unsigned char * buffer ;
struct urb * urb ;
int status ;
2006-10-14 10:53:21 +04:00
portdata = usb_get_serial_port_data ( port ) ;
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s: write (%d chars) " , __func__ , count ) ;
2006-10-14 10:53:21 +04:00
2007-06-20 09:22:23 +04:00
spin_lock_irqsave ( & portdata - > lock , flags ) ;
if ( portdata - > outstanding_urbs > N_OUT_URB ) {
spin_unlock_irqrestore ( & portdata - > lock , flags ) ;
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s - write limit hit \n " , __func__ ) ;
2007-06-20 09:22:23 +04:00
return 0 ;
}
portdata - > outstanding_urbs + + ;
spin_unlock_irqrestore ( & portdata - > lock , flags ) ;
buffer = kmalloc ( count , GFP_ATOMIC ) ;
if ( ! buffer ) {
dev_err ( & port - > dev , " out of memory \n " ) ;
count = - ENOMEM ;
goto error_no_buffer ;
}
2006-10-14 10:53:21 +04:00
2007-06-20 09:22:23 +04:00
urb = usb_alloc_urb ( 0 , GFP_ATOMIC ) ;
if ( ! urb ) {
dev_err ( & port - > dev , " no more free urbs \n " ) ;
count = - ENOMEM ;
goto error_no_urb ;
}
2006-10-14 10:53:21 +04:00
2007-06-20 09:22:23 +04:00
memcpy ( buffer , buf , count ) ;
2006-10-14 10:53:21 +04:00
2008-03-04 03:08:34 +03:00
usb_serial_debug_data ( debug , & port - > dev , __func__ , count , buffer ) ;
2007-06-20 09:22:23 +04:00
usb_fill_bulk_urb ( urb , serial - > dev ,
usb_sndbulkpipe ( serial - > dev ,
port - > bulk_out_endpointAddress ) ,
buffer , count , sierra_outdat_callback , port ) ;
/* send it down the pipe */
status = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( status ) {
dev_err ( & port - > dev , " %s - usb_submit_urb(write bulk) failed "
2008-03-04 03:08:34 +03:00
" with status = %d \n " , __func__ , status ) ;
2007-06-20 09:22:23 +04:00
count = status ;
goto error ;
2006-10-14 10:53:21 +04:00
}
2007-06-20 09:22:23 +04:00
/* we are done with this urb, so let the host driver
* really free it when it is finished with it */
usb_free_urb ( urb ) ;
return count ;
error :
usb_free_urb ( urb ) ;
error_no_urb :
kfree ( buffer ) ;
error_no_buffer :
spin_lock_irqsave ( & portdata - > lock , flags ) ;
- - portdata - > outstanding_urbs ;
spin_unlock_irqrestore ( & portdata - > lock , flags ) ;
2006-10-14 10:53:21 +04:00
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 ;
2007-06-16 02:44:13 +04:00
int status = urb - > status ;
2006-10-14 10:53:21 +04:00
2008-03-04 03:08:34 +03:00
dbg ( " %s: %p " , __func__ , urb ) ;
2006-10-14 10:53:21 +04:00
endpoint = usb_pipeendpoint ( urb - > pipe ) ;
2008-02-24 13:41:47 +03:00
port = urb - > context ;
2006-10-14 10:53:21 +04:00
2007-06-16 02:44:13 +04:00
if ( status ) {
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s: nonzero status: %d on "
" endpoint %02x. " , __func__ , status , endpoint ) ;
2006-10-14 10:53:21 +04:00
} else {
if ( urb - > actual_length ) {
2009-01-02 16:42:56 +03:00
tty = tty_port_tty_get ( & port - > port ) ;
2006-10-14 10:53:21 +04:00
tty_buffer_request_room ( tty , urb - > actual_length ) ;
tty_insert_flip_string ( tty , data , urb - > actual_length ) ;
tty_flip_buffer_push ( tty ) ;
2008-10-13 13:39:46 +04:00
tty_kref_put ( tty ) ;
} else
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s: empty read urb "
" received " , __func__ ) ;
2006-10-14 10:53:21 +04:00
/* Resubmit urb so we continue receiving */
2008-07-22 14:09:07 +04:00
if ( port - > port . count & & status ! = - ESHUTDOWN ) {
2006-10-14 10:53:21 +04:00
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err )
2007-06-20 09:22:23 +04:00
dev_err ( & port - > dev , " resubmit read urb failed. "
2007-10-18 14:06:30 +04:00
" (%d) \n " , err ) ;
2006-10-14 10:53:21 +04:00
}
}
return ;
}
static void sierra_instat_callback ( struct urb * urb )
{
int err ;
2007-06-16 02:44:13 +04:00
int status = urb - > status ;
2008-02-24 13:41:47 +03:00
struct usb_serial_port * port = urb - > context ;
2006-10-14 10:53:21 +04:00
struct sierra_port_private * portdata = usb_get_serial_port_data ( port ) ;
struct usb_serial * serial = port - > serial ;
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s " , __func__ ) ;
dev_dbg ( & port - > dev , " %s: urb %p port %p has data %p " , __func__ ,
urb , port , portdata ) ;
2006-10-14 10:53:21 +04:00
2007-06-16 02:44:13 +04:00
if ( status = = 0 ) {
2006-10-14 10:53:21 +04:00
struct usb_ctrlrequest * req_pkt =
( struct usb_ctrlrequest * ) urb - > transfer_buffer ;
if ( ! req_pkt ) {
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s: NULL req_pkt \n " ,
__func__ ) ;
2006-10-14 10:53:21 +04:00
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 ) ) ;
2008-10-13 13:39:46 +04:00
struct tty_struct * tty ;
2006-10-14 10:53:21 +04:00
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s: signal x%x " , __func__ ,
signals ) ;
2006-10-14 10:53:21 +04:00
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 ) ;
2008-10-13 13:39:46 +04:00
tty = tty_port_tty_get ( & port - > port ) ;
if ( tty & & ! C_CLOCAL ( tty ) & &
2006-10-14 10:53:21 +04:00
old_dcd_state & & ! portdata - > dcd_state )
2008-10-13 13:39:46 +04:00
tty_hangup ( tty ) ;
tty_kref_put ( tty ) ;
2006-10-14 10:53:21 +04:00
} else {
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s: type %x req %x " ,
__func__ , req_pkt - > bRequestType ,
req_pkt - > bRequest ) ;
2006-10-14 10:53:21 +04:00
}
} else
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s: error %d " , __func__ , status ) ;
2006-10-14 10:53:21 +04:00
/* Resubmit urb so we continue receiving IRQ data */
2007-06-16 02:44:13 +04:00
if ( status ! = - ESHUTDOWN ) {
2006-10-14 10:53:21 +04:00
urb - > dev = serial - > dev ;
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err )
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s: resubmit intr urb "
" failed. (%d) " , __func__ , err ) ;
2006-10-14 10:53:21 +04:00
}
}
2008-07-22 14:09:07 +04:00
static int sierra_write_room ( struct tty_struct * tty )
2006-10-14 10:53:21 +04:00
{
2008-07-22 14:09:07 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2007-06-20 09:22:23 +04:00
struct sierra_port_private * portdata = usb_get_serial_port_data ( port ) ;
unsigned long flags ;
2006-10-14 10:53:21 +04:00
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s - port %d " , __func__ , port - > number ) ;
2006-10-14 10:53:21 +04:00
2007-06-20 09:22:23 +04:00
/* try to give a good number back based on if we have any free urbs at
* this point in time */
spin_lock_irqsave ( & portdata - > lock , flags ) ;
if ( portdata - > outstanding_urbs > N_OUT_URB * 2 / 3 ) {
spin_unlock_irqrestore ( & portdata - > lock , flags ) ;
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s - write limit hit \n " , __func__ ) ;
2007-06-20 09:22:23 +04:00
return 0 ;
2006-10-14 10:53:21 +04:00
}
2007-06-20 09:22:23 +04:00
spin_unlock_irqrestore ( & portdata - > lock , flags ) ;
2006-10-14 10:53:21 +04:00
2007-06-20 09:22:23 +04:00
return 2048 ;
2006-10-14 10:53:21 +04:00
}
2008-07-22 14:09:07 +04:00
static int sierra_open ( struct tty_struct * tty ,
struct usb_serial_port * port , struct file * filp )
2006-10-14 10:53:21 +04:00
{
struct sierra_port_private * portdata ;
struct usb_serial * serial = port - > serial ;
2007-06-20 09:22:23 +04:00
int i ;
2006-10-14 10:53:21 +04:00
struct urb * urb ;
2007-01-18 03:04:18 +03:00
int result ;
2006-10-14 10:53:21 +04:00
portdata = usb_get_serial_port_data ( port ) ;
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s " , __func__ ) ;
2006-10-14 10:53:21 +04:00
/* 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 ] ;
2007-06-20 09:22:23 +04:00
if ( ! urb )
2006-10-14 10:53:21 +04:00
continue ;
if ( urb - > dev ! = serial - > dev ) {
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s: dev %p != %p " ,
__func__ , urb - > dev , serial - > dev ) ;
2006-10-14 10:53:21 +04:00
continue ;
}
/*
* make sure endpoint data toggle is synchronized with the
* device
*/
usb_clear_halt ( urb - > dev , urb - > pipe ) ;
2007-06-20 09:22:23 +04:00
result = usb_submit_urb ( urb , GFP_KERNEL ) ;
if ( result ) {
2007-10-18 14:06:30 +04:00
dev_err ( & port - > dev , " submit urb %d failed (%d) %d \n " ,
2007-06-20 09:22:23 +04:00
i , result , urb - > transfer_buffer_length ) ;
2006-10-14 10:53:21 +04:00
}
}
2008-07-22 14:09:07 +04:00
if ( tty )
tty - > low_latency = 1 ;
2006-10-14 10:53:21 +04:00
2008-07-22 14:09:07 +04:00
sierra_send_setup ( tty , port ) ;
2006-10-14 10:53:21 +04:00
2007-06-20 09:22:23 +04:00
/* start up the interrupt endpoint if we have one */
if ( port - > interrupt_in_urb ) {
result = usb_submit_urb ( port - > interrupt_in_urb , GFP_KERNEL ) ;
if ( result )
2007-10-18 14:06:30 +04:00
dev_err ( & port - > dev , " submit irq_in urb failed %d \n " ,
2007-06-20 09:22:23 +04:00
result ) ;
}
return 0 ;
2006-10-14 10:53:21 +04:00
}
2008-07-22 14:09:07 +04:00
static void sierra_close ( struct tty_struct * tty ,
struct usb_serial_port * port , struct file * filp )
2006-10-14 10:53:21 +04:00
{
int i ;
struct usb_serial * serial = port - > serial ;
struct sierra_port_private * portdata ;
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s " , __func__ ) ;
2006-10-14 10:53:21 +04:00
portdata = usb_get_serial_port_data ( port ) ;
portdata - > rts_state = 0 ;
portdata - > dtr_state = 0 ;
if ( serial - > dev ) {
2008-01-21 19:44:10 +03:00
mutex_lock ( & serial - > disc_mutex ) ;
if ( ! serial - > disconnected )
2008-07-22 14:09:07 +04:00
sierra_send_setup ( tty , port ) ;
2008-01-21 19:44:10 +03:00
mutex_unlock ( & serial - > disc_mutex ) ;
2006-10-14 10:53:21 +04:00
/* Stop reading/writing urbs */
for ( i = 0 ; i < N_IN_URB ; i + + )
2007-06-20 09:22:23 +04:00
usb_kill_urb ( portdata - > in_urbs [ i ] ) ;
2006-10-14 10:53:21 +04:00
}
2007-06-20 09:22:23 +04:00
usb_kill_urb ( port - > interrupt_in_urb ) ;
2008-10-13 13:39:46 +04:00
tty_port_tty_set ( & port - > port , NULL ) ;
2006-10-14 10:53:21 +04:00
}
static int sierra_startup ( struct usb_serial * serial )
{
struct usb_serial_port * port ;
struct sierra_port_private * portdata ;
2007-06-20 09:22:23 +04:00
struct urb * urb ;
int i ;
int j ;
2006-10-14 10:53:21 +04:00
2008-09-17 08:05:24 +04:00
dev_dbg ( & serial - > dev - > dev , " %s " , __func__ ) ;
2006-10-14 10:53:21 +04:00
2008-01-10 22:11:04 +03:00
/* Set Device mode to D0 */
2007-07-17 00:49:27 +04:00
sierra_set_power_state ( serial - > dev , 0x0000 ) ;
2008-01-10 22:11:04 +03:00
/* Check NMEA and set */
if ( nmea )
sierra_vsc_set_nmea ( serial - > dev , 1 ) ;
2006-10-14 10:53:21 +04:00
/* 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 ) {
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s: kmalloc for "
" sierra_port_private (%d) failed!. " ,
__func__ , i ) ;
2007-06-20 09:22:23 +04:00
return - ENOMEM ;
2006-10-14 10:53:21 +04:00
}
2007-06-20 09:22:23 +04:00
spin_lock_init ( & portdata - > lock ) ;
2008-03-14 10:53:24 +03:00
for ( j = 0 ; j < N_IN_URB ; j + + ) {
portdata - > in_buffer [ j ] = kmalloc ( IN_BUFLEN , GFP_KERNEL ) ;
if ( ! portdata - > in_buffer [ j ] ) {
for ( - - j ; j > = 0 ; j - - )
kfree ( portdata - > in_buffer [ j ] ) ;
kfree ( portdata ) ;
return - ENOMEM ;
}
}
2006-10-14 10:53:21 +04:00
usb_set_serial_port_data ( port , portdata ) ;
2007-06-20 09:22:23 +04:00
/* initialize the in urbs */
for ( j = 0 ; j < N_IN_URB ; + + j ) {
urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( urb = = NULL ) {
2008-09-17 08:05:24 +04:00
dev_dbg ( & port - > dev , " %s: alloc for in "
" port failed. " , __func__ ) ;
2007-06-20 09:22:23 +04:00
continue ;
}
/* Fill URB using supplied data. */
usb_fill_bulk_urb ( urb , serial - > dev ,
usb_rcvbulkpipe ( serial - > dev ,
port - > bulk_in_endpointAddress ) ,
portdata - > in_buffer [ j ] , IN_BUFLEN ,
sierra_indat_callback , port ) ;
portdata - > in_urbs [ j ] = urb ;
}
2006-10-14 10:53:21 +04:00
}
2007-06-20 09:22:23 +04:00
return 0 ;
2006-10-14 10:53:21 +04:00
}
static void sierra_shutdown ( struct usb_serial * serial )
{
int i , j ;
struct usb_serial_port * port ;
struct sierra_port_private * portdata ;
2008-09-17 08:05:24 +04:00
dev_dbg ( & serial - > dev - > dev , " %s " , __func__ ) ;
2006-10-14 10:53:21 +04:00
for ( i = 0 ; i < serial - > num_ports ; + + i ) {
port = serial - > port [ i ] ;
2007-04-26 11:12:01 +04:00
if ( ! port )
continue ;
2006-10-14 10:53:21 +04:00
portdata = usb_get_serial_port_data ( port ) ;
2007-04-26 11:12:01 +04:00
if ( ! portdata )
continue ;
2006-10-14 10:53:21 +04:00
for ( j = 0 ; j < N_IN_URB ; j + + ) {
2007-06-20 09:22:23 +04:00
usb_kill_urb ( portdata - > in_urbs [ j ] ) ;
usb_free_urb ( portdata - > in_urbs [ j ] ) ;
2008-03-14 10:53:24 +03:00
kfree ( portdata - > in_buffer [ j ] ) ;
2006-10-14 10:53:21 +04:00
}
2007-06-20 09:22:23 +04:00
kfree ( portdata ) ;
usb_set_serial_port_data ( port , NULL ) ;
2006-10-14 10:53:21 +04:00
}
}
2008-01-10 22:11:04 +03:00
static struct usb_serial_driver sierra_device = {
2006-10-17 21:17:58 +04:00
. driver = {
. owner = THIS_MODULE ,
2008-07-11 01:14:47 +04:00
. name = " sierra " ,
2006-10-17 21:17:58 +04:00
} ,
2008-01-10 22:11:04 +03:00
. description = " Sierra USB modem " ,
. id_table = id_table ,
2006-12-17 23:50:24 +03:00
. usb_driver = & sierra_driver ,
2008-01-10 22:11:04 +03:00
. calc_num_ports = sierra_calc_num_ports ,
. probe = sierra_probe ,
2006-10-17 21:17:58 +04:00
. open = sierra_open ,
. close = sierra_close ,
. write = sierra_write ,
. write_room = sierra_write_room ,
. set_termios = sierra_set_termios ,
. 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 ;
2008-01-10 22:11:04 +03:00
retval = usb_serial_register ( & sierra_device ) ;
2006-10-17 21:17:58 +04:00
if ( retval )
2008-01-10 22:11:04 +03:00
goto failed_device_register ;
2006-10-17 21:17:58 +04:00
retval = usb_register ( & sierra_driver ) ;
if ( retval )
goto failed_driver_register ;
2008-08-19 00:21:04 +04:00
printk ( KERN_INFO KBUILD_MODNAME " : " DRIVER_VERSION " : "
DRIVER_DESC " \n " ) ;
2006-10-17 21:17:58 +04:00
return 0 ;
failed_driver_register :
2008-01-10 22:11:04 +03:00
usb_serial_deregister ( & sierra_device ) ;
failed_device_register :
2006-10-17 21:17:58 +04:00
return retval ;
}
static void __exit sierra_exit ( void )
{
2008-07-22 14:14:59 +04:00
usb_deregister ( & sierra_driver ) ;
2008-01-10 22:11:04 +03:00
usb_serial_deregister ( & sierra_device ) ;
2006-10-17 21:17:58 +04:00
}
module_init ( sierra_init ) ;
module_exit ( sierra_exit ) ;
2006-10-14 10:53:21 +04:00
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_VERSION ( DRIVER_VERSION ) ;
2006-06-30 22:17:55 +04:00
MODULE_LICENSE ( " GPL " ) ;
2006-10-14 10:53:21 +04:00
2008-07-11 01:14:47 +04:00
module_param ( nmea , bool , S_IRUGO | S_IWUSR ) ;
2008-01-10 22:11:04 +03:00
MODULE_PARM_DESC ( nmea , " NMEA streaming " ) ;
2006-10-14 10:53:21 +04:00
module_param ( debug , bool , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( debug , " Debug messages " ) ;