aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorMartin Fuzzey <mfuzzey@gmail.com>2010-09-30 18:21:48 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-10-22 13:22:02 -0400
commit7a7e7896422baced4757070b813ba28ab24a214a (patch)
tree65a90261855fc34da9088ab3a9f7f2160c320787 /drivers/usb/host
parentb2a068d058e71519e14d2c03e92459c4c1d63c8b (diff)
USB: imx21-hcd: Fix isochronous endpoint idle
Release the hardware resources and reset the internal HCD state associated with an isochronous endpoint when the last URB queued for it completes. Previously this was only done in then endpoint_disable() method causing usbtest 15 and 16 to hang when run twice in succession without a disconnect. Signed-off-by: Martin Fuzzey <mfuzzey@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/imx21-hcd.c56
1 files changed, 39 insertions, 17 deletions
diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c
index 1be144167da4..890a41ccc5cd 100644
--- a/drivers/usb/host/imx21-hcd.c
+++ b/drivers/usb/host/imx21-hcd.c
@@ -390,15 +390,19 @@ static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb);
390/* Endpoint now idle - release it's ETD(s) or asssign to queued request */ 390/* Endpoint now idle - release it's ETD(s) or asssign to queued request */
391static void ep_idle(struct imx21 *imx21, struct ep_priv *ep_priv) 391static void ep_idle(struct imx21 *imx21, struct ep_priv *ep_priv)
392{ 392{
393 int etd_num;
394 int i; 393 int i;
395 394
396 for (i = 0; i < NUM_ISO_ETDS; i++) { 395 for (i = 0; i < NUM_ISO_ETDS; i++) {
397 etd_num = ep_priv->etd[i]; 396 int etd_num = ep_priv->etd[i];
397 struct etd_priv *etd;
398 if (etd_num < 0) 398 if (etd_num < 0)
399 continue; 399 continue;
400 400
401 etd = &imx21->etd[etd_num];
401 ep_priv->etd[i] = -1; 402 ep_priv->etd[i] = -1;
403
404 free_dmem(imx21, etd); /* for isoc */
405
402 if (list_empty(&imx21->queue_for_etd)) { 406 if (list_empty(&imx21->queue_for_etd)) {
403 free_etd(imx21, etd_num); 407 free_etd(imx21, etd_num);
404 continue; 408 continue;
@@ -576,30 +580,43 @@ static struct ep_priv *alloc_isoc_ep(
576 int i; 580 int i;
577 581
578 ep_priv = kzalloc(sizeof(struct ep_priv), GFP_ATOMIC); 582 ep_priv = kzalloc(sizeof(struct ep_priv), GFP_ATOMIC);
579 if (ep_priv == NULL) 583 if (!ep_priv)
580 return NULL; 584 return NULL;
581 585
582 /* Allocate the ETDs */ 586 for (i = 0; i < NUM_ISO_ETDS; i++)
583 for (i = 0; i < NUM_ISO_ETDS; i++) { 587 ep_priv->etd[i] = -1;
584 ep_priv->etd[i] = alloc_etd(imx21);
585 if (ep_priv->etd[i] < 0) {
586 int j;
587 dev_err(imx21->dev, "isoc: Couldn't allocate etd\n");
588 for (j = 0; j < i; j++)
589 free_etd(imx21, ep_priv->etd[j]);
590 goto alloc_etd_failed;
591 }
592 imx21->etd[ep_priv->etd[i]].ep = ep;
593 }
594 588
595 INIT_LIST_HEAD(&ep_priv->td_list); 589 INIT_LIST_HEAD(&ep_priv->td_list);
596 ep_priv->ep = ep; 590 ep_priv->ep = ep;
597 ep->hcpriv = ep_priv; 591 ep->hcpriv = ep_priv;
598 return ep_priv; 592 return ep_priv;
593}
594
595static int alloc_isoc_etds(struct imx21 *imx21, struct ep_priv *ep_priv)
596{
597 int i, j;
598 int etd_num;
599
600 /* Allocate the ETDs if required */
601 for (i = 0; i < NUM_ISO_ETDS; i++) {
602 if (ep_priv->etd[i] < 0) {
603 etd_num = alloc_etd(imx21);
604 if (etd_num < 0)
605 goto alloc_etd_failed;
606
607 ep_priv->etd[i] = etd_num;
608 imx21->etd[etd_num].ep = ep_priv->ep;
609 }
610 }
611 return 0;
599 612
600alloc_etd_failed: 613alloc_etd_failed:
601 kfree(ep_priv); 614 dev_err(imx21->dev, "isoc: Couldn't allocate etd\n");
602 return NULL; 615 for (j = 0; j < i; j++) {
616 free_etd(imx21, ep_priv->etd[j]);
617 ep_priv->etd[j] = -1;
618 }
619 return -ENOMEM;
603} 620}
604 621
605static int imx21_hc_urb_enqueue_isoc(struct usb_hcd *hcd, 622static int imx21_hc_urb_enqueue_isoc(struct usb_hcd *hcd,
@@ -639,6 +656,10 @@ static int imx21_hc_urb_enqueue_isoc(struct usb_hcd *hcd,
639 ep_priv = ep->hcpriv; 656 ep_priv = ep->hcpriv;
640 } 657 }
641 658
659 ret = alloc_isoc_etds(imx21, ep_priv);
660 if (ret)
661 goto alloc_etd_failed;
662
642 ret = usb_hcd_link_urb_to_ep(hcd, urb); 663 ret = usb_hcd_link_urb_to_ep(hcd, urb);
643 if (ret) 664 if (ret)
644 goto link_failed; 665 goto link_failed;
@@ -718,6 +739,7 @@ alloc_dmem_failed:
718 usb_hcd_unlink_urb_from_ep(hcd, urb); 739 usb_hcd_unlink_urb_from_ep(hcd, urb);
719 740
720link_failed: 741link_failed:
742alloc_etd_failed:
721alloc_ep_failed: 743alloc_ep_failed:
722 spin_unlock_irqrestore(&imx21->lock, flags); 744 spin_unlock_irqrestore(&imx21->lock, flags);
723 kfree(urb_priv->isoc_td); 745 kfree(urb_priv->isoc_td);