diff options
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 86 |
1 files changed, 46 insertions, 40 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 0725b1871f23..efc4373ededb 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c | |||
@@ -51,6 +51,7 @@ | |||
51 | */ | 51 | */ |
52 | 52 | ||
53 | #undef DEBUG | 53 | #undef DEBUG |
54 | #undef VERBOSE_DEBUG | ||
54 | 55 | ||
55 | #include <linux/kernel.h> | 56 | #include <linux/kernel.h> |
56 | #include <linux/errno.h> | 57 | #include <linux/errno.h> |
@@ -70,6 +71,9 @@ | |||
70 | 71 | ||
71 | #include "cdc-acm.h" | 72 | #include "cdc-acm.h" |
72 | 73 | ||
74 | |||
75 | #define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */ | ||
76 | |||
73 | /* | 77 | /* |
74 | * Version Information | 78 | * Version Information |
75 | */ | 79 | */ |
@@ -85,6 +89,12 @@ static DEFINE_MUTEX(open_mutex); | |||
85 | 89 | ||
86 | #define ACM_READY(acm) (acm && acm->dev && acm->used) | 90 | #define ACM_READY(acm) (acm && acm->dev && acm->used) |
87 | 91 | ||
92 | #ifdef VERBOSE_DEBUG | ||
93 | #define verbose 1 | ||
94 | #else | ||
95 | #define verbose 0 | ||
96 | #endif | ||
97 | |||
88 | /* | 98 | /* |
89 | * Functions for ACM control messages. | 99 | * Functions for ACM control messages. |
90 | */ | 100 | */ |
@@ -136,19 +146,17 @@ static int acm_wb_alloc(struct acm *acm) | |||
136 | static int acm_wb_is_avail(struct acm *acm) | 146 | static int acm_wb_is_avail(struct acm *acm) |
137 | { | 147 | { |
138 | int i, n; | 148 | int i, n; |
149 | unsigned long flags; | ||
139 | 150 | ||
140 | n = ACM_NW; | 151 | n = ACM_NW; |
152 | spin_lock_irqsave(&acm->write_lock, flags); | ||
141 | for (i = 0; i < ACM_NW; i++) { | 153 | for (i = 0; i < ACM_NW; i++) { |
142 | n -= acm->wb[i].use; | 154 | n -= acm->wb[i].use; |
143 | } | 155 | } |
156 | spin_unlock_irqrestore(&acm->write_lock, flags); | ||
144 | return n; | 157 | return n; |
145 | } | 158 | } |
146 | 159 | ||
147 | static inline int acm_wb_is_used(struct acm *acm, int wbn) | ||
148 | { | ||
149 | return acm->wb[wbn].use; | ||
150 | } | ||
151 | |||
152 | /* | 160 | /* |
153 | * Finish write. | 161 | * Finish write. |
154 | */ | 162 | */ |
@@ -157,7 +165,6 @@ static void acm_write_done(struct acm *acm, struct acm_wb *wb) | |||
157 | unsigned long flags; | 165 | unsigned long flags; |
158 | 166 | ||
159 | spin_lock_irqsave(&acm->write_lock, flags); | 167 | spin_lock_irqsave(&acm->write_lock, flags); |
160 | acm->write_ready = 1; | ||
161 | wb->use = 0; | 168 | wb->use = 0; |
162 | acm->transmitting--; | 169 | acm->transmitting--; |
163 | spin_unlock_irqrestore(&acm->write_lock, flags); | 170 | spin_unlock_irqrestore(&acm->write_lock, flags); |
@@ -190,40 +197,25 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb) | |||
190 | static int acm_write_start(struct acm *acm, int wbn) | 197 | static int acm_write_start(struct acm *acm, int wbn) |
191 | { | 198 | { |
192 | unsigned long flags; | 199 | unsigned long flags; |
193 | struct acm_wb *wb; | 200 | struct acm_wb *wb = &acm->wb[wbn]; |
194 | int rc; | 201 | int rc; |
195 | 202 | ||
196 | spin_lock_irqsave(&acm->write_lock, flags); | 203 | spin_lock_irqsave(&acm->write_lock, flags); |
197 | if (!acm->dev) { | 204 | if (!acm->dev) { |
205 | wb->use = 0; | ||
198 | spin_unlock_irqrestore(&acm->write_lock, flags); | 206 | spin_unlock_irqrestore(&acm->write_lock, flags); |
199 | return -ENODEV; | 207 | return -ENODEV; |
200 | } | 208 | } |
201 | 209 | ||
202 | if (!acm->write_ready) { | ||
203 | spin_unlock_irqrestore(&acm->write_lock, flags); | ||
204 | return 0; /* A white lie */ | ||
205 | } | ||
206 | |||
207 | wb = &acm->wb[wbn]; | ||
208 | if(acm_wb_is_avail(acm) <= 1) | ||
209 | acm->write_ready = 0; | ||
210 | |||
211 | dbg("%s susp_count: %d", __func__, acm->susp_count); | 210 | dbg("%s susp_count: %d", __func__, acm->susp_count); |
212 | if (acm->susp_count) { | 211 | if (acm->susp_count) { |
213 | acm->old_ready = acm->write_ready; | ||
214 | acm->delayed_wb = wb; | 212 | acm->delayed_wb = wb; |
215 | acm->write_ready = 0; | ||
216 | schedule_work(&acm->waker); | 213 | schedule_work(&acm->waker); |
217 | spin_unlock_irqrestore(&acm->write_lock, flags); | 214 | spin_unlock_irqrestore(&acm->write_lock, flags); |
218 | return 0; /* A white lie */ | 215 | return 0; /* A white lie */ |
219 | } | 216 | } |
220 | usb_mark_last_busy(acm->dev); | 217 | usb_mark_last_busy(acm->dev); |
221 | 218 | ||
222 | if (!acm_wb_is_used(acm, wbn)) { | ||
223 | spin_unlock_irqrestore(&acm->write_lock, flags); | ||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | rc = acm_start_wb(acm, wb); | 219 | rc = acm_start_wb(acm, wb); |
228 | spin_unlock_irqrestore(&acm->write_lock, flags); | 220 | spin_unlock_irqrestore(&acm->write_lock, flags); |
229 | 221 | ||
@@ -488,22 +480,28 @@ urbs: | |||
488 | /* data interface wrote those outgoing bytes */ | 480 | /* data interface wrote those outgoing bytes */ |
489 | static void acm_write_bulk(struct urb *urb) | 481 | static void acm_write_bulk(struct urb *urb) |
490 | { | 482 | { |
491 | struct acm *acm; | ||
492 | struct acm_wb *wb = urb->context; | 483 | struct acm_wb *wb = urb->context; |
484 | struct acm *acm = wb->instance; | ||
493 | 485 | ||
494 | dbg("Entering acm_write_bulk with status %d", urb->status); | 486 | if (verbose || urb->status |
487 | || (urb->actual_length != urb->transfer_buffer_length)) | ||
488 | dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n", | ||
489 | urb->actual_length, | ||
490 | urb->transfer_buffer_length, | ||
491 | urb->status); | ||
495 | 492 | ||
496 | acm = wb->instance; | ||
497 | acm_write_done(acm, wb); | 493 | acm_write_done(acm, wb); |
498 | if (ACM_READY(acm)) | 494 | if (ACM_READY(acm)) |
499 | schedule_work(&acm->work); | 495 | schedule_work(&acm->work); |
496 | else | ||
497 | wake_up_interruptible(&acm->drain_wait); | ||
500 | } | 498 | } |
501 | 499 | ||
502 | static void acm_softint(struct work_struct *work) | 500 | static void acm_softint(struct work_struct *work) |
503 | { | 501 | { |
504 | struct acm *acm = container_of(work, struct acm, work); | 502 | struct acm *acm = container_of(work, struct acm, work); |
505 | dbg("Entering acm_softint."); | 503 | |
506 | 504 | dev_vdbg(&acm->data->dev, "tx work\n"); | |
507 | if (!ACM_READY(acm)) | 505 | if (!ACM_READY(acm)) |
508 | return; | 506 | return; |
509 | tty_wakeup(acm->tty); | 507 | tty_wakeup(acm->tty); |
@@ -512,7 +510,6 @@ static void acm_softint(struct work_struct *work) | |||
512 | static void acm_waker(struct work_struct *waker) | 510 | static void acm_waker(struct work_struct *waker) |
513 | { | 511 | { |
514 | struct acm *acm = container_of(waker, struct acm, waker); | 512 | struct acm *acm = container_of(waker, struct acm, waker); |
515 | long flags; | ||
516 | int rv; | 513 | int rv; |
517 | 514 | ||
518 | rv = usb_autopm_get_interface(acm->control); | 515 | rv = usb_autopm_get_interface(acm->control); |
@@ -524,9 +521,6 @@ static void acm_waker(struct work_struct *waker) | |||
524 | acm_start_wb(acm, acm->delayed_wb); | 521 | acm_start_wb(acm, acm->delayed_wb); |
525 | acm->delayed_wb = NULL; | 522 | acm->delayed_wb = NULL; |
526 | } | 523 | } |
527 | spin_lock_irqsave(&acm->write_lock, flags); | ||
528 | acm->write_ready = acm->old_ready; | ||
529 | spin_unlock_irqrestore(&acm->write_lock, flags); | ||
530 | usb_autopm_put_interface(acm->control); | 524 | usb_autopm_put_interface(acm->control); |
531 | } | 525 | } |
532 | 526 | ||
@@ -628,6 +622,8 @@ static void acm_tty_unregister(struct acm *acm) | |||
628 | kfree(acm); | 622 | kfree(acm); |
629 | } | 623 | } |
630 | 624 | ||
625 | static int acm_tty_chars_in_buffer(struct tty_struct *tty); | ||
626 | |||
631 | static void acm_tty_close(struct tty_struct *tty, struct file *filp) | 627 | static void acm_tty_close(struct tty_struct *tty, struct file *filp) |
632 | { | 628 | { |
633 | struct acm *acm = tty->driver_data; | 629 | struct acm *acm = tty->driver_data; |
@@ -642,6 +638,13 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp) | |||
642 | if (acm->dev) { | 638 | if (acm->dev) { |
643 | usb_autopm_get_interface(acm->control); | 639 | usb_autopm_get_interface(acm->control); |
644 | acm_set_control(acm, acm->ctrlout = 0); | 640 | acm_set_control(acm, acm->ctrlout = 0); |
641 | |||
642 | /* try letting the last writes drain naturally */ | ||
643 | wait_event_interruptible_timeout(acm->drain_wait, | ||
644 | (ACM_NW == acm_wb_is_avail(acm)) | ||
645 | || !acm->dev, | ||
646 | ACM_CLOSE_TIMEOUT * HZ); | ||
647 | |||
645 | usb_kill_urb(acm->ctrlurb); | 648 | usb_kill_urb(acm->ctrlurb); |
646 | for (i = 0; i < ACM_NW; i++) | 649 | for (i = 0; i < ACM_NW; i++) |
647 | usb_kill_urb(acm->wb[i].urb); | 650 | usb_kill_urb(acm->wb[i].urb); |
@@ -697,7 +700,7 @@ static int acm_tty_write_room(struct tty_struct *tty) | |||
697 | * Do not let the line discipline to know that we have a reserve, | 700 | * Do not let the line discipline to know that we have a reserve, |
698 | * or it might get too enthusiastic. | 701 | * or it might get too enthusiastic. |
699 | */ | 702 | */ |
700 | return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0; | 703 | return acm_wb_is_avail(acm) ? acm->writesize : 0; |
701 | } | 704 | } |
702 | 705 | ||
703 | static int acm_tty_chars_in_buffer(struct tty_struct *tty) | 706 | static int acm_tty_chars_in_buffer(struct tty_struct *tty) |
@@ -1072,11 +1075,11 @@ skip_normal_probe: | |||
1072 | acm->urb_task.data = (unsigned long) acm; | 1075 | acm->urb_task.data = (unsigned long) acm; |
1073 | INIT_WORK(&acm->work, acm_softint); | 1076 | INIT_WORK(&acm->work, acm_softint); |
1074 | INIT_WORK(&acm->waker, acm_waker); | 1077 | INIT_WORK(&acm->waker, acm_waker); |
1078 | init_waitqueue_head(&acm->drain_wait); | ||
1075 | spin_lock_init(&acm->throttle_lock); | 1079 | spin_lock_init(&acm->throttle_lock); |
1076 | spin_lock_init(&acm->write_lock); | 1080 | spin_lock_init(&acm->write_lock); |
1077 | spin_lock_init(&acm->read_lock); | 1081 | spin_lock_init(&acm->read_lock); |
1078 | mutex_init(&acm->mutex); | 1082 | mutex_init(&acm->mutex); |
1079 | acm->write_ready = 1; | ||
1080 | acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); | 1083 | acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); |
1081 | 1084 | ||
1082 | buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); | 1085 | buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); |
@@ -1108,9 +1111,11 @@ skip_normal_probe: | |||
1108 | rcv->instance = acm; | 1111 | rcv->instance = acm; |
1109 | } | 1112 | } |
1110 | for (i = 0; i < num_rx_buf; i++) { | 1113 | for (i = 0; i < num_rx_buf; i++) { |
1111 | struct acm_rb *buf = &(acm->rb[i]); | 1114 | struct acm_rb *rb = &(acm->rb[i]); |
1112 | 1115 | ||
1113 | if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) { | 1116 | rb->base = usb_buffer_alloc(acm->dev, readsize, |
1117 | GFP_KERNEL, &rb->dma); | ||
1118 | if (!rb->base) { | ||
1114 | dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n"); | 1119 | dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n"); |
1115 | goto alloc_fail7; | 1120 | goto alloc_fail7; |
1116 | } | 1121 | } |
@@ -1172,6 +1177,7 @@ skip_countries: | |||
1172 | acm_set_line(acm, &acm->line); | 1177 | acm_set_line(acm, &acm->line); |
1173 | 1178 | ||
1174 | usb_driver_claim_interface(&acm_driver, data_interface, acm); | 1179 | usb_driver_claim_interface(&acm_driver, data_interface, acm); |
1180 | usb_set_intfdata(data_interface, acm); | ||
1175 | 1181 | ||
1176 | usb_get_intf(control_interface); | 1182 | usb_get_intf(control_interface); |
1177 | tty_register_device(acm_tty_driver, minor, &control_interface->dev); | 1183 | tty_register_device(acm_tty_driver, minor, &control_interface->dev); |
@@ -1221,11 +1227,11 @@ static void acm_disconnect(struct usb_interface *intf) | |||
1221 | struct acm *acm = usb_get_intfdata(intf); | 1227 | struct acm *acm = usb_get_intfdata(intf); |
1222 | struct usb_device *usb_dev = interface_to_usbdev(intf); | 1228 | struct usb_device *usb_dev = interface_to_usbdev(intf); |
1223 | 1229 | ||
1224 | mutex_lock(&open_mutex); | 1230 | /* sibling interface is already cleaning up */ |
1225 | if (!acm || !acm->dev) { | 1231 | if (!acm) |
1226 | mutex_unlock(&open_mutex); | ||
1227 | return; | 1232 | return; |
1228 | } | 1233 | |
1234 | mutex_lock(&open_mutex); | ||
1229 | if (acm->country_codes){ | 1235 | if (acm->country_codes){ |
1230 | device_remove_file(&acm->control->dev, | 1236 | device_remove_file(&acm->control->dev, |
1231 | &dev_attr_wCountryCodes); | 1237 | &dev_attr_wCountryCodes); |