diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2005-10-13 17:00:24 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-10-28 19:47:44 -0400 |
commit | b81d34363c0b17c47f4ef63d5888c4f47f315d29 (patch) | |
tree | af4151e7d296da8f9e9a493ed81c5513f2454618 /drivers/usb/host/uhci-q.c | |
parent | 0c0382e32d46f606951010b202382be14d180a17 (diff) |
[PATCH] UHCI: Improve handling of iso TDs
The uhci-hcd driver is fairly lax about the way it handles isochronous
transfers. This patch (as579) improves it in three respects:
TDs for a new URB aren't added to the schedule until all of
them have been allocated. This way there's no risk of the
controller executing some of them when an allocation fails.
TDs for an unlinked URB are removed from the schedule as soon
as the URB is unlinked, rather than waiting until the URB is
given back. This way there's no risk of the controller still
executing a TD after the URB completes.
The urb->error_count values are now reported correctly.
Although since they aren't used in any drivers except for
debug messages in the system log, probably nobody cares.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/uhci-q.c')
-rw-r--r-- | drivers/usb/host/uhci-q.c | 39 |
1 files changed, 26 insertions, 13 deletions
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 51de06bc438d..7e46887d9e12 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c | |||
@@ -108,13 +108,16 @@ static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, | |||
108 | } | 108 | } |
109 | } | 109 | } |
110 | 110 | ||
111 | static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td) | 111 | static inline void uhci_remove_td_frame_list(struct uhci_hcd *uhci, |
112 | struct uhci_td *td) | ||
112 | { | 113 | { |
113 | /* If it's not inserted, don't remove it */ | 114 | /* If it's not inserted, don't remove it */ |
114 | if (td->frame == -1 && list_empty(&td->fl_list)) | 115 | if (td->frame == -1) { |
116 | WARN_ON(!list_empty(&td->fl_list)); | ||
115 | return; | 117 | return; |
118 | } | ||
116 | 119 | ||
117 | if (td->frame != -1 && uhci->frame_cpu[td->frame] == td) { | 120 | if (uhci->frame_cpu[td->frame] == td) { |
118 | if (list_empty(&td->fl_list)) { | 121 | if (list_empty(&td->fl_list)) { |
119 | uhci->frame[td->frame] = td->link; | 122 | uhci->frame[td->frame] = td->link; |
120 | uhci->frame_cpu[td->frame] = NULL; | 123 | uhci->frame_cpu[td->frame] = NULL; |
@@ -132,13 +135,20 @@ static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td) | |||
132 | ptd->link = td->link; | 135 | ptd->link = td->link; |
133 | } | 136 | } |
134 | 137 | ||
135 | wmb(); | ||
136 | td->link = UHCI_PTR_TERM; | ||
137 | |||
138 | list_del_init(&td->fl_list); | 138 | list_del_init(&td->fl_list); |
139 | td->frame = -1; | 139 | td->frame = -1; |
140 | } | 140 | } |
141 | 141 | ||
142 | static void unlink_isochronous_tds(struct uhci_hcd *uhci, struct urb *urb) | ||
143 | { | ||
144 | struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; | ||
145 | struct uhci_td *td; | ||
146 | |||
147 | list_for_each_entry(td, &urbp->td_list, list) | ||
148 | uhci_remove_td_frame_list(uhci, td); | ||
149 | wmb(); | ||
150 | } | ||
151 | |||
142 | /* | 152 | /* |
143 | * Inserts a td list into qh. | 153 | * Inserts a td list into qh. |
144 | */ | 154 | */ |
@@ -498,7 +508,6 @@ static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb) | |||
498 | 508 | ||
499 | list_for_each_entry_safe(td, tmp, &urbp->td_list, list) { | 509 | list_for_each_entry_safe(td, tmp, &urbp->td_list, list) { |
500 | uhci_remove_td_from_urb(td); | 510 | uhci_remove_td_from_urb(td); |
501 | uhci_remove_td(uhci, td); | ||
502 | list_add(&td->remove_list, &uhci->td_remove_list); | 511 | list_add(&td->remove_list, &uhci->td_remove_list); |
503 | } | 512 | } |
504 | 513 | ||
@@ -1068,6 +1077,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb) | |||
1068 | struct uhci_td *td; | 1077 | struct uhci_td *td; |
1069 | int i, ret, frame; | 1078 | int i, ret, frame; |
1070 | int status, destination; | 1079 | int status, destination; |
1080 | struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; | ||
1071 | 1081 | ||
1072 | status = TD_CTRL_ACTIVE | TD_CTRL_IOS; | 1082 | status = TD_CTRL_ACTIVE | TD_CTRL_IOS; |
1073 | destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); | 1083 | destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); |
@@ -1076,11 +1086,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb) | |||
1076 | if (ret) | 1086 | if (ret) |
1077 | return ret; | 1087 | return ret; |
1078 | 1088 | ||
1079 | frame = urb->start_frame; | 1089 | for (i = 0; i < urb->number_of_packets; i++) { |
1080 | for (i = 0; i < urb->number_of_packets; i++, frame += urb->interval) { | ||
1081 | if (!urb->iso_frame_desc[i].length) | ||
1082 | continue; | ||
1083 | |||
1084 | td = uhci_alloc_td(uhci); | 1090 | td = uhci_alloc_td(uhci); |
1085 | if (!td) | 1091 | if (!td) |
1086 | return -ENOMEM; | 1092 | return -ENOMEM; |
@@ -1091,8 +1097,12 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb) | |||
1091 | 1097 | ||
1092 | if (i + 1 >= urb->number_of_packets) | 1098 | if (i + 1 >= urb->number_of_packets) |
1093 | td->status |= cpu_to_le32(TD_CTRL_IOC); | 1099 | td->status |= cpu_to_le32(TD_CTRL_IOC); |
1100 | } | ||
1094 | 1101 | ||
1102 | frame = urb->start_frame; | ||
1103 | list_for_each_entry(td, &urbp->td_list, list) { | ||
1095 | uhci_insert_td_frame_list(uhci, td, frame); | 1104 | uhci_insert_td_frame_list(uhci, td, frame); |
1105 | frame += urb->interval; | ||
1096 | } | 1106 | } |
1097 | 1107 | ||
1098 | return -EINPROGRESS; | 1108 | return -EINPROGRESS; |
@@ -1105,7 +1115,7 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) | |||
1105 | int status; | 1115 | int status; |
1106 | int i, ret = 0; | 1116 | int i, ret = 0; |
1107 | 1117 | ||
1108 | urb->actual_length = 0; | 1118 | urb->actual_length = urb->error_count = 0; |
1109 | 1119 | ||
1110 | i = 0; | 1120 | i = 0; |
1111 | list_for_each_entry(td, &urbp->td_list, list) { | 1121 | list_for_each_entry(td, &urbp->td_list, list) { |
@@ -1129,6 +1139,7 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) | |||
1129 | 1139 | ||
1130 | i++; | 1140 | i++; |
1131 | } | 1141 | } |
1142 | unlink_isochronous_tds(uhci, urb); | ||
1132 | 1143 | ||
1133 | return ret; | 1144 | return ret; |
1134 | } | 1145 | } |
@@ -1361,6 +1372,8 @@ static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) | |||
1361 | goto done; | 1372 | goto done; |
1362 | list_del_init(&urbp->urb_list); | 1373 | list_del_init(&urbp->urb_list); |
1363 | 1374 | ||
1375 | if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) | ||
1376 | unlink_isochronous_tds(uhci, urb); | ||
1364 | uhci_unlink_generic(uhci, urb); | 1377 | uhci_unlink_generic(uhci, urb); |
1365 | 1378 | ||
1366 | uhci_get_current_frame_number(uhci); | 1379 | uhci_get_current_frame_number(uhci); |