diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2005-12-17 18:00:12 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-03-20 17:49:57 -0500 |
commit | af0bb5998abe8ed28ee354dd4c71689cacdc91e9 (patch) | |
tree | 20172dd9551d0e2497ef4b6dff1a84f010f981ab /drivers/usb/host/uhci-q.c | |
parent | dccf4a48d47120a42382ba526f1a0848c13ba2a4 (diff) |
[PATCH] UHCI: use dummy TDs
This patch (as624) fixes a hardware race in uhci-hcd by adding a dummy
TD to the end of each endpoint's queue. Without the dummy the host
controller will effectively turn off the queue when it reaches the end,
which happens asynchronously. This leads to a potential problem when
new transfer descriptors are added to the end of the queue; they may
never get used.
With a dummy TD present the controller never turns off the queue;
instead it just stops at the dummy and leaves the queue on but inactive.
When new TDs are added to the end of the queue, the first new one gets
written over the dummy. Thus there's never any question about whether
the queue is running or needs to be restarted.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/uhci-q.c')
-rw-r--r-- | drivers/usb/host/uhci-q.c | 138 |
1 files changed, 77 insertions, 61 deletions
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index b1b551a3d14e..c4194182dcc4 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c | |||
@@ -48,10 +48,6 @@ static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci) | |||
48 | return NULL; | 48 | return NULL; |
49 | 49 | ||
50 | td->dma_handle = dma_handle; | 50 | td->dma_handle = dma_handle; |
51 | |||
52 | td->link = UHCI_PTR_TERM; | ||
53 | td->buffer = 0; | ||
54 | |||
55 | td->frame = -1; | 51 | td->frame = -1; |
56 | 52 | ||
57 | INIT_LIST_HEAD(&td->list); | 53 | INIT_LIST_HEAD(&td->list); |
@@ -221,6 +217,11 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, | |||
221 | INIT_LIST_HEAD(&qh->node); | 217 | INIT_LIST_HEAD(&qh->node); |
222 | 218 | ||
223 | if (udev) { /* Normal QH */ | 219 | if (udev) { /* Normal QH */ |
220 | qh->dummy_td = uhci_alloc_td(uhci); | ||
221 | if (!qh->dummy_td) { | ||
222 | dma_pool_free(uhci->qh_pool, qh, dma_handle); | ||
223 | return NULL; | ||
224 | } | ||
224 | qh->state = QH_STATE_IDLE; | 225 | qh->state = QH_STATE_IDLE; |
225 | qh->hep = hep; | 226 | qh->hep = hep; |
226 | qh->udev = udev; | 227 | qh->udev = udev; |
@@ -244,6 +245,7 @@ static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) | |||
244 | if (qh->udev) { | 245 | if (qh->udev) { |
245 | qh->hep->hcpriv = NULL; | 246 | qh->hep->hcpriv = NULL; |
246 | usb_put_dev(qh->udev); | 247 | usb_put_dev(qh->udev); |
248 | uhci_free_td(uhci, qh->dummy_td); | ||
247 | } | 249 | } |
248 | dma_pool_free(uhci->qh_pool, qh, qh->dma_handle); | 250 | dma_pool_free(uhci->qh_pool, qh, qh->dma_handle); |
249 | } | 251 | } |
@@ -531,22 +533,20 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, | |||
531 | /* The "pipe" thing contains the destination in bits 8--18 */ | 533 | /* The "pipe" thing contains the destination in bits 8--18 */ |
532 | destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; | 534 | destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; |
533 | 535 | ||
534 | /* 3 errors */ | 536 | /* 3 errors, dummy TD remains inactive */ |
535 | status = TD_CTRL_ACTIVE | uhci_maxerr(3); | 537 | status = uhci_maxerr(3); |
536 | if (urb->dev->speed == USB_SPEED_LOW) | 538 | if (urb->dev->speed == USB_SPEED_LOW) |
537 | status |= TD_CTRL_LS; | 539 | status |= TD_CTRL_LS; |
538 | 540 | ||
539 | /* | 541 | /* |
540 | * Build the TD for the control request setup packet | 542 | * Build the TD for the control request setup packet |
541 | */ | 543 | */ |
542 | td = uhci_alloc_td(uhci); | 544 | td = qh->dummy_td; |
543 | if (!td) | ||
544 | return -ENOMEM; | ||
545 | |||
546 | uhci_add_td_to_urb(urb, td); | 545 | uhci_add_td_to_urb(urb, td); |
547 | uhci_fill_td(td, status, destination | uhci_explen(8), | 546 | uhci_fill_td(td, status, destination | uhci_explen(8), |
548 | urb->setup_dma); | 547 | urb->setup_dma); |
549 | plink = &td->link; | 548 | plink = &td->link; |
549 | status |= TD_CTRL_ACTIVE; | ||
550 | 550 | ||
551 | /* | 551 | /* |
552 | * If direction is "send", change the packet ID from SETUP (0x2D) | 552 | * If direction is "send", change the packet ID from SETUP (0x2D) |
@@ -568,7 +568,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, | |||
568 | 568 | ||
569 | td = uhci_alloc_td(uhci); | 569 | td = uhci_alloc_td(uhci); |
570 | if (!td) | 570 | if (!td) |
571 | return -ENOMEM; | 571 | goto nomem; |
572 | *plink = cpu_to_le32(td->dma_handle); | 572 | *plink = cpu_to_le32(td->dma_handle); |
573 | 573 | ||
574 | /* Alternate Data0/1 (start with Data1) */ | 574 | /* Alternate Data0/1 (start with Data1) */ |
@@ -588,7 +588,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, | |||
588 | */ | 588 | */ |
589 | td = uhci_alloc_td(uhci); | 589 | td = uhci_alloc_td(uhci); |
590 | if (!td) | 590 | if (!td) |
591 | return -ENOMEM; | 591 | goto nomem; |
592 | *plink = cpu_to_le32(td->dma_handle); | 592 | *plink = cpu_to_le32(td->dma_handle); |
593 | 593 | ||
594 | /* | 594 | /* |
@@ -608,6 +608,20 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, | |||
608 | uhci_add_td_to_urb(urb, td); | 608 | uhci_add_td_to_urb(urb, td); |
609 | uhci_fill_td(td, status | TD_CTRL_IOC, | 609 | uhci_fill_td(td, status | TD_CTRL_IOC, |
610 | destination | uhci_explen(0), 0); | 610 | destination | uhci_explen(0), 0); |
611 | plink = &td->link; | ||
612 | |||
613 | /* | ||
614 | * Build the new dummy TD and activate the old one | ||
615 | */ | ||
616 | td = uhci_alloc_td(uhci); | ||
617 | if (!td) | ||
618 | goto nomem; | ||
619 | *plink = cpu_to_le32(td->dma_handle); | ||
620 | |||
621 | uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0); | ||
622 | wmb(); | ||
623 | qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE); | ||
624 | qh->dummy_td = td; | ||
611 | 625 | ||
612 | /* Low-speed transfers get a different queue, and won't hog the bus. | 626 | /* Low-speed transfers get a different queue, and won't hog the bus. |
613 | * Also, some devices enumerate better without FSBR; the easiest way | 627 | * Also, some devices enumerate better without FSBR; the easiest way |
@@ -620,8 +634,12 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, | |||
620 | qh->skel = uhci->skel_fs_control_qh; | 634 | qh->skel = uhci->skel_fs_control_qh; |
621 | uhci_inc_fsbr(uhci, urb); | 635 | uhci_inc_fsbr(uhci, urb); |
622 | } | 636 | } |
623 | |||
624 | return 0; | 637 | return 0; |
638 | |||
639 | nomem: | ||
640 | /* Remove the dummy TD from the td_list so it doesn't get freed */ | ||
641 | uhci_remove_td_from_urb(qh->dummy_td); | ||
642 | return -ENOMEM; | ||
625 | } | 643 | } |
626 | 644 | ||
627 | /* | 645 | /* |
@@ -761,16 +779,19 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, | |||
761 | int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize); | 779 | int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize); |
762 | int len = urb->transfer_buffer_length; | 780 | int len = urb->transfer_buffer_length; |
763 | dma_addr_t data = urb->transfer_dma; | 781 | dma_addr_t data = urb->transfer_dma; |
764 | __le32 *plink, fake_link; | 782 | __le32 *plink; |
783 | unsigned int toggle; | ||
765 | 784 | ||
766 | if (len < 0) | 785 | if (len < 0) |
767 | return -EINVAL; | 786 | return -EINVAL; |
768 | 787 | ||
769 | /* The "pipe" thing contains the destination in bits 8--18 */ | 788 | /* The "pipe" thing contains the destination in bits 8--18 */ |
770 | destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); | 789 | destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); |
790 | toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), | ||
791 | usb_pipeout(urb->pipe)); | ||
771 | 792 | ||
772 | /* 3 errors */ | 793 | /* 3 errors, dummy TD remains inactive */ |
773 | status = TD_CTRL_ACTIVE | uhci_maxerr(3); | 794 | status = uhci_maxerr(3); |
774 | if (urb->dev->speed == USB_SPEED_LOW) | 795 | if (urb->dev->speed == USB_SPEED_LOW) |
775 | status |= TD_CTRL_LS; | 796 | status |= TD_CTRL_LS; |
776 | if (usb_pipein(urb->pipe)) | 797 | if (usb_pipein(urb->pipe)) |
@@ -779,7 +800,8 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, | |||
779 | /* | 800 | /* |
780 | * Build the DATA TDs | 801 | * Build the DATA TDs |
781 | */ | 802 | */ |
782 | plink = &fake_link; | 803 | plink = NULL; |
804 | td = qh->dummy_td; | ||
783 | do { /* Allow zero length packets */ | 805 | do { /* Allow zero length packets */ |
784 | int pktsze = maxsze; | 806 | int pktsze = maxsze; |
785 | 807 | ||
@@ -789,24 +811,23 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, | |||
789 | status &= ~TD_CTRL_SPD; | 811 | status &= ~TD_CTRL_SPD; |
790 | } | 812 | } |
791 | 813 | ||
792 | td = uhci_alloc_td(uhci); | 814 | if (plink) { |
793 | if (!td) | 815 | td = uhci_alloc_td(uhci); |
794 | return -ENOMEM; | 816 | if (!td) |
795 | *plink = cpu_to_le32(td->dma_handle); | 817 | goto nomem; |
796 | 818 | *plink = cpu_to_le32(td->dma_handle); | |
819 | } | ||
797 | uhci_add_td_to_urb(urb, td); | 820 | uhci_add_td_to_urb(urb, td); |
798 | uhci_fill_td(td, status, | 821 | uhci_fill_td(td, status, |
799 | destination | uhci_explen(pktsze) | | 822 | destination | uhci_explen(pktsze) | |
800 | (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), | 823 | (toggle << TD_TOKEN_TOGGLE_SHIFT), |
801 | usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), | 824 | data); |
802 | data); | ||
803 | plink = &td->link; | 825 | plink = &td->link; |
826 | status |= TD_CTRL_ACTIVE; | ||
804 | 827 | ||
805 | data += pktsze; | 828 | data += pktsze; |
806 | len -= maxsze; | 829 | len -= maxsze; |
807 | 830 | toggle ^= 1; | |
808 | usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), | ||
809 | usb_pipeout(urb->pipe)); | ||
810 | } while (len > 0); | 831 | } while (len > 0); |
811 | 832 | ||
812 | /* | 833 | /* |
@@ -821,17 +842,17 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, | |||
821 | urb->transfer_buffer_length > 0) { | 842 | urb->transfer_buffer_length > 0) { |
822 | td = uhci_alloc_td(uhci); | 843 | td = uhci_alloc_td(uhci); |
823 | if (!td) | 844 | if (!td) |
824 | return -ENOMEM; | 845 | goto nomem; |
825 | *plink = cpu_to_le32(td->dma_handle); | 846 | *plink = cpu_to_le32(td->dma_handle); |
826 | 847 | ||
827 | uhci_add_td_to_urb(urb, td); | 848 | uhci_add_td_to_urb(urb, td); |
828 | uhci_fill_td(td, status, destination | uhci_explen(0) | | 849 | uhci_fill_td(td, status, |
829 | (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), | 850 | destination | uhci_explen(0) | |
830 | usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), | 851 | (toggle << TD_TOKEN_TOGGLE_SHIFT), |
831 | data); | 852 | data); |
853 | plink = &td->link; | ||
832 | 854 | ||
833 | usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), | 855 | toggle ^= 1; |
834 | usb_pipeout(urb->pipe)); | ||
835 | } | 856 | } |
836 | 857 | ||
837 | /* Set the interrupt-on-completion flag on the last packet. | 858 | /* Set the interrupt-on-completion flag on the last packet. |
@@ -842,7 +863,27 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, | |||
842 | * flag setting. */ | 863 | * flag setting. */ |
843 | td->status |= __constant_cpu_to_le32(TD_CTRL_IOC); | 864 | td->status |= __constant_cpu_to_le32(TD_CTRL_IOC); |
844 | 865 | ||
866 | /* | ||
867 | * Build the new dummy TD and activate the old one | ||
868 | */ | ||
869 | td = uhci_alloc_td(uhci); | ||
870 | if (!td) | ||
871 | goto nomem; | ||
872 | *plink = cpu_to_le32(td->dma_handle); | ||
873 | |||
874 | uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0); | ||
875 | wmb(); | ||
876 | qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE); | ||
877 | qh->dummy_td = td; | ||
878 | |||
879 | usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), | ||
880 | usb_pipeout(urb->pipe), toggle); | ||
845 | return 0; | 881 | return 0; |
882 | |||
883 | nomem: | ||
884 | /* Remove the dummy TD from the td_list so it doesn't get freed */ | ||
885 | uhci_remove_td_from_urb(qh->dummy_td); | ||
886 | return -ENOMEM; | ||
846 | } | 887 | } |
847 | 888 | ||
848 | /* | 889 | /* |
@@ -1169,31 +1210,6 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd, | |||
1169 | * become idle, so we can activate it right away. */ | 1210 | * become idle, so we can activate it right away. */ |
1170 | if (qh->queue.next == &urbp->node) | 1211 | if (qh->queue.next == &urbp->node) |
1171 | uhci_activate_qh(uhci, qh); | 1212 | uhci_activate_qh(uhci, qh); |
1172 | |||
1173 | /* If the QH is already active, we have a race with the hardware. | ||
1174 | * This won't get fixed until dummy TDs are added. */ | ||
1175 | else if (qh->state == QH_STATE_ACTIVE) { | ||
1176 | |||
1177 | /* If the URB isn't first on its queue, adjust the link pointer | ||
1178 | * of the last TD in the previous URB. */ | ||
1179 | if (urbp->node.prev != &urbp->qh->queue) { | ||
1180 | struct urb_priv *purbp = list_entry(urbp->node.prev, | ||
1181 | struct urb_priv, node); | ||
1182 | struct uhci_td *ptd = list_entry(purbp->td_list.prev, | ||
1183 | struct uhci_td, list); | ||
1184 | struct uhci_td *td = list_entry(urbp->td_list.next, | ||
1185 | struct uhci_td, list); | ||
1186 | |||
1187 | ptd->link = cpu_to_le32(td->dma_handle); | ||
1188 | |||
1189 | } | ||
1190 | if (qh_element(qh) == UHCI_PTR_TERM) { | ||
1191 | struct uhci_td *td = list_entry(urbp->td_list.next, | ||
1192 | struct uhci_td, list); | ||
1193 | |||
1194 | qh->element = cpu_to_le32(td->dma_handle); | ||
1195 | } | ||
1196 | } | ||
1197 | goto done; | 1213 | goto done; |
1198 | 1214 | ||
1199 | err_submit_failed: | 1215 | err_submit_failed: |