summaryrefslogtreecommitdiffstats
path: root/drivers/usb/uhci.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-03-19 01:28:40 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-03-19 01:28:40 +0000
commit8abb719409c9060a7c0676f76e9182c1e0b8ca46 (patch)
treeb88cc5a6cd513a04a512b7e6215c873c90a1c5dd /drivers/usb/uhci.c
parentf01bd7aeafd95a08aafc9e3636bb26974df69d82 (diff)
Merge with 2.3.99-pre1.
Diffstat (limited to 'drivers/usb/uhci.c')
-rw-r--r--drivers/usb/uhci.c56
1 files changed, 51 insertions, 5 deletions
diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c
index c3b4ebc2e..480c6252d 100644
--- a/drivers/usb/uhci.c
+++ b/drivers/usb/uhci.c
@@ -236,7 +236,7 @@ static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td)
static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct uhci_td *td, *prevtd = NULL;
+ struct uhci_td *td, *prevtd;
if (!urbp)
return;
@@ -617,6 +617,8 @@ static int uhci_submit_control(urb_t *urb)
return -EINPROGRESS;
}
+static int usb_control_retrigger_status(urb_t *urb);
+
static int uhci_result_control(urb_t *urb)
{
struct urb_priv *urbp = urb->hcpriv;
@@ -630,6 +632,9 @@ static int uhci_result_control(urb_t *urb)
if (!td)
return -EINVAL;
+ if (urbp->short_control_packet)
+ goto status_phase;
+
/* The first TD is the SETUP phase, check the status, but skip */
/* the count */
status = uhci_status_bits(td->status);
@@ -653,10 +658,9 @@ static int uhci_result_control(urb_t *urb)
/* If SPD is set then we received a short packet */
/* There will be no status phase at the end */
- /* FIXME: Re-setup the queue to run the STATUS phase? */
if ((td->status & TD_CTRL_SPD) &&
(uhci_actual_length(td->status) < uhci_expected_length(td->info)))
- return 0;
+ return usb_control_retrigger_status(urb);
if (status)
goto td_error;
@@ -664,12 +668,13 @@ static int uhci_result_control(urb_t *urb)
td = td->list.next;
}
+status_phase:
/* Control status phase */
status = uhci_status_bits(td->status);
/* APC BackUPS Pro kludge */
- /* It tries to send all of the descriptor instead of */
- /* the amount we requested */
+ /* It tries to send all of the descriptor instead of the amount */
+ /* we requested */
if (td->status & TD_CTRL_IOC &&
status & TD_CTRL_ACTIVE &&
status & TD_CTRL_NAK)
@@ -700,6 +705,47 @@ td_error:
return uhci_map_status(status, uhci_packetout(td->info));
}
+static int usb_control_retrigger_status(urb_t *urb)
+{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ struct uhci *uhci = urb->dev->bus->hcpriv;
+ struct uhci_td *td, *nexttd;
+
+ urbp->short_control_packet = 1;
+
+ /* Delete all of the TD's except for the status TD at the end */
+ td = urbp->list.begin;
+ while (td && td->list.next) {
+ nexttd = td->list.next;
+
+ uhci_remove_td_from_urb(urb, td);
+
+ uhci_remove_td(uhci, td);
+
+ uhci_free_td(td);
+
+ td = nexttd;
+ }
+
+ /* Create a new QH to avoid pointer overwriting problems */
+ uhci_remove_qh(uhci, urbp->qh);
+
+ urbp->qh = uhci_alloc_qh(urb->dev);
+ if (!urbp->qh)
+ return -ENOMEM;
+
+ /* One TD, who cares about Breadth first? */
+ uhci_insert_tds_in_qh(urbp->qh, urb, 0);
+
+ /* Low speed or small transfers gets a different queue and treatment */
+ if (urb->pipe & TD_CTRL_LS)
+ uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, urbp->qh);
+ else
+ uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, urbp->qh);
+
+ return -EINPROGRESS;
+}
+
/*
* Interrupt transfers
*/