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

UHCI: short control URBs get a status stage

It has recently been pointed out that short control transfers should
have a status stage, even if they generate an error because
URB_SHORT_NOT_OK was set.  This patch (as935) changes uhci-hcd to
enable the status stage when this happens.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent e94fa28f
...@@ -827,8 +827,10 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, ...@@ -827,8 +827,10 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
* If direction is "send", change the packet ID from SETUP (0x2D) * If direction is "send", change the packet ID from SETUP (0x2D)
* to OUT (0xE1). Else change it from SETUP to IN (0x69) and * to OUT (0xE1). Else change it from SETUP to IN (0x69) and
* set Short Packet Detect (SPD) for all data packets. * set Short Packet Detect (SPD) for all data packets.
*
* 0-length transfers always get treated as "send".
*/ */
if (usb_pipeout(urb->pipe)) if (usb_pipeout(urb->pipe) || len == 0)
destination ^= (USB_PID_SETUP ^ USB_PID_OUT); destination ^= (USB_PID_SETUP ^ USB_PID_OUT);
else { else {
destination ^= (USB_PID_SETUP ^ USB_PID_IN); destination ^= (USB_PID_SETUP ^ USB_PID_IN);
...@@ -839,7 +841,12 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, ...@@ -839,7 +841,12 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
* Build the DATA TDs * Build the DATA TDs
*/ */
while (len > 0) { while (len > 0) {
int pktsze = min(len, maxsze); int pktsze = maxsze;
if (len <= pktsze) { /* The last data packet */
pktsze = len;
status &= ~TD_CTRL_SPD;
}
td = uhci_alloc_td(uhci); td = uhci_alloc_td(uhci);
if (!td) if (!td)
...@@ -866,20 +873,10 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, ...@@ -866,20 +873,10 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
goto nomem; goto nomem;
*plink = LINK_TO_TD(td); *plink = LINK_TO_TD(td);
/* /* Change direction for the status transaction */
* It's IN if the pipe is an output pipe or we're not expecting destination ^= (USB_PID_IN ^ USB_PID_OUT);
* data back.
*/
destination &= ~TD_TOKEN_PID_MASK;
if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)
destination |= USB_PID_IN;
else
destination |= USB_PID_OUT;
destination |= TD_TOKEN_TOGGLE; /* End in Data1 */ destination |= TD_TOKEN_TOGGLE; /* End in Data1 */
status &= ~TD_CTRL_SPD;
uhci_add_td_to_urbp(td, urbp); uhci_add_td_to_urbp(td, urbp);
uhci_fill_td(td, status | TD_CTRL_IOC, uhci_fill_td(td, status | TD_CTRL_IOC,
destination | uhci_explen(0), 0); destination | uhci_explen(0), 0);
...@@ -1185,10 +1182,18 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) ...@@ -1185,10 +1182,18 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
} }
} }
/* Did we receive a short packet? */
} else if (len < uhci_expected_length(td_token(td))) { } else if (len < uhci_expected_length(td_token(td))) {
/* We received a short packet */ /* For control transfers, go to the status TD if
if (urb->transfer_flags & URB_SHORT_NOT_OK) * this isn't already the last data TD */
if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
if (td->list.next != urbp->td_list.prev)
ret = 1;
}
/* For bulk and interrupt, this may be an error */
else if (urb->transfer_flags & URB_SHORT_NOT_OK)
ret = -EREMOTEIO; ret = -EREMOTEIO;
/* Fixup needed only if this isn't the URB's last TD */ /* Fixup needed only if this isn't the URB's last TD */
...@@ -1208,10 +1213,6 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) ...@@ -1208,10 +1213,6 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
err: err:
if (ret < 0) { if (ret < 0) {
/* In case a control transfer gets an error
* during the setup stage */
urb->actual_length = max(urb->actual_length, 0);
/* Note that the queue has stopped and save /* Note that the queue has stopped and save
* the next toggle value */ * the next toggle value */
qh->element = UHCI_PTR_TERM; qh->element = UHCI_PTR_TERM;
...@@ -1489,9 +1490,25 @@ __acquires(uhci->lock) ...@@ -1489,9 +1490,25 @@ __acquires(uhci->lock)
{ {
struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
/* urb->actual_length < 0 means the setup transaction didn't
* complete successfully. Either it failed or the URB was
* unlinked first. Regardless, don't confuse people with a
* negative length. */
urb->actual_length = max(urb->actual_length, 0);
/* Report erroneous short transfers */
if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
urb->actual_length <
urb->transfer_buffer_length &&
urb->status == 0))
urb->status = -EREMOTEIO;
}
/* When giving back the first URB in an Isochronous queue, /* When giving back the first URB in an Isochronous queue,
* reinitialize the QH's iso-related members for the next URB. */ * reinitialize the QH's iso-related members for the next URB. */
if (qh->type == USB_ENDPOINT_XFER_ISOC && else if (qh->type == USB_ENDPOINT_XFER_ISOC &&
urbp->node.prev == &qh->queue && urbp->node.prev == &qh->queue &&
urbp->node.next != &qh->queue) { urbp->node.next != &qh->queue) {
struct urb *nurb = list_entry(urbp->node.next, struct urb *nurb = list_entry(urbp->node.next,
......
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