diff options
Diffstat (limited to 'drivers/usb/class/cdc-wdm.c')
| -rw-r--r-- | drivers/usb/class/cdc-wdm.c | 136 |
1 files changed, 79 insertions, 57 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 3e564bfe17d1..189141ca4e05 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c | |||
| @@ -31,7 +31,7 @@ | |||
| 31 | #define DRIVER_AUTHOR "Oliver Neukum" | 31 | #define DRIVER_AUTHOR "Oliver Neukum" |
| 32 | #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management" | 32 | #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management" |
| 33 | 33 | ||
| 34 | static struct usb_device_id wdm_ids[] = { | 34 | static const struct usb_device_id wdm_ids[] = { |
| 35 | { | 35 | { |
| 36 | .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | | 36 | .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | |
| 37 | USB_DEVICE_ID_MATCH_INT_SUBCLASS, | 37 | USB_DEVICE_ID_MATCH_INT_SUBCLASS, |
| @@ -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, |
