diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2006-05-19 16:52:35 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-06-21 18:04:12 -0400 |
commit | c8155cc5d839838f8425dbea568fc537337176a7 (patch) | |
tree | fc50ffe774fa59a1a3d18e8543bb7e04d1e80726 /drivers | |
parent | caf3827a65af476c71eaeb79636869a4ab128d48 (diff) |
[PATCH] UHCI: remove ISO TDs as they are used
This patch (as690) does the same thing for ISO TDs as as680 did for
non-ISO TDs: free them as they are used rather than all at once when an
URB is complete. At the same time it fixes a minor buglet (I'm not
aware of it ever affecting anyone): An ISO TD should be retired when its
frame is over, regardless of whether or not the hardware has marked it
inactive.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/host/uhci-debug.c | 14 | ||||
-rw-r--r-- | drivers/usb/host/uhci-hcd.h | 10 | ||||
-rw-r--r-- | drivers/usb/host/uhci-q.c | 103 |
3 files changed, 90 insertions, 37 deletions
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index ab8ba8220ad1..6637a0e49978 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c | |||
@@ -127,7 +127,8 @@ static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space) | |||
127 | 127 | ||
128 | i = nactive = ninactive = 0; | 128 | i = nactive = ninactive = 0; |
129 | list_for_each_entry(td, &urbp->td_list, list) { | 129 | list_for_each_entry(td, &urbp->td_list, list) { |
130 | if (++i <= 10 || debug > 2) { | 130 | if (urbp->qh->type != USB_ENDPOINT_XFER_ISOC && |
131 | (++i <= 10 || debug > 2)) { | ||
131 | out += sprintf(out, "%*s%d: ", space + 2, "", i); | 132 | out += sprintf(out, "%*s%d: ", space + 2, "", i); |
132 | out += uhci_show_td(td, out, len - (out - buf), 0); | 133 | out += uhci_show_td(td, out, len - (out - buf), 0); |
133 | } else { | 134 | } else { |
@@ -168,8 +169,9 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) | |||
168 | space, "", qh, qtype, | 169 | space, "", qh, qtype, |
169 | le32_to_cpu(qh->link), le32_to_cpu(element)); | 170 | le32_to_cpu(qh->link), le32_to_cpu(element)); |
170 | if (qh->type == USB_ENDPOINT_XFER_ISOC) | 171 | if (qh->type == USB_ENDPOINT_XFER_ISOC) |
171 | out += sprintf(out, "%*s period %d\n", | 172 | out += sprintf(out, "%*s period %d frame %x desc [%p]\n", |
172 | space, "", qh->period); | 173 | space, "", qh->period, qh->iso_frame, |
174 | qh->iso_packet_desc); | ||
173 | 175 | ||
174 | if (element & UHCI_PTR_QH) | 176 | if (element & UHCI_PTR_QH) |
175 | out += sprintf(out, "%*s Element points to QH (bug?)\n", space, ""); | 177 | out += sprintf(out, "%*s Element points to QH (bug?)\n", space, ""); |
@@ -331,8 +333,10 @@ static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len) | |||
331 | out += sprintf(out, " sof = %02x\n", sof); | 333 | out += sprintf(out, " sof = %02x\n", sof); |
332 | out += uhci_show_sc(1, portsc1, out, len - (out - buf)); | 334 | out += uhci_show_sc(1, portsc1, out, len - (out - buf)); |
333 | out += uhci_show_sc(2, portsc2, out, len - (out - buf)); | 335 | out += uhci_show_sc(2, portsc2, out, len - (out - buf)); |
334 | out += sprintf(out, "Most recent frame: %x\n", | 336 | out += sprintf(out, "Most recent frame: %x (%d) " |
335 | uhci->frame_number); | 337 | "Last ISO frame: %x (%d)\n", |
338 | uhci->frame_number, uhci->frame_number & 1023, | ||
339 | uhci->last_iso_frame, uhci->last_iso_frame & 1023); | ||
336 | 340 | ||
337 | return out - buf; | 341 | return out - buf; |
338 | } | 342 | } |
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index eaac6ddf03a0..469b4268b850 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h | |||
@@ -128,8 +128,6 @@ struct uhci_qh { | |||
128 | __le32 element; /* Queue element (TD) pointer */ | 128 | __le32 element; /* Queue element (TD) pointer */ |
129 | 129 | ||
130 | /* Software fields */ | 130 | /* Software fields */ |
131 | dma_addr_t dma_handle; | ||
132 | |||
133 | struct list_head node; /* Node in the list of QHs */ | 131 | struct list_head node; /* Node in the list of QHs */ |
134 | struct usb_host_endpoint *hep; /* Endpoint information */ | 132 | struct usb_host_endpoint *hep; /* Endpoint information */ |
135 | struct usb_device *udev; | 133 | struct usb_device *udev; |
@@ -138,13 +136,19 @@ struct uhci_qh { | |||
138 | struct uhci_td *dummy_td; /* Dummy TD to end the queue */ | 136 | struct uhci_td *dummy_td; /* Dummy TD to end the queue */ |
139 | struct uhci_td *post_td; /* Last TD completed */ | 137 | struct uhci_td *post_td; /* Last TD completed */ |
140 | 138 | ||
139 | struct usb_iso_packet_descriptor *iso_packet_desc; | ||
140 | /* Next urb->iso_frame_desc entry */ | ||
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 */ | 143 | unsigned int period; /* For Interrupt and Isochronous QHs */ |
144 | unsigned int iso_frame; /* Frame # for iso_packet_desc */ | ||
145 | int iso_status; /* Status for Isochronous URBs */ | ||
144 | 146 | ||
145 | int state; /* QH_STATE_xxx; see above */ | 147 | int state; /* QH_STATE_xxx; see above */ |
146 | int type; /* Queue type (control, bulk, etc) */ | 148 | int type; /* Queue type (control, bulk, etc) */ |
147 | 149 | ||
150 | dma_addr_t dma_handle; | ||
151 | |||
148 | unsigned int initial_toggle:1; /* Endpoint's current toggle value */ | 152 | unsigned int initial_toggle:1; /* Endpoint's current toggle value */ |
149 | unsigned int needs_fixup:1; /* Must fix the TD toggle values */ | 153 | unsigned int needs_fixup:1; /* Must fix the TD toggle values */ |
150 | unsigned int is_stopped:1; /* Queue was stopped by error/unlink */ | 154 | unsigned int is_stopped:1; /* Queue was stopped by error/unlink */ |
@@ -386,6 +390,8 @@ struct uhci_hcd { | |||
386 | unsigned int frame_number; /* As of last check */ | 390 | unsigned int frame_number; /* As of last check */ |
387 | unsigned int is_stopped; | 391 | unsigned int is_stopped; |
388 | #define UHCI_IS_STOPPED 9999 /* Larger than a frame # */ | 392 | #define UHCI_IS_STOPPED 9999 /* Larger than a frame # */ |
393 | unsigned int last_iso_frame; /* Frame of last scan */ | ||
394 | unsigned int cur_iso_frame; /* Frame for current scan */ | ||
389 | 395 | ||
390 | unsigned int scan_in_progress:1; /* Schedule scan is running */ | 396 | unsigned int scan_in_progress:1; /* Schedule scan is running */ |
391 | unsigned int need_rescan:1; /* Redo the schedule scan */ | 397 | unsigned int need_rescan:1; /* Redo the schedule scan */ |
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 7acc23473c63..cbbaa4c1740f 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c | |||
@@ -184,6 +184,24 @@ static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci, | |||
184 | td->frame = -1; | 184 | td->frame = -1; |
185 | } | 185 | } |
186 | 186 | ||
187 | static inline void uhci_remove_tds_from_frame(struct uhci_hcd *uhci, | ||
188 | unsigned int framenum) | ||
189 | { | ||
190 | struct uhci_td *ftd, *ltd; | ||
191 | |||
192 | framenum &= (UHCI_NUMFRAMES - 1); | ||
193 | |||
194 | ftd = uhci->frame_cpu[framenum]; | ||
195 | if (ftd) { | ||
196 | ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list); | ||
197 | uhci->frame[framenum] = ltd->link; | ||
198 | uhci->frame_cpu[framenum] = NULL; | ||
199 | |||
200 | while (!list_empty(&ftd->fl_list)) | ||
201 | list_del_init(ftd->fl_list.prev); | ||
202 | } | ||
203 | } | ||
204 | |||
187 | /* | 205 | /* |
188 | * Remove all the TDs for an Isochronous URB from the frame list | 206 | * Remove all the TDs for an Isochronous URB from the frame list |
189 | */ | 207 | */ |
@@ -523,7 +541,6 @@ static int uhci_map_status(int status, int dir_out) | |||
523 | return -ENOSR; | 541 | return -ENOSR; |
524 | if (status & TD_CTRL_STALLED) /* Stalled */ | 542 | if (status & TD_CTRL_STALLED) /* Stalled */ |
525 | return -EPIPE; | 543 | return -EPIPE; |
526 | WARN_ON(status & TD_CTRL_ACTIVE); /* Active */ | ||
527 | return 0; | 544 | return 0; |
528 | } | 545 | } |
529 | 546 | ||
@@ -960,12 +977,12 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, | |||
960 | return -EFBIG; | 977 | return -EFBIG; |
961 | 978 | ||
962 | /* Check the period and figure out the starting frame number */ | 979 | /* Check the period and figure out the starting frame number */ |
963 | uhci_get_current_frame_number(uhci); | ||
964 | if (qh->period == 0) { | 980 | if (qh->period == 0) { |
965 | if (urb->transfer_flags & URB_ISO_ASAP) { | 981 | if (urb->transfer_flags & URB_ISO_ASAP) { |
982 | uhci_get_current_frame_number(uhci); | ||
966 | urb->start_frame = uhci->frame_number + 10; | 983 | urb->start_frame = uhci->frame_number + 10; |
967 | } else { | 984 | } else { |
968 | i = urb->start_frame - uhci->frame_number; | 985 | i = urb->start_frame - uhci->last_iso_frame; |
969 | if (i <= 0 || i >= UHCI_NUMFRAMES) | 986 | if (i <= 0 || i >= UHCI_NUMFRAMES) |
970 | return -EINVAL; | 987 | return -EINVAL; |
971 | } | 988 | } |
@@ -974,7 +991,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, | |||
974 | 991 | ||
975 | } else { /* Pick up where the last URB leaves off */ | 992 | } else { /* Pick up where the last URB leaves off */ |
976 | if (list_empty(&qh->queue)) { | 993 | if (list_empty(&qh->queue)) { |
977 | frame = uhci->frame_number + 10; | 994 | frame = qh->iso_frame; |
978 | } else { | 995 | } else { |
979 | struct urb *lurb; | 996 | struct urb *lurb; |
980 | 997 | ||
@@ -986,11 +1003,12 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, | |||
986 | } | 1003 | } |
987 | if (urb->transfer_flags & URB_ISO_ASAP) | 1004 | if (urb->transfer_flags & URB_ISO_ASAP) |
988 | urb->start_frame = frame; | 1005 | urb->start_frame = frame; |
989 | /* FIXME: Sanity check */ | 1006 | else if (urb->start_frame != frame) |
1007 | return -EINVAL; | ||
990 | } | 1008 | } |
991 | 1009 | ||
992 | /* Make sure we won't have to go too far into the future */ | 1010 | /* Make sure we won't have to go too far into the future */ |
993 | if (uhci_frame_before_eq(uhci->frame_number + UHCI_NUMFRAMES, | 1011 | if (uhci_frame_before_eq(uhci->last_iso_frame + UHCI_NUMFRAMES, |
994 | urb->start_frame + urb->number_of_packets * | 1012 | urb->start_frame + urb->number_of_packets * |
995 | urb->interval)) | 1013 | urb->interval)) |
996 | return -EFBIG; | 1014 | return -EFBIG; |
@@ -1020,7 +1038,13 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, | |||
1020 | frame = urb->start_frame; | 1038 | frame = urb->start_frame; |
1021 | list_for_each_entry(td, &urbp->td_list, list) { | 1039 | list_for_each_entry(td, &urbp->td_list, list) { |
1022 | uhci_insert_td_in_frame_list(uhci, td, frame); | 1040 | uhci_insert_td_in_frame_list(uhci, td, frame); |
1023 | frame += urb->interval; | 1041 | frame += qh->period; |
1042 | } | ||
1043 | |||
1044 | if (list_empty(&qh->queue)) { | ||
1045 | qh->iso_packet_desc = &urb->iso_frame_desc[0]; | ||
1046 | qh->iso_frame = urb->start_frame; | ||
1047 | qh->iso_status = 0; | ||
1024 | } | 1048 | } |
1025 | 1049 | ||
1026 | return 0; | 1050 | return 0; |
@@ -1028,37 +1052,44 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, | |||
1028 | 1052 | ||
1029 | static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) | 1053 | static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) |
1030 | { | 1054 | { |
1031 | struct uhci_td *td; | 1055 | struct uhci_td *td, *tmp; |
1032 | struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; | 1056 | struct urb_priv *urbp = urb->hcpriv; |
1033 | int status; | 1057 | struct uhci_qh *qh = urbp->qh; |
1034 | int i, ret = 0; | ||
1035 | |||
1036 | urb->actual_length = urb->error_count = 0; | ||
1037 | 1058 | ||
1038 | i = 0; | 1059 | list_for_each_entry_safe(td, tmp, &urbp->td_list, list) { |
1039 | list_for_each_entry(td, &urbp->td_list, list) { | 1060 | unsigned int ctrlstat; |
1061 | int status; | ||
1040 | int actlength; | 1062 | int actlength; |
1041 | unsigned int ctrlstat = td_status(td); | ||
1042 | 1063 | ||
1043 | if (ctrlstat & TD_CTRL_ACTIVE) | 1064 | if (uhci_frame_before_eq(uhci->cur_iso_frame, qh->iso_frame)) |
1044 | return -EINPROGRESS; | 1065 | return -EINPROGRESS; |
1045 | 1066 | ||
1046 | actlength = uhci_actual_length(ctrlstat); | 1067 | uhci_remove_tds_from_frame(uhci, qh->iso_frame); |
1047 | urb->iso_frame_desc[i].actual_length = actlength; | 1068 | |
1048 | urb->actual_length += actlength; | 1069 | ctrlstat = td_status(td); |
1070 | if (ctrlstat & TD_CTRL_ACTIVE) { | ||
1071 | status = -EXDEV; /* TD was added too late? */ | ||
1072 | } else { | ||
1073 | status = uhci_map_status(uhci_status_bits(ctrlstat), | ||
1074 | usb_pipeout(urb->pipe)); | ||
1075 | actlength = uhci_actual_length(ctrlstat); | ||
1076 | |||
1077 | urb->actual_length += actlength; | ||
1078 | qh->iso_packet_desc->actual_length = actlength; | ||
1079 | qh->iso_packet_desc->status = status; | ||
1080 | } | ||
1049 | 1081 | ||
1050 | status = uhci_map_status(uhci_status_bits(ctrlstat), | ||
1051 | usb_pipeout(urb->pipe)); | ||
1052 | urb->iso_frame_desc[i].status = status; | ||
1053 | if (status) { | 1082 | if (status) { |
1054 | urb->error_count++; | 1083 | urb->error_count++; |
1055 | ret = status; | 1084 | qh->iso_status = status; |
1056 | } | 1085 | } |
1057 | 1086 | ||
1058 | i++; | 1087 | uhci_remove_td_from_urbp(td); |
1088 | uhci_free_td(uhci, td); | ||
1089 | qh->iso_frame += qh->period; | ||
1090 | ++qh->iso_packet_desc; | ||
1059 | } | 1091 | } |
1060 | 1092 | return qh->iso_status; | |
1061 | return ret; | ||
1062 | } | 1093 | } |
1063 | 1094 | ||
1064 | static int uhci_urb_enqueue(struct usb_hcd *hcd, | 1095 | static int uhci_urb_enqueue(struct usb_hcd *hcd, |
@@ -1119,6 +1150,7 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd, | |||
1119 | } | 1150 | } |
1120 | break; | 1151 | break; |
1121 | case USB_ENDPOINT_XFER_ISOC: | 1152 | case USB_ENDPOINT_XFER_ISOC: |
1153 | urb->error_count = 0; | ||
1122 | bustime = usb_check_bandwidth(urb->dev, urb); | 1154 | bustime = usb_check_bandwidth(urb->dev, urb); |
1123 | if (bustime < 0) { | 1155 | if (bustime < 0) { |
1124 | ret = bustime; | 1156 | ret = bustime; |
@@ -1200,9 +1232,18 @@ __acquires(uhci->lock) | |||
1200 | { | 1232 | { |
1201 | struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; | 1233 | struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; |
1202 | 1234 | ||
1203 | /* Isochronous TDs get unlinked directly from the frame list */ | 1235 | /* When giving back the first URB in an Isochronous queue, |
1204 | if (qh->type == USB_ENDPOINT_XFER_ISOC) | 1236 | * reinitialize the QH's iso-related members for the next URB. */ |
1205 | uhci_unlink_isochronous_tds(uhci, urb); | 1237 | if (qh->type == USB_ENDPOINT_XFER_ISOC && |
1238 | urbp->node.prev == &qh->queue && | ||
1239 | urbp->node.next != &qh->queue) { | ||
1240 | struct urb *nurb = list_entry(urbp->node.next, | ||
1241 | struct urb_priv, node)->urb; | ||
1242 | |||
1243 | qh->iso_packet_desc = &nurb->iso_frame_desc[0]; | ||
1244 | qh->iso_frame = nurb->start_frame; | ||
1245 | qh->iso_status = 0; | ||
1246 | } | ||
1206 | 1247 | ||
1207 | /* Take the URB off the QH's queue. If the queue is now empty, | 1248 | /* Take the URB off the QH's queue. If the queue is now empty, |
1208 | * this is a perfect time for a toggle fixup. */ | 1249 | * this is a perfect time for a toggle fixup. */ |
@@ -1434,6 +1475,7 @@ rescan: | |||
1434 | 1475 | ||
1435 | uhci_clear_next_interrupt(uhci); | 1476 | uhci_clear_next_interrupt(uhci); |
1436 | uhci_get_current_frame_number(uhci); | 1477 | uhci_get_current_frame_number(uhci); |
1478 | uhci->cur_iso_frame = uhci->frame_number; | ||
1437 | 1479 | ||
1438 | /* Go through all the QH queues and process the URBs in each one */ | 1480 | /* Go through all the QH queues and process the URBs in each one */ |
1439 | for (i = 0; i < UHCI_NUM_SKELQH - 1; ++i) { | 1481 | for (i = 0; i < UHCI_NUM_SKELQH - 1; ++i) { |
@@ -1451,6 +1493,7 @@ rescan: | |||
1451 | } | 1493 | } |
1452 | } | 1494 | } |
1453 | 1495 | ||
1496 | uhci->last_iso_frame = uhci->cur_iso_frame; | ||
1454 | if (uhci->need_rescan) | 1497 | if (uhci->need_rescan) |
1455 | goto rescan; | 1498 | goto rescan; |
1456 | uhci->scan_in_progress = 0; | 1499 | uhci->scan_in_progress = 0; |