aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/isdn
diff options
context:
space:
mode:
authorTilman Schmidt <tilman@imap.cc>2010-09-30 09:35:42 -0400
committerDavid S. Miller <davem@davemloft.net>2010-10-01 03:33:36 -0400
commitc9c0c3043452d30ab87135ba3753ce7855a3a8e7 (patch)
tree921a8165440f3efc5f0a4ae8521a8c470485b109 /drivers/isdn
parent4cb5e42f6132bf2d2851f8dd67bd8499979c7ebc (diff)
isdn/gigaset: fix bas_gigaset interrupt read error handling
Rework the handling of USB errors in interrupt input reads to clear halts correctly, delay URB resubmission after errors, limit retries, and improve error recovery. Signed-off-by: Tilman Schmidt <tilman@imap.cc> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/isdn')
-rw-r--r--drivers/isdn/gigaset/bas-gigaset.c106
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
605static 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 */
635static 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);