aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/class
diff options
context:
space:
mode:
authorOliver Neukum <oliver@neukum.org>2007-10-12 11:24:28 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2008-02-01 17:34:47 -0500
commit1365baf7249bb2d05e774e7681237b8e86f5007a (patch)
treed9a77d461a6cacf1154c59468f47db854982f254 /drivers/usb/class
parenteedffd12e07d8fd150618d603d010b491dc90354 (diff)
USB: autosuspend for cdc-acm
Here we go. This patch implements suspend/resume and autosuspend for the CDC ACM driver. Signed-off-by: Oliver Neukum <oneukum@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/class')
-rw-r--r--drivers/usb/class/cdc-acm.c92
-rw-r--r--drivers/usb/class/cdc-acm.h2
2 files changed, 79 insertions, 15 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 912d97aaf9bf..b0f9873ce6b7 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -496,10 +496,19 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
496 otherwise it is scheduled, and with high data rates data can get lost. */ 496 otherwise it is scheduled, and with high data rates data can get lost. */
497 tty->low_latency = 1; 497 tty->low_latency = 1;
498 498
499 if (usb_autopm_get_interface(acm->control)) {
500 mutex_unlock(&open_mutex);
501 return -EIO;
502 }
503
504 mutex_lock(&acm->mutex);
505 mutex_unlock(&open_mutex);
499 if (acm->used++) { 506 if (acm->used++) {
507 usb_autopm_put_interface(acm->control);
500 goto done; 508 goto done;
501 } 509 }
502 510
511
503 acm->ctrlurb->dev = acm->dev; 512 acm->ctrlurb->dev = acm->dev;
504 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) { 513 if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
505 dbg("usb_submit_urb(ctrl irq) failed"); 514 dbg("usb_submit_urb(ctrl irq) failed");
@@ -526,14 +535,15 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
526 535
527done: 536done:
528err_out: 537err_out:
529 mutex_unlock(&open_mutex); 538 mutex_unlock(&acm->mutex);
530 return rv; 539 return rv;
531 540
532full_bailout: 541full_bailout:
533 usb_kill_urb(acm->ctrlurb); 542 usb_kill_urb(acm->ctrlurb);
534bail_out: 543bail_out:
544 usb_autopm_put_interface(acm->control);
535 acm->used--; 545 acm->used--;
536 mutex_unlock(&open_mutex); 546 mutex_unlock(&acm->mutex);
537 return -EIO; 547 return -EIO;
538} 548}
539 549
@@ -570,6 +580,7 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
570 usb_kill_urb(acm->writeurb); 580 usb_kill_urb(acm->writeurb);
571 for (i = 0; i < nr; i++) 581 for (i = 0; i < nr; i++)
572 usb_kill_urb(acm->ru[i].urb); 582 usb_kill_urb(acm->ru[i].urb);
583 usb_autopm_put_interface(acm->control);
573 } else 584 } else
574 acm_tty_unregister(acm); 585 acm_tty_unregister(acm);
575 } 586 }
@@ -980,6 +991,7 @@ skip_normal_probe:
980 spin_lock_init(&acm->throttle_lock); 991 spin_lock_init(&acm->throttle_lock);
981 spin_lock_init(&acm->write_lock); 992 spin_lock_init(&acm->write_lock);
982 spin_lock_init(&acm->read_lock); 993 spin_lock_init(&acm->read_lock);
994 mutex_init(&acm->mutex);
983 acm->write_ready = 1; 995 acm->write_ready = 1;
984 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); 996 acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
985 997
@@ -1096,6 +1108,25 @@ alloc_fail:
1096 return -ENOMEM; 1108 return -ENOMEM;
1097} 1109}
1098 1110
1111static void stop_data_traffic(struct acm *acm)
1112{
1113 int i;
1114
1115 tasklet_disable(&acm->urb_task);
1116
1117 usb_kill_urb(acm->ctrlurb);
1118 usb_kill_urb(acm->writeurb);
1119 for (i = 0; i < acm->rx_buflimit; i++)
1120 usb_kill_urb(acm->ru[i].urb);
1121
1122 INIT_LIST_HEAD(&acm->filled_read_bufs);
1123 INIT_LIST_HEAD(&acm->spare_read_bufs);
1124
1125 tasklet_enable(&acm->urb_task);
1126
1127 cancel_work_sync(&acm->work);
1128}
1129
1099static void acm_disconnect(struct usb_interface *intf) 1130static void acm_disconnect(struct usb_interface *intf)
1100{ 1131{
1101 struct acm *acm = usb_get_intfdata(intf); 1132 struct acm *acm = usb_get_intfdata(intf);
@@ -1123,19 +1154,7 @@ static void acm_disconnect(struct usb_interface *intf)
1123 usb_set_intfdata(acm->control, NULL); 1154 usb_set_intfdata(acm->control, NULL);
1124 usb_set_intfdata(acm->data, NULL); 1155 usb_set_intfdata(acm->data, NULL);
1125 1156
1126 tasklet_disable(&acm->urb_task); 1157 stop_data_traffic(acm);
1127
1128 usb_kill_urb(acm->ctrlurb);
1129 usb_kill_urb(acm->writeurb);
1130 for (i = 0; i < acm->rx_buflimit; i++)
1131 usb_kill_urb(acm->ru[i].urb);
1132
1133 INIT_LIST_HEAD(&acm->filled_read_bufs);
1134 INIT_LIST_HEAD(&acm->spare_read_bufs);
1135
1136 tasklet_enable(&acm->urb_task);
1137
1138 flush_scheduled_work(); /* wait for acm_softint */
1139 1158
1140 acm_write_buffers_free(acm); 1159 acm_write_buffers_free(acm);
1141 usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); 1160 usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
@@ -1156,6 +1175,46 @@ static void acm_disconnect(struct usb_interface *intf)
1156 tty_hangup(acm->tty); 1175 tty_hangup(acm->tty);
1157} 1176}
1158 1177
1178static int acm_suspend(struct usb_interface *intf, pm_message_t message)
1179{
1180 struct acm *acm = usb_get_intfdata(intf);
1181
1182 if (acm->susp_count++)
1183 return 0;
1184 /*
1185 we treat opened interfaces differently,
1186 we must guard against open
1187 */
1188 mutex_lock(&acm->mutex);
1189
1190 if (acm->used)
1191 stop_data_traffic(acm);
1192
1193 mutex_unlock(&acm->mutex);
1194 return 0;
1195}
1196
1197static int acm_resume(struct usb_interface *intf)
1198{
1199 struct acm *acm = usb_get_intfdata(intf);
1200 int rv = 0;
1201
1202 if (--acm->susp_count)
1203 return 0;
1204
1205 mutex_lock(&acm->mutex);
1206 if (acm->used) {
1207 rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
1208 if (rv < 0)
1209 goto err_out;
1210
1211 tasklet_schedule(&acm->urb_task);
1212 }
1213
1214err_out:
1215 mutex_unlock(&acm->mutex);
1216 return rv;
1217}
1159/* 1218/*
1160 * USB driver structure. 1219 * USB driver structure.
1161 */ 1220 */
@@ -1208,7 +1267,10 @@ static struct usb_driver acm_driver = {
1208 .name = "cdc_acm", 1267 .name = "cdc_acm",
1209 .probe = acm_probe, 1268 .probe = acm_probe,
1210 .disconnect = acm_disconnect, 1269 .disconnect = acm_disconnect,
1270 .suspend = acm_suspend,
1271 .resume = acm_resume,
1211 .id_table = acm_ids, 1272 .id_table = acm_ids,
1273 .supports_autosuspend = 1,
1212}; 1274};
1213 1275
1214/* 1276/*
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 09f7765dbf8d..8df6a57dcf9e 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -107,6 +107,7 @@ struct acm {
107 int write_used; /* number of non-empty write buffers */ 107 int write_used; /* number of non-empty write buffers */
108 int write_ready; /* write urb is not running */ 108 int write_ready; /* write urb is not running */
109 spinlock_t write_lock; 109 spinlock_t write_lock;
110 struct mutex mutex;
110 struct usb_cdc_line_coding line; /* bits, stop, parity */ 111 struct usb_cdc_line_coding line; /* bits, stop, parity */
111 struct work_struct work; /* work queue entry for line discipline waking up */ 112 struct work_struct work; /* work queue entry for line discipline waking up */
112 struct tasklet_struct urb_task; /* rx processing */ 113 struct tasklet_struct urb_task; /* rx processing */
@@ -120,6 +121,7 @@ struct acm {
120 unsigned char throttle; /* throttled by tty layer */ 121 unsigned char throttle; /* throttled by tty layer */
121 unsigned char clocal; /* termios CLOCAL */ 122 unsigned char clocal; /* termios CLOCAL */
122 unsigned int ctrl_caps; /* control capabilities from the class specific header */ 123 unsigned int ctrl_caps; /* control capabilities from the class specific header */
124 unsigned int susp_count; /* number of suspended interfaces */
123}; 125};
124 126
125#define CDC_DATA_INTERFACE_TYPE 0x0a 127#define CDC_DATA_INTERFACE_TYPE 0x0a