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 /drivers/usb/host | |
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>
Diffstat (limited to 'drivers/usb/host')
-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, |