[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>
This commit is contained in:
parent
0c0382e32d
commit
b81d34363c
|
@ -108,13 +108,16 @@ static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td,
|
|||
}
|
||||
}
|
||||
|
||||
static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td)
|
||||
static inline void uhci_remove_td_frame_list(struct uhci_hcd *uhci,
|
||||
struct uhci_td *td)
|
||||
{
|
||||
/* If it's not inserted, don't remove it */
|
||||
if (td->frame == -1 && list_empty(&td->fl_list))
|
||||
if (td->frame == -1) {
|
||||
WARN_ON(!list_empty(&td->fl_list));
|
||||
return;
|
||||
}
|
||||
|
||||
if (td->frame != -1 && uhci->frame_cpu[td->frame] == td) {
|
||||
if (uhci->frame_cpu[td->frame] == td) {
|
||||
if (list_empty(&td->fl_list)) {
|
||||
uhci->frame[td->frame] = td->link;
|
||||
uhci->frame_cpu[td->frame] = NULL;
|
||||
|
@ -132,13 +135,20 @@ static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td)
|
|||
ptd->link = td->link;
|
||||
}
|
||||
|
||||
wmb();
|
||||
td->link = UHCI_PTR_TERM;
|
||||
|
||||
list_del_init(&td->fl_list);
|
||||
td->frame = -1;
|
||||
}
|
||||
|
||||
static void unlink_isochronous_tds(struct uhci_hcd *uhci, struct urb *urb)
|
||||
{
|
||||
struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
|
||||
struct uhci_td *td;
|
||||
|
||||
list_for_each_entry(td, &urbp->td_list, list)
|
||||
uhci_remove_td_frame_list(uhci, td);
|
||||
wmb();
|
||||
}
|
||||
|
||||
/*
|
||||
* Inserts a td list into qh.
|
||||
*/
|
||||
|
@ -498,7 +508,6 @@ static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
|
|||
|
||||
list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
|
||||
uhci_remove_td_from_urb(td);
|
||||
uhci_remove_td(uhci, td);
|
||||
list_add(&td->remove_list, &uhci->td_remove_list);
|
||||
}
|
||||
|
||||
|
@ -1068,6 +1077,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
|
|||
struct uhci_td *td;
|
||||
int i, ret, frame;
|
||||
int status, destination;
|
||||
struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
|
||||
|
||||
status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
|
||||
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)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
frame = urb->start_frame;
|
||||
for (i = 0; i < urb->number_of_packets; i++, frame += urb->interval) {
|
||||
if (!urb->iso_frame_desc[i].length)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < urb->number_of_packets; i++) {
|
||||
td = uhci_alloc_td(uhci);
|
||||
if (!td)
|
||||
return -ENOMEM;
|
||||
|
@ -1091,8 +1097,12 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
|
|||
|
||||
if (i + 1 >= urb->number_of_packets)
|
||||
td->status |= cpu_to_le32(TD_CTRL_IOC);
|
||||
}
|
||||
|
||||
frame = urb->start_frame;
|
||||
list_for_each_entry(td, &urbp->td_list, list) {
|
||||
uhci_insert_td_frame_list(uhci, td, frame);
|
||||
frame += urb->interval;
|
||||
}
|
||||
|
||||
return -EINPROGRESS;
|
||||
|
@ -1105,7 +1115,7 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
|
|||
int status;
|
||||
int i, ret = 0;
|
||||
|
||||
urb->actual_length = 0;
|
||||
urb->actual_length = urb->error_count = 0;
|
||||
|
||||
i = 0;
|
||||
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)
|
|||
|
||||
i++;
|
||||
}
|
||||
unlink_isochronous_tds(uhci, urb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1361,6 +1372,8 @@ static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
|
|||
goto done;
|
||||
list_del_init(&urbp->urb_list);
|
||||
|
||||
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
|
||||
unlink_isochronous_tds(uhci, urb);
|
||||
uhci_unlink_generic(uhci, urb);
|
||||
|
||||
uhci_get_current_frame_number(uhci);
|
||||
|
|
Loading…
Reference in New Issue