diff options
| author | Alan Stern <stern@rowland.harvard.edu> | 2006-05-12 11:19:19 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-06-21 18:04:11 -0400 |
| commit | 59e29ed91cff90b27d393c7a3d3ac9c3fcaea7dd (patch) | |
| tree | 5003afaca5960954fe25a2fe6401f43fef48f8bb | |
| parent | b1869000a60b0c72022811f24110a52d3e300b1e (diff) | |
[PATCH] UHCI: Remove non-iso TDs as they are used
This patch (as680) frees non-isochronous TDs as they are used, rather
than all at once when an URB is complete. Although not a terribly
important change in itself, it opens the door to a later enhancement
that will reduce storage requirements by allocating only a limited
number of TDs at any time for each endpoint queue.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
| -rw-r--r-- | drivers/usb/host/uhci-debug.c | 1 | ||||
| -rw-r--r-- | drivers/usb/host/uhci-hcd.h | 5 | ||||
| -rw-r--r-- | drivers/usb/host/uhci-q.c | 78 |
3 files changed, 45 insertions, 39 deletions
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index 28c1c51ec475..6bbd33db9358 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c | |||
| @@ -119,6 +119,7 @@ static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space) | |||
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | out += sprintf(out, "%s%s", ptype, (urbp->fsbr ? " FSBR" : "")); | 121 | out += sprintf(out, "%s%s", ptype, (urbp->fsbr ? " FSBR" : "")); |
| 122 | out += sprintf(out, " Actlen=%d", urbp->urb->actual_length); | ||
| 122 | 123 | ||
| 123 | if (urbp->urb->status != -EINPROGRESS) | 124 | if (urbp->urb->status != -EINPROGRESS) |
| 124 | out += sprintf(out, " Status=%d", urbp->urb->status); | 125 | out += sprintf(out, " Status=%d", urbp->urb->status); |
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 8e5778650493..3093ca250942 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h | |||
| @@ -129,6 +129,7 @@ struct uhci_qh { | |||
| 129 | struct list_head queue; /* Queue of urbps for this QH */ | 129 | struct list_head queue; /* Queue of urbps for this QH */ |
| 130 | struct uhci_qh *skel; /* Skeleton for this QH */ | 130 | struct uhci_qh *skel; /* Skeleton for this QH */ |
| 131 | struct uhci_td *dummy_td; /* Dummy TD to end the queue */ | 131 | struct uhci_td *dummy_td; /* Dummy TD to end the queue */ |
| 132 | struct uhci_td *post_td; /* Last TD completed */ | ||
| 132 | 133 | ||
| 133 | unsigned int unlink_frame; /* When the QH was unlinked */ | 134 | unsigned int unlink_frame; /* When the QH was unlinked */ |
| 134 | int state; /* QH_STATE_xxx; see above */ | 135 | int state; /* QH_STATE_xxx; see above */ |
| @@ -136,7 +137,7 @@ struct uhci_qh { | |||
| 136 | 137 | ||
| 137 | unsigned int initial_toggle:1; /* Endpoint's current toggle value */ | 138 | unsigned int initial_toggle:1; /* Endpoint's current toggle value */ |
| 138 | unsigned int needs_fixup:1; /* Must fix the TD toggle values */ | 139 | unsigned int needs_fixup:1; /* Must fix the TD toggle values */ |
| 139 | unsigned int is_stopped:1; /* Queue was stopped by an error */ | 140 | unsigned int is_stopped:1; /* Queue was stopped by error/unlink */ |
| 140 | } __attribute__((aligned(16))); | 141 | } __attribute__((aligned(16))); |
| 141 | 142 | ||
| 142 | /* | 143 | /* |
| @@ -456,8 +457,6 @@ struct urb_priv { | |||
| 456 | struct list_head td_list; | 457 | struct list_head td_list; |
| 457 | 458 | ||
| 458 | unsigned fsbr : 1; /* URB turned on FSBR */ | 459 | unsigned fsbr : 1; /* URB turned on FSBR */ |
| 459 | unsigned short_transfer : 1; /* URB got a short transfer, no | ||
| 460 | * need to rescan */ | ||
| 461 | }; | 460 | }; |
| 462 | 461 | ||
| 463 | 462 | ||
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 30e36031fe21..888938d78230 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c | |||
| @@ -161,6 +161,7 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, | |||
| 161 | if (!qh) | 161 | if (!qh) |
| 162 | return NULL; | 162 | return NULL; |
| 163 | 163 | ||
| 164 | memset(qh, 0, sizeof(*qh)); | ||
| 164 | qh->dma_handle = dma_handle; | 165 | qh->dma_handle = dma_handle; |
| 165 | 166 | ||
| 166 | qh->element = UHCI_PTR_TERM; | 167 | qh->element = UHCI_PTR_TERM; |
| @@ -183,7 +184,6 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, | |||
| 183 | 184 | ||
| 184 | } else { /* Skeleton QH */ | 185 | } else { /* Skeleton QH */ |
| 185 | qh->state = QH_STATE_ACTIVE; | 186 | qh->state = QH_STATE_ACTIVE; |
| 186 | qh->udev = NULL; | ||
| 187 | qh->type = -1; | 187 | qh->type = -1; |
| 188 | } | 188 | } |
| 189 | return qh; | 189 | return qh; |
| @@ -223,16 +223,10 @@ static void uhci_save_toggle(struct uhci_qh *qh, struct urb *urb) | |||
| 223 | qh->type == USB_ENDPOINT_XFER_INT)) | 223 | qh->type == USB_ENDPOINT_XFER_INT)) |
| 224 | return; | 224 | return; |
| 225 | 225 | ||
| 226 | /* Find the first active TD; that's the device's toggle state */ | 226 | WARN_ON(list_empty(&urbp->td_list)); |
| 227 | list_for_each_entry(td, &urbp->td_list, list) { | 227 | td = list_entry(urbp->td_list.next, struct uhci_td, list); |
| 228 | if (td_status(td) & TD_CTRL_ACTIVE) { | 228 | qh->needs_fixup = 1; |
| 229 | qh->needs_fixup = 1; | 229 | qh->initial_toggle = uhci_toggle(td_token(td)); |
| 230 | qh->initial_toggle = uhci_toggle(td_token(td)); | ||
| 231 | return; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | WARN_ON(1); | ||
| 236 | } | 230 | } |
| 237 | 231 | ||
| 238 | /* | 232 | /* |
| @@ -372,6 +366,12 @@ static void uhci_make_qh_idle(struct uhci_hcd *uhci, struct uhci_qh *qh) | |||
| 372 | list_move(&qh->node, &uhci->idle_qh_list); | 366 | list_move(&qh->node, &uhci->idle_qh_list); |
| 373 | qh->state = QH_STATE_IDLE; | 367 | qh->state = QH_STATE_IDLE; |
| 374 | 368 | ||
| 369 | /* Now that the QH is idle, its post_td isn't being used */ | ||
| 370 | if (qh->post_td) { | ||
| 371 | uhci_free_td(uhci, qh->post_td); | ||
| 372 | qh->post_td = NULL; | ||
| 373 | } | ||
| 374 | |||
| 375 | /* If anyone is waiting for a QH to become idle, wake them up */ | 375 | /* If anyone is waiting for a QH to become idle, wake them up */ |
| 376 | if (uhci->num_waiting) | 376 | if (uhci->num_waiting) |
| 377 | wake_up_all(&uhci->waitqh); | 377 | wake_up_all(&uhci->waitqh); |
| @@ -610,6 +610,8 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, | |||
| 610 | qh->skel = uhci->skel_fs_control_qh; | 610 | qh->skel = uhci->skel_fs_control_qh; |
| 611 | uhci_inc_fsbr(uhci, urb); | 611 | uhci_inc_fsbr(uhci, urb); |
| 612 | } | 612 | } |
| 613 | |||
| 614 | urb->actual_length = -8; /* Account for the SETUP packet */ | ||
| 613 | return 0; | 615 | return 0; |
| 614 | 616 | ||
| 615 | nomem: | 617 | nomem: |
| @@ -767,34 +769,46 @@ static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, | |||
| 767 | * Fix up the data structures following a short transfer | 769 | * Fix up the data structures following a short transfer |
| 768 | */ | 770 | */ |
| 769 | static int uhci_fixup_short_transfer(struct uhci_hcd *uhci, | 771 | static int uhci_fixup_short_transfer(struct uhci_hcd *uhci, |
| 770 | struct uhci_qh *qh, struct urb_priv *urbp, | 772 | struct uhci_qh *qh, struct urb_priv *urbp) |
| 771 | struct uhci_td *short_td) | ||
| 772 | { | 773 | { |
| 773 | struct uhci_td *td; | 774 | struct uhci_td *td; |
| 774 | int ret = 0; | 775 | struct list_head *tmp; |
| 776 | int ret; | ||
| 775 | 777 | ||
| 776 | td = list_entry(urbp->td_list.prev, struct uhci_td, list); | 778 | td = list_entry(urbp->td_list.prev, struct uhci_td, list); |
| 777 | if (qh->type == USB_ENDPOINT_XFER_CONTROL) { | 779 | if (qh->type == USB_ENDPOINT_XFER_CONTROL) { |
| 778 | urbp->short_transfer = 1; | ||
| 779 | 780 | ||
| 780 | /* When a control transfer is short, we have to restart | 781 | /* When a control transfer is short, we have to restart |
| 781 | * the queue at the status stage transaction, which is | 782 | * the queue at the status stage transaction, which is |
| 782 | * the last TD. */ | 783 | * the last TD. */ |
| 784 | WARN_ON(list_empty(&urbp->td_list)); | ||
| 783 | qh->element = cpu_to_le32(td->dma_handle); | 785 | qh->element = cpu_to_le32(td->dma_handle); |
| 786 | tmp = td->list.prev; | ||
| 784 | ret = -EINPROGRESS; | 787 | ret = -EINPROGRESS; |
| 785 | 788 | ||
| 786 | } else if (!urbp->short_transfer) { | 789 | } else { |
| 787 | urbp->short_transfer = 1; | ||
| 788 | 790 | ||
| 789 | /* When a bulk/interrupt transfer is short, we have to | 791 | /* When a bulk/interrupt transfer is short, we have to |
| 790 | * fix up the toggles of the following URBs on the queue | 792 | * fix up the toggles of the following URBs on the queue |
| 791 | * before restarting the queue at the next URB. */ | 793 | * before restarting the queue at the next URB. */ |
| 792 | qh->initial_toggle = uhci_toggle(td_token(short_td)) ^ 1; | 794 | qh->initial_toggle = uhci_toggle(td_token(qh->post_td)) ^ 1; |
| 793 | uhci_fixup_toggles(qh, 1); | 795 | uhci_fixup_toggles(qh, 1); |
| 794 | 796 | ||
| 797 | if (list_empty(&urbp->td_list)) | ||
| 798 | td = qh->post_td; | ||
| 795 | qh->element = td->link; | 799 | qh->element = td->link; |
| 800 | tmp = urbp->td_list.prev; | ||
| 801 | ret = 0; | ||
| 796 | } | 802 | } |
| 797 | 803 | ||
| 804 | /* Remove all the TDs we skipped over, from tmp back to the start */ | ||
| 805 | while (tmp != &urbp->td_list) { | ||
| 806 | td = list_entry(tmp, struct uhci_td, list); | ||
| 807 | tmp = tmp->prev; | ||
| 808 | |||
| 809 | uhci_remove_td_from_urb(td); | ||
| 810 | list_add(&td->remove_list, &uhci->td_remove_list); | ||
| 811 | } | ||
| 798 | return ret; | 812 | return ret; |
| 799 | } | 813 | } |
| 800 | 814 | ||
| @@ -805,29 +819,14 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) | |||
| 805 | { | 819 | { |
| 806 | struct urb_priv *urbp = urb->hcpriv; | 820 | struct urb_priv *urbp = urb->hcpriv; |
| 807 | struct uhci_qh *qh = urbp->qh; | 821 | struct uhci_qh *qh = urbp->qh; |
| 808 | struct uhci_td *td; | 822 | struct uhci_td *td, *tmp; |
| 809 | struct list_head *tmp; | ||
| 810 | unsigned status; | 823 | unsigned status; |
| 811 | int ret = 0; | 824 | int ret = 0; |
| 812 | 825 | ||
| 813 | tmp = urbp->td_list.next; | 826 | list_for_each_entry_safe(td, tmp, &urbp->td_list, list) { |
| 814 | |||
| 815 | if (qh->type == USB_ENDPOINT_XFER_CONTROL) { | ||
| 816 | if (urbp->short_transfer) | ||
| 817 | tmp = urbp->td_list.prev; | ||
| 818 | else | ||
| 819 | urb->actual_length = -8; /* SETUP packet */ | ||
| 820 | } else | ||
| 821 | urb->actual_length = 0; | ||
| 822 | |||
| 823 | |||
| 824 | while (tmp != &urbp->td_list) { | ||
| 825 | unsigned int ctrlstat; | 827 | unsigned int ctrlstat; |
| 826 | int len; | 828 | int len; |
| 827 | 829 | ||
| 828 | td = list_entry(tmp, struct uhci_td, list); | ||
| 829 | tmp = tmp->next; | ||
| 830 | |||
| 831 | ctrlstat = td_status(td); | 830 | ctrlstat = td_status(td); |
| 832 | status = uhci_status_bits(ctrlstat); | 831 | status = uhci_status_bits(ctrlstat); |
| 833 | if (status & TD_CTRL_ACTIVE) | 832 | if (status & TD_CTRL_ACTIVE) |
| @@ -862,6 +861,12 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) | |||
| 862 | ret = 1; | 861 | ret = 1; |
| 863 | } | 862 | } |
| 864 | 863 | ||
| 864 | uhci_remove_td_from_urb(td); | ||
| 865 | if (qh->post_td) | ||
| 866 | list_add(&qh->post_td->remove_list, | ||
| 867 | &uhci->td_remove_list); | ||
| 868 | qh->post_td = td; | ||
| 869 | |||
| 865 | if (ret != 0) | 870 | if (ret != 0) |
| 866 | goto err; | 871 | goto err; |
| 867 | } | 872 | } |
| @@ -882,7 +887,7 @@ err: | |||
| 882 | (ret == -EREMOTEIO); | 887 | (ret == -EREMOTEIO); |
| 883 | 888 | ||
| 884 | } else /* Short packet received */ | 889 | } else /* Short packet received */ |
| 885 | ret = uhci_fixup_short_transfer(uhci, qh, urbp, td); | 890 | ret = uhci_fixup_short_transfer(uhci, qh, urbp); |
| 886 | return ret; | 891 | return ret; |
| 887 | } | 892 | } |
| 888 | 893 | ||
| @@ -1123,6 +1128,7 @@ __acquires(uhci->lock) | |||
| 1123 | struct uhci_td *ptd, *ltd; | 1128 | struct uhci_td *ptd, *ltd; |
| 1124 | 1129 | ||
| 1125 | purbp = list_entry(urbp->node.prev, struct urb_priv, node); | 1130 | purbp = list_entry(urbp->node.prev, struct urb_priv, node); |
| 1131 | WARN_ON(list_empty(&purbp->td_list)); | ||
| 1126 | ptd = list_entry(purbp->td_list.prev, struct uhci_td, | 1132 | ptd = list_entry(purbp->td_list.prev, struct uhci_td, |
| 1127 | list); | 1133 | list); |
| 1128 | ltd = list_entry(urbp->td_list.prev, struct uhci_td, | 1134 | ltd = list_entry(urbp->td_list.prev, struct uhci_td, |
