2006-06-30 11:17:55 -07:00
/*
2006-10-13 23:53:21 -07:00
USB Driver for Sierra Wireless
2008-03-28 10:05:08 -07:00
Copyright ( C ) 2006 , 2007 , 2008 Kevin Lloyd < klloyd @ sierrawireless . com >
2006-10-13 23:53:21 -07: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 >
*/
2009-04-24 18:41:49 -07:00
# define DRIVER_VERSION "v.1.3.4"
2008-03-28 10:05:08 -07:00
# define DRIVER_AUTHOR "Kevin Lloyd <klloyd@sierrawireless.com>"
2006-10-13 23:53:21 -07:00
# define DRIVER_DESC "USB Driver for Sierra Wireless USB modems"
2006-06-30 11:17:55 -07:00
# include <linux/kernel.h>
2006-10-13 23:53:21 -07:00
# include <linux/jiffies.h>
# include <linux/errno.h>
2006-06-30 11:17:55 -07:00
# include <linux/tty.h>
2006-10-13 23:53:21 -07:00
# include <linux/tty_flip.h>
2006-06-30 11:17:55 -07:00
# include <linux/module.h>
# include <linux/usb.h>
2006-07-11 21:22:58 -07:00
# include <linux/usb/serial.h>
2006-06-30 11:17:55 -07:00
2008-01-10 11:11:04 -08:00
# define SWIMS_USB_REQUEST_SetPower 0x00
# define SWIMS_USB_REQUEST_SetNmea 0x07
2007-07-16 13:49:27 -07:00
2009-04-24 18:41:49 -07:00
# define N_IN_URB 8
# define N_OUT_URB 64
2007-07-16 13:49:27 -07:00
# define IN_BUFLEN 4096
static int debug ;
2008-01-10 11:11:04 -08:00
static int nmea ;
2007-07-16 13:49:27 -07:00
2009-06-11 14:32:01 +01:00
/* Used in interface blacklisting */
struct sierra_iface_info {
const u32 infolen ; /* number of interface numbers on blacklist */
const u8 * ifaceinfo ; /* pointer to the array holding the numbers */
} ;
2007-07-29 16:59:02 +02:00
static int sierra_set_power_state ( struct usb_device * udev , __u16 swiState )
2007-07-16 13:49:27 -07:00
{
int result ;
2008-07-10 14:14:47 -07:00
dev_dbg ( & udev - > dev , " %s " , __func__ ) ;
2007-07-16 13:49:27 -07:00
result = usb_control_msg ( udev , usb_sndctrlpipe ( udev , 0 ) ,
2008-01-10 11:11:04 -08:00
SWIMS_USB_REQUEST_SetPower , /* __u8 request */
2008-03-28 10:05:08 -07:00
USB_TYPE_VENDOR , /* __u8 request type */
2008-01-10 11:11:04 -08:00
swiState , /* __u16 value */
0 , /* __u16 index */
NULL , /* void *data */
0 , /* __u16 size */
USB_CTRL_SET_TIMEOUT ) ; /* int timeout */
2007-07-16 13:49:27 -07:00
return result ;
}
2008-01-10 11:11:04 -08:00
static int sierra_vsc_set_nmea ( struct usb_device * udev , __u16 enable )
2007-07-16 13:49:27 -07:00
{
int result ;
2008-07-10 14:14:47 -07:00
dev_dbg ( & udev - > dev , " %s " , __func__ ) ;
2008-01-10 11:11:04 -08:00
result = usb_control_msg ( udev , usb_sndctrlpipe ( udev , 0 ) ,
SWIMS_USB_REQUEST_SetNmea , /* __u8 request */
2008-03-28 10:05:08 -07:00
USB_TYPE_VENDOR , /* __u8 request type */
2008-01-10 11:11:04 -08: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-10 14:14:47 -07:00
dev_dbg ( & serial - > dev - > dev , " %s " , __func__ ) ;
2008-01-10 11:11:04 -08:00
result = * num_ports ;
if ( result ) {
kfree ( num_ports ) ;
usb_set_serial_data ( serial , NULL ) ;
}
return result ;
}
2009-06-11 14:32:01 +01:00
static int is_blacklisted ( const u8 ifnum ,
const struct sierra_iface_info * blacklist )
{
const u8 * info ;
int i ;
if ( blacklist ) {
info = blacklist - > ifaceinfo ;
for ( i = 0 ; i < blacklist - > infolen ; i + + ) {
if ( info [ i ] = = ifnum )
return 1 ;
}
}
return 0 ;
}
2008-04-02 11:24:56 -07:00
static int sierra_calc_interface ( struct usb_serial * serial )
{
2008-07-10 14:14:47 -07: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 11:24:56 -07:00
2008-07-10 14:14:47 -07:00
/* Get the interface structure pointer from the serial struct */
p_interface = serial - > interface ;
2008-04-02 11:24:56 -07:00
2008-07-10 14:14:47 -07:00
/* Get a pointer to the host interface structure */
p_host_interface = p_interface - > cur_altsetting ;
2008-04-02 11:24:56 -07:00
2008-07-10 14:14:47 -07: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 11:24:56 -07:00
2008-07-10 14:14:47 -07:00
return interface ;
2008-04-02 11:24:56 -07:00
}
2008-01-10 11:11:04 -08:00
static int sierra_probe ( struct usb_serial * serial ,
const struct usb_device_id * id )
{
int result = 0 ;
2007-07-16 13:49:27 -07:00
struct usb_device * udev ;
2008-01-10 11:11:04 -08:00
int * num_ports ;
u8 ifnum ;
2008-07-10 14:14:51 -07:00
u8 numendpoints ;
2008-07-10 14:14:47 -07:00
dev_dbg ( & serial - > dev - > dev , " %s " , __func__ ) ;
2007-07-16 13:49:27 -07:00
2008-01-10 11:11:04 -08:00
num_ports = kmalloc ( sizeof ( * num_ports ) , GFP_KERNEL ) ;
if ( ! num_ports )
return - ENOMEM ;
ifnum = serial - > interface - > cur_altsetting - > desc . bInterfaceNumber ;
2008-07-10 14:14:51 -07:00
numendpoints = serial - > interface - > cur_altsetting - > desc . bNumEndpoints ;
2008-01-10 11:11:04 -08:00
udev = serial - > dev ;
2007-07-16 13:49:27 -07:00
2008-07-10 14:14:51 -07:00
/* Figure out the interface number from the serial structure */
ifnum = sierra_calc_interface ( serial ) ;
2008-04-02 11:24:56 -07:00
2008-07-10 14:14:51 -07: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 11:24:56 -07:00
2008-07-10 14:14:51 -07:00
/* Dummy interface present on some SKUs should be ignored */
2008-07-10 14:14:54 -07:00
if ( ifnum = = 0x99 )
2008-01-10 11:11:04 -08:00
* num_ports = 0 ;
2008-07-10 14:14:51 -07:00
else if ( numendpoints < = 3 )
* num_ports = 1 ;
2008-01-10 11:11:04 -08:00
else
2008-07-10 14:14:51 -07:00
* num_ports = ( numendpoints - 1 ) / 2 ;
2008-01-10 11:11:04 -08: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-16 13:49:27 -07:00
2009-06-11 14:32:01 +01:00
/* ifnum could have changed - by calling usb_set_interface */
ifnum = sierra_calc_interface ( serial ) ;
if ( is_blacklisted ( ifnum ,
( struct sierra_iface_info * ) id - > driver_info ) ) {
dev_dbg ( & serial - > dev - > dev ,
" Ignoring blacklisted interface #%d \n " , ifnum ) ;
return - ENODEV ;
}
2008-01-10 11:11:04 -08:00
return result ;
2007-07-16 13:49:27 -07:00
}
2006-10-13 23:53:21 -07:00
2009-06-11 14:32:01 +01:00
static const u8 direct_ip_non_serial_ifaces [ ] = { 7 , 8 , 9 , 10 , 11 } ;
static const struct sierra_iface_info direct_ip_interface_blacklist = {
. infolen = ARRAY_SIZE ( direct_ip_non_serial_ifaces ) ,
. ifaceinfo = direct_ip_non_serial_ifaces ,
} ;
2006-06-30 11:17:55 -07:00
static struct usb_device_id id_table [ ] = {
2007-01-17 16:04:18 -08:00
{ USB_DEVICE ( 0x1199 , 0x0017 ) } , /* Sierra Wireless EM5625 */
2006-06-30 11:17:55 -07:00
{ USB_DEVICE ( 0x1199 , 0x0018 ) } , /* Sierra Wireless MC5720 */
2007-01-17 16:04:18 -08:00
{ USB_DEVICE ( 0x1199 , 0x0218 ) } , /* Sierra Wireless MC5720 */
2008-08-22 17:30:44 -05:00
{ USB_DEVICE ( 0x03f0 , 0x1b1d ) } , /* HP ev2200 a.k.a MC5720 */
2006-06-30 11:17:55 -07:00
{ USB_DEVICE ( 0x1199 , 0x0020 ) } , /* Sierra Wireless MC5725 */
2008-07-10 14:14:47 -07:00
{ USB_DEVICE ( 0x1199 , 0x0024 ) } , /* Sierra Wireless MC5727 */
2007-12-04 11:37:12 -07:00
{ USB_DEVICE ( 0x1199 , 0x0220 ) } , /* Sierra Wireless MC5725 */
2006-06-30 11:17:55 -07:00
{ USB_DEVICE ( 0x1199 , 0x0019 ) } , /* Sierra Wireless AirCard 595 */
2007-01-17 16:04:18 -08:00
{ USB_DEVICE ( 0x1199 , 0x0021 ) } , /* Sierra Wireless AirCard 597E */
2007-07-16 13:49:29 -07:00
{ USB_DEVICE ( 0x1199 , 0x0120 ) } , /* Sierra Wireless USB Dongle 595U */
2008-07-10 14:14:47 -07:00
/* Sierra Wireless C597 */
{ USB_DEVICE_AND_INTERFACE_INFO ( 0x1199 , 0x0023 , 0xFF , 0xFF , 0xFF ) } ,
2008-07-10 14:14:51 -07:00
/* Sierra Wireless Device */
{ USB_DEVICE_AND_INTERFACE_INFO ( 0x1199 , 0x0025 , 0xFF , 0xFF , 0xFF ) } ,
{ USB_DEVICE ( 0x1199 , 0x0026 ) } , /* Sierra Wireless Device */
2008-08-25 19:20:40 -07:00
{ USB_DEVICE ( 0x1199 , 0x0027 ) } , /* Sierra Wireless Device */
{ USB_DEVICE ( 0x1199 , 0x0028 ) } , /* Sierra Wireless Device */
2007-07-16 13:49:29 -07:00
2006-06-30 11:17:55 -07:00
{ USB_DEVICE ( 0x1199 , 0x6802 ) } , /* Sierra Wireless MC8755 */
2007-01-17 16:04:18 -08:00
{ USB_DEVICE ( 0x1199 , 0x6804 ) } , /* Sierra Wireless MC8755 */
2006-06-30 11:17:55 -07:00
{ USB_DEVICE ( 0x1199 , 0x6803 ) } , /* Sierra Wireless MC8765 */
2007-07-16 13:49:29 -07:00
{ USB_DEVICE ( 0x1199 , 0x6812 ) } , /* Sierra Wireless MC8775 & AC 875U */
2008-07-10 14:14:47 -07:00
{ USB_DEVICE ( 0x1199 , 0x6813 ) } , /* Sierra Wireless MC8775 (Lenovo) */
2008-03-14 00:53:24 -07:00
{ USB_DEVICE ( 0x1199 , 0x6815 ) } , /* Sierra Wireless MC8775 */
2008-04-17 07:47:34 +02:00
{ USB_DEVICE ( 0x03f0 , 0x1e1d ) } , /* HP hs2300 a.k.a MC8775 */
2006-06-30 11:17:55 -07:00
{ USB_DEVICE ( 0x1199 , 0x6820 ) } , /* Sierra Wireless AirCard 875 */
2008-04-02 11:24:56 -07:00
{ USB_DEVICE ( 0x1199 , 0x6821 ) } , /* Sierra Wireless AirCard 875U */
2008-07-10 14:14:47 -07:00
{ USB_DEVICE ( 0x1199 , 0x6832 ) } , /* Sierra Wireless MC8780 */
{ USB_DEVICE ( 0x1199 , 0x6833 ) } , /* Sierra Wireless MC8781 */
2008-09-17 09:03:38 -07:00
{ USB_DEVICE ( 0x1199 , 0x683A ) } , /* Sierra Wireless MC8785 */
2008-07-10 14:14:51 -07:00
{ USB_DEVICE ( 0x1199 , 0x683B ) } , /* Sierra Wireless MC8785 Composite */
2009-06-11 14:32:01 +01:00
/* Sierra Wireless MC8790, MC8791, MC8792 Composite */
{ USB_DEVICE ( 0x1199 , 0x683C ) } ,
{ USB_DEVICE ( 0x1199 , 0x683D ) } , /* Sierra Wireless MC8791 Composite */
/* Sierra Wireless MC8790, MC8791, MC8792 */
{ USB_DEVICE ( 0x1199 , 0x683E ) } ,
2007-07-16 13:49:29 -07: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 11:11:04 -08:00
{ USB_DEVICE ( 0x1199 , 0x6855 ) } , /* Sierra Wireless AirCard 880 U */
2007-12-30 20:11:35 -06:00
{ USB_DEVICE ( 0x1199 , 0x6856 ) } , /* Sierra Wireless AirCard 881 U */
2008-07-10 14:14:51 -07: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-25 19:20:40 -07:00
{ USB_DEVICE_AND_INTERFACE_INFO ( 0x1199 , 0x6891 , 0xFF , 0xFF , 0xFF ) } ,
/* Sierra Wireless Device */
2008-07-10 14:14:51 -07: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-16 13:49:27 -07:00
2009-06-11 14:32:01 +01:00
{ USB_DEVICE ( 0x1199 , 0x68A3 ) , /* Sierra Wireless Direct IP modems */
. driver_info = ( kernel_ulong_t ) & direct_ip_interface_blacklist
} ,
2006-10-13 23:53:21 -07:00
{ }
} ;
2006-10-17 10:17:58 -07:00
MODULE_DEVICE_TABLE ( usb , id_table ) ;
2006-10-13 23:53:21 -07:00
2006-06-30 11:17:55 -07:00
static struct usb_driver sierra_driver = {
2006-10-13 23:53:21 -07:00
. name = " sierra " ,
2008-01-10 11:11:04 -08:00
. probe = usb_serial_probe ,
2006-10-13 23:53:21 -07:00
. disconnect = usb_serial_disconnect ,
. id_table = id_table ,
2006-10-17 10:17:58 -07:00
. no_dynamic_id = 1 ,
2006-10-13 23:53:21 -07:00
} ;
struct sierra_port_private {
2007-06-20 14:22:23 +09:00
spinlock_t lock ; /* lock the structure */
int outstanding_urbs ; /* number of out urbs in flight */
2008-03-14 00:53:24 -07:00
/* Input endpoints and buffers for this port */
2006-10-13 23:53:21 -07:00
struct urb * in_urbs [ N_IN_URB ] ;
/* 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 ;
} ;
2009-06-11 12:26:29 +01:00
static int sierra_send_setup ( struct usb_serial_port * port )
2006-06-30 11:17:55 -07:00
{
2006-10-17 10:17:58 -07:00
struct usb_serial * serial = port - > serial ;
struct sierra_port_private * portdata ;
2008-01-10 11:11:04 -08:00
__u16 interface = 0 ;
2009-06-11 12:26:29 +01:00
int val = 0 ;
2006-10-13 23:53:21 -07:00
2008-09-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s " , __func__ ) ;
2006-10-13 23:53:21 -07:00
2006-10-17 10:17:58 -07:00
portdata = usb_get_serial_port_data ( port ) ;
2006-10-13 23:53:21 -07:00
2009-06-11 12:26:29 +01:00
if ( portdata - > dtr_state )
val | = 0x01 ;
if ( portdata - > rts_state )
val | = 0x02 ;
/* If composite device then properly report interface */
2009-06-11 14:29:29 +01:00
if ( serial - > num_ports = = 1 ) {
2009-06-11 12:26:29 +01:00
interface = sierra_calc_interface ( serial ) ;
2009-06-11 14:29:29 +01:00
/* Control message is sent only to interfaces with
* interrupt_in endpoints
*/
if ( port - > interrupt_in_urb ) {
/* send control message */
return usb_control_msg ( serial - > dev ,
usb_rcvctrlpipe ( serial - > dev , 0 ) ,
0x22 , 0x21 , val , interface ,
NULL , 0 , USB_CTRL_SET_TIMEOUT ) ;
}
}
2009-06-11 12:26:29 +01:00
/* 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 ;
2009-06-11 14:29:29 +01:00
return usb_control_msg ( serial - > dev ,
2009-06-11 12:26:29 +01:00
usb_rcvctrlpipe ( serial - > dev , 0 ) ,
0x22 , 0x21 , val , interface ,
NULL , 0 , USB_CTRL_SET_TIMEOUT ) ;
2009-06-11 14:29:29 +01:00
}
2006-10-17 10:17:58 -07:00
return 0 ;
2006-06-30 11:17:55 -07:00
}
2008-07-22 11:09:07 +01:00
static void sierra_set_termios ( struct tty_struct * tty ,
struct usb_serial_port * port , struct ktermios * old_termios )
2006-10-13 23:53:21 -07:00
{
2008-09-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s " , __func__ ) ;
2008-07-22 11:09:07 +01:00
tty_termios_copy_hw ( tty - > termios , old_termios ) ;
2009-06-11 12:26:29 +01:00
sierra_send_setup ( port ) ;
2006-10-13 23:53:21 -07:00
}
2008-07-22 11:09:07 +01:00
static int sierra_tiocmget ( struct tty_struct * tty , struct file * file )
2006-10-13 23:53:21 -07:00
{
2008-07-22 11:09:07 +01:00
struct usb_serial_port * port = tty - > driver_data ;
2006-10-13 23:53:21 -07:00
unsigned int value ;
struct sierra_port_private * portdata ;
2008-09-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s " , __func__ ) ;
2006-10-13 23:53:21 -07: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 11:09:07 +01:00
static int sierra_tiocmset ( struct tty_struct * tty , struct file * file ,
2006-10-13 23:53:21 -07:00
unsigned int set , unsigned int clear )
{
2008-07-22 11:09:07 +01:00
struct usb_serial_port * port = tty - > driver_data ;
2006-10-13 23:53:21 -07: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 ;
2009-06-11 12:26:29 +01:00
return sierra_send_setup ( port ) ;
2006-10-13 23:53:21 -07:00
}
2009-06-11 14:30:21 +01:00
static void sierra_release_urb ( struct urb * urb )
{
struct usb_serial_port * port ;
if ( urb ) {
port = urb - > context ;
dev_dbg ( & port - > dev , " %s: %p \n " , __func__ , urb ) ;
kfree ( urb - > transfer_buffer ) ;
usb_free_urb ( urb ) ;
}
}
2007-06-20 14:22:23 +09: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-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s - port %d " , __func__ , port - > number ) ;
2007-06-20 14:22:23 +09:00
/* free up the transfer buffer, as usb_free_urb() does not do this */
kfree ( urb - > transfer_buffer ) ;
if ( status )
2008-09-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s - nonzero write bulk status "
" received: %d " , __func__ , status ) ;
2007-06-20 14:22:23 +09:00
spin_lock_irqsave ( & portdata - > lock , flags ) ;
- - portdata - > outstanding_urbs ;
spin_unlock_irqrestore ( & portdata - > lock , flags ) ;
usb_serial_port_softint ( port ) ;
}
2006-10-13 23:53:21 -07:00
/* Write */
2008-07-22 11:09:07 +01:00
static int sierra_write ( struct tty_struct * tty , struct usb_serial_port * port ,
const unsigned char * buf , int count )
2006-10-13 23:53:21 -07:00
{
2007-06-20 14:22:23 +09: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-13 23:53:21 -07:00
portdata = usb_get_serial_port_data ( port ) ;
2008-09-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s: write (%d chars) " , __func__ , count ) ;
2006-10-13 23:53:21 -07:00
2007-06-20 14:22:23 +09:00
spin_lock_irqsave ( & portdata - > lock , flags ) ;
if ( portdata - > outstanding_urbs > N_OUT_URB ) {
spin_unlock_irqrestore ( & portdata - > lock , flags ) ;
2008-09-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s - write limit hit \n " , __func__ ) ;
2007-06-20 14:22:23 +09: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-13 23:53:21 -07:00
2007-06-20 14:22:23 +09: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-13 23:53:21 -07:00
2007-06-20 14:22:23 +09:00
memcpy ( buffer , buf , count ) ;
2006-10-13 23:53:21 -07:00
2008-03-03 16:08:34 -08:00
usb_serial_debug_data ( debug , & port - > dev , __func__ , count , buffer ) ;
2007-06-20 14:22:23 +09: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-03 16:08:34 -08:00
" with status = %d \n " , __func__ , status ) ;
2007-06-20 14:22:23 +09:00
count = status ;
goto error ;
2006-10-13 23:53:21 -07:00
}
2007-06-20 14:22:23 +09: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-13 23:53:21 -07: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-15 15:44:13 -07:00
int status = urb - > status ;
2006-10-13 23:53:21 -07:00
2008-03-03 16:08:34 -08:00
dbg ( " %s: %p " , __func__ , urb ) ;
2006-10-13 23:53:21 -07:00
endpoint = usb_pipeendpoint ( urb - > pipe ) ;
2008-02-24 18:41:47 +08:00
port = urb - > context ;
2006-10-13 23:53:21 -07:00
2007-06-15 15:44:13 -07:00
if ( status ) {
2008-09-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s: nonzero status: %d on "
" endpoint %02x. " , __func__ , status , endpoint ) ;
2006-10-13 23:53:21 -07:00
} else {
if ( urb - > actual_length ) {
2009-01-02 13:42:56 +00:00
tty = tty_port_tty_get ( & port - > port ) ;
2006-10-13 23:53:21 -07: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 10:39:46 +01:00
tty_kref_put ( tty ) ;
} else
2008-09-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s: empty read urb "
" received " , __func__ ) ;
2006-10-13 23:53:21 -07:00
/* Resubmit urb so we continue receiving */
2009-06-11 14:30:21 +01:00
if ( port - > port . count & & status ! = - ESHUTDOWN & & status ! = - EPERM ) {
2006-10-13 23:53:21 -07:00
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err )
2007-06-20 14:22:23 +09:00
dev_err ( & port - > dev , " resubmit read urb failed. "
2007-10-18 03:06:30 -07:00
" (%d) \n " , err ) ;
2006-10-13 23:53:21 -07:00
}
}
return ;
}
static void sierra_instat_callback ( struct urb * urb )
{
int err ;
2007-06-15 15:44:13 -07:00
int status = urb - > status ;
2008-02-24 18:41:47 +08:00
struct usb_serial_port * port = urb - > context ;
2006-10-13 23:53:21 -07:00
struct sierra_port_private * portdata = usb_get_serial_port_data ( port ) ;
struct usb_serial * serial = port - > serial ;
2008-09-16 21:05:24 -07: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-13 23:53:21 -07:00
2007-06-15 15:44:13 -07:00
if ( status = = 0 ) {
2006-10-13 23:53:21 -07:00
struct usb_ctrlrequest * req_pkt =
( struct usb_ctrlrequest * ) urb - > transfer_buffer ;
if ( ! req_pkt ) {
2008-09-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s: NULL req_pkt \n " ,
__func__ ) ;
2006-10-13 23:53:21 -07: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 10:39:46 +01:00
struct tty_struct * tty ;
2006-10-13 23:53:21 -07:00
2008-09-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s: signal x%x " , __func__ ,
signals ) ;
2006-10-13 23:53:21 -07: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 10:39:46 +01:00
tty = tty_port_tty_get ( & port - > port ) ;
if ( tty & & ! C_CLOCAL ( tty ) & &
2006-10-13 23:53:21 -07:00
old_dcd_state & & ! portdata - > dcd_state )
2008-10-13 10:39:46 +01:00
tty_hangup ( tty ) ;
tty_kref_put ( tty ) ;
2006-10-13 23:53:21 -07:00
} else {
2008-09-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s: type %x req %x " ,
__func__ , req_pkt - > bRequestType ,
req_pkt - > bRequest ) ;
2006-10-13 23:53:21 -07:00
}
} else
2008-09-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s: error %d " , __func__ , status ) ;
2006-10-13 23:53:21 -07:00
/* Resubmit urb so we continue receiving IRQ data */
2007-06-15 15:44:13 -07:00
if ( status ! = - ESHUTDOWN ) {
2006-10-13 23:53:21 -07:00
urb - > dev = serial - > dev ;
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err )
2008-09-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s: resubmit intr urb "
" failed. (%d) " , __func__ , err ) ;
2006-10-13 23:53:21 -07:00
}
}
2008-07-22 11:09:07 +01:00
static int sierra_write_room ( struct tty_struct * tty )
2006-10-13 23:53:21 -07:00
{
2008-07-22 11:09:07 +01:00
struct usb_serial_port * port = tty - > driver_data ;
2007-06-20 14:22:23 +09:00
struct sierra_port_private * portdata = usb_get_serial_port_data ( port ) ;
unsigned long flags ;
2006-10-13 23:53:21 -07:00
2008-09-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s - port %d " , __func__ , port - > number ) ;
2006-10-13 23:53:21 -07:00
2007-06-20 14:22:23 +09: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-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s - write limit hit \n " , __func__ ) ;
2007-06-20 14:22:23 +09:00
return 0 ;
2006-10-13 23:53:21 -07:00
}
2007-06-20 14:22:23 +09:00
spin_unlock_irqrestore ( & portdata - > lock , flags ) ;
2006-10-13 23:53:21 -07:00
2007-06-20 14:22:23 +09:00
return 2048 ;
2006-10-13 23:53:21 -07:00
}
2009-06-11 14:30:21 +01:00
static void sierra_stop_rx_urbs ( struct usb_serial_port * port )
2006-10-13 23:53:21 -07:00
{
2007-06-20 14:22:23 +09:00
int i ;
2009-06-11 14:30:21 +01:00
struct sierra_port_private * portdata = usb_get_serial_port_data ( port ) ;
2006-10-13 23:53:21 -07:00
2009-06-11 14:30:21 +01:00
for ( i = 0 ; i < ARRAY_SIZE ( portdata - > in_urbs ) ; i + + )
usb_kill_urb ( portdata - > in_urbs [ i ] ) ;
2006-10-13 23:53:21 -07:00
2009-06-11 14:30:21 +01:00
usb_kill_urb ( port - > interrupt_in_urb ) ;
}
2006-10-13 23:53:21 -07:00
2009-06-11 14:30:21 +01:00
static int sierra_submit_rx_urbs ( struct usb_serial_port * port , gfp_t mem_flags )
{
int ok_cnt ;
int err = - EINVAL ;
int i ;
struct urb * urb ;
struct sierra_port_private * portdata = usb_get_serial_port_data ( port ) ;
2006-10-13 23:53:21 -07:00
2009-06-11 14:30:21 +01:00
ok_cnt = 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( portdata - > in_urbs ) ; i + + ) {
2006-10-13 23:53:21 -07:00
urb = portdata - > in_urbs [ i ] ;
2007-06-20 14:22:23 +09:00
if ( ! urb )
2006-10-13 23:53:21 -07:00
continue ;
2009-06-11 14:30:21 +01:00
err = usb_submit_urb ( urb , mem_flags ) ;
if ( err ) {
dev_err ( & port - > dev , " %s: submit urb failed: %d \n " ,
__func__ , err ) ;
} else {
ok_cnt + + ;
2006-10-13 23:53:21 -07:00
}
2009-06-11 14:30:21 +01:00
}
2006-10-13 23:53:21 -07:00
2009-06-11 14:30:21 +01:00
if ( ok_cnt & & port - > interrupt_in_urb ) {
err = usb_submit_urb ( port - > interrupt_in_urb , mem_flags ) ;
if ( err ) {
dev_err ( & port - > dev , " %s: submit intr urb failed: %d \n " ,
__func__ , err ) ;
2006-10-13 23:53:21 -07:00
}
}
2009-06-11 14:30:21 +01:00
if ( ok_cnt > 0 ) /* at least one rx urb submitted */
return 0 ;
else
return err ;
}
static struct urb * sierra_setup_urb ( struct usb_serial * serial , int endpoint ,
int dir , void * ctx , int len ,
gfp_t mem_flags ,
usb_complete_t callback )
{
struct urb * urb ;
u8 * buf ;
if ( endpoint = = - 1 )
return NULL ;
2006-10-13 23:53:21 -07:00
2009-06-11 14:30:21 +01:00
urb = usb_alloc_urb ( 0 , mem_flags ) ;
if ( urb = = NULL ) {
dev_dbg ( & serial - > dev - > dev , " %s: alloc for endpoint %d failed \n " ,
__func__ , endpoint ) ;
return NULL ;
2007-06-20 14:22:23 +09:00
}
2009-06-11 14:30:21 +01:00
buf = kmalloc ( len , mem_flags ) ;
if ( buf ) {
/* Fill URB using supplied data */
usb_fill_bulk_urb ( urb , serial - > dev ,
usb_sndbulkpipe ( serial - > dev , endpoint ) | dir ,
buf , len , callback , ctx ) ;
/* debug */
dev_dbg ( & serial - > dev - > dev , " %s %c u : %p d:%p \n " , __func__ ,
dir = = USB_DIR_IN ? ' i ' : ' o ' , urb , buf ) ;
} else {
dev_dbg ( & serial - > dev - > dev , " %s %c u:%p d:%p \n " , __func__ ,
dir = = USB_DIR_IN ? ' i ' : ' o ' , urb , buf ) ;
sierra_release_urb ( urb ) ;
urb = NULL ;
}
return urb ;
2006-10-13 23:53:21 -07:00
}
2009-06-11 14:30:21 +01:00
static void sierra_close ( struct usb_serial_port * port )
2006-10-13 23:53:21 -07:00
{
2009-06-11 14:30:21 +01:00
int i ;
2006-10-13 23:53:21 -07:00
struct usb_serial * serial = port - > serial ;
struct sierra_port_private * portdata ;
2009-06-11 14:30:21 +01:00
dev_dbg ( & port - > dev , " %s \n " , __func__ ) ;
2006-10-13 23:53:21 -07:00
portdata = usb_get_serial_port_data ( port ) ;
2009-06-11 14:30:21 +01:00
portdata - > rts_state = 0 ;
portdata - > dtr_state = 0 ;
2006-10-13 23:53:21 -07:00
if ( serial - > dev ) {
2008-01-21 17:44:10 +01:00
mutex_lock ( & serial - > disc_mutex ) ;
if ( ! serial - > disconnected )
2009-06-11 12:26:29 +01:00
sierra_send_setup ( port ) ;
2008-01-21 17:44:10 +01:00
mutex_unlock ( & serial - > disc_mutex ) ;
2009-06-11 14:30:21 +01:00
/* Stop reading urbs */
sierra_stop_rx_urbs ( port ) ;
/* .. and release them */
for ( i = 0 ; i < N_IN_URB ; i + + ) {
sierra_release_urb ( portdata - > in_urbs [ i ] ) ;
portdata - > in_urbs [ i ] = NULL ;
}
2009-06-11 12:26:29 +01:00
}
}
2009-06-11 14:30:21 +01:00
static int sierra_open ( struct tty_struct * tty ,
struct usb_serial_port * port , struct file * filp )
2009-06-11 12:26:29 +01:00
{
2009-06-11 14:30:21 +01:00
struct sierra_port_private * portdata ;
struct usb_serial * serial = port - > serial ;
2009-06-11 12:26:29 +01:00
int i ;
2009-06-11 14:30:21 +01:00
int err ;
int endpoint ;
struct urb * urb ;
portdata = usb_get_serial_port_data ( port ) ;
dev_dbg ( & port - > dev , " %s " , __func__ ) ;
/* Set some sane defaults */
portdata - > rts_state = 1 ;
portdata - > dtr_state = 1 ;
endpoint = port - > bulk_in_endpointAddress ;
for ( i = 0 ; i < ARRAY_SIZE ( portdata - > in_urbs ) ; i + + ) {
urb = sierra_setup_urb ( serial , endpoint , USB_DIR_IN , port ,
IN_BUFLEN , GFP_KERNEL ,
sierra_indat_callback ) ;
portdata - > in_urbs [ i ] = urb ;
}
/* clear halt condition */
usb_clear_halt ( serial - > dev ,
usb_sndbulkpipe ( serial - > dev , endpoint ) | USB_DIR_IN ) ;
err = sierra_submit_rx_urbs ( port , GFP_KERNEL ) ;
if ( err ) {
/* get rid of everything as in close */
sierra_close ( port ) ;
return err ;
}
sierra_send_setup ( port ) ;
return 0 ;
}
static void sierra_dtr_rts ( struct usb_serial_port * port , int on )
{
2009-06-11 12:26:29 +01:00
struct usb_serial * serial = port - > serial ;
struct sierra_port_private * portdata ;
2006-10-13 23:53:21 -07:00
2009-06-11 12:26:29 +01:00
portdata = usb_get_serial_port_data ( port ) ;
2009-06-11 14:30:21 +01:00
portdata - > rts_state = on ;
portdata - > dtr_state = on ;
2009-06-11 12:26:29 +01:00
if ( serial - > dev ) {
2009-06-11 14:30:21 +01:00
mutex_lock ( & serial - > disc_mutex ) ;
if ( ! serial - > disconnected )
sierra_send_setup ( port ) ;
mutex_unlock ( & serial - > disc_mutex ) ;
2006-10-13 23:53:21 -07:00
}
}
static int sierra_startup ( struct usb_serial * serial )
{
struct usb_serial_port * port ;
struct sierra_port_private * portdata ;
2007-06-20 14:22:23 +09:00
int i ;
2006-10-13 23:53:21 -07:00
2008-09-16 21:05:24 -07:00
dev_dbg ( & serial - > dev - > dev , " %s " , __func__ ) ;
2006-10-13 23:53:21 -07:00
2008-01-10 11:11:04 -08:00
/* Set Device mode to D0 */
2007-07-16 13:49:27 -07:00
sierra_set_power_state ( serial - > dev , 0x0000 ) ;
2008-01-10 11:11:04 -08:00
/* Check NMEA and set */
if ( nmea )
sierra_vsc_set_nmea ( serial - > dev , 1 ) ;
2006-10-13 23:53:21 -07: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-16 21:05:24 -07:00
dev_dbg ( & port - > dev , " %s: kmalloc for "
" sierra_port_private (%d) failed!. " ,
__func__ , i ) ;
2007-06-20 14:22:23 +09:00
return - ENOMEM ;
2006-10-13 23:53:21 -07:00
}
2007-06-20 14:22:23 +09:00
spin_lock_init ( & portdata - > lock ) ;
2009-06-11 14:30:21 +01:00
/* Set the port private data pointer */
2006-10-13 23:53:21 -07:00
usb_set_serial_port_data ( port , portdata ) ;
}
2007-06-20 14:22:23 +09:00
return 0 ;
2006-10-13 23:53:21 -07:00
}
static void sierra_shutdown ( struct usb_serial * serial )
{
2009-06-11 14:30:21 +01:00
int i ;
2006-10-13 23:53:21 -07:00
struct usb_serial_port * port ;
struct sierra_port_private * portdata ;
2008-09-16 21:05:24 -07:00
dev_dbg ( & serial - > dev - > dev , " %s " , __func__ ) ;
2006-10-13 23:53:21 -07:00
for ( i = 0 ; i < serial - > num_ports ; + + i ) {
port = serial - > port [ i ] ;
2007-04-26 00:12:01 -07:00
if ( ! port )
continue ;
2006-10-13 23:53:21 -07:00
portdata = usb_get_serial_port_data ( port ) ;
2007-04-26 00:12:01 -07:00
if ( ! portdata )
continue ;
2007-06-20 14:22:23 +09:00
kfree ( portdata ) ;
usb_set_serial_port_data ( port , NULL ) ;
2006-10-13 23:53:21 -07:00
}
}
2008-01-10 11:11:04 -08:00
static struct usb_serial_driver sierra_device = {
2006-10-17 10:17:58 -07:00
. driver = {
. owner = THIS_MODULE ,
2008-07-10 14:14:47 -07:00
. name = " sierra " ,
2006-10-17 10:17:58 -07:00
} ,
2008-01-10 11:11:04 -08:00
. description = " Sierra USB modem " ,
. id_table = id_table ,
2006-12-17 21:50:24 +01:00
. usb_driver = & sierra_driver ,
2008-01-10 11:11:04 -08:00
. calc_num_ports = sierra_calc_num_ports ,
. probe = sierra_probe ,
2006-10-17 10:17:58 -07:00
. open = sierra_open ,
. close = sierra_close ,
2009-06-11 12:26:29 +01:00
. dtr_rts = sierra_dtr_rts ,
2006-10-17 10:17:58 -07:00
. 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 11:11:04 -08:00
retval = usb_serial_register ( & sierra_device ) ;
2006-10-17 10:17:58 -07:00
if ( retval )
2008-01-10 11:11:04 -08:00
goto failed_device_register ;
2006-10-17 10:17:58 -07:00
retval = usb_register ( & sierra_driver ) ;
if ( retval )
goto failed_driver_register ;
2008-08-18 13:21:04 -07:00
printk ( KERN_INFO KBUILD_MODNAME " : " DRIVER_VERSION " : "
DRIVER_DESC " \n " ) ;
2006-10-17 10:17:58 -07:00
return 0 ;
failed_driver_register :
2008-01-10 11:11:04 -08:00
usb_serial_deregister ( & sierra_device ) ;
failed_device_register :
2006-10-17 10:17:58 -07:00
return retval ;
}
static void __exit sierra_exit ( void )
{
2008-07-22 11:14:59 +01:00
usb_deregister ( & sierra_driver ) ;
2008-01-10 11:11:04 -08:00
usb_serial_deregister ( & sierra_device ) ;
2006-10-17 10:17:58 -07:00
}
module_init ( sierra_init ) ;
module_exit ( sierra_exit ) ;
2006-10-13 23:53:21 -07:00
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_VERSION ( DRIVER_VERSION ) ;
2006-06-30 11:17:55 -07:00
MODULE_LICENSE ( " GPL " ) ;
2006-10-13 23:53:21 -07:00
2008-07-10 14:14:47 -07:00
module_param ( nmea , bool , S_IRUGO | S_IWUSR ) ;
2008-01-10 11:11:04 -08:00
MODULE_PARM_DESC ( nmea , " NMEA streaming " ) ;
2006-10-13 23:53:21 -07:00
module_param ( debug , bool , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( debug , " Debug messages " ) ;