aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2006-05-19 16:44:55 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2006-06-21 18:04:12 -0400
commitcaf3827a65af476c71eaeb79636869a4ab128d48 (patch)
tree78ef5d5ec466e59d9ac626faf34fb1031ba4fd8e
parent10b8e47d6b32bfba22874354c62770cb4e42aa6c (diff)
[PATCH] UHCI: store the period in the queue header
This patch (as689) stores the period for periodic transfers (interrupt and ISO) in the queue header. This is necessary for proper bandwidth tracking (not yet implemented). It also makes the scheduling of ISO transfers a bit more rigorous, with checks for out-of-bounds frame numbers. 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.c5
-rw-r--r--drivers/usb/host/uhci-hcd.h36
-rw-r--r--drivers/usb/host/uhci-q.c72
3 files changed, 63 insertions, 50 deletions
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c
index ecef5880cfd..ab8ba8220ad 100644
--- a/drivers/usb/host/uhci-debug.c
+++ b/drivers/usb/host/uhci-debug.c
@@ -153,7 +153,7 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
153 char *qtype; 153 char *qtype;
154 154
155 /* Try to make sure there's enough memory */ 155 /* Try to make sure there's enough memory */
156 if (len < 80 * 6) 156 if (len < 80 * 7)
157 return 0; 157 return 0;
158 158
159 switch (qh->type) { 159 switch (qh->type) {
@@ -167,6 +167,9 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
167 out += sprintf(out, "%*s[%p] %s QH link (%08x) element (%08x)\n", 167 out += sprintf(out, "%*s[%p] %s QH link (%08x) element (%08x)\n",
168 space, "", qh, qtype, 168 space, "", qh, qtype,
169 le32_to_cpu(qh->link), le32_to_cpu(element)); 169 le32_to_cpu(qh->link), le32_to_cpu(element));
170 if (qh->type == USB_ENDPOINT_XFER_ISOC)
171 out += sprintf(out, "%*s period %d\n",
172 space, "", qh->period);
170 173
171 if (element & UHCI_PTR_QH) 174 if (element & UHCI_PTR_QH)
172 out += sprintf(out, "%*s Element points to QH (bug?)\n", space, ""); 175 out += sprintf(out, "%*s Element points to QH (bug?)\n", space, "");
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
index c87ceaa178b..eaac6ddf03a 100644
--- a/drivers/usb/host/uhci-hcd.h
+++ b/drivers/usb/host/uhci-hcd.h
@@ -140,6 +140,8 @@ struct uhci_qh {
140 140
141 unsigned long advance_jiffies; /* Time of last queue advance */ 141 unsigned long advance_jiffies; /* Time of last queue advance */
142 unsigned int unlink_frame; /* When the QH was unlinked */ 142 unsigned int unlink_frame; /* When the QH was unlinked */
143 unsigned int period; /* For Interrupt and Isochronous QHs */
144
143 int state; /* QH_STATE_xxx; see above */ 145 int state; /* QH_STATE_xxx; see above */
144 int type; /* Queue type (control, bulk, etc) */ 146 int type; /* Queue type (control, bulk, etc) */
145 147
@@ -315,38 +317,8 @@ static inline u32 td_status(struct uhci_td *td) {
315#define skel_bulk_qh skelqh[12] 317#define skel_bulk_qh skelqh[12]
316#define skel_term_qh skelqh[13] 318#define skel_term_qh skelqh[13]
317 319
318/* 320/* Find the skelqh entry corresponding to an interval exponent */
319 * Search tree for determining where <interval> fits in the skelqh[] 321#define UHCI_SKEL_INDEX(exponent) (9 - exponent)
320 * skeleton.
321 *
322 * An interrupt request should be placed into the slowest skelqh[]
323 * which meets the interval/period/frequency requirement.
324 * An interrupt request is allowed to be faster than <interval> but not slower.
325 *
326 * For a given <interval>, this function returns the appropriate/matching
327 * skelqh[] index value.
328 */
329static inline int __interval_to_skel(int interval)
330{
331 if (interval < 16) {
332 if (interval < 4) {
333 if (interval < 2)
334 return 9; /* int1 for 0-1 ms */
335 return 8; /* int2 for 2-3 ms */
336 }
337 if (interval < 8)
338 return 7; /* int4 for 4-7 ms */
339 return 6; /* int8 for 8-15 ms */
340 }
341 if (interval < 64) {
342 if (interval < 32)
343 return 5; /* int16 for 16-31 ms */
344 return 4; /* int32 for 32-63 ms */
345 }
346 if (interval < 128)
347 return 3; /* int64 for 64-127 ms */
348 return 2; /* int128 for 128-255 ms (Max.) */
349}
350 322
351 323
352/* 324/*
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index 96ce4c87c87..7acc23473c6 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -763,6 +763,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
763 wmb(); 763 wmb();
764 qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE); 764 qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE);
765 qh->dummy_td = td; 765 qh->dummy_td = td;
766 qh->period = urb->interval;
766 767
767 usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), 768 usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
768 usb_pipeout(urb->pipe), toggle); 769 usb_pipeout(urb->pipe), toggle);
@@ -790,14 +791,30 @@ static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
790 return ret; 791 return ret;
791} 792}
792 793
793static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, 794static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
794 struct uhci_qh *qh) 795 struct uhci_qh *qh)
795{ 796{
797 int exponent;
798
796 /* USB 1.1 interrupt transfers only involve one packet per interval. 799 /* USB 1.1 interrupt transfers only involve one packet per interval.
797 * Drivers can submit URBs of any length, but longer ones will need 800 * Drivers can submit URBs of any length, but longer ones will need
798 * multiple intervals to complete. 801 * multiple intervals to complete.
799 */ 802 */
800 qh->skel = uhci->skelqh[__interval_to_skel(urb->interval)]; 803
804 /* Figure out which power-of-two queue to use */
805 for (exponent = 7; exponent >= 0; --exponent) {
806 if ((1 << exponent) <= urb->interval)
807 break;
808 }
809 if (exponent < 0)
810 return -EINVAL;
811 urb->interval = 1 << exponent;
812
813 if (qh->period == 0)
814 qh->skel = uhci->skelqh[UHCI_SKEL_INDEX(exponent)];
815 else if (qh->period != urb->interval)
816 return -EINVAL; /* Can't change the period */
817
801 return uhci_submit_common(uhci, urb, qh); 818 return uhci_submit_common(uhci, urb, qh);
802} 819}
803 820
@@ -937,31 +954,50 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
937 unsigned long destination, status; 954 unsigned long destination, status;
938 struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; 955 struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
939 956
940 if (urb->number_of_packets > 900) /* 900? Why? */ 957 /* Values must not be too big (could overflow below) */
958 if (urb->interval >= UHCI_NUMFRAMES ||
959 urb->number_of_packets >= UHCI_NUMFRAMES)
941 return -EFBIG; 960 return -EFBIG;
942 961
943 status = TD_CTRL_ACTIVE | TD_CTRL_IOS; 962 /* Check the period and figure out the starting frame number */
944 destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); 963 uhci_get_current_frame_number(uhci);
964 if (qh->period == 0) {
965 if (urb->transfer_flags & URB_ISO_ASAP) {
966 urb->start_frame = uhci->frame_number + 10;
967 } else {
968 i = urb->start_frame - uhci->frame_number;
969 if (i <= 0 || i >= UHCI_NUMFRAMES)
970 return -EINVAL;
971 }
972 } else if (qh->period != urb->interval) {
973 return -EINVAL; /* Can't change the period */
945 974
946 /* Figure out the starting frame number */ 975 } else { /* Pick up where the last URB leaves off */
947 if (urb->transfer_flags & URB_ISO_ASAP) {
948 if (list_empty(&qh->queue)) { 976 if (list_empty(&qh->queue)) {
949 uhci_get_current_frame_number(uhci); 977 frame = uhci->frame_number + 10;
950 urb->start_frame = (uhci->frame_number + 10); 978 } else {
951 979 struct urb *lurb;
952 } else { /* Go right after the last one */
953 struct urb *last_urb;
954 980
955 last_urb = list_entry(qh->queue.prev, 981 lurb = list_entry(qh->queue.prev,
956 struct urb_priv, node)->urb; 982 struct urb_priv, node)->urb;
957 urb->start_frame = (last_urb->start_frame + 983 frame = lurb->start_frame +
958 last_urb->number_of_packets * 984 lurb->number_of_packets *
959 last_urb->interval); 985 lurb->interval;
960 } 986 }
961 } else { 987 if (urb->transfer_flags & URB_ISO_ASAP)
988 urb->start_frame = frame;
962 /* FIXME: Sanity check */ 989 /* FIXME: Sanity check */
963 } 990 }
964 991
992 /* Make sure we won't have to go too far into the future */
993 if (uhci_frame_before_eq(uhci->frame_number + UHCI_NUMFRAMES,
994 urb->start_frame + urb->number_of_packets *
995 urb->interval))
996 return -EFBIG;
997
998 status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
999 destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
1000
965 for (i = 0; i < urb->number_of_packets; i++) { 1001 for (i = 0; i < urb->number_of_packets; i++) {
966 td = uhci_alloc_td(uhci); 1002 td = uhci_alloc_td(uhci);
967 if (!td) 1003 if (!td)
@@ -978,6 +1014,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
978 td->status |= __constant_cpu_to_le32(TD_CTRL_IOC); 1014 td->status |= __constant_cpu_to_le32(TD_CTRL_IOC);
979 1015
980 qh->skel = uhci->skel_iso_qh; 1016 qh->skel = uhci->skel_iso_qh;
1017 qh->period = urb->interval;
981 1018
982 /* Add the TDs to the frame list */ 1019 /* Add the TDs to the frame list */
983 frame = urb->start_frame; 1020 frame = urb->start_frame;
@@ -1206,6 +1243,7 @@ __acquires(uhci->lock)
1206 uhci_unlink_qh(uhci, qh); 1243 uhci_unlink_qh(uhci, qh);
1207 1244
1208 /* Bandwidth stuff not yet implemented */ 1245 /* Bandwidth stuff not yet implemented */
1246 qh->period = 0;
1209 } 1247 }
1210} 1248}
1211 1249