2010-04-01 20:31:07 +04:00
/*
USB Driver layer for GSM modems
Copyright ( C ) 2005 Matthias Urlichs < smurf @ smurf . noris . de >
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 copied from the Keyspan driver by Hugh Blemings < hugh @ blemings . org >
History : see the git log .
Work sponsored by : Sigos GmbH , Germany < info @ sigos . de >
This driver exists because the " normal " serial driver doesn ' t work too well
with GSM modems . Issues :
- data loss - - one single Receive URB is not nearly enough
- controlling the baud rate doesn ' t make sense
*/
# define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"
# define DRIVER_DESC "USB Driver for GSM modems"
# include <linux/kernel.h>
# include <linux/jiffies.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/tty.h>
# include <linux/tty_flip.h>
# include <linux/module.h>
# include <linux/bitops.h>
2010-12-10 01:27:35 +03:00
# include <linux/uaccess.h>
2010-04-01 20:31:07 +04:00
# include <linux/usb.h>
# include <linux/usb/serial.h>
2010-11-20 01:04:00 +03:00
# include <linux/serial.h>
2010-04-01 20:31:07 +04:00
# include "usb-wwan.h"
2015-09-16 19:27:57 +03:00
/*
* Generate DTR / RTS signals on the port using the SET_CONTROL_LINE_STATE request
* in CDC ACM .
*/
static int usb_wwan_send_setup ( struct usb_serial_port * port )
{
struct usb_serial * serial = port - > serial ;
struct usb_wwan_port_private * portdata ;
int val = 0 ;
int ifnum ;
int res ;
portdata = usb_get_serial_port_data ( port ) ;
if ( portdata - > dtr_state )
val | = 0x01 ;
if ( portdata - > rts_state )
val | = 0x02 ;
ifnum = serial - > interface - > cur_altsetting - > desc . bInterfaceNumber ;
res = usb_autopm_get_interface ( serial - > interface ) ;
if ( res )
return res ;
res = usb_control_msg ( serial - > dev , usb_sndctrlpipe ( serial - > dev , 0 ) ,
0x22 , 0x21 , val , ifnum , NULL , 0 ,
USB_CTRL_SET_TIMEOUT ) ;
usb_autopm_put_interface ( port - > serial - > interface ) ;
return res ;
}
2010-04-01 20:31:07 +04:00
void usb_wwan_dtr_rts ( struct usb_serial_port * port , int on )
{
struct usb_wwan_port_private * portdata ;
struct usb_wwan_intf_private * intfdata ;
2014-05-26 21:23:26 +04:00
intfdata = usb_get_serial_data ( port - > serial ) ;
2010-04-01 20:31:07 +04:00
2015-09-16 19:27:57 +03:00
if ( ! intfdata - > use_send_setup )
2010-04-01 20:31:07 +04:00
return ;
portdata = usb_get_serial_port_data ( port ) ;
2013-02-13 20:53:28 +04:00
/* FIXME: locking */
2010-04-01 20:31:07 +04:00
portdata - > rts_state = on ;
portdata - > dtr_state = on ;
2013-02-13 20:53:28 +04:00
2015-09-16 19:27:57 +03:00
usb_wwan_send_setup ( port ) ;
2010-04-01 20:31:07 +04:00
}
EXPORT_SYMBOL ( usb_wwan_dtr_rts ) ;
2011-02-14 19:26:14 +03:00
int usb_wwan_tiocmget ( struct tty_struct * tty )
2010-04-01 20:31:07 +04:00
{
struct usb_serial_port * port = tty - > driver_data ;
unsigned int value ;
struct usb_wwan_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 ;
}
EXPORT_SYMBOL ( usb_wwan_tiocmget ) ;
2011-02-14 19:26:50 +03:00
int usb_wwan_tiocmset ( struct tty_struct * tty ,
2010-04-01 20:31:07 +04:00
unsigned int set , unsigned int clear )
{
struct usb_serial_port * port = tty - > driver_data ;
struct usb_wwan_port_private * portdata ;
struct usb_wwan_intf_private * intfdata ;
portdata = usb_get_serial_port_data ( port ) ;
2014-05-26 21:23:26 +04:00
intfdata = usb_get_serial_data ( port - > serial ) ;
2010-04-01 20:31:07 +04:00
2015-09-16 19:27:57 +03:00
if ( ! intfdata - > use_send_setup )
2010-04-01 20:31:07 +04:00
return - EINVAL ;
/* FIXME: what locks portdata fields ? */
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 ;
2015-09-16 19:27:57 +03:00
return usb_wwan_send_setup ( port ) ;
2010-04-01 20:31:07 +04:00
}
EXPORT_SYMBOL ( usb_wwan_tiocmset ) ;
2010-11-20 01:04:00 +03:00
static int get_serial_info ( struct usb_serial_port * port ,
struct serial_struct __user * retinfo )
{
struct serial_struct tmp ;
if ( ! retinfo )
return - EFAULT ;
memset ( & tmp , 0 , sizeof ( tmp ) ) ;
2013-06-07 22:04:28 +04:00
tmp . line = port - > minor ;
2013-06-06 21:32:00 +04:00
tmp . port = port - > port_number ;
2010-11-20 01:04:00 +03:00
tmp . baud_base = tty_get_baud_rate ( port - > port . tty ) ;
tmp . close_delay = port - > port . close_delay / 10 ;
tmp . closing_wait = port - > port . closing_wait = = ASYNC_CLOSING_WAIT_NONE ?
ASYNC_CLOSING_WAIT_NONE :
port - > port . closing_wait / 10 ;
if ( copy_to_user ( retinfo , & tmp , sizeof ( * retinfo ) ) )
return - EFAULT ;
return 0 ;
}
static int set_serial_info ( struct usb_serial_port * port ,
struct serial_struct __user * newinfo )
{
struct serial_struct new_serial ;
unsigned int closing_wait , close_delay ;
int retval = 0 ;
if ( copy_from_user ( & new_serial , newinfo , sizeof ( new_serial ) ) )
return - EFAULT ;
close_delay = new_serial . close_delay * 10 ;
closing_wait = new_serial . closing_wait = = ASYNC_CLOSING_WAIT_NONE ?
ASYNC_CLOSING_WAIT_NONE : new_serial . closing_wait * 10 ;
mutex_lock ( & port - > port . mutex ) ;
if ( ! capable ( CAP_SYS_ADMIN ) ) {
if ( ( close_delay ! = port - > port . close_delay ) | |
( closing_wait ! = port - > port . closing_wait ) )
retval = - EPERM ;
else
retval = - EOPNOTSUPP ;
} else {
port - > port . close_delay = close_delay ;
port - > port . closing_wait = closing_wait ;
}
mutex_unlock ( & port - > port . mutex ) ;
return retval ;
}
2011-02-14 19:27:06 +03:00
int usb_wwan_ioctl ( struct tty_struct * tty ,
2010-11-20 01:04:00 +03:00
unsigned int cmd , unsigned long arg )
{
struct usb_serial_port * port = tty - > driver_data ;
2012-09-14 04:41:50 +04:00
dev_dbg ( & port - > dev , " %s cmd 0x%04x \n " , __func__ , cmd ) ;
2010-11-20 01:04:00 +03:00
switch ( cmd ) {
case TIOCGSERIAL :
return get_serial_info ( port ,
( struct serial_struct __user * ) arg ) ;
case TIOCSSERIAL :
return set_serial_info ( port ,
( struct serial_struct __user * ) arg ) ;
default :
break ;
}
2012-09-14 04:41:50 +04:00
dev_dbg ( & port - > dev , " %s arg not supported \n " , __func__ ) ;
2010-11-20 01:04:00 +03:00
return - ENOIOCTLCMD ;
}
EXPORT_SYMBOL ( usb_wwan_ioctl ) ;
2010-04-01 20:31:07 +04:00
int usb_wwan_write ( struct tty_struct * tty , struct usb_serial_port * port ,
const unsigned char * buf , int count )
{
struct usb_wwan_port_private * portdata ;
struct usb_wwan_intf_private * intfdata ;
int i ;
int left , todo ;
struct urb * this_urb = NULL ; /* spurious */
int err ;
unsigned long flags ;
portdata = usb_get_serial_port_data ( port ) ;
2014-05-26 21:23:26 +04:00
intfdata = usb_get_serial_data ( port - > serial ) ;
2010-04-01 20:31:07 +04:00
2012-09-14 04:41:50 +04:00
dev_dbg ( & port - > dev , " %s: write (%d chars) \n " , __func__ , count ) ;
2010-04-01 20:31:07 +04:00
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 ( test_and_set_bit ( i , & portdata - > out_busy ) ) {
if ( time_before ( jiffies ,
portdata - > tx_start_time [ i ] + 10 * HZ ) )
continue ;
usb_unlink_urb ( this_urb ) ;
continue ;
}
2012-09-14 04:41:50 +04:00
dev_dbg ( & port - > dev , " %s: endpoint %d buf %d \n " , __func__ ,
usb_pipeendpoint ( this_urb - > pipe ) , i ) ;
2010-04-01 20:31:07 +04:00
err = usb_autopm_get_interface_async ( port - > serial - > interface ) ;
2014-05-26 21:23:13 +04:00
if ( err < 0 ) {
clear_bit ( i , & portdata - > out_busy ) ;
2010-04-01 20:31:07 +04:00
break ;
2014-05-26 21:23:13 +04:00
}
2010-04-01 20:31:07 +04:00
/* send the data */
memcpy ( this_urb - > transfer_buffer , buf , todo ) ;
this_urb - > transfer_buffer_length = todo ;
spin_lock_irqsave ( & intfdata - > susp_lock , flags ) ;
if ( intfdata - > suspended ) {
usb_anchor_urb ( this_urb , & portdata - > delayed ) ;
spin_unlock_irqrestore ( & intfdata - > susp_lock , flags ) ;
} else {
intfdata - > in_flight + + ;
spin_unlock_irqrestore ( & intfdata - > susp_lock , flags ) ;
err = usb_submit_urb ( this_urb , GFP_ATOMIC ) ;
if ( err ) {
2014-05-26 21:23:31 +04:00
dev_err ( & port - > dev ,
" %s: submit urb %d failed: %d \n " ,
__func__ , i , err ) ;
2010-04-01 20:31:07 +04:00
clear_bit ( i , & portdata - > out_busy ) ;
spin_lock_irqsave ( & intfdata - > susp_lock , flags ) ;
intfdata - > in_flight - - ;
spin_unlock_irqrestore ( & intfdata - > susp_lock ,
flags ) ;
2011-02-10 17:33:17 +03:00
usb_autopm_put_interface_async ( port - > serial - > interface ) ;
2011-02-10 17:33:23 +03:00
break ;
2010-04-01 20:31:07 +04:00
}
}
portdata - > tx_start_time [ i ] = jiffies ;
buf + = todo ;
left - = todo ;
}
count - = left ;
2012-09-14 04:41:50 +04:00
dev_dbg ( & port - > dev , " %s: wrote (did %d) \n " , __func__ , count ) ;
2010-04-01 20:31:07 +04:00
return count ;
}
EXPORT_SYMBOL ( usb_wwan_write ) ;
static void usb_wwan_indat_callback ( struct urb * urb )
{
int err ;
int endpoint ;
struct usb_serial_port * port ;
2012-09-14 04:41:50 +04:00
struct device * dev ;
2010-04-01 20:31:07 +04:00
unsigned char * data = urb - > transfer_buffer ;
int status = urb - > status ;
endpoint = usb_pipeendpoint ( urb - > pipe ) ;
port = urb - > context ;
2012-09-14 04:41:50 +04:00
dev = & port - > dev ;
2010-04-01 20:31:07 +04:00
if ( status ) {
2012-09-14 04:41:50 +04:00
dev_dbg ( dev , " %s: nonzero status: %d on endpoint %02x. \n " ,
__func__ , status , endpoint ) ;
2010-04-01 20:31:07 +04:00
} else {
2013-01-03 18:53:06 +04:00
if ( urb - > actual_length ) {
tty_insert_flip_string ( & port - > port , data ,
urb - > actual_length ) ;
tty_flip_buffer_push ( & port - > port ) ;
} else
dev_dbg ( dev , " %s: empty read urb received \n " , __func__ ) ;
2013-08-12 21:11:39 +04:00
}
/* Resubmit urb so we continue receiving */
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err ) {
2015-08-17 18:35:24 +03:00
if ( err ! = - EPERM & & err ! = - ENODEV ) {
2013-08-12 21:11:39 +04:00
dev_err ( dev , " %s: resubmit read urb failed. (%d) \n " ,
__func__ , err ) ;
/* busy also in error unless we are killed */
2012-04-20 10:33:31 +04:00
usb_mark_last_busy ( port - > serial - > dev ) ;
2010-04-01 20:31:07 +04:00
}
2013-08-12 21:11:39 +04:00
} else {
usb_mark_last_busy ( port - > serial - > dev ) ;
2010-04-01 20:31:07 +04:00
}
}
static void usb_wwan_outdat_callback ( struct urb * urb )
{
struct usb_serial_port * port ;
struct usb_wwan_port_private * portdata ;
struct usb_wwan_intf_private * intfdata ;
int i ;
port = urb - > context ;
2014-05-26 21:23:26 +04:00
intfdata = usb_get_serial_data ( port - > serial ) ;
2010-04-01 20:31:07 +04:00
usb_serial_port_softint ( port ) ;
usb_autopm_put_interface_async ( port - > serial - > interface ) ;
portdata = usb_get_serial_port_data ( port ) ;
spin_lock ( & intfdata - > susp_lock ) ;
intfdata - > in_flight - - ;
spin_unlock ( & intfdata - > susp_lock ) ;
for ( i = 0 ; i < N_OUT_URB ; + + i ) {
if ( portdata - > out_urbs [ i ] = = urb ) {
2014-03-17 21:06:10 +04:00
smp_mb__before_atomic ( ) ;
2010-04-01 20:31:07 +04:00
clear_bit ( i , & portdata - > out_busy ) ;
break ;
}
}
}
int usb_wwan_write_room ( struct tty_struct * tty )
{
struct usb_serial_port * port = tty - > driver_data ;
struct usb_wwan_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 & & ! test_bit ( i , & portdata - > out_busy ) )
data_len + = OUT_BUFLEN ;
}
2012-09-14 04:41:50 +04:00
dev_dbg ( & port - > dev , " %s: %d \n " , __func__ , data_len ) ;
2010-04-01 20:31:07 +04:00
return data_len ;
}
EXPORT_SYMBOL ( usb_wwan_write_room ) ;
int usb_wwan_chars_in_buffer ( struct tty_struct * tty )
{
struct usb_serial_port * port = tty - > driver_data ;
struct usb_wwan_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 ] ;
/* FIXME: This locking is insufficient as this_urb may
go unused during the test */
if ( this_urb & & test_bit ( i , & portdata - > out_busy ) )
data_len + = this_urb - > transfer_buffer_length ;
}
2012-09-14 04:41:50 +04:00
dev_dbg ( & port - > dev , " %s: %d \n " , __func__ , data_len ) ;
2010-04-01 20:31:07 +04:00
return data_len ;
}
EXPORT_SYMBOL ( usb_wwan_chars_in_buffer ) ;
int usb_wwan_open ( struct tty_struct * tty , struct usb_serial_port * port )
{
struct usb_wwan_port_private * portdata ;
struct usb_wwan_intf_private * intfdata ;
struct usb_serial * serial = port - > serial ;
int i , err ;
struct urb * urb ;
portdata = usb_get_serial_port_data ( port ) ;
2014-05-26 21:23:26 +04:00
intfdata = usb_get_serial_data ( serial ) ;
2010-04-01 20:31:07 +04:00
2014-05-26 21:23:17 +04:00
if ( port - > interrupt_in_urb ) {
err = usb_submit_urb ( port - > interrupt_in_urb , GFP_KERNEL ) ;
if ( err ) {
2014-05-26 21:23:31 +04:00
dev_err ( & port - > dev , " %s: submit int urb failed: %d \n " ,
2014-05-26 21:23:17 +04:00
__func__ , err ) ;
}
}
2010-04-01 20:31:07 +04:00
/* Start reading from the IN endpoint */
for ( i = 0 ; i < N_IN_URB ; i + + ) {
urb = portdata - > in_urbs [ i ] ;
if ( ! urb )
continue ;
err = usb_submit_urb ( urb , GFP_KERNEL ) ;
if ( err ) {
2014-05-26 21:23:31 +04:00
dev_err ( & port - > dev ,
" %s: submit read urb %d failed: %d \n " ,
__func__ , i , err ) ;
2010-04-01 20:31:07 +04:00
}
}
spin_lock_irq ( & intfdata - > susp_lock ) ;
2014-05-26 21:23:20 +04:00
if ( + + intfdata - > open_ports = = 1 )
serial - > interface - > needs_remote_wakeup = 1 ;
2010-04-01 20:31:07 +04:00
spin_unlock_irq ( & intfdata - > susp_lock ) ;
2011-02-10 17:33:37 +03:00
/* this balances a get in the generic USB serial code */
2010-04-01 20:31:07 +04:00
usb_autopm_put_interface ( serial - > interface ) ;
return 0 ;
}
EXPORT_SYMBOL ( usb_wwan_open ) ;
2014-05-26 21:23:16 +04:00
static void unbusy_queued_urb ( struct urb * urb ,
struct usb_wwan_port_private * portdata )
{
int i ;
for ( i = 0 ; i < N_OUT_URB ; i + + ) {
if ( urb = = portdata - > out_urbs [ i ] ) {
clear_bit ( i , & portdata - > out_busy ) ;
break ;
}
}
}
2010-04-01 20:31:07 +04:00
void usb_wwan_close ( struct usb_serial_port * port )
{
int i ;
struct usb_serial * serial = port - > serial ;
struct usb_wwan_port_private * portdata ;
2014-05-26 21:23:26 +04:00
struct usb_wwan_intf_private * intfdata = usb_get_serial_data ( serial ) ;
2014-05-26 21:23:16 +04:00
struct urb * urb ;
2010-04-01 20:31:07 +04:00
portdata = usb_get_serial_port_data ( port ) ;
2014-05-26 21:23:32 +04:00
/*
* Need to take susp_lock to make sure port is not already being
* resumed , but no need to hold it due to ASYNC_INITIALIZED .
*/
2013-03-21 15:36:37 +04:00
spin_lock_irq ( & intfdata - > susp_lock ) ;
2014-05-26 21:23:20 +04:00
if ( - - intfdata - > open_ports = = 0 )
serial - > interface - > needs_remote_wakeup = 0 ;
2013-03-21 15:36:37 +04:00
spin_unlock_irq ( & intfdata - > susp_lock ) ;
2014-05-26 21:23:16 +04:00
for ( ; ; ) {
urb = usb_get_from_anchor ( & portdata - > delayed ) ;
if ( ! urb )
break ;
unbusy_queued_urb ( urb , portdata ) ;
usb_autopm_put_interface_async ( serial - > interface ) ;
}
2013-03-21 15:36:37 +04:00
for ( i = 0 ; i < N_IN_URB ; i + + )
usb_kill_urb ( portdata - > in_urbs [ i ] ) ;
for ( i = 0 ; i < N_OUT_URB ; i + + )
usb_kill_urb ( portdata - > out_urbs [ i ] ) ;
2014-05-26 21:23:17 +04:00
usb_kill_urb ( port - > interrupt_in_urb ) ;
2013-03-21 15:36:37 +04:00
usb_autopm_get_interface_no_resume ( serial - > interface ) ;
2010-04-01 20:31:07 +04:00
}
EXPORT_SYMBOL ( usb_wwan_close ) ;
2012-10-25 12:29:16 +04:00
static struct urb * usb_wwan_setup_urb ( struct usb_serial_port * port ,
int endpoint ,
2010-04-01 20:31:07 +04:00
int dir , void * ctx , char * buf , int len ,
void ( * callback ) ( struct urb * ) )
{
2012-10-25 12:29:16 +04:00
struct usb_serial * serial = port - > serial ;
2010-04-01 20:31:07 +04:00
struct urb * urb ;
urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ; /* No ISO */
2013-12-29 22:22:56 +04:00
if ( ! urb )
2010-04-01 20:31:07 +04:00
return NULL ;
usb_fill_bulk_urb ( urb , serial - > dev ,
usb_sndbulkpipe ( serial - > dev , endpoint ) | dir ,
buf , len , callback , ctx ) ;
return urb ;
}
2012-10-25 12:29:16 +04:00
int usb_wwan_port_probe ( struct usb_serial_port * port )
2010-04-01 20:31:07 +04:00
{
struct usb_wwan_port_private * portdata ;
2012-10-25 12:29:16 +04:00
struct urb * urb ;
u8 * buffer ;
int i ;
2010-04-01 20:31:07 +04:00
2014-04-03 15:06:46 +04:00
if ( ! port - > bulk_in_size | | ! port - > bulk_out_size )
return - ENODEV ;
2012-10-25 12:29:16 +04:00
portdata = kzalloc ( sizeof ( * portdata ) , GFP_KERNEL ) ;
if ( ! portdata )
return - ENOMEM ;
2010-04-01 20:31:07 +04:00
2012-10-25 12:29:16 +04:00
init_usb_anchor ( & portdata - > delayed ) ;
2010-04-01 20:31:07 +04:00
2012-10-25 12:29:16 +04:00
for ( i = 0 ; i < N_IN_URB ; i + + ) {
buffer = ( u8 * ) __get_free_page ( GFP_KERNEL ) ;
if ( ! buffer )
goto bail_out_error ;
portdata - > in_buffer [ i ] = buffer ;
urb = usb_wwan_setup_urb ( port , port - > bulk_in_endpointAddress ,
USB_DIR_IN , port ,
buffer , IN_BUFLEN ,
usb_wwan_indat_callback ) ;
portdata - > in_urbs [ i ] = urb ;
}
2010-04-01 20:31:07 +04:00
2012-10-25 12:29:16 +04:00
for ( i = 0 ; i < N_OUT_URB ; i + + ) {
buffer = kmalloc ( OUT_BUFLEN , GFP_KERNEL ) ;
if ( ! buffer )
goto bail_out_error2 ;
portdata - > out_buffer [ i ] = buffer ;
2010-04-01 20:31:07 +04:00
2012-10-25 12:29:16 +04:00
urb = usb_wwan_setup_urb ( port , port - > bulk_out_endpointAddress ,
USB_DIR_OUT , port ,
buffer , OUT_BUFLEN ,
usb_wwan_outdat_callback ) ;
portdata - > out_urbs [ i ] = urb ;
}
2010-04-01 20:31:07 +04:00
2012-10-25 12:29:16 +04:00
usb_set_serial_port_data ( port , portdata ) ;
2010-04-01 20:31:07 +04:00
return 0 ;
bail_out_error2 :
2012-10-25 12:29:16 +04:00
for ( i = 0 ; i < N_OUT_URB ; i + + ) {
usb_free_urb ( portdata - > out_urbs [ i ] ) ;
kfree ( portdata - > out_buffer [ i ] ) ;
}
2010-04-01 20:31:07 +04:00
bail_out_error :
2012-10-25 12:29:16 +04:00
for ( i = 0 ; i < N_IN_URB ; i + + ) {
usb_free_urb ( portdata - > in_urbs [ i ] ) ;
free_page ( ( unsigned long ) portdata - > in_buffer [ i ] ) ;
}
2010-04-01 20:31:07 +04:00
kfree ( portdata ) ;
2012-10-25 12:29:16 +04:00
return - ENOMEM ;
2010-04-01 20:31:07 +04:00
}
2012-10-25 12:29:16 +04:00
EXPORT_SYMBOL_GPL ( usb_wwan_port_probe ) ;
2010-04-01 20:31:07 +04:00
2012-07-27 03:11:41 +04:00
int usb_wwan_port_remove ( struct usb_serial_port * port )
2010-04-01 20:31:07 +04:00
{
2012-07-27 03:11:41 +04:00
int i ;
2010-04-01 20:31:07 +04:00
struct usb_wwan_port_private * portdata ;
2012-07-27 03:11:41 +04:00
portdata = usb_get_serial_port_data ( port ) ;
usb_set_serial_port_data ( port , NULL ) ;
for ( i = 0 ; i < N_IN_URB ; i + + ) {
usb_free_urb ( portdata - > in_urbs [ i ] ) ;
free_page ( ( unsigned long ) portdata - > in_buffer [ i ] ) ;
}
for ( i = 0 ; i < N_OUT_URB ; i + + ) {
usb_free_urb ( portdata - > out_urbs [ i ] ) ;
kfree ( portdata - > out_buffer [ i ] ) ;
2010-04-01 20:31:07 +04:00
}
2012-07-27 03:11:41 +04:00
kfree ( portdata ) ;
2014-05-26 21:23:23 +04:00
2012-07-27 03:11:41 +04:00
return 0 ;
2010-04-01 20:31:07 +04:00
}
2012-07-27 03:11:41 +04:00
EXPORT_SYMBOL ( usb_wwan_port_remove ) ;
2010-04-01 20:31:07 +04:00
2012-07-27 03:11:41 +04:00
# ifdef CONFIG_PM
2014-05-26 21:23:24 +04:00
static void stop_urbs ( struct usb_serial * serial )
2010-04-01 20:31:07 +04:00
{
int i , j ;
struct usb_serial_port * port ;
struct usb_wwan_port_private * portdata ;
for ( i = 0 ; i < serial - > num_ports ; + + i ) {
port = serial - > port [ i ] ;
portdata = usb_get_serial_port_data ( port ) ;
2012-07-27 03:11:43 +04:00
if ( ! portdata )
continue ;
2012-07-27 03:11:41 +04:00
for ( j = 0 ; j < N_IN_URB ; j + + )
usb_kill_urb ( portdata - > in_urbs [ j ] ) ;
for ( j = 0 ; j < N_OUT_URB ; j + + )
usb_kill_urb ( portdata - > out_urbs [ j ] ) ;
2014-05-26 21:23:24 +04:00
usb_kill_urb ( port - > interrupt_in_urb ) ;
2010-04-01 20:31:07 +04:00
}
}
int usb_wwan_suspend ( struct usb_serial * serial , pm_message_t message )
{
2014-05-26 21:23:26 +04:00
struct usb_wwan_intf_private * intfdata = usb_get_serial_data ( serial ) ;
2010-04-01 20:31:07 +04:00
2014-05-26 21:23:15 +04:00
spin_lock_irq ( & intfdata - > susp_lock ) ;
2011-08-20 01:49:48 +04:00
if ( PMSG_IS_AUTO ( message ) ) {
2014-05-26 21:23:15 +04:00
if ( intfdata - > in_flight ) {
spin_unlock_irq ( & intfdata - > susp_lock ) ;
2010-04-01 20:31:07 +04:00
return - EBUSY ;
2014-05-26 21:23:15 +04:00
}
2010-04-01 20:31:07 +04:00
}
intfdata - > suspended = 1 ;
spin_unlock_irq ( & intfdata - > susp_lock ) ;
2014-05-26 21:23:15 +04:00
2014-05-26 21:23:24 +04:00
stop_urbs ( serial ) ;
2010-04-01 20:31:07 +04:00
return 0 ;
}
EXPORT_SYMBOL ( usb_wwan_suspend ) ;
2014-05-26 21:23:27 +04:00
/* Caller must hold susp_lock. */
static int usb_wwan_submit_delayed_urbs ( struct usb_serial_port * port )
2010-04-01 20:31:07 +04:00
{
2014-05-26 21:23:19 +04:00
struct usb_serial * serial = port - > serial ;
2014-05-26 21:23:26 +04:00
struct usb_wwan_intf_private * data = usb_get_serial_data ( serial ) ;
2010-04-01 20:31:07 +04:00
struct usb_wwan_port_private * portdata ;
struct urb * urb ;
2014-05-26 21:23:19 +04:00
int err_count = 0 ;
int err ;
2010-04-01 20:31:07 +04:00
portdata = usb_get_serial_port_data ( port ) ;
2014-05-26 21:23:26 +04:00
2014-05-26 21:23:27 +04:00
for ( ; ; ) {
urb = usb_get_from_anchor ( & portdata - > delayed ) ;
if ( ! urb )
break ;
2010-04-01 20:31:07 +04:00
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
2014-05-26 21:23:19 +04:00
if ( err ) {
2014-05-26 21:23:27 +04:00
dev_err ( & port - > dev , " %s: submit urb failed: %d \n " ,
2014-05-26 21:23:19 +04:00
__func__ , err ) ;
err_count + + ;
unbusy_queued_urb ( urb , portdata ) ;
usb_autopm_put_interface_async ( serial - > interface ) ;
continue ;
2011-02-10 17:33:29 +03:00
}
2014-05-26 21:23:19 +04:00
data - > in_flight + + ;
2010-04-01 20:31:07 +04:00
}
2014-05-26 21:23:18 +04:00
2014-05-26 21:23:19 +04:00
if ( err_count )
return - EIO ;
return 0 ;
2010-04-01 20:31:07 +04:00
}
int usb_wwan_resume ( struct usb_serial * serial )
{
int i , j ;
struct usb_serial_port * port ;
2014-05-26 21:23:26 +04:00
struct usb_wwan_intf_private * intfdata = usb_get_serial_data ( serial ) ;
2010-04-01 20:31:07 +04:00
struct usb_wwan_port_private * portdata ;
struct urb * urb ;
2014-05-26 21:23:18 +04:00
int err ;
int err_count = 0 ;
2010-04-01 20:31:07 +04:00
2014-05-26 21:23:14 +04:00
spin_lock_irq ( & intfdata - > susp_lock ) ;
2010-04-01 20:31:07 +04:00
for ( i = 0 ; i < serial - > num_ports ; i + + ) {
port = serial - > port [ i ] ;
2014-05-26 21:23:32 +04:00
if ( ! test_bit ( ASYNCB_INITIALIZED , & port - > port . flags ) )
2010-04-01 20:31:07 +04:00
continue ;
2014-05-26 21:23:32 +04:00
portdata = usb_get_serial_port_data ( port ) ;
2014-05-26 21:23:17 +04:00
if ( port - > interrupt_in_urb ) {
err = usb_submit_urb ( port - > interrupt_in_urb ,
GFP_ATOMIC ) ;
if ( err ) {
dev_err ( & port - > dev ,
" %s: submit int urb failed: %d \n " ,
__func__ , err ) ;
2014-05-26 21:23:18 +04:00
err_count + + ;
2014-05-26 21:23:17 +04:00
}
}
2014-05-26 21:23:27 +04:00
err = usb_wwan_submit_delayed_urbs ( port ) ;
2014-05-26 21:23:18 +04:00
if ( err )
err_count + + ;
2010-04-01 20:31:07 +04:00
for ( j = 0 ; j < N_IN_URB ; j + + ) {
urb = portdata - > in_urbs [ j ] ;
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err < 0 ) {
2014-05-26 21:23:25 +04:00
dev_err ( & port - > dev ,
" %s: submit read urb %d failed: %d \n " ,
__func__ , i , err ) ;
2014-05-26 21:23:18 +04:00
err_count + + ;
2010-04-01 20:31:07 +04:00
}
}
}
intfdata - > suspended = 0 ;
spin_unlock_irq ( & intfdata - > susp_lock ) ;
2014-05-26 21:23:18 +04:00
if ( err_count )
return - EIO ;
return 0 ;
2010-04-01 20:31:07 +04:00
}
EXPORT_SYMBOL ( usb_wwan_resume ) ;
# endif
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;