[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:
Alan Stern 2005-10-13 17:00:24 -04:00 committed by Greg Kroah-Hartman
parent 0c0382e32d
commit b81d34363c
1 changed files with 26 additions and 13 deletions

View File

@ -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 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; return;
}
if (td->frame != -1 && uhci->frame_cpu[td->frame] == td) { if (uhci->frame_cpu[td->frame] == td) {
if (list_empty(&td->fl_list)) { if (list_empty(&td->fl_list)) {
uhci->frame[td->frame] = td->link; uhci->frame[td->frame] = td->link;
uhci->frame_cpu[td->frame] = NULL; 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; ptd->link = td->link;
} }
wmb();
td->link = UHCI_PTR_TERM;
list_del_init(&td->fl_list); list_del_init(&td->fl_list);
td->frame = -1; 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. * 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) { list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
uhci_remove_td_from_urb(td); uhci_remove_td_from_urb(td);
uhci_remove_td(uhci, td);
list_add(&td->remove_list, &uhci->td_remove_list); 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; struct uhci_td *td;
int i, ret, frame; int i, ret, frame;
int status, destination; int status, destination;
struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
status = TD_CTRL_ACTIVE | TD_CTRL_IOS; status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); 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) if (ret)
return ret; return ret;
frame = urb->start_frame; for (i = 0; i < urb->number_of_packets; i++) {
for (i = 0; i < urb->number_of_packets; i++, frame += urb->interval) {
if (!urb->iso_frame_desc[i].length)
continue;
td = uhci_alloc_td(uhci); td = uhci_alloc_td(uhci);
if (!td) if (!td)
return -ENOMEM; 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) if (i + 1 >= urb->number_of_packets)
td->status |= cpu_to_le32(TD_CTRL_IOC); 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); uhci_insert_td_frame_list(uhci, td, frame);
frame += urb->interval;
} }
return -EINPROGRESS; return -EINPROGRESS;
@ -1105,7 +1115,7 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
int status; int status;
int i, ret = 0; int i, ret = 0;
urb->actual_length = 0; urb->actual_length = urb->error_count = 0;
i = 0; i = 0;
list_for_each_entry(td, &urbp->td_list, list) { 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++; i++;
} }
unlink_isochronous_tds(uhci, urb);
return ret; return ret;
} }
@ -1361,6 +1372,8 @@ static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
goto done; goto done;
list_del_init(&urbp->urb_list); list_del_init(&urbp->urb_list);
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
unlink_isochronous_tds(uhci, urb);
uhci_unlink_generic(uhci, urb); uhci_unlink_generic(uhci, urb);
uhci_get_current_frame_number(uhci); uhci_get_current_frame_number(uhci);