summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/host/uhci-debug.c5
-rw-r--r--drivers/usb/host/uhci-hcd.h36
-rw-r--r--drivers/usb/host/uhci-q.c72
3 files changed, 63 insertions, 50 deletions
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c
index ecef5880cfd9..ab8ba8220ad1 100644
--- a/drivers/usb/host/uhci-debug.c
+++ b/drivers/usb/host/uhci-debug.c
@@ -153,7 +153,7 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
char *qtype;
/* Try to make sure there's enough memory */
- if (len < 80 * 6)
+ if (len < 80 * 7)
return 0;
switch (qh->type) {
@@ -167,6 +167,9 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
out += sprintf(out, "%*s[%p] %s QH link (%08x) element (%08x)\n",
space, "", qh, qtype,
le32_to_cpu(qh->link), le32_to_cpu(element));
+ if (qh->type == USB_ENDPOINT_XFER_ISOC)
+ out += sprintf(out, "%*s period %d\n",
+ space, "", qh->period);
if (element & UHCI_PTR_QH)
out += sprintf(out, "%*s Element points to QH (bug?)\n", space, "");
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
index c87ceaa178b6..eaac6ddf03a0 100644
--- a/drivers/usb/host/uhci-hcd.h
+++ b/drivers/usb/host/uhci-hcd.h
@@ -140,6 +140,8 @@ struct uhci_qh {
unsigned long advance_jiffies; /* Time of last queue advance */
unsigned int unlink_frame; /* When the QH was unlinked */
+ unsigned int period; /* For Interrupt and Isochronous QHs */
+
int state; /* QH_STATE_xxx; see above */
int type; /* Queue type (control, bulk, etc) */
@@ -315,38 +317,8 @@ static inline u32 td_status(struct uhci_td *td) {
#define skel_bulk_qh skelqh[12]
#define skel_term_qh skelqh[13]
-/*
- * Search tree for determining where <interval> fits in the skelqh[]
- * skeleton.
- *
- * An interrupt request should be placed into the slowest skelqh[]
- * which meets the interval/period/frequency requirement.
- * An interrupt request is allowed to be faster than <interval> but not slower.
- *
- * For a given <interval>, this function returns the appropriate/matching
- * skelqh[] index value.
- */
-static inline int __interval_to_skel(int interval)
-{
- if (interval < 16) {
- if (interval < 4) {
- if (interval < 2)
- return 9; /* int1 for 0-1 ms */
- return 8; /* int2 for 2-3 ms */
- }
- if (interval < 8)
- return 7; /* int4 for 4-7 ms */
- return 6; /* int8 for 8-15 ms */
- }
- if (interval < 64) {
- if (interval < 32)
- return 5; /* int16 for 16-31 ms */
- return 4; /* int32 for 32-63 ms */
- }
- if (interval < 128)
- return 3; /* int64 for 64-127 ms */
- return 2; /* int128 for 128-255 ms (Max.) */
-}
+/* Find the skelqh entry corresponding to an interval exponent */
+#define UHCI_SKEL_INDEX(exponent) (9 - exponent)
/*
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index 96ce4c87c871..7acc23473c63 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -763,6 +763,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
wmb();
qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE);
qh->dummy_td = td;
+ qh->period = urb->interval;
usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe), toggle);
@@ -790,14 +791,30 @@ static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
return ret;
}
-static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
+static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
struct uhci_qh *qh)
{
+ int exponent;
+
/* USB 1.1 interrupt transfers only involve one packet per interval.
* Drivers can submit URBs of any length, but longer ones will need
* multiple intervals to complete.
*/
- qh->skel = uhci->skelqh[__interval_to_skel(urb->interval)];
+
+ /* Figure out which power-of-two queue to use */
+ for (exponent = 7; exponent >= 0; --exponent) {
+ if ((1 << exponent) <= urb->interval)
+ break;
+ }
+ if (exponent < 0)
+ return -EINVAL;
+ urb->interval = 1 << exponent;
+
+ if (qh->period == 0)
+ qh->skel = uhci->skelqh[UHCI_SKEL_INDEX(exponent)];
+ else if (qh->period != urb->interval)
+ return -EINVAL; /* Can't change the period */
+
return uhci_submit_common(uhci, urb, qh);
}
@@ -937,31 +954,50 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
unsigned long destination, status;
struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
- if (urb->number_of_packets > 900) /* 900? Why? */
+ /* Values must not be too big (could overflow below) */
+ if (urb->interval >= UHCI_NUMFRAMES ||
+ urb->number_of_packets >= UHCI_NUMFRAMES)
return -EFBIG;
- status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
- destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+ /* Check the period and figure out the starting frame number */
+ uhci_get_current_frame_number(uhci);
+ if (qh->period == 0) {
+ if (urb->transfer_flags & URB_ISO_ASAP) {
+ urb->start_frame = uhci->frame_number + 10;
+ } else {
+ i = urb->start_frame - uhci->frame_number;
+ if (i <= 0 || i >= UHCI_NUMFRAMES)
+ return -EINVAL;
+ }
+ } else if (qh->period != urb->interval) {
+ return -EINVAL; /* Can't change the period */
- /* Figure out the starting frame number */
- if (urb->transfer_flags & URB_ISO_ASAP) {
+ } else { /* Pick up where the last URB leaves off */
if (list_empty(&qh->queue)) {
- uhci_get_current_frame_number(uhci);
- urb->start_frame = (uhci->frame_number + 10);
-
- } else { /* Go right after the last one */
- struct urb *last_urb;
+ frame = uhci->frame_number + 10;
+ } else {
+ struct urb *lurb;
- last_urb = list_entry(qh->queue.prev,
+ lurb = list_entry(qh->queue.prev,
struct urb_priv, node)->urb;
- urb->start_frame = (last_urb->start_frame +
- last_urb->number_of_packets *
- last_urb->interval);
+ frame = lurb->start_frame +
+ lurb->number_of_packets *
+ lurb->interval;
}
- } else {
+ if (urb->transfer_flags & URB_ISO_ASAP)
+ urb->start_frame = frame;
/* FIXME: Sanity check */
}
+ /* Make sure we won't have to go too far into the future */
+ if (uhci_frame_before_eq(uhci->frame_number + UHCI_NUMFRAMES,
+ urb->start_frame + urb->number_of_packets *
+ urb->interval))
+ return -EFBIG;
+
+ status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
+ destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
for (i = 0; i < urb->number_of_packets; i++) {
td = uhci_alloc_td(uhci);
if (!td)
@@ -978,6 +1014,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
td->status |= __constant_cpu_to_le32(TD_CTRL_IOC);
qh->skel = uhci->skel_iso_qh;
+ qh->period = urb->interval;
/* Add the TDs to the frame list */
frame = urb->start_frame;
@@ -1206,6 +1243,7 @@ __acquires(uhci->lock)
uhci_unlink_qh(uhci, qh);
/* Bandwidth stuff not yet implemented */
+ qh->period = 0;
}
}