Hi, this implements autosuspend for cdc-acm devices.
Regards Oliver Signed-off-by: Oliver Neukum <[EMAIL PROTECTED]> -- --- a/drivers/usb/class/cdc-acm.c 2007-06-11 13:27:17.000000000 +0200 +++ b/drivers/usb/class/cdc-acm.c 2007-05-30 20:56:00.000000000 +0200 @@ -330,26 +330,26 @@ static void acm_read_bulk(struct urb *ur return; if (status) - dev_dbg(&acm->data->dev, "bulk rx status %d", status); + dev_dbg(&acm->data->dev, "bulk rx status %d\n", status); buf = rcv->buffer; buf->size = urb->actual_length; + spin_lock(&acm->read_lock); if (likely(status == 0)) { - spin_lock(&acm->read_lock); list_add_tail(&rcv->list, &acm->spare_read_urbs); list_add_tail(&buf->list, &acm->filled_read_bufs); - spin_unlock(&acm->read_lock); } else { /* we drop the buffer due to an error */ - spin_lock(&acm->read_lock); list_add_tail(&rcv->list, &acm->spare_read_urbs); list_add(&buf->list, &acm->spare_read_bufs); - spin_unlock(&acm->read_lock); /* nevertheless the tasklet must be kicked unconditionally so the queue cannot dry up */ } - tasklet_schedule(&acm->urb_task); + + if (!acm->suspending) + tasklet_schedule(&acm->urb_task); + spin_unlock(&acm->read_lock); } static void acm_rx_tasklet(unsigned long _acm) @@ -835,7 +835,7 @@ static int acm_probe (struct usb_interfa if (!buflen) { if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) { - dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint"); + dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n"); buflen = intf->cur_altsetting->endpoint->extralen; buffer = intf->cur_altsetting->endpoint->extra; } else { @@ -885,24 +885,24 @@ next_desc: if (!union_header) { if (call_interface_num > 0) { - dev_dbg(&intf->dev,"No union descriptor, using call management descriptor"); + dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n"); data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); control_interface = intf; } else { - dev_dbg(&intf->dev,"No union descriptor, giving up"); + dev_dbg(&intf->dev,"No union descriptor, giving up\n"); return -ENODEV; } } else { control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0)); if (!control_interface || !data_interface) { - dev_dbg(&intf->dev,"no interfaces"); + dev_dbg(&intf->dev,"no interfaces\n"); return -ENODEV; } } if (data_interface_num != call_interface_num) - dev_dbg(&intf->dev,"Seperate call control interface. That is not fully supported."); + dev_dbg(&intf->dev,"Seperate call control interface. That is not fully supported.\n"); skip_normal_probe: @@ -910,7 +910,7 @@ skip_normal_probe: if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) { if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) { struct usb_interface *t; - dev_dbg(&intf->dev,"Your device has switched interfaces."); + dev_dbg(&intf->dev,"Your device has switched interfaces.\n"); t = control_interface; control_interface = data_interface; @@ -921,7 +921,7 @@ skip_normal_probe: } if (usb_interface_claimed(data_interface)) { /* valid in this context */ - dev_dbg(&intf->dev,"The data interface isn't available"); + dev_dbg(&intf->dev,"The data interface isn't available\n"); return -EBUSY; } @@ -938,7 +938,7 @@ skip_normal_probe: if (!usb_endpoint_dir_in(epread)) { /* descriptors are swapped */ struct usb_endpoint_descriptor *t; - dev_dbg(&intf->dev,"The data interface has switched endpoints"); + dev_dbg(&intf->dev,"The data interface has switched endpoints\n"); t = epread; epread = epwrite; @@ -1143,11 +1143,60 @@ static void acm_disconnect(struct usb_in } mutex_unlock(&open_mutex); - if (acm->tty) tty_hangup(acm->tty); } +static int acm_suspend (struct usb_interface *intf, pm_message_t message) +{ + struct acm *acm = usb_get_intfdata(intf); + int i; + + if (acm->suspend_counter++) { + dev_dbg(&intf->dev, "Suspend on additional interface\n"); + return 0; + } + + spin_lock_irq(&acm->read_lock); + acm->suspending = 1; + spin_unlock_irq(&acm->read_lock); + + tasklet_disable(&acm->urb_task); + + usb_kill_urb(acm->ctrlurb); + usb_kill_urb(acm->writeurb); + for (i = 0; i < acm->rx_buflimit; i++) + usb_kill_urb(acm->ru[i].urb); + + dev_dbg(&intf->dev, "suspension of ACM device.\n"); + return 0; +} + +static int acm_resume (struct usb_interface *intf) +{ + struct acm *acm = usb_get_intfdata(intf); + + if (--acm->suspend_counter) + return 0; + + spin_lock_irq(&acm->read_lock); + acm->suspending = 0; + spin_unlock_irq(&acm->read_lock); + + if (!acm->used) + return 0; + + if (usb_submit_urb(acm->ctrlurb, GFP_NOIO)) { + dev_dbg(&intf->dev, "cannot resume control urb"); + return -EIO; + } + + /* This restarts the read queue */ + tasklet_schedule(&acm->urb_task); + + return 0; +} + /* * USB driver structure. */ @@ -1197,6 +1246,8 @@ static struct usb_driver acm_driver = { .name = "cdc_acm", .probe = acm_probe, .disconnect = acm_disconnect, + .suspend = acm_suspend, + .resume = acm_resume, .id_table = acm_ids, }; --- a/drivers/usb/class/cdc-acm.h 2007-06-11 13:27:17.000000000 +0200 +++ b/drivers/usb/class/cdc-acm.h 2007-05-30 11:57:43.000000000 +0200 @@ -119,6 +119,8 @@ struct acm { unsigned int minor; /* acm minor number */ unsigned char throttle; /* throttled by tty layer */ unsigned char clocal; /* termios CLOCAL */ + unsigned char suspend_counter; /* count of suspended interfaces */ + unsigned char suspending; /* suspension is in progress */ unsigned int ctrl_caps; /* control capabilities from the class specific header */ }; ------------------------------------------------------------------------- This SF.net email is sponsored by DB2 Express Download DB2 Express C - the FREE version of DB2 express and take control of your XML. No limits. Just data. Click to get it now. http://sourceforge.net/powerbar/db2/ _______________________________________________ linux-usb-devel@lists.sourceforge.net To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel