diff options
| -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; |
