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