Commit c8155cc5 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

[PATCH] UHCI: remove ISO TDs as they are used

This patch (as690) does the same thing for ISO TDs as as680 did for
non-ISO TDs: free them as they are used rather than all at once when an
URB is complete.  At the same time it fixes a minor buglet (I'm not
aware of it ever affecting anyone): An ISO TD should be retired when its
frame is over, regardless of whether or not the hardware has marked it
inactive.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent caf3827a
...@@ -127,7 +127,8 @@ static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space) ...@@ -127,7 +127,8 @@ static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space)
i = nactive = ninactive = 0; i = nactive = ninactive = 0;
list_for_each_entry(td, &urbp->td_list, list) { list_for_each_entry(td, &urbp->td_list, list) {
if (++i <= 10 || debug > 2) { if (urbp->qh->type != USB_ENDPOINT_XFER_ISOC &&
(++i <= 10 || debug > 2)) {
out += sprintf(out, "%*s%d: ", space + 2, "", i); out += sprintf(out, "%*s%d: ", space + 2, "", i);
out += uhci_show_td(td, out, len - (out - buf), 0); out += uhci_show_td(td, out, len - (out - buf), 0);
} else { } else {
...@@ -168,8 +169,9 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) ...@@ -168,8 +169,9 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
space, "", qh, qtype, space, "", qh, qtype,
le32_to_cpu(qh->link), le32_to_cpu(element)); le32_to_cpu(qh->link), le32_to_cpu(element));
if (qh->type == USB_ENDPOINT_XFER_ISOC) if (qh->type == USB_ENDPOINT_XFER_ISOC)
out += sprintf(out, "%*s period %d\n", out += sprintf(out, "%*s period %d frame %x desc [%p]\n",
space, "", qh->period); space, "", qh->period, qh->iso_frame,
qh->iso_packet_desc);
if (element & UHCI_PTR_QH) if (element & UHCI_PTR_QH)
out += sprintf(out, "%*s Element points to QH (bug?)\n", space, ""); 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,8 +333,10 @@ static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len)
out += sprintf(out, " sof = %02x\n", sof); out += sprintf(out, " sof = %02x\n", sof);
out += uhci_show_sc(1, portsc1, out, len - (out - buf)); out += uhci_show_sc(1, portsc1, out, len - (out - buf));
out += uhci_show_sc(2, portsc2, out, len - (out - buf)); out += uhci_show_sc(2, portsc2, out, len - (out - buf));
out += sprintf(out, "Most recent frame: %x\n", out += sprintf(out, "Most recent frame: %x (%d) "
uhci->frame_number); "Last ISO frame: %x (%d)\n",
uhci->frame_number, uhci->frame_number & 1023,
uhci->last_iso_frame, uhci->last_iso_frame & 1023);
return out - buf; return out - buf;
} }
......
...@@ -128,8 +128,6 @@ struct uhci_qh { ...@@ -128,8 +128,6 @@ struct uhci_qh {
__le32 element; /* Queue element (TD) pointer */ __le32 element; /* Queue element (TD) pointer */
/* Software fields */ /* Software fields */
dma_addr_t dma_handle;
struct list_head node; /* Node in the list of QHs */ struct list_head node; /* Node in the list of QHs */
struct usb_host_endpoint *hep; /* Endpoint information */ struct usb_host_endpoint *hep; /* Endpoint information */
struct usb_device *udev; struct usb_device *udev;
...@@ -138,13 +136,19 @@ struct uhci_qh { ...@@ -138,13 +136,19 @@ struct uhci_qh {
struct uhci_td *dummy_td; /* Dummy TD to end the queue */ struct uhci_td *dummy_td; /* Dummy TD to end the queue */
struct uhci_td *post_td; /* Last TD completed */ struct uhci_td *post_td; /* Last TD completed */
struct usb_iso_packet_descriptor *iso_packet_desc;
/* Next urb->iso_frame_desc entry */
unsigned long advance_jiffies; /* Time of last queue advance */ unsigned long advance_jiffies; /* Time of last queue advance */
unsigned int unlink_frame; /* When the QH was unlinked */ unsigned int unlink_frame; /* When the QH was unlinked */
unsigned int period; /* For Interrupt and Isochronous QHs */ unsigned int period; /* For Interrupt and Isochronous QHs */
unsigned int iso_frame; /* Frame # for iso_packet_desc */
int iso_status; /* Status for Isochronous URBs */
int state; /* QH_STATE_xxx; see above */ int state; /* QH_STATE_xxx; see above */
int type; /* Queue type (control, bulk, etc) */ int type; /* Queue type (control, bulk, etc) */
dma_addr_t dma_handle;
unsigned int initial_toggle:1; /* Endpoint's current toggle value */ unsigned int initial_toggle:1; /* Endpoint's current toggle value */
unsigned int needs_fixup:1; /* Must fix the TD toggle values */ unsigned int needs_fixup:1; /* Must fix the TD toggle values */
unsigned int is_stopped:1; /* Queue was stopped by error/unlink */ unsigned int is_stopped:1; /* Queue was stopped by error/unlink */
...@@ -386,6 +390,8 @@ struct uhci_hcd { ...@@ -386,6 +390,8 @@ struct uhci_hcd {
unsigned int frame_number; /* As of last check */ unsigned int frame_number; /* As of last check */
unsigned int is_stopped; unsigned int is_stopped;
#define UHCI_IS_STOPPED 9999 /* Larger than a frame # */ #define UHCI_IS_STOPPED 9999 /* Larger than a frame # */
unsigned int last_iso_frame; /* Frame of last scan */
unsigned int cur_iso_frame; /* Frame for current scan */
unsigned int scan_in_progress:1; /* Schedule scan is running */ unsigned int scan_in_progress:1; /* Schedule scan is running */
unsigned int need_rescan:1; /* Redo the schedule scan */ unsigned int need_rescan:1; /* Redo the schedule scan */
......
...@@ -184,6 +184,24 @@ static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci, ...@@ -184,6 +184,24 @@ static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci,
td->frame = -1; td->frame = -1;
} }
static inline void uhci_remove_tds_from_frame(struct uhci_hcd *uhci,
unsigned int framenum)
{
struct uhci_td *ftd, *ltd;
framenum &= (UHCI_NUMFRAMES - 1);
ftd = uhci->frame_cpu[framenum];
if (ftd) {
ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
uhci->frame[framenum] = ltd->link;
uhci->frame_cpu[framenum] = NULL;
while (!list_empty(&ftd->fl_list))
list_del_init(ftd->fl_list.prev);
}
}
/* /*
* Remove all the TDs for an Isochronous URB from the frame list * Remove all the TDs for an Isochronous URB from the frame list
*/ */
...@@ -523,7 +541,6 @@ static int uhci_map_status(int status, int dir_out) ...@@ -523,7 +541,6 @@ static int uhci_map_status(int status, int dir_out)
return -ENOSR; return -ENOSR;
if (status & TD_CTRL_STALLED) /* Stalled */ if (status & TD_CTRL_STALLED) /* Stalled */
return -EPIPE; return -EPIPE;
WARN_ON(status & TD_CTRL_ACTIVE); /* Active */
return 0; return 0;
} }
...@@ -960,12 +977,12 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, ...@@ -960,12 +977,12 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
return -EFBIG; return -EFBIG;
/* Check the period and figure out the starting frame number */ /* Check the period and figure out the starting frame number */
uhci_get_current_frame_number(uhci);
if (qh->period == 0) { if (qh->period == 0) {
if (urb->transfer_flags & URB_ISO_ASAP) { if (urb->transfer_flags & URB_ISO_ASAP) {
uhci_get_current_frame_number(uhci);
urb->start_frame = uhci->frame_number + 10; urb->start_frame = uhci->frame_number + 10;
} else { } else {
i = urb->start_frame - uhci->frame_number; i = urb->start_frame - uhci->last_iso_frame;
if (i <= 0 || i >= UHCI_NUMFRAMES) if (i <= 0 || i >= UHCI_NUMFRAMES)
return -EINVAL; return -EINVAL;
} }
...@@ -974,7 +991,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, ...@@ -974,7 +991,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
} else { /* Pick up where the last URB leaves off */ } else { /* Pick up where the last URB leaves off */
if (list_empty(&qh->queue)) { if (list_empty(&qh->queue)) {
frame = uhci->frame_number + 10; frame = qh->iso_frame;
} else { } else {
struct urb *lurb; struct urb *lurb;
...@@ -986,11 +1003,12 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, ...@@ -986,11 +1003,12 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
} }
if (urb->transfer_flags & URB_ISO_ASAP) if (urb->transfer_flags & URB_ISO_ASAP)
urb->start_frame = frame; urb->start_frame = frame;
/* FIXME: Sanity check */ else if (urb->start_frame != frame)
return -EINVAL;
} }
/* Make sure we won't have to go too far into the future */ /* Make sure we won't have to go too far into the future */
if (uhci_frame_before_eq(uhci->frame_number + UHCI_NUMFRAMES, if (uhci_frame_before_eq(uhci->last_iso_frame + UHCI_NUMFRAMES,
urb->start_frame + urb->number_of_packets * urb->start_frame + urb->number_of_packets *
urb->interval)) urb->interval))
return -EFBIG; return -EFBIG;
...@@ -1020,7 +1038,13 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, ...@@ -1020,7 +1038,13 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
frame = urb->start_frame; frame = urb->start_frame;
list_for_each_entry(td, &urbp->td_list, list) { list_for_each_entry(td, &urbp->td_list, list) {
uhci_insert_td_in_frame_list(uhci, td, frame); uhci_insert_td_in_frame_list(uhci, td, frame);
frame += urb->interval; frame += qh->period;
}
if (list_empty(&qh->queue)) {
qh->iso_packet_desc = &urb->iso_frame_desc[0];
qh->iso_frame = urb->start_frame;
qh->iso_status = 0;
} }
return 0; return 0;
...@@ -1028,37 +1052,44 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, ...@@ -1028,37 +1052,44 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
{ {
struct uhci_td *td; struct uhci_td *td, *tmp;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct urb_priv *urbp = urb->hcpriv;
int status; struct uhci_qh *qh = urbp->qh;
int i, ret = 0;
urb->actual_length = urb->error_count = 0;
i = 0; list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
list_for_each_entry(td, &urbp->td_list, list) { unsigned int ctrlstat;
int status;
int actlength; int actlength;
unsigned int ctrlstat = td_status(td);
if (ctrlstat & TD_CTRL_ACTIVE) if (uhci_frame_before_eq(uhci->cur_iso_frame, qh->iso_frame))
return -EINPROGRESS; return -EINPROGRESS;
actlength = uhci_actual_length(ctrlstat); uhci_remove_tds_from_frame(uhci, qh->iso_frame);
urb->iso_frame_desc[i].actual_length = actlength;
urb->actual_length += actlength; ctrlstat = td_status(td);
if (ctrlstat & TD_CTRL_ACTIVE) {
status = -EXDEV; /* TD was added too late? */
} else {
status = uhci_map_status(uhci_status_bits(ctrlstat),
usb_pipeout(urb->pipe));
actlength = uhci_actual_length(ctrlstat);
urb->actual_length += actlength;
qh->iso_packet_desc->actual_length = actlength;
qh->iso_packet_desc->status = status;
}
status = uhci_map_status(uhci_status_bits(ctrlstat),
usb_pipeout(urb->pipe));
urb->iso_frame_desc[i].status = status;
if (status) { if (status) {
urb->error_count++; urb->error_count++;
ret = status; qh->iso_status = status;
} }
i++; uhci_remove_td_from_urbp(td);
uhci_free_td(uhci, td);
qh->iso_frame += qh->period;
++qh->iso_packet_desc;
} }
return qh->iso_status;
return ret;
} }
static int uhci_urb_enqueue(struct usb_hcd *hcd, static int uhci_urb_enqueue(struct usb_hcd *hcd,
...@@ -1119,6 +1150,7 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd, ...@@ -1119,6 +1150,7 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd,
} }
break; break;
case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_ISOC:
urb->error_count = 0;
bustime = usb_check_bandwidth(urb->dev, urb); bustime = usb_check_bandwidth(urb->dev, urb);
if (bustime < 0) { if (bustime < 0) {
ret = bustime; ret = bustime;
...@@ -1200,9 +1232,18 @@ __acquires(uhci->lock) ...@@ -1200,9 +1232,18 @@ __acquires(uhci->lock)
{ {
struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
/* Isochronous TDs get unlinked directly from the frame list */ /* When giving back the first URB in an Isochronous queue,
if (qh->type == USB_ENDPOINT_XFER_ISOC) * reinitialize the QH's iso-related members for the next URB. */
uhci_unlink_isochronous_tds(uhci, urb); if (qh->type == USB_ENDPOINT_XFER_ISOC &&
urbp->node.prev == &qh->queue &&
urbp->node.next != &qh->queue) {
struct urb *nurb = list_entry(urbp->node.next,
struct urb_priv, node)->urb;
qh->iso_packet_desc = &nurb->iso_frame_desc[0];
qh->iso_frame = nurb->start_frame;
qh->iso_status = 0;
}
/* Take the URB off the QH's queue. If the queue is now empty, /* Take the URB off the QH's queue. If the queue is now empty,
* this is a perfect time for a toggle fixup. */ * this is a perfect time for a toggle fixup. */
...@@ -1434,6 +1475,7 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs) ...@@ -1434,6 +1475,7 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs)
uhci_clear_next_interrupt(uhci); uhci_clear_next_interrupt(uhci);
uhci_get_current_frame_number(uhci); uhci_get_current_frame_number(uhci);
uhci->cur_iso_frame = uhci->frame_number;
/* Go through all the QH queues and process the URBs in each one */ /* Go through all the QH queues and process the URBs in each one */
for (i = 0; i < UHCI_NUM_SKELQH - 1; ++i) { for (i = 0; i < UHCI_NUM_SKELQH - 1; ++i) {
...@@ -1451,6 +1493,7 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs) ...@@ -1451,6 +1493,7 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs)
} }
} }
uhci->last_iso_frame = uhci->cur_iso_frame;
if (uhci->need_rescan) if (uhci->need_rescan)
goto rescan; goto rescan;
uhci->scan_in_progress = 0; uhci->scan_in_progress = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment