Commit 1365baf7249bb2d05e774e7681237b8e86f5007a
Committed by
Greg Kroah-Hartman
1 parent
eedffd12e0
Exists in
master
and in
7 other branches
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>
Showing 2 changed files with 79 additions and 15 deletions Side-by-side Diff
drivers/usb/class/cdc-acm.c
... | ... | @@ -496,10 +496,19 @@ |
496 | 496 | otherwise it is scheduled, and with high data rates data can get lost. */ |
497 | 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 | 506 | if (acm->used++) { |
507 | + usb_autopm_put_interface(acm->control); | |
500 | 508 | goto done; |
501 | 509 | } |
502 | 510 | |
511 | + | |
503 | 512 | acm->ctrlurb->dev = acm->dev; |
504 | 513 | if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) { |
505 | 514 | dbg("usb_submit_urb(ctrl irq) failed"); |
506 | 515 | |
507 | 516 | |
... | ... | @@ -526,14 +535,15 @@ |
526 | 535 | |
527 | 536 | done: |
528 | 537 | err_out: |
529 | - mutex_unlock(&open_mutex); | |
538 | + mutex_unlock(&acm->mutex); | |
530 | 539 | return rv; |
531 | 540 | |
532 | 541 | full_bailout: |
533 | 542 | usb_kill_urb(acm->ctrlurb); |
534 | 543 | bail_out: |
544 | + usb_autopm_put_interface(acm->control); | |
535 | 545 | acm->used--; |
536 | - mutex_unlock(&open_mutex); | |
546 | + mutex_unlock(&acm->mutex); | |
537 | 547 | return -EIO; |
538 | 548 | } |
539 | 549 | |
... | ... | @@ -570,6 +580,7 @@ |
570 | 580 | usb_kill_urb(acm->writeurb); |
571 | 581 | for (i = 0; i < nr; i++) |
572 | 582 | usb_kill_urb(acm->ru[i].urb); |
583 | + usb_autopm_put_interface(acm->control); | |
573 | 584 | } else |
574 | 585 | acm_tty_unregister(acm); |
575 | 586 | } |
... | ... | @@ -980,6 +991,7 @@ |
980 | 991 | spin_lock_init(&acm->throttle_lock); |
981 | 992 | spin_lock_init(&acm->write_lock); |
982 | 993 | spin_lock_init(&acm->read_lock); |
994 | + mutex_init(&acm->mutex); | |
983 | 995 | acm->write_ready = 1; |
984 | 996 | acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); |
985 | 997 | |
... | ... | @@ -1096,6 +1108,25 @@ |
1096 | 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 | 1130 | static void acm_disconnect(struct usb_interface *intf) |
1100 | 1131 | { |
1101 | 1132 | struct acm *acm = usb_get_intfdata(intf); |
1102 | 1133 | |
... | ... | @@ -1123,20 +1154,8 @@ |
1123 | 1154 | usb_set_intfdata(acm->control, NULL); |
1124 | 1155 | usb_set_intfdata(acm->data, NULL); |
1125 | 1156 | |
1126 | - tasklet_disable(&acm->urb_task); | |
1157 | + stop_data_traffic(acm); | |
1127 | 1158 | |
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 | - | |
1140 | 1159 | acm_write_buffers_free(acm); |
1141 | 1160 | usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); |
1142 | 1161 | for (i = 0; i < acm->rx_buflimit; i++) |
... | ... | @@ -1156,6 +1175,46 @@ |
1156 | 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 | 1219 | * USB driver structure. |
1161 | 1220 | */ |
1162 | 1221 | |
... | ... | @@ -1208,7 +1267,10 @@ |
1208 | 1267 | .name = "cdc_acm", |
1209 | 1268 | .probe = acm_probe, |
1210 | 1269 | .disconnect = acm_disconnect, |
1270 | + .suspend = acm_suspend, | |
1271 | + .resume = acm_resume, | |
1211 | 1272 | .id_table = acm_ids, |
1273 | + .supports_autosuspend = 1, | |
1212 | 1274 | }; |
1213 | 1275 | |
1214 | 1276 | /* |
drivers/usb/class/cdc-acm.h
... | ... | @@ -107,6 +107,7 @@ |
107 | 107 | int write_used; /* number of non-empty write buffers */ |
108 | 108 | int write_ready; /* write urb is not running */ |
109 | 109 | spinlock_t write_lock; |
110 | + struct mutex mutex; | |
110 | 111 | struct usb_cdc_line_coding line; /* bits, stop, parity */ |
111 | 112 | struct work_struct work; /* work queue entry for line discipline waking up */ |
112 | 113 | struct tasklet_struct urb_task; /* rx processing */ |
... | ... | @@ -120,6 +121,7 @@ |
120 | 121 | unsigned char throttle; /* throttled by tty layer */ |
121 | 122 | unsigned char clocal; /* termios CLOCAL */ |
122 | 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 | 127 | #define CDC_DATA_INTERFACE_TYPE 0x0a |