diff options
Diffstat (limited to 'drivers/usb/class/cdc-wdm.c')
-rw-r--r-- | drivers/usb/class/cdc-wdm.c | 134 |
1 files changed, 78 insertions, 56 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 18aafcb08fc8..189141ca4e05 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c | |||
@@ -52,7 +52,8 @@ MODULE_DEVICE_TABLE (usb, wdm_ids); | |||
52 | #define WDM_READ 4 | 52 | #define WDM_READ 4 |
53 | #define WDM_INT_STALL 5 | 53 | #define WDM_INT_STALL 5 |
54 | #define WDM_POLL_RUNNING 6 | 54 | #define WDM_POLL_RUNNING 6 |
55 | 55 | #define WDM_RESPONDING 7 | |
56 | #define WDM_SUSPENDING 8 | ||
56 | 57 | ||
57 | #define WDM_MAX 16 | 58 | #define WDM_MAX 16 |
58 | 59 | ||
@@ -87,9 +88,7 @@ struct wdm_device { | |||
87 | int count; | 88 | int count; |
88 | dma_addr_t shandle; | 89 | dma_addr_t shandle; |
89 | dma_addr_t ihandle; | 90 | dma_addr_t ihandle; |
90 | struct mutex wlock; | 91 | struct mutex lock; |
91 | struct mutex rlock; | ||
92 | struct mutex plock; | ||
93 | wait_queue_head_t wait; | 92 | wait_queue_head_t wait; |
94 | struct work_struct rxwork; | 93 | struct work_struct rxwork; |
95 | int werr; | 94 | int werr; |
@@ -117,21 +116,22 @@ static void wdm_in_callback(struct urb *urb) | |||
117 | int status = urb->status; | 116 | int status = urb->status; |
118 | 117 | ||
119 | spin_lock(&desc->iuspin); | 118 | spin_lock(&desc->iuspin); |
119 | clear_bit(WDM_RESPONDING, &desc->flags); | ||
120 | 120 | ||
121 | if (status) { | 121 | if (status) { |
122 | switch (status) { | 122 | switch (status) { |
123 | case -ENOENT: | 123 | case -ENOENT: |
124 | dev_dbg(&desc->intf->dev, | 124 | dev_dbg(&desc->intf->dev, |
125 | "nonzero urb status received: -ENOENT"); | 125 | "nonzero urb status received: -ENOENT"); |
126 | break; | 126 | goto skip_error; |
127 | case -ECONNRESET: | 127 | case -ECONNRESET: |
128 | dev_dbg(&desc->intf->dev, | 128 | dev_dbg(&desc->intf->dev, |
129 | "nonzero urb status received: -ECONNRESET"); | 129 | "nonzero urb status received: -ECONNRESET"); |
130 | break; | 130 | goto skip_error; |
131 | case -ESHUTDOWN: | 131 | case -ESHUTDOWN: |
132 | dev_dbg(&desc->intf->dev, | 132 | dev_dbg(&desc->intf->dev, |
133 | "nonzero urb status received: -ESHUTDOWN"); | 133 | "nonzero urb status received: -ESHUTDOWN"); |
134 | break; | 134 | goto skip_error; |
135 | case -EPIPE: | 135 | case -EPIPE: |
136 | dev_err(&desc->intf->dev, | 136 | dev_err(&desc->intf->dev, |
137 | "nonzero urb status received: -EPIPE\n"); | 137 | "nonzero urb status received: -EPIPE\n"); |
@@ -147,6 +147,7 @@ static void wdm_in_callback(struct urb *urb) | |||
147 | desc->reslength = urb->actual_length; | 147 | desc->reslength = urb->actual_length; |
148 | memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength); | 148 | memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength); |
149 | desc->length += desc->reslength; | 149 | desc->length += desc->reslength; |
150 | skip_error: | ||
150 | wake_up(&desc->wait); | 151 | wake_up(&desc->wait); |
151 | 152 | ||
152 | set_bit(WDM_READ, &desc->flags); | 153 | set_bit(WDM_READ, &desc->flags); |
@@ -229,13 +230,16 @@ static void wdm_int_callback(struct urb *urb) | |||
229 | desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | 230 | desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
230 | spin_lock(&desc->iuspin); | 231 | spin_lock(&desc->iuspin); |
231 | clear_bit(WDM_READ, &desc->flags); | 232 | clear_bit(WDM_READ, &desc->flags); |
232 | if (!test_bit(WDM_DISCONNECTING, &desc->flags)) { | 233 | set_bit(WDM_RESPONDING, &desc->flags); |
234 | if (!test_bit(WDM_DISCONNECTING, &desc->flags) | ||
235 | && !test_bit(WDM_SUSPENDING, &desc->flags)) { | ||
233 | rv = usb_submit_urb(desc->response, GFP_ATOMIC); | 236 | rv = usb_submit_urb(desc->response, GFP_ATOMIC); |
234 | dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", | 237 | dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", |
235 | __func__, rv); | 238 | __func__, rv); |
236 | } | 239 | } |
237 | spin_unlock(&desc->iuspin); | 240 | spin_unlock(&desc->iuspin); |
238 | if (rv < 0) { | 241 | if (rv < 0) { |
242 | clear_bit(WDM_RESPONDING, &desc->flags); | ||
239 | if (rv == -EPERM) | 243 | if (rv == -EPERM) |
240 | return; | 244 | return; |
241 | if (rv == -ENOMEM) { | 245 | if (rv == -ENOMEM) { |
@@ -305,14 +309,38 @@ static ssize_t wdm_write | |||
305 | if (we < 0) | 309 | if (we < 0) |
306 | return -EIO; | 310 | return -EIO; |
307 | 311 | ||
308 | r = mutex_lock_interruptible(&desc->wlock); /* concurrent writes */ | 312 | desc->outbuf = buf = kmalloc(count, GFP_KERNEL); |
313 | if (!buf) { | ||
314 | rv = -ENOMEM; | ||
315 | goto outnl; | ||
316 | } | ||
317 | |||
318 | r = copy_from_user(buf, buffer, count); | ||
319 | if (r > 0) { | ||
320 | kfree(buf); | ||
321 | rv = -EFAULT; | ||
322 | goto outnl; | ||
323 | } | ||
324 | |||
325 | /* concurrent writes and disconnect */ | ||
326 | r = mutex_lock_interruptible(&desc->lock); | ||
309 | rv = -ERESTARTSYS; | 327 | rv = -ERESTARTSYS; |
310 | if (r) | 328 | if (r) { |
329 | kfree(buf); | ||
311 | goto outnl; | 330 | goto outnl; |
331 | } | ||
332 | |||
333 | if (test_bit(WDM_DISCONNECTING, &desc->flags)) { | ||
334 | kfree(buf); | ||
335 | rv = -ENODEV; | ||
336 | goto outnp; | ||
337 | } | ||
312 | 338 | ||
313 | r = usb_autopm_get_interface(desc->intf); | 339 | r = usb_autopm_get_interface(desc->intf); |
314 | if (r < 0) | 340 | if (r < 0) { |
341 | kfree(buf); | ||
315 | goto outnp; | 342 | goto outnp; |
343 | } | ||
316 | 344 | ||
317 | if (!file->f_flags && O_NONBLOCK) | 345 | if (!file->f_flags && O_NONBLOCK) |
318 | r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, | 346 | r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, |
@@ -320,24 +348,8 @@ static ssize_t wdm_write | |||
320 | else | 348 | else |
321 | if (test_bit(WDM_IN_USE, &desc->flags)) | 349 | if (test_bit(WDM_IN_USE, &desc->flags)) |
322 | r = -EAGAIN; | 350 | r = -EAGAIN; |
323 | if (r < 0) | 351 | if (r < 0) { |
324 | goto out; | ||
325 | |||
326 | if (test_bit(WDM_DISCONNECTING, &desc->flags)) { | ||
327 | rv = -ENODEV; | ||
328 | goto out; | ||
329 | } | ||
330 | |||
331 | desc->outbuf = buf = kmalloc(count, GFP_KERNEL); | ||
332 | if (!buf) { | ||
333 | rv = -ENOMEM; | ||
334 | goto out; | ||
335 | } | ||
336 | |||
337 | r = copy_from_user(buf, buffer, count); | ||
338 | if (r > 0) { | ||
339 | kfree(buf); | 352 | kfree(buf); |
340 | rv = -EFAULT; | ||
341 | goto out; | 353 | goto out; |
342 | } | 354 | } |
343 | 355 | ||
@@ -374,7 +386,7 @@ static ssize_t wdm_write | |||
374 | out: | 386 | out: |
375 | usb_autopm_put_interface(desc->intf); | 387 | usb_autopm_put_interface(desc->intf); |
376 | outnp: | 388 | outnp: |
377 | mutex_unlock(&desc->wlock); | 389 | mutex_unlock(&desc->lock); |
378 | outnl: | 390 | outnl: |
379 | return rv < 0 ? rv : count; | 391 | return rv < 0 ? rv : count; |
380 | } | 392 | } |
@@ -387,7 +399,7 @@ static ssize_t wdm_read | |||
387 | struct wdm_device *desc = file->private_data; | 399 | struct wdm_device *desc = file->private_data; |
388 | 400 | ||
389 | 401 | ||
390 | rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */ | 402 | rv = mutex_lock_interruptible(&desc->lock); /*concurrent reads */ |
391 | if (rv < 0) | 403 | if (rv < 0) |
392 | return -ERESTARTSYS; | 404 | return -ERESTARTSYS; |
393 | 405 | ||
@@ -424,11 +436,8 @@ retry: | |||
424 | spin_lock_irq(&desc->iuspin); | 436 | spin_lock_irq(&desc->iuspin); |
425 | 437 | ||
426 | if (desc->rerr) { /* read completed, error happened */ | 438 | if (desc->rerr) { /* read completed, error happened */ |
427 | int t = desc->rerr; | ||
428 | desc->rerr = 0; | 439 | desc->rerr = 0; |
429 | spin_unlock_irq(&desc->iuspin); | 440 | spin_unlock_irq(&desc->iuspin); |
430 | dev_err(&desc->intf->dev, | ||
431 | "reading had resulted in %d\n", t); | ||
432 | rv = -EIO; | 441 | rv = -EIO; |
433 | goto err; | 442 | goto err; |
434 | } | 443 | } |
@@ -465,9 +474,7 @@ retry: | |||
465 | rv = cntr; | 474 | rv = cntr; |
466 | 475 | ||
467 | err: | 476 | err: |
468 | mutex_unlock(&desc->rlock); | 477 | mutex_unlock(&desc->lock); |
469 | if (rv < 0 && rv != -EAGAIN) | ||
470 | dev_err(&desc->intf->dev, "wdm_read: exit error\n"); | ||
471 | return rv; | 478 | return rv; |
472 | } | 479 | } |
473 | 480 | ||
@@ -533,7 +540,7 @@ static int wdm_open(struct inode *inode, struct file *file) | |||
533 | } | 540 | } |
534 | intf->needs_remote_wakeup = 1; | 541 | intf->needs_remote_wakeup = 1; |
535 | 542 | ||
536 | mutex_lock(&desc->plock); | 543 | mutex_lock(&desc->lock); |
537 | if (!desc->count++) { | 544 | if (!desc->count++) { |
538 | rv = usb_submit_urb(desc->validity, GFP_KERNEL); | 545 | rv = usb_submit_urb(desc->validity, GFP_KERNEL); |
539 | if (rv < 0) { | 546 | if (rv < 0) { |
@@ -544,7 +551,7 @@ static int wdm_open(struct inode *inode, struct file *file) | |||
544 | } else { | 551 | } else { |
545 | rv = 0; | 552 | rv = 0; |
546 | } | 553 | } |
547 | mutex_unlock(&desc->plock); | 554 | mutex_unlock(&desc->lock); |
548 | usb_autopm_put_interface(desc->intf); | 555 | usb_autopm_put_interface(desc->intf); |
549 | out: | 556 | out: |
550 | mutex_unlock(&wdm_mutex); | 557 | mutex_unlock(&wdm_mutex); |
@@ -556,9 +563,9 @@ static int wdm_release(struct inode *inode, struct file *file) | |||
556 | struct wdm_device *desc = file->private_data; | 563 | struct wdm_device *desc = file->private_data; |
557 | 564 | ||
558 | mutex_lock(&wdm_mutex); | 565 | mutex_lock(&wdm_mutex); |
559 | mutex_lock(&desc->plock); | 566 | mutex_lock(&desc->lock); |
560 | desc->count--; | 567 | desc->count--; |
561 | mutex_unlock(&desc->plock); | 568 | mutex_unlock(&desc->lock); |
562 | 569 | ||
563 | if (!desc->count) { | 570 | if (!desc->count) { |
564 | dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); | 571 | dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); |
@@ -655,9 +662,7 @@ next_desc: | |||
655 | desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL); | 662 | desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL); |
656 | if (!desc) | 663 | if (!desc) |
657 | goto out; | 664 | goto out; |
658 | mutex_init(&desc->wlock); | 665 | mutex_init(&desc->lock); |
659 | mutex_init(&desc->rlock); | ||
660 | mutex_init(&desc->plock); | ||
661 | spin_lock_init(&desc->iuspin); | 666 | spin_lock_init(&desc->iuspin); |
662 | init_waitqueue_head(&desc->wait); | 667 | init_waitqueue_head(&desc->wait); |
663 | desc->wMaxCommand = maxcom; | 668 | desc->wMaxCommand = maxcom; |
@@ -771,14 +776,17 @@ static void wdm_disconnect(struct usb_interface *intf) | |||
771 | /* to terminate pending flushes */ | 776 | /* to terminate pending flushes */ |
772 | clear_bit(WDM_IN_USE, &desc->flags); | 777 | clear_bit(WDM_IN_USE, &desc->flags); |
773 | spin_unlock_irqrestore(&desc->iuspin, flags); | 778 | spin_unlock_irqrestore(&desc->iuspin, flags); |
774 | cancel_work_sync(&desc->rxwork); | 779 | mutex_lock(&desc->lock); |
775 | kill_urbs(desc); | 780 | kill_urbs(desc); |
781 | cancel_work_sync(&desc->rxwork); | ||
782 | mutex_unlock(&desc->lock); | ||
776 | wake_up_all(&desc->wait); | 783 | wake_up_all(&desc->wait); |
777 | if (!desc->count) | 784 | if (!desc->count) |
778 | cleanup(desc); | 785 | cleanup(desc); |
779 | mutex_unlock(&wdm_mutex); | 786 | mutex_unlock(&wdm_mutex); |
780 | } | 787 | } |
781 | 788 | ||
789 | #ifdef CONFIG_PM | ||
782 | static int wdm_suspend(struct usb_interface *intf, pm_message_t message) | 790 | static int wdm_suspend(struct usb_interface *intf, pm_message_t message) |
783 | { | 791 | { |
784 | struct wdm_device *desc = usb_get_intfdata(intf); | 792 | struct wdm_device *desc = usb_get_intfdata(intf); |
@@ -786,22 +794,30 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message) | |||
786 | 794 | ||
787 | dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); | 795 | dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); |
788 | 796 | ||
789 | mutex_lock(&desc->plock); | 797 | /* if this is an autosuspend the caller does the locking */ |
790 | #ifdef CONFIG_PM | 798 | if (!(message.event & PM_EVENT_AUTO)) |
799 | mutex_lock(&desc->lock); | ||
800 | spin_lock_irq(&desc->iuspin); | ||
801 | |||
791 | if ((message.event & PM_EVENT_AUTO) && | 802 | if ((message.event & PM_EVENT_AUTO) && |
792 | test_bit(WDM_IN_USE, &desc->flags)) { | 803 | (test_bit(WDM_IN_USE, &desc->flags) |
804 | || test_bit(WDM_RESPONDING, &desc->flags))) { | ||
805 | spin_unlock_irq(&desc->iuspin); | ||
793 | rv = -EBUSY; | 806 | rv = -EBUSY; |
794 | } else { | 807 | } else { |
795 | #endif | 808 | |
796 | cancel_work_sync(&desc->rxwork); | 809 | set_bit(WDM_SUSPENDING, &desc->flags); |
810 | spin_unlock_irq(&desc->iuspin); | ||
811 | /* callback submits work - order is essential */ | ||
797 | kill_urbs(desc); | 812 | kill_urbs(desc); |
798 | #ifdef CONFIG_PM | 813 | cancel_work_sync(&desc->rxwork); |
799 | } | 814 | } |
800 | #endif | 815 | if (!(message.event & PM_EVENT_AUTO)) |
801 | mutex_unlock(&desc->plock); | 816 | mutex_unlock(&desc->lock); |
802 | 817 | ||
803 | return rv; | 818 | return rv; |
804 | } | 819 | } |
820 | #endif | ||
805 | 821 | ||
806 | static int recover_from_urb_loss(struct wdm_device *desc) | 822 | static int recover_from_urb_loss(struct wdm_device *desc) |
807 | { | 823 | { |
@@ -815,23 +831,27 @@ static int recover_from_urb_loss(struct wdm_device *desc) | |||
815 | } | 831 | } |
816 | return rv; | 832 | return rv; |
817 | } | 833 | } |
834 | |||
835 | #ifdef CONFIG_PM | ||
818 | static int wdm_resume(struct usb_interface *intf) | 836 | static int wdm_resume(struct usb_interface *intf) |
819 | { | 837 | { |
820 | struct wdm_device *desc = usb_get_intfdata(intf); | 838 | struct wdm_device *desc = usb_get_intfdata(intf); |
821 | int rv; | 839 | int rv; |
822 | 840 | ||
823 | dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); | 841 | dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); |
824 | mutex_lock(&desc->plock); | 842 | |
843 | clear_bit(WDM_SUSPENDING, &desc->flags); | ||
825 | rv = recover_from_urb_loss(desc); | 844 | rv = recover_from_urb_loss(desc); |
826 | mutex_unlock(&desc->plock); | 845 | |
827 | return rv; | 846 | return rv; |
828 | } | 847 | } |
848 | #endif | ||
829 | 849 | ||
830 | static int wdm_pre_reset(struct usb_interface *intf) | 850 | static int wdm_pre_reset(struct usb_interface *intf) |
831 | { | 851 | { |
832 | struct wdm_device *desc = usb_get_intfdata(intf); | 852 | struct wdm_device *desc = usb_get_intfdata(intf); |
833 | 853 | ||
834 | mutex_lock(&desc->plock); | 854 | mutex_lock(&desc->lock); |
835 | return 0; | 855 | return 0; |
836 | } | 856 | } |
837 | 857 | ||
@@ -841,7 +861,7 @@ static int wdm_post_reset(struct usb_interface *intf) | |||
841 | int rv; | 861 | int rv; |
842 | 862 | ||
843 | rv = recover_from_urb_loss(desc); | 863 | rv = recover_from_urb_loss(desc); |
844 | mutex_unlock(&desc->plock); | 864 | mutex_unlock(&desc->lock); |
845 | return 0; | 865 | return 0; |
846 | } | 866 | } |
847 | 867 | ||
@@ -849,9 +869,11 @@ static struct usb_driver wdm_driver = { | |||
849 | .name = "cdc_wdm", | 869 | .name = "cdc_wdm", |
850 | .probe = wdm_probe, | 870 | .probe = wdm_probe, |
851 | .disconnect = wdm_disconnect, | 871 | .disconnect = wdm_disconnect, |
872 | #ifdef CONFIG_PM | ||
852 | .suspend = wdm_suspend, | 873 | .suspend = wdm_suspend, |
853 | .resume = wdm_resume, | 874 | .resume = wdm_resume, |
854 | .reset_resume = wdm_resume, | 875 | .reset_resume = wdm_resume, |
876 | #endif | ||
855 | .pre_reset = wdm_pre_reset, | 877 | .pre_reset = wdm_pre_reset, |
856 | .post_reset = wdm_post_reset, | 878 | .post_reset = wdm_post_reset, |
857 | .id_table = wdm_ids, | 879 | .id_table = wdm_ids, |