diff options
author | Oliver Neukum <oliver@neukum.org> | 2007-10-12 11:24:28 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-02-01 17:34:47 -0500 |
commit | 1365baf7249bb2d05e774e7681237b8e86f5007a (patch) | |
tree | d9a77d461a6cacf1154c59468f47db854982f254 /drivers/usb/class | |
parent | eedffd12e07d8fd150618d603d010b491dc90354 (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.c | 92 | ||||
-rw-r--r-- | drivers/usb/class/cdc-acm.h | 2 |
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 | ||
527 | done: | 536 | done: |
528 | err_out: | 537 | err_out: |
529 | mutex_unlock(&open_mutex); | 538 | mutex_unlock(&acm->mutex); |
530 | return rv; | 539 | return rv; |
531 | 540 | ||
532 | full_bailout: | 541 | full_bailout: |
533 | usb_kill_urb(acm->ctrlurb); | 542 | usb_kill_urb(acm->ctrlurb); |
534 | bail_out: | 543 | bail_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 | ||
1111 | static 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 | |||
1099 | static void acm_disconnect(struct usb_interface *intf) | 1130 | static 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 | ||
1178 | static 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 | |||
1197 | static 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 | |||
1214 | err_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 |