@ -596,6 +596,7 @@ static struct usb_driver option_driver = {
# ifdef CONFIG_PM
. suspend = usb_serial_suspend ,
. resume = usb_serial_resume ,
. supports_autosuspend = 1 ,
# endif
. id_table = option_ids ,
. no_dynamic_id = 1 ,
@ -643,6 +644,12 @@ static int debug;
# define IN_BUFLEN 4096
# define OUT_BUFLEN 4096
struct option_intf_private {
spinlock_t susp_lock ;
unsigned int suspended : 1 ;
int in_flight ;
} ;
struct option_port_private {
/* Input endpoints and buffer for this port */
struct urb * in_urbs [ N_IN_URB ] ;
@ -651,6 +658,8 @@ struct option_port_private {
struct urb * out_urbs [ N_OUT_URB ] ;
u8 * out_buffer [ N_OUT_URB ] ;
unsigned long out_busy ; /* Bit vector of URBs in use */
int opened ;
struct usb_anchor delayed ;
/* Settings for the port */
int rts_state ; /* Handshaking pins (outputs) */
@ -697,12 +706,17 @@ module_exit(option_exit);
static int option_probe ( struct usb_serial * serial ,
const struct usb_device_id * id )
{
struct option_intf_private * data ;
/* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */
if ( serial - > dev - > descriptor . idVendor = = DLINK_VENDOR_ID & &
serial - > dev - > descriptor . idProduct = = DLINK_PRODUCT_DWM_652 & &
serial - > interface - > cur_altsetting - > desc . bInterfaceClass = = 0x8 )
return - ENODEV ;
data = serial - > private = kzalloc ( sizeof ( struct option_intf_private ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
spin_lock_init ( & data - > susp_lock ) ;
return 0 ;
}
@ -759,12 +773,15 @@ static int option_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char * buf , int count )
{
struct option_port_private * portdata ;
struct option_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 ) ;
intfdata = port - > serial - > private ;
dbg ( " %s: write (%d chars) " , __func__ , count ) ;
@ -786,17 +803,33 @@ static int option_write(struct tty_struct *tty, struct usb_serial_port *port,
dbg ( " %s: endpoint %d buf %d " , __func__ ,
usb_pipeendpoint ( this_urb - > pipe ) , i ) ;
err = usb_autopm_get_interface_async ( port - > serial - > interface ) ;
if ( err < 0 )
break ;
/* send the data */
memcpy ( this_urb - > transfer_buffer , buf , todo ) ;
this_urb - > transfer_buffer_length = todo ;
err = usb_submit_urb ( this_urb , GFP_ATOMIC ) ;
if ( err ) {
dbg ( " usb_submit_urb %p (write bulk) failed "
" (%d) " , this_urb , err ) ;
clear_bit ( i , & portdata - > out_busy ) ;
continue ;
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 ) {
dbg ( " usb_submit_urb %p (write bulk) failed "
" (%d) " , this_urb , err ) ;
clear_bit ( i , & portdata - > out_busy ) ;
spin_lock_irqsave ( & intfdata - > susp_lock , flags ) ;
intfdata - > in_flight - - ;
spin_unlock_irqrestore ( & intfdata - > susp_lock , flags ) ;
continue ;
}
}
portdata - > tx_start_time [ i ] = jiffies ;
buf + = todo ;
left - = todo ;
@ -840,7 +873,10 @@ static void option_indat_callback(struct urb *urb)
if ( err )
printk ( KERN_ERR " %s: resubmit read urb failed. "
" (%d) " , __func__ , err ) ;
else
usb_mark_last_busy ( port - > serial - > dev ) ;
}
}
return ;
}
@ -849,15 +885,21 @@ static void option_outdat_callback(struct urb *urb)
{
struct usb_serial_port * port ;
struct option_port_private * portdata ;
struct option_intf_private * intfdata ;
int i ;
dbg ( " %s " , __func__ ) ;
port = urb - > context ;
intfdata = port - > serial - > private ;
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 ) {
smp_mb__before_clear_bit ( ) ;
@ -967,10 +1009,13 @@ static int option_chars_in_buffer(struct tty_struct *tty)
static int option_open ( struct tty_struct * tty , struct usb_serial_port * port )
{
struct option_port_private * portdata ;
struct option_intf_private * intfdata ;
struct usb_serial * serial = port - > serial ;
int i , err ;
struct urb * urb ;
portdata = usb_get_serial_port_data ( port ) ;
intfdata = serial - > private ;
dbg ( " %s " , __func__ ) ;
@ -989,6 +1034,12 @@ static int option_open(struct tty_struct *tty, struct usb_serial_port *port)
option_send_setup ( port ) ;
serial - > interface - > needs_remote_wakeup = 1 ;
spin_lock_irq ( & intfdata - > susp_lock ) ;
portdata - > opened = 1 ;
spin_unlock_irq ( & intfdata - > susp_lock ) ;
usb_autopm_put_interface ( serial - > interface ) ;
return 0 ;
}
@ -1013,16 +1064,23 @@ static void option_close(struct usb_serial_port *port)
int i ;
struct usb_serial * serial = port - > serial ;
struct option_port_private * portdata ;
struct option_intf_private * intfdata = port - > serial - > private ;
dbg ( " %s " , __func__ ) ;
portdata = usb_get_serial_port_data ( port ) ;
if ( serial - > dev ) {
/* Stop reading/writing urbs */
spin_lock_irq ( & intfdata - > susp_lock ) ;
portdata - > opened = 0 ;
spin_unlock_irq ( & intfdata - > susp_lock ) ;
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 ] ) ;
usb_autopm_get_interface ( serial - > interface ) ;
serial - > interface - > needs_remote_wakeup = 0 ;
}
}
@ -1127,6 +1185,7 @@ static int option_startup(struct usb_serial *serial)
__func__ , i ) ;
return 1 ;
}
init_usb_anchor ( & portdata - > delayed ) ;
for ( j = 0 ; j < N_IN_URB ; j + + ) {
buffer = ( u8 * ) __get_free_page ( GFP_KERNEL ) ;
@ -1229,18 +1288,52 @@ static void option_release(struct usb_serial *serial)
# ifdef CONFIG_PM
static int option_suspend ( struct usb_serial * serial , pm_message_t message )
{
struct option_intf_private * intfdata = serial - > private ;
int b ;
dbg ( " %s entered " , __func__ ) ;
if ( serial - > dev - > auto_pm ) {
spin_lock_irq ( & intfdata - > susp_lock ) ;
b = intfdata - > in_flight ;
spin_unlock_irq ( & intfdata - > susp_lock ) ;
if ( b )
return - EBUSY ;
}
spin_lock_irq ( & intfdata - > susp_lock ) ;
intfdata - > suspended = 1 ;
spin_unlock_irq ( & intfdata - > susp_lock ) ;
stop_read_write_urbs ( serial ) ;
return 0 ;
}
static void play_delayed ( struct usb_serial_port * port )
{
struct option_intf_private * data ;
struct option_port_private * portdata ;
struct urb * urb ;
int err ;
portdata = usb_get_serial_port_data ( port ) ;
data = port - > serial - > private ;
while ( ( urb = usb_get_from_anchor ( & portdata - > delayed ) ) ) {
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( ! err )
data - > in_flight + + ;
}
}
static int option_resume ( struct usb_serial * serial )
{
int err , i , j ;
int i , j ;
struct usb_serial_port * port ;
struct urb * urb ;
struct option_intf_private * intfdata = serial - > private ;
struct option_port_private * portdata ;
struct urb * urb ;
int err = 0 ;
dbg ( " %s entered " , __func__ ) ;
/* get the interrupt URBs resubmitted unconditionally */
@ -1255,7 +1348,7 @@ static int option_resume(struct usb_serial *serial)
if ( err < 0 ) {
err ( " %s: Error %d for interrupt URB of port%d " ,
__func__ , err , i ) ;
return err ;
goto err_out ;
}
}
@ -1263,27 +1356,32 @@ static int option_resume(struct usb_serial *serial)
/* walk all ports */
port = serial - > port [ i ] ;
portdata = usb_get_serial_port_data ( port ) ;
mutex_lock ( & port - > mutex ) ;
/* skip closed ports */
if ( ! port - > port . count ) {
mutex_unlock ( & port - > mutex ) ;
spin_lock_irq ( & intfdata - > susp_lock ) ;
if ( ! portdata - > opened ) {
spin_unlock_irq ( & intfdata - > susp_lock ) ;
continue ;
}
for ( j = 0 ; j < N_IN_URB ; j + + ) {
urb = portdata - > in_urbs [ j ] ;
err = usb_submit_urb ( urb , GFP_NOIO ) ;
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err < 0 ) {
mutex_unlock ( & port - > mutex ) ;
err ( " %s: Error %d for bulk URB %d " ,
__func__ , err , i ) ;
return err ;
spin_unlock_irq ( & intfdata - > susp_lock ) ;
goto err_out ;
}
}
mutex_unlock ( & port - > mutex ) ;
play_delayed ( port ) ;
spin_unlock_irq ( & intfdata - > susp_lock ) ;
}
return 0 ;
spin_lock_irq ( & intfdata - > susp_lock ) ;
intfdata - > suspended = 0 ;
spin_unlock_irq ( & intfdata - > susp_lock ) ;
err_out :
return err ;
}
# endif