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/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel