aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/class/cdc-wdm.c107
1 files changed, 101 insertions, 6 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 3a656f8f9935..1103ce7016b1 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -88,6 +88,7 @@ struct wdm_device {
88 dma_addr_t ihandle; 88 dma_addr_t ihandle;
89 struct mutex wlock; 89 struct mutex wlock;
90 struct mutex rlock; 90 struct mutex rlock;
91 struct mutex plock;
91 wait_queue_head_t wait; 92 wait_queue_head_t wait;
92 struct work_struct rxwork; 93 struct work_struct rxwork;
93 int werr; 94 int werr;
@@ -248,6 +249,7 @@ exit:
248 249
249static void kill_urbs(struct wdm_device *desc) 250static void kill_urbs(struct wdm_device *desc)
250{ 251{
252 /* the order here is essential */
251 usb_kill_urb(desc->command); 253 usb_kill_urb(desc->command);
252 usb_kill_urb(desc->validity); 254 usb_kill_urb(desc->validity);
253 usb_kill_urb(desc->response); 255 usb_kill_urb(desc->response);
@@ -300,6 +302,9 @@ static ssize_t wdm_write
300 if (r) 302 if (r)
301 goto outnl; 303 goto outnl;
302 304
305 r = usb_autopm_get_interface(desc->intf);
306 if (r < 0)
307 goto outnp;
303 r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, 308 r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
304 &desc->flags)); 309 &desc->flags));
305 if (r < 0) 310 if (r < 0)
@@ -354,6 +359,8 @@ static ssize_t wdm_write
354 req->wIndex); 359 req->wIndex);
355 } 360 }
356out: 361out:
362 usb_autopm_put_interface(desc->intf);
363outnp:
357 mutex_unlock(&desc->wlock); 364 mutex_unlock(&desc->wlock);
358outnl: 365outnl:
359 return rv < 0 ? rv : count; 366 return rv < 0 ? rv : count;
@@ -378,6 +385,11 @@ retry:
378 rv = wait_event_interruptible(desc->wait, 385 rv = wait_event_interruptible(desc->wait,
379 test_bit(WDM_READ, &desc->flags)); 386 test_bit(WDM_READ, &desc->flags));
380 387
388 if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
389 rv = -ENODEV;
390 goto err;
391 }
392 usb_mark_last_busy(interface_to_usbdev(desc->intf));
381 if (rv < 0) { 393 if (rv < 0) {
382 rv = -ERESTARTSYS; 394 rv = -ERESTARTSYS;
383 goto err; 395 goto err;
@@ -485,18 +497,28 @@ static int wdm_open(struct inode *inode, struct file *file)
485 if (test_bit(WDM_DISCONNECTING, &desc->flags)) 497 if (test_bit(WDM_DISCONNECTING, &desc->flags))
486 goto out; 498 goto out;
487 499
488 desc->count++; 500 ;
489 file->private_data = desc; 501 file->private_data = desc;
490 502
491 rv = usb_submit_urb(desc->validity, GFP_KERNEL); 503 rv = usb_autopm_get_interface(desc->intf);
492
493 if (rv < 0) { 504 if (rv < 0) {
494 desc->count--; 505 err("Error autopm - %d", rv);
495 err("Error submitting int urb - %d", rv);
496 goto out; 506 goto out;
497 } 507 }
498 rv = 0; 508 intf->needs_remote_wakeup = 1;
499 509
510 mutex_lock(&desc->plock);
511 if (!desc->count++) {
512 rv = usb_submit_urb(desc->validity, GFP_KERNEL);
513 if (rv < 0) {
514 desc->count--;
515 err("Error submitting int urb - %d", rv);
516 }
517 } else {
518 rv = 0;
519 }
520 mutex_unlock(&desc->plock);
521 usb_autopm_put_interface(desc->intf);
500out: 522out:
501 mutex_unlock(&wdm_mutex); 523 mutex_unlock(&wdm_mutex);
502 return rv; 524 return rv;
@@ -507,10 +529,15 @@ static int wdm_release(struct inode *inode, struct file *file)
507 struct wdm_device *desc = file->private_data; 529 struct wdm_device *desc = file->private_data;
508 530
509 mutex_lock(&wdm_mutex); 531 mutex_lock(&wdm_mutex);
532 mutex_lock(&desc->plock);
510 desc->count--; 533 desc->count--;
534 mutex_unlock(&desc->plock);
535
511 if (!desc->count) { 536 if (!desc->count) {
512 dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); 537 dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
513 kill_urbs(desc); 538 kill_urbs(desc);
539 if (!test_bit(WDM_DISCONNECTING, &desc->flags))
540 desc->intf->needs_remote_wakeup = 0;
514 } 541 }
515 mutex_unlock(&wdm_mutex); 542 mutex_unlock(&wdm_mutex);
516 return 0; 543 return 0;
@@ -602,6 +629,7 @@ next_desc:
602 goto out; 629 goto out;
603 mutex_init(&desc->wlock); 630 mutex_init(&desc->wlock);
604 mutex_init(&desc->rlock); 631 mutex_init(&desc->rlock);
632 mutex_init(&desc->plock);
605 spin_lock_init(&desc->iuspin); 633 spin_lock_init(&desc->iuspin);
606 init_waitqueue_head(&desc->wait); 634 init_waitqueue_head(&desc->wait);
607 desc->wMaxCommand = maxcom; 635 desc->wMaxCommand = maxcom;
@@ -703,6 +731,7 @@ static void wdm_disconnect(struct usb_interface *intf)
703 spin_lock_irqsave(&desc->iuspin, flags); 731 spin_lock_irqsave(&desc->iuspin, flags);
704 set_bit(WDM_DISCONNECTING, &desc->flags); 732 set_bit(WDM_DISCONNECTING, &desc->flags);
705 set_bit(WDM_READ, &desc->flags); 733 set_bit(WDM_READ, &desc->flags);
734 /* to terminate pending flushes */
706 clear_bit(WDM_IN_USE, &desc->flags); 735 clear_bit(WDM_IN_USE, &desc->flags);
707 spin_unlock_irqrestore(&desc->iuspin, flags); 736 spin_unlock_irqrestore(&desc->iuspin, flags);
708 cancel_work_sync(&desc->rxwork); 737 cancel_work_sync(&desc->rxwork);
@@ -713,11 +742,77 @@ static void wdm_disconnect(struct usb_interface *intf)
713 mutex_unlock(&wdm_mutex); 742 mutex_unlock(&wdm_mutex);
714} 743}
715 744
745static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
746{
747 struct wdm_device *desc = usb_get_intfdata(intf);
748 int rv = 0;
749
750 dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);
751
752 mutex_lock(&desc->plock);
753 if (interface_to_usbdev(desc->intf)->auto_pm && test_bit(WDM_IN_USE, &desc->flags)) {
754 rv = -EBUSY;
755 } else {
756 cancel_work_sync(&desc->rxwork);
757 kill_urbs(desc);
758 }
759 mutex_unlock(&desc->plock);
760
761 return rv;
762}
763
764static int recover_from_urb_loss(struct wdm_device *desc)
765{
766 int rv = 0;
767
768 if (desc->count) {
769 rv = usb_submit_urb(desc->validity, GFP_NOIO);
770 if (rv < 0)
771 err("Error resume submitting int urb - %d", rv);
772 }
773 return rv;
774}
775static int wdm_resume(struct usb_interface *intf)
776{
777 struct wdm_device *desc = usb_get_intfdata(intf);
778 int rv;
779
780 dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor);
781 mutex_lock(&desc->plock);
782 rv = recover_from_urb_loss(desc);
783 mutex_unlock(&desc->plock);
784 return rv;
785}
786
787static int wdm_pre_reset(struct usb_interface *intf)
788{
789 struct wdm_device *desc = usb_get_intfdata(intf);
790
791 mutex_lock(&desc->plock);
792 return 0;
793}
794
795static int wdm_post_reset(struct usb_interface *intf)
796{
797 struct wdm_device *desc = usb_get_intfdata(intf);
798 int rv;
799
800 rv = recover_from_urb_loss(desc);
801 mutex_unlock(&desc->plock);
802 return 0;
803}
804
716static struct usb_driver wdm_driver = { 805static struct usb_driver wdm_driver = {
717 .name = "cdc_wdm", 806 .name = "cdc_wdm",
718 .probe = wdm_probe, 807 .probe = wdm_probe,
719 .disconnect = wdm_disconnect, 808 .disconnect = wdm_disconnect,
809 .suspend = wdm_suspend,
810 .resume = wdm_resume,
811 .reset_resume = wdm_resume,
812 .pre_reset = wdm_pre_reset,
813 .post_reset = wdm_post_reset,
720 .id_table = wdm_ids, 814 .id_table = wdm_ids,
815 .supports_autosuspend = 1,
721}; 816};
722 817
723/* --- low level module stuff --- */ 818/* --- low level module stuff --- */