Commit c8155cc5d839838f8425dbea568fc537337176a7
Committed by
Greg Kroah-Hartman
1 parent
caf3827a65
Exists in
master
and in
7 other branches
[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>
Showing 3 changed files with 90 additions and 37 deletions Side-by-side Diff
drivers/usb/host/uhci-debug.c
... | ... | @@ -127,7 +127,8 @@ |
127 | 127 | |
128 | 128 | i = nactive = ninactive = 0; |
129 | 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 | 132 | out += sprintf(out, "%*s%d: ", space + 2, "", i); |
132 | 133 | out += uhci_show_td(td, out, len - (out - buf), 0); |
133 | 134 | } else { |
... | ... | @@ -168,8 +169,9 @@ |
168 | 169 | space, "", qh, qtype, |
169 | 170 | le32_to_cpu(qh->link), le32_to_cpu(element)); |
170 | 171 | if (qh->type == USB_ENDPOINT_XFER_ISOC) |
171 | - out += sprintf(out, "%*s period %d\n", | |
172 | - space, "", qh->period); | |
172 | + out += sprintf(out, "%*s period %d frame %x desc [%p]\n", | |
173 | + space, "", qh->period, qh->iso_frame, | |
174 | + qh->iso_packet_desc); | |
173 | 175 | |
174 | 176 | if (element & UHCI_PTR_QH) |
175 | 177 | out += sprintf(out, "%*s Element points to QH (bug?)\n", space, ""); |
... | ... | @@ -331,8 +333,10 @@ |
331 | 333 | out += sprintf(out, " sof = %02x\n", sof); |
332 | 334 | out += uhci_show_sc(1, portsc1, out, len - (out - buf)); |
333 | 335 | out += uhci_show_sc(2, portsc2, out, len - (out - buf)); |
334 | - out += sprintf(out, "Most recent frame: %x\n", | |
335 | - uhci->frame_number); | |
336 | + out += sprintf(out, "Most recent frame: %x (%d) " | |
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 | 341 | return out - buf; |
338 | 342 | } |
drivers/usb/host/uhci-hcd.h
... | ... | @@ -128,8 +128,6 @@ |
128 | 128 | __le32 element; /* Queue element (TD) pointer */ |
129 | 129 | |
130 | 130 | /* Software fields */ |
131 | - dma_addr_t dma_handle; | |
132 | - | |
133 | 131 | struct list_head node; /* Node in the list of QHs */ |
134 | 132 | struct usb_host_endpoint *hep; /* Endpoint information */ |
135 | 133 | struct usb_device *udev; |
136 | 134 | |
137 | 135 | |
... | ... | @@ -138,13 +136,19 @@ |
138 | 136 | struct uhci_td *dummy_td; /* Dummy TD to end the queue */ |
139 | 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 | 141 | unsigned long advance_jiffies; /* Time of last queue advance */ |
142 | 142 | unsigned int unlink_frame; /* When the QH was unlinked */ |
143 | 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 | 147 | int state; /* QH_STATE_xxx; see above */ |
146 | 148 | int type; /* Queue type (control, bulk, etc) */ |
147 | 149 | |
150 | + dma_addr_t dma_handle; | |
151 | + | |
148 | 152 | unsigned int initial_toggle:1; /* Endpoint's current toggle value */ |
149 | 153 | unsigned int needs_fixup:1; /* Must fix the TD toggle values */ |
150 | 154 | unsigned int is_stopped:1; /* Queue was stopped by error/unlink */ |
... | ... | @@ -386,6 +390,8 @@ |
386 | 390 | unsigned int frame_number; /* As of last check */ |
387 | 391 | unsigned int is_stopped; |
388 | 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 | 396 | unsigned int scan_in_progress:1; /* Schedule scan is running */ |
391 | 397 | unsigned int need_rescan:1; /* Redo the schedule scan */ |
drivers/usb/host/uhci-q.c
... | ... | @@ -184,6 +184,24 @@ |
184 | 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 | 206 | * Remove all the TDs for an Isochronous URB from the frame list |
189 | 207 | */ |
... | ... | @@ -523,7 +541,6 @@ |
523 | 541 | return -ENOSR; |
524 | 542 | if (status & TD_CTRL_STALLED) /* Stalled */ |
525 | 543 | return -EPIPE; |
526 | - WARN_ON(status & TD_CTRL_ACTIVE); /* Active */ | |
527 | 544 | return 0; |
528 | 545 | } |
529 | 546 | |
530 | 547 | |
531 | 548 | |
... | ... | @@ -960,12 +977,12 @@ |
960 | 977 | return -EFBIG; |
961 | 978 | |
962 | 979 | /* Check the period and figure out the starting frame number */ |
963 | - uhci_get_current_frame_number(uhci); | |
964 | 980 | if (qh->period == 0) { |
965 | 981 | if (urb->transfer_flags & URB_ISO_ASAP) { |
982 | + uhci_get_current_frame_number(uhci); | |
966 | 983 | urb->start_frame = uhci->frame_number + 10; |
967 | 984 | } else { |
968 | - i = urb->start_frame - uhci->frame_number; | |
985 | + i = urb->start_frame - uhci->last_iso_frame; | |
969 | 986 | if (i <= 0 || i >= UHCI_NUMFRAMES) |
970 | 987 | return -EINVAL; |
971 | 988 | } |
... | ... | @@ -974,7 +991,7 @@ |
974 | 991 | |
975 | 992 | } else { /* Pick up where the last URB leaves off */ |
976 | 993 | if (list_empty(&qh->queue)) { |
977 | - frame = uhci->frame_number + 10; | |
994 | + frame = qh->iso_frame; | |
978 | 995 | } else { |
979 | 996 | struct urb *lurb; |
980 | 997 | |
981 | 998 | |
... | ... | @@ -986,11 +1003,12 @@ |
986 | 1003 | } |
987 | 1004 | if (urb->transfer_flags & URB_ISO_ASAP) |
988 | 1005 | urb->start_frame = frame; |
989 | - /* FIXME: Sanity check */ | |
1006 | + else if (urb->start_frame != frame) | |
1007 | + return -EINVAL; | |
990 | 1008 | } |
991 | 1009 | |
992 | 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 | 1012 | urb->start_frame + urb->number_of_packets * |
995 | 1013 | urb->interval)) |
996 | 1014 | return -EFBIG; |
997 | 1015 | |
998 | 1016 | |
999 | 1017 | |
1000 | 1018 | |
1001 | 1019 | |
1002 | 1020 | |
1003 | 1021 | |
1004 | 1022 | |
1005 | 1023 | |
1006 | 1024 | |
... | ... | @@ -1020,45 +1038,58 @@ |
1020 | 1038 | frame = urb->start_frame; |
1021 | 1039 | list_for_each_entry(td, &urbp->td_list, list) { |
1022 | 1040 | uhci_insert_td_in_frame_list(uhci, td, frame); |
1023 | - frame += urb->interval; | |
1041 | + frame += qh->period; | |
1024 | 1042 | } |
1025 | 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; | |
1048 | + } | |
1049 | + | |
1026 | 1050 | return 0; |
1027 | 1051 | } |
1028 | 1052 | |
1029 | 1053 | static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) |
1030 | 1054 | { |
1031 | - struct uhci_td *td; | |
1032 | - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; | |
1033 | - int status; | |
1034 | - int i, ret = 0; | |
1055 | + struct uhci_td *td, *tmp; | |
1056 | + struct urb_priv *urbp = urb->hcpriv; | |
1057 | + struct uhci_qh *qh = urbp->qh; | |
1035 | 1058 | |
1036 | - urb->actual_length = urb->error_count = 0; | |
1037 | - | |
1038 | - i = 0; | |
1039 | - list_for_each_entry(td, &urbp->td_list, list) { | |
1059 | + list_for_each_entry_safe(td, tmp, &urbp->td_list, list) { | |
1060 | + unsigned int ctrlstat; | |
1061 | + int status; | |
1040 | 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 | 1065 | return -EINPROGRESS; |
1045 | 1066 | |
1046 | - actlength = uhci_actual_length(ctrlstat); | |
1047 | - urb->iso_frame_desc[i].actual_length = actlength; | |
1048 | - urb->actual_length += actlength; | |
1067 | + uhci_remove_tds_from_frame(uhci, qh->iso_frame); | |
1049 | 1068 | |
1050 | - status = uhci_map_status(uhci_status_bits(ctrlstat), | |
1051 | - usb_pipeout(urb->pipe)); | |
1052 | - urb->iso_frame_desc[i].status = status; | |
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 | + } | |
1081 | + | |
1053 | 1082 | if (status) { |
1054 | 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 | - | |
1061 | - return ret; | |
1092 | + return qh->iso_status; | |
1062 | 1093 | } |
1063 | 1094 | |
1064 | 1095 | static int uhci_urb_enqueue(struct usb_hcd *hcd, |
... | ... | @@ -1119,6 +1150,7 @@ |
1119 | 1150 | } |
1120 | 1151 | break; |
1121 | 1152 | case USB_ENDPOINT_XFER_ISOC: |
1153 | + urb->error_count = 0; | |
1122 | 1154 | bustime = usb_check_bandwidth(urb->dev, urb); |
1123 | 1155 | if (bustime < 0) { |
1124 | 1156 | ret = bustime; |
1125 | 1157 | |
... | ... | @@ -1200,10 +1232,19 @@ |
1200 | 1232 | { |
1201 | 1233 | struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; |
1202 | 1234 | |
1203 | - /* Isochronous TDs get unlinked directly from the frame list */ | |
1204 | - if (qh->type == USB_ENDPOINT_XFER_ISOC) | |
1205 | - uhci_unlink_isochronous_tds(uhci, urb); | |
1235 | + /* When giving back the first URB in an Isochronous queue, | |
1236 | + * reinitialize the QH's iso-related members for the next 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; | |
1206 | 1242 | |
1243 | + qh->iso_packet_desc = &nurb->iso_frame_desc[0]; | |
1244 | + qh->iso_frame = nurb->start_frame; | |
1245 | + qh->iso_status = 0; | |
1246 | + } | |
1247 | + | |
1207 | 1248 | /* Take the URB off the QH's queue. If the queue is now empty, |
1208 | 1249 | * this is a perfect time for a toggle fixup. */ |
1209 | 1250 | list_del_init(&urbp->node); |
... | ... | @@ -1434,6 +1475,7 @@ |
1434 | 1475 | |
1435 | 1476 | uhci_clear_next_interrupt(uhci); |
1436 | 1477 | uhci_get_current_frame_number(uhci); |
1478 | + uhci->cur_iso_frame = uhci->frame_number; | |
1437 | 1479 | |
1438 | 1480 | /* Go through all the QH queues and process the URBs in each one */ |
1439 | 1481 | for (i = 0; i < UHCI_NUM_SKELQH - 1; ++i) { |
... | ... | @@ -1451,6 +1493,7 @@ |
1451 | 1493 | } |
1452 | 1494 | } |
1453 | 1495 | |
1496 | + uhci->last_iso_frame = uhci->cur_iso_frame; | |
1454 | 1497 | if (uhci->need_rescan) |
1455 | 1498 | goto rescan; |
1456 | 1499 | uhci->scan_in_progress = 0; |