diff options
Diffstat (limited to 'drivers/isdn')
-rw-r--r-- | drivers/isdn/gigaset/bas-gigaset.c | 106 |
1 files changed, 93 insertions, 13 deletions
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c index 540f6d0bb754..71e3fdee4410 100644 --- a/drivers/isdn/gigaset/bas-gigaset.c +++ b/drivers/isdn/gigaset/bas-gigaset.c | |||
@@ -109,6 +109,9 @@ struct bas_cardstate { | |||
109 | 109 | ||
110 | struct urb *urb_int_in; /* URB for interrupt pipe */ | 110 | struct urb *urb_int_in; /* URB for interrupt pipe */ |
111 | unsigned char *int_in_buf; | 111 | unsigned char *int_in_buf; |
112 | struct work_struct int_in_wq; /* for usb_clear_halt() */ | ||
113 | struct timer_list timer_int_in; /* int read retry delay */ | ||
114 | int retry_int_in; | ||
112 | 115 | ||
113 | spinlock_t lock; /* locks all following */ | 116 | spinlock_t lock; /* locks all following */ |
114 | int basstate; /* bitmap (BS_*) */ | 117 | int basstate; /* bitmap (BS_*) */ |
@@ -595,6 +598,62 @@ static int atread_submit(struct cardstate *cs, int timeout) | |||
595 | return 0; | 598 | return 0; |
596 | } | 599 | } |
597 | 600 | ||
601 | /* int_in_work | ||
602 | * workqueue routine to clear halt on interrupt in endpoint | ||
603 | */ | ||
604 | |||
605 | static void int_in_work(struct work_struct *work) | ||
606 | { | ||
607 | struct bas_cardstate *ucs = | ||
608 | container_of(work, struct bas_cardstate, int_in_wq); | ||
609 | struct urb *urb = ucs->urb_int_in; | ||
610 | struct cardstate *cs = urb->context; | ||
611 | int rc; | ||
612 | |||
613 | /* clear halt condition */ | ||
614 | rc = usb_clear_halt(ucs->udev, urb->pipe); | ||
615 | gig_dbg(DEBUG_USBREQ, "clear_halt: %s", get_usb_rcmsg(rc)); | ||
616 | if (rc == 0) | ||
617 | /* success, resubmit interrupt read URB */ | ||
618 | rc = usb_submit_urb(urb, GFP_ATOMIC); | ||
619 | if (rc != 0 && rc != -ENODEV) { | ||
620 | dev_err(cs->dev, "clear halt failed: %s\n", get_usb_rcmsg(rc)); | ||
621 | rc = usb_lock_device_for_reset(ucs->udev, ucs->interface); | ||
622 | if (rc == 0) { | ||
623 | rc = usb_reset_device(ucs->udev); | ||
624 | usb_unlock_device(ucs->udev); | ||
625 | } | ||
626 | } | ||
627 | ucs->retry_int_in = 0; | ||
628 | } | ||
629 | |||
630 | /* int_in_resubmit | ||
631 | * timer routine for interrupt read delayed resubmit | ||
632 | * argument: | ||
633 | * controller state structure | ||
634 | */ | ||
635 | static void int_in_resubmit(unsigned long data) | ||
636 | { | ||
637 | struct cardstate *cs = (struct cardstate *) data; | ||
638 | struct bas_cardstate *ucs = cs->hw.bas; | ||
639 | int rc; | ||
640 | |||
641 | if (ucs->retry_int_in++ >= BAS_RETRY) { | ||
642 | dev_err(cs->dev, "interrupt read: giving up after %d tries\n", | ||
643 | ucs->retry_int_in); | ||
644 | usb_queue_reset_device(ucs->interface); | ||
645 | return; | ||
646 | } | ||
647 | |||
648 | gig_dbg(DEBUG_USBREQ, "%s: retry %d", __func__, ucs->retry_int_in); | ||
649 | rc = usb_submit_urb(ucs->urb_int_in, GFP_ATOMIC); | ||
650 | if (rc != 0 && rc != -ENODEV) { | ||
651 | dev_err(cs->dev, "could not resubmit interrupt URB: %s\n", | ||
652 | get_usb_rcmsg(rc)); | ||
653 | usb_queue_reset_device(ucs->interface); | ||
654 | } | ||
655 | } | ||
656 | |||
598 | /* read_int_callback | 657 | /* read_int_callback |
599 | * USB completion handler for interrupt pipe input | 658 | * USB completion handler for interrupt pipe input |
600 | * called by the USB subsystem in interrupt context | 659 | * called by the USB subsystem in interrupt context |
@@ -615,19 +674,29 @@ static void read_int_callback(struct urb *urb) | |||
615 | 674 | ||
616 | switch (status) { | 675 | switch (status) { |
617 | case 0: /* success */ | 676 | case 0: /* success */ |
677 | ucs->retry_int_in = 0; | ||
618 | break; | 678 | break; |
679 | case -EPIPE: /* endpoint stalled */ | ||
680 | schedule_work(&ucs->int_in_wq); | ||
681 | /* fall through */ | ||
619 | case -ENOENT: /* cancelled */ | 682 | case -ENOENT: /* cancelled */ |
620 | case -ECONNRESET: /* cancelled (async) */ | 683 | case -ECONNRESET: /* cancelled (async) */ |
621 | case -EINPROGRESS: /* pending */ | 684 | case -EINPROGRESS: /* pending */ |
622 | /* ignore silently */ | 685 | case -ENODEV: /* device removed */ |
686 | case -ESHUTDOWN: /* device shut down */ | ||
687 | /* no further action necessary */ | ||
623 | gig_dbg(DEBUG_USBREQ, "%s: %s", | 688 | gig_dbg(DEBUG_USBREQ, "%s: %s", |
624 | __func__, get_usb_statmsg(status)); | 689 | __func__, get_usb_statmsg(status)); |
625 | return; | 690 | return; |
626 | case -ENODEV: /* device removed */ | 691 | case -EPROTO: /* protocol error or unplug */ |
627 | case -ESHUTDOWN: /* device shut down */ | 692 | case -EILSEQ: |
628 | gig_dbg(DEBUG_USBREQ, "%s: device disconnected", __func__); | 693 | case -ETIME: |
694 | /* resubmit after delay */ | ||
695 | gig_dbg(DEBUG_USBREQ, "%s: %s", | ||
696 | __func__, get_usb_statmsg(status)); | ||
697 | mod_timer(&ucs->timer_int_in, jiffies + HZ / 10); | ||
629 | return; | 698 | return; |
630 | default: /* severe trouble */ | 699 | default: /* other errors: just resubmit */ |
631 | dev_warn(cs->dev, "interrupt read: %s\n", | 700 | dev_warn(cs->dev, "interrupt read: %s\n", |
632 | get_usb_statmsg(status)); | 701 | get_usb_statmsg(status)); |
633 | goto resubmit; | 702 | goto resubmit; |
@@ -705,6 +774,13 @@ static void read_int_callback(struct urb *urb) | |||
705 | break; | 774 | break; |
706 | } | 775 | } |
707 | spin_lock_irqsave(&cs->lock, flags); | 776 | spin_lock_irqsave(&cs->lock, flags); |
777 | if (ucs->basstate & BS_ATRDPEND) { | ||
778 | spin_unlock_irqrestore(&cs->lock, flags); | ||
779 | dev_warn(cs->dev, | ||
780 | "HD_RECEIVEATDATA_ACK(%d) during HD_READ_ATMESSAGE(%d) ignored\n", | ||
781 | l, ucs->rcvbuf_size); | ||
782 | break; | ||
783 | } | ||
708 | if (ucs->rcvbuf_size) { | 784 | if (ucs->rcvbuf_size) { |
709 | /* throw away previous buffer - we have no queue */ | 785 | /* throw away previous buffer - we have no queue */ |
710 | dev_err(cs->dev, | 786 | dev_err(cs->dev, |
@@ -717,7 +793,6 @@ static void read_int_callback(struct urb *urb) | |||
717 | if (ucs->rcvbuf == NULL) { | 793 | if (ucs->rcvbuf == NULL) { |
718 | spin_unlock_irqrestore(&cs->lock, flags); | 794 | spin_unlock_irqrestore(&cs->lock, flags); |
719 | dev_err(cs->dev, "out of memory receiving AT data\n"); | 795 | dev_err(cs->dev, "out of memory receiving AT data\n"); |
720 | error_reset(cs); | ||
721 | break; | 796 | break; |
722 | } | 797 | } |
723 | ucs->rcvbuf_size = l; | 798 | ucs->rcvbuf_size = l; |
@@ -727,13 +802,10 @@ static void read_int_callback(struct urb *urb) | |||
727 | kfree(ucs->rcvbuf); | 802 | kfree(ucs->rcvbuf); |
728 | ucs->rcvbuf = NULL; | 803 | ucs->rcvbuf = NULL; |
729 | ucs->rcvbuf_size = 0; | 804 | ucs->rcvbuf_size = 0; |
730 | if (rc != -ENODEV) { | ||
731 | spin_unlock_irqrestore(&cs->lock, flags); | ||
732 | error_reset(cs); | ||
733 | break; | ||
734 | } | ||
735 | } | 805 | } |
736 | spin_unlock_irqrestore(&cs->lock, flags); | 806 | spin_unlock_irqrestore(&cs->lock, flags); |
807 | if (rc < 0 && rc != -ENODEV) | ||
808 | error_reset(cs); | ||
737 | break; | 809 | break; |
738 | 810 | ||
739 | case HD_RESET_INTERRUPT_PIPE_ACK: | 811 | case HD_RESET_INTERRUPT_PIPE_ACK: |
@@ -2138,7 +2210,9 @@ static int gigaset_initcshw(struct cardstate *cs) | |||
2138 | setup_timer(&ucs->timer_ctrl, req_timeout, (unsigned long) cs); | 2210 | setup_timer(&ucs->timer_ctrl, req_timeout, (unsigned long) cs); |
2139 | setup_timer(&ucs->timer_atrdy, atrdy_timeout, (unsigned long) cs); | 2211 | setup_timer(&ucs->timer_atrdy, atrdy_timeout, (unsigned long) cs); |
2140 | setup_timer(&ucs->timer_cmd_in, cmd_in_timeout, (unsigned long) cs); | 2212 | setup_timer(&ucs->timer_cmd_in, cmd_in_timeout, (unsigned long) cs); |
2213 | setup_timer(&ucs->timer_int_in, int_in_resubmit, (unsigned long) cs); | ||
2141 | init_waitqueue_head(&ucs->waitqueue); | 2214 | init_waitqueue_head(&ucs->waitqueue); |
2215 | INIT_WORK(&ucs->int_in_wq, int_in_work); | ||
2142 | 2216 | ||
2143 | return 1; | 2217 | return 1; |
2144 | } | 2218 | } |
@@ -2286,6 +2360,7 @@ static int gigaset_probe(struct usb_interface *interface, | |||
2286 | get_usb_rcmsg(rc)); | 2360 | get_usb_rcmsg(rc)); |
2287 | goto error; | 2361 | goto error; |
2288 | } | 2362 | } |
2363 | ucs->retry_int_in = 0; | ||
2289 | 2364 | ||
2290 | /* tell the device that the driver is ready */ | 2365 | /* tell the device that the driver is ready */ |
2291 | rc = req_submit(cs->bcs, HD_DEVICE_INIT_ACK, 0, 0); | 2366 | rc = req_submit(cs->bcs, HD_DEVICE_INIT_ACK, 0, 0); |
@@ -2338,10 +2413,12 @@ static void gigaset_disconnect(struct usb_interface *interface) | |||
2338 | /* stop driver (common part) */ | 2413 | /* stop driver (common part) */ |
2339 | gigaset_stop(cs); | 2414 | gigaset_stop(cs); |
2340 | 2415 | ||
2341 | /* stop timers and URBs, free ressources */ | 2416 | /* stop delayed work and URBs, free ressources */ |
2342 | del_timer_sync(&ucs->timer_ctrl); | 2417 | del_timer_sync(&ucs->timer_ctrl); |
2343 | del_timer_sync(&ucs->timer_atrdy); | 2418 | del_timer_sync(&ucs->timer_atrdy); |
2344 | del_timer_sync(&ucs->timer_cmd_in); | 2419 | del_timer_sync(&ucs->timer_cmd_in); |
2420 | del_timer_sync(&ucs->timer_int_in); | ||
2421 | cancel_work_sync(&ucs->int_in_wq); | ||
2345 | freeurbs(cs); | 2422 | freeurbs(cs); |
2346 | usb_set_intfdata(interface, NULL); | 2423 | usb_set_intfdata(interface, NULL); |
2347 | kfree(ucs->rcvbuf); | 2424 | kfree(ucs->rcvbuf); |
@@ -2404,12 +2481,14 @@ static int gigaset_suspend(struct usb_interface *intf, pm_message_t message) | |||
2404 | /* in case of timeout, proceed anyway */ | 2481 | /* in case of timeout, proceed anyway */ |
2405 | } | 2482 | } |
2406 | 2483 | ||
2407 | /* kill all URBs and timers that might still be pending */ | 2484 | /* kill all URBs and delayed work that might still be pending */ |
2408 | usb_kill_urb(ucs->urb_ctrl); | 2485 | usb_kill_urb(ucs->urb_ctrl); |
2409 | usb_kill_urb(ucs->urb_int_in); | 2486 | usb_kill_urb(ucs->urb_int_in); |
2410 | del_timer_sync(&ucs->timer_ctrl); | 2487 | del_timer_sync(&ucs->timer_ctrl); |
2411 | del_timer_sync(&ucs->timer_atrdy); | 2488 | del_timer_sync(&ucs->timer_atrdy); |
2412 | del_timer_sync(&ucs->timer_cmd_in); | 2489 | del_timer_sync(&ucs->timer_cmd_in); |
2490 | del_timer_sync(&ucs->timer_int_in); | ||
2491 | cancel_work_sync(&ucs->int_in_wq); | ||
2413 | 2492 | ||
2414 | gig_dbg(DEBUG_SUSPEND, "suspend complete"); | 2493 | gig_dbg(DEBUG_SUSPEND, "suspend complete"); |
2415 | return 0; | 2494 | return 0; |
@@ -2431,6 +2510,7 @@ static int gigaset_resume(struct usb_interface *intf) | |||
2431 | get_usb_rcmsg(rc)); | 2510 | get_usb_rcmsg(rc)); |
2432 | return rc; | 2511 | return rc; |
2433 | } | 2512 | } |
2513 | ucs->retry_int_in = 0; | ||
2434 | 2514 | ||
2435 | /* clear suspend flag to reallow activity */ | 2515 | /* clear suspend flag to reallow activity */ |
2436 | update_basstate(ucs, 0, BS_SUSPEND); | 2516 | update_basstate(ucs, 0, BS_SUSPEND); |