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

USB: add urb->unlinked field

This patch (as970) adds a new urb->unlinked field, which is used to
store the status of unlinked URBs since we can't use urb->status for
that purpose any more.  To help simplify the HCDs, usbcore will check
urb->unlinked before calling the completion handler; if the value is
set it will automatically override the status reported by the HCD.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
CC: David Brownell <david-b@pacbell.net>
CC: Olav Kongas <ok@artecdesign.ee>
CC: Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
CC: Tony Olech <tony.olech@elandigitalsystems.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent b0d9efba
......@@ -532,8 +532,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
/* any errors get returned through the urb completion */
spin_lock_irq(&hcd_root_hub_lock);
if (urb->status == -EINPROGRESS)
urb->status = status;
urb->status = status;
usb_hcd_unlink_urb_from_ep(hcd, urb);
/* This peculiar use of spinlocks echoes what real HC drivers do.
......@@ -1024,6 +1023,7 @@ int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb)
switch (hcd->state) {
case HC_STATE_RUNNING:
case HC_STATE_RESUMING:
urb->unlinked = 0;
list_add_tail(&urb->urb_list, &urb->ep->urb_list);
break;
default:
......@@ -1071,9 +1071,9 @@ int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb,
/* Any status except -EINPROGRESS means something already started to
* unlink this URB from the hardware. So there's no more work to do.
*/
if (urb->status != -EINPROGRESS)
if (urb->unlinked)
return -EBUSY;
urb->status = status;
urb->unlinked = status;
/* IRQ setup can easily be broken so that USB controllers
* never get completion IRQs ... maybe even the ones we need to
......@@ -1259,6 +1259,10 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
* (and is done using urb->hcpriv). It also released all HCD locks;
* the device driver won't cause problems if it frees, modifies,
* or resubmits this URB.
*
* If @urb was unlinked, the value of @urb->status will be overridden by
* @urb->unlinked. Erroneous short transfers are detected in case
* the HCD hasn't checked for them.
*/
void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
{
......@@ -1266,7 +1270,9 @@ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
usbmon_urb_complete (&hcd->self, urb);
usb_unanchor_urb(urb);
urb->hcpriv = NULL;
if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
if (unlikely(urb->unlinked))
urb->status = urb->unlinked;
else if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
urb->actual_length < urb->transfer_buffer_length &&
!urb->status))
urb->status = -EREMOTEIO;
......@@ -1305,8 +1311,7 @@ void usb_hcd_endpoint_disable (struct usb_device *udev,
list_for_each_entry (urb, &ep->urb_list, urb_list) {
int is_in;
/* the urb may already have been unlinked */
if (urb->status != -EINPROGRESS)
if (urb->unlinked)
continue;
usb_get_urb (urb);
is_in = usb_urb_dir_in(urb);
......
......@@ -1029,8 +1029,7 @@ static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
static void maybe_set_status (struct urb *urb, int status)
{
spin_lock (&urb->lock);
if (urb->status == -EINPROGRESS)
urb->status = status;
urb->status = status;
spin_unlock (&urb->lock);
}
......@@ -1257,10 +1256,9 @@ static void dummy_timer (unsigned long _dum)
int type;
urb = urbp->urb;
if (urb->status != -EINPROGRESS) {
/* likely it was just unlinked */
if (urb->unlinked)
goto return_urb;
} else if (dum->rh_state != DUMMY_RH_RUNNING)
else if (dum->rh_state != DUMMY_RH_RUNNING)
continue;
type = usb_pipetype (urb->pipe);
......
......@@ -151,7 +151,7 @@ static void qtd_copy_status (
urb->actual_length += length - QTD_LENGTH (token);
/* don't modify error codes */
if (unlikely (urb->status != -EINPROGRESS))
if (unlikely(urb->unlinked))
return;
/* force cleanup after short read; not always an error */
......@@ -232,21 +232,14 @@ __acquires(ehci->lock)
}
spin_lock (&urb->lock);
switch (urb->status) {
case -EINPROGRESS: /* success */
urb->status = 0;
default: /* fault */
COUNT (ehci->stats.complete);
break;
case -EREMOTEIO: /* fault or normal */
if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
if (unlikely(urb->unlinked)) {
COUNT(ehci->stats.unlink);
} else {
if (likely(urb->status == -EINPROGRESS ||
(urb->status == -EREMOTEIO &&
!(urb->transfer_flags & URB_SHORT_NOT_OK))))
urb->status = 0;
COUNT (ehci->stats.complete);
break;
case -ECONNRESET: /* canceled */
case -ENOENT:
COUNT (ehci->stats.unlink);
break;
COUNT(ehci->stats.complete);
}
spin_unlock (&urb->lock);
......@@ -364,7 +357,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
* for the urb faulted (including short read) or
* its urb was canceled. we may patch qh or qtds.
*/
if (likely (urb->status == -EINPROGRESS))
if (likely(urb->status == -EINPROGRESS &&
!urb->unlinked))
continue;
/* issue status after short control reads */
......@@ -395,7 +389,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
spin_lock (&urb->lock);
qtd_copy_status (ehci, urb, qtd->length, token);
if (unlikely(urb->status == -EREMOTEIO)) {
do_status = usb_pipecontrol(urb->pipe);
do_status = (!urb->unlinked &&
usb_pipecontrol(urb->pipe));
urb->status = 0;
}
spin_unlock (&urb->lock);
......
......@@ -455,11 +455,10 @@ static void postproc_atl_queue(struct isp116x *isp116x)
done:
if (status != -EINPROGRESS) {
spin_lock(&urb->lock);
if (urb->status == -EINPROGRESS)
urb->status = status;
urb->status = status;
spin_unlock(&urb->lock);
}
if (urb->status != -EINPROGRESS)
if (urb->status != -EINPROGRESS || urb->unlinked)
finish_request(isp116x, ep, urb);
}
}
......
......@@ -758,8 +758,7 @@ static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td)
cc = TD_CC_NOERROR;
if (cc != TD_CC_NOERROR && cc < 0x0E) {
spin_lock (&urb->lock);
if (urb->status == -EINPROGRESS)
urb->status = cc_to_error [cc];
urb->status = cc_to_error[cc];
spin_unlock (&urb->lock);
}
......@@ -972,7 +971,7 @@ finish_unlinks (struct ohci_hcd *ohci, u16 tick)
urb = td->urb;
urb_priv = td->urb->hcpriv;
if (urb->status == -EINPROGRESS) {
if (!urb->unlinked) {
prev = &td->hwNextTD;
continue;
}
......
......@@ -1118,7 +1118,7 @@ __releases(r8a66597->lock) __acquires(r8a66597->lock)
r8a66597->timeout_map &= ~(1 << pipenum);
if (likely(td)) {
if (td->set_address && urb->status != 0)
if (td->set_address && (urb->status != 0 || urb->unlinked))
r8a66597->address_map &= ~(1 << urb->setup_packet[2]);
pipe_toggle_save(r8a66597, td->pipe, urb);
......@@ -1225,8 +1225,7 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum)
}
if (finish && pipenum != 0) {
if (td->urb->status == -EINPROGRESS)
td->urb->status = status;
td->urb->status = status;
finish_request(r8a66597, td, pipenum, urb);
}
}
......@@ -1308,32 +1307,24 @@ static void check_next_phase(struct r8a66597 *r8a66597)
switch (td->type) {
case USB_PID_IN:
case USB_PID_OUT:
if (urb->status != -EINPROGRESS) {
finish = 1;
break;
}
if (check_transfer_finish(td, urb))
td->type = USB_PID_ACK;
break;
case USB_PID_SETUP:
if (urb->status != -EINPROGRESS)
finish = 1;
else if (urb->transfer_buffer_length == urb->actual_length) {
if (urb->transfer_buffer_length == urb->actual_length)
td->type = USB_PID_ACK;
urb->status = 0;
} else if (usb_pipeout(urb->pipe))
else if (usb_pipeout(urb->pipe))
td->type = USB_PID_OUT;
else
td->type = USB_PID_IN;
break;
case USB_PID_ACK:
finish = 1;
if (urb->status == -EINPROGRESS)
urb->status = 0;
urb->status = 0;
break;
}
if (finish)
if (finish || urb->unlinked)
finish_request(r8a66597, td, 0, urb);
else
start_transfer(r8a66597, td);
......@@ -1418,8 +1409,7 @@ static void irq_pipe_empty(struct r8a66597 *r8a66597)
if ((tmp & INBUFM) == 0) {
disable_irq_empty(r8a66597, pipenum);
pipe_irq_disable(r8a66597, pipenum);
if (td->urb->status == -EINPROGRESS)
td->urb->status = 0;
td->urb->status = 0;
finish_request(r8a66597, td, pipenum, td->urb);
}
}
......
......@@ -436,8 +436,7 @@ static void finish_request(
ep->nextpid = USB_PID_SETUP;
spin_lock(&urb->lock);
if (urb->status == -EINPROGRESS)
urb->status = status;
urb->status = status;
spin_unlock(&urb->lock);
usb_hcd_unlink_urb_from_ep(sl811_to_hcd(sl811), urb);
......@@ -598,7 +597,7 @@ done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank)
bank, status, ep, urbstat);
}
if (urb && (urbstat != -EINPROGRESS || urb->status != -EINPROGRESS))
if (urb && (urbstat != -EINPROGRESS || urb->unlinked))
finish_request(sl811, ep, urb, urbstat);
}
......
This diff is collapsed.
......@@ -120,8 +120,8 @@ static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space)
out += sprintf(out, "%s%s", ptype, (urbp->fsbr ? " FSBR" : ""));
out += sprintf(out, " Actlen=%d", urbp->urb->actual_length);
if (urbp->urb->status != -EINPROGRESS)
out += sprintf(out, " Status=%d", urbp->urb->status);
if (urbp->urb->unlinked)
out += sprintf(out, " Unlinked=%d", urbp->urb->unlinked);
out += sprintf(out, "\n");
i = nactive = ninactive = 0;
......
......@@ -1557,15 +1557,12 @@ static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
break;
spin_lock(&urb->lock);
if (urb->status == -EINPROGRESS) /* Not dequeued */
urb->status = status;
else
status = ECONNRESET; /* Not -ECONNRESET */
urb->status = status;
spin_unlock(&urb->lock);
/* Dequeued but completed URBs can't be given back unless
* the QH is stopped or has finished unlinking. */
if (status == ECONNRESET) {
if (urb->unlinked) {
if (QH_FINISHED_UNLINKING(qh))
qh->is_stopped = 1;
else if (!qh->is_stopped)
......@@ -1588,7 +1585,7 @@ static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
restart:
list_for_each_entry(urbp, &qh->queue, node) {
urb = urbp->urb;
if (urb->status != -EINPROGRESS) {
if (urb->unlinked) {
/* Fix up the TD links and save the toggles for
* non-Isochronous queues. For Isochronous queues,
......
......@@ -1245,6 +1245,7 @@ struct urb
void *hcpriv; /* private data for host controller */
atomic_t use_count; /* concurrent submissions counter */
u8 reject; /* submissions will fail */
int unlinked; /* unlink error code */
/* public: documented fields in the urb that can be used by drivers */
struct list_head urb_list; /* list head for use by the urb's
......
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