Skip to content

Commit

Permalink
USB: add urb->unlinked field
Browse files Browse the repository at this point in the history
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: Alan 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: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed Oct 12, 2007
1 parent b0d9efb commit eb23105
Show file tree
Hide file tree
Showing 11 changed files with 108 additions and 125 deletions.
19 changes: 12 additions & 7 deletions drivers/usb/core/hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1259,14 +1259,20 @@ 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)
{
unmap_urb_for_dma(hcd, 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;
Expand Down Expand Up @@ -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);
Expand Down
8 changes: 3 additions & 5 deletions drivers/usb/gadget/dummy_hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);

Expand Down
29 changes: 12 additions & 17 deletions drivers/usb/host/ehci-q.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 2 additions & 3 deletions drivers/usb/host/isp116x-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
5 changes: 2 additions & 3 deletions drivers/usb/host/ohci-q.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
}
Expand Down
24 changes: 7 additions & 17 deletions drivers/usb/host/r8a66597-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}
Expand Down
5 changes: 2 additions & 3 deletions drivers/usb/host/sl811-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}

Expand Down
Loading

0 comments on commit eb23105

Please sign in to comment.