diff options
Diffstat (limited to 'drivers/usb/host/uhci-q.c')
-rw-r--r-- | drivers/usb/host/uhci-q.c | 103 |
1 files changed, 73 insertions, 30 deletions
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; |