summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2007-07-12 17:03:01 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-07-19 17:46:04 -0700
commit1b4cd43bd3f9aa7a794e29b80b0d984a8e144df4 (patch)
tree668dab10c66128f107447612188e889716b4d5e2 /drivers/usb
parent10e485221edd2799dc495e4cde98fe74aeb292b1 (diff)
downloadblackbird-op-linux-1b4cd43bd3f9aa7a794e29b80b0d984a8e144df4.tar.gz
blackbird-op-linux-1b4cd43bd3f9aa7a794e29b80b0d984a8e144df4.zip
isp116x-hcd: prepare for urb->status
This patch (as931b), adapted from a patch by Olav Kongas, makes a small set of conservative changes to the isp116x-hcd driver in preparation for the removal of urb->status. finish_request() is moved up in the source and is called as soon as the URB is known to have completed, rather than after all the active endpoints have been scanned. The status of a completed URB is kept in a local variable and copied to urb->status only when the URB is about to be given back. -EREMOTEIO error status for control transfers is set after the status stage rather than when the short packet arrives. Some unnecessary uses of urb->lock are removed. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Cc: Olav Kongas <ok@artecdesign.ee> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/isp116x-hcd.c187
1 files changed, 86 insertions, 101 deletions
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index 46873f2534b5..5c851a36de72 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -228,7 +228,6 @@ static void preproc_atl_queue(struct isp116x *isp116x)
struct urb, urb_list);
ptd = &ep->ptd;
len = ep->length;
- spin_lock(&urb->lock);
ep->data = (unsigned char *)urb->transfer_buffer
+ urb->actual_length;
@@ -264,7 +263,6 @@ static void preproc_atl_queue(struct isp116x *isp116x)
| PTD_EP(ep->epnum);
ptd->len = PTD_LEN(len) | PTD_DIR(dir);
ptd->faddr = PTD_FA(usb_pipedevice(urb->pipe));
- spin_unlock(&urb->lock);
if (!ep->active) {
ptd->mps |= PTD_LAST_MSK;
isp116x->atl_last_dir = dir;
@@ -275,6 +273,61 @@ static void preproc_atl_queue(struct isp116x *isp116x)
}
/*
+ Take done or failed requests out of schedule. Give back
+ processed urbs.
+*/
+static void finish_request(struct isp116x *isp116x, struct isp116x_ep *ep,
+ struct urb *urb)
+__releases(isp116x->lock) __acquires(isp116x->lock)
+{
+ unsigned i;
+
+ urb->hcpriv = NULL;
+ ep->error_count = 0;
+
+ if (usb_pipecontrol(urb->pipe))
+ ep->nextpid = USB_PID_SETUP;
+
+ urb_dbg(urb, "Finish");
+
+ spin_unlock(&isp116x->lock);
+ usb_hcd_giveback_urb(isp116x_to_hcd(isp116x), urb);
+ spin_lock(&isp116x->lock);
+
+ /* take idle endpoints out of the schedule */
+ if (!list_empty(&ep->hep->urb_list))
+ return;
+
+ /* async deschedule */
+ if (!list_empty(&ep->schedule)) {
+ list_del_init(&ep->schedule);
+ return;
+ }
+
+ /* periodic deschedule */
+ DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);
+ for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {
+ struct isp116x_ep *temp;
+ struct isp116x_ep **prev = &isp116x->periodic[i];
+
+ while (*prev && ((temp = *prev) != ep))
+ prev = &temp->next;
+ if (*prev)
+ *prev = ep->next;
+ isp116x->load[i] -= ep->load;
+ }
+ ep->branch = PERIODIC_SIZE;
+ isp116x_to_hcd(isp116x)->self.bandwidth_allocated -=
+ ep->load / ep->period;
+
+ /* switch irq type? */
+ if (!--isp116x->periodic_count) {
+ isp116x->irqenb &= ~HCuPINT_SOF;
+ isp116x->irqenb |= HCuPINT_ATL;
+ }
+}
+
+/*
Analyze transfer results, handle partial transfers and errors
*/
static void postproc_atl_queue(struct isp116x *isp116x)
@@ -284,6 +337,7 @@ static void postproc_atl_queue(struct isp116x *isp116x)
struct usb_device *udev;
struct ptd *ptd;
int short_not_ok;
+ int status;
u8 cc;
for (ep = isp116x->atl_active; ep; ep = ep->active) {
@@ -294,7 +348,7 @@ static void postproc_atl_queue(struct isp116x *isp116x)
ptd = &ep->ptd;
cc = PTD_GET_CC(ptd);
short_not_ok = 1;
- spin_lock(&urb->lock);
+ status = -EINPROGRESS;
/* Data underrun is special. For allowed underrun
we clear the error and continue as normal. For
@@ -302,47 +356,36 @@ static void postproc_atl_queue(struct isp116x *isp116x)
immediately while for control transfer,
we do a STATUS stage. */
if (cc == TD_DATAUNDERRUN) {
- if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) {
- DBG("Allowed data underrun\n");
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK) ||
+ usb_pipecontrol(urb->pipe)) {
+ DBG("Allowed or control data underrun\n");
cc = TD_CC_NOERROR;
short_not_ok = 0;
} else {
ep->error_count = 1;
- if (usb_pipecontrol(urb->pipe))
- ep->nextpid = USB_PID_ACK;
- else
- usb_settoggle(udev, ep->epnum,
- ep->nextpid ==
- USB_PID_OUT,
- PTD_GET_TOGGLE(ptd));
+ usb_settoggle(udev, ep->epnum,
+ ep->nextpid == USB_PID_OUT,
+ PTD_GET_TOGGLE(ptd));
urb->actual_length += PTD_GET_COUNT(ptd);
- urb->status = cc_to_error[TD_DATAUNDERRUN];
- spin_unlock(&urb->lock);
- continue;
+ status = cc_to_error[TD_DATAUNDERRUN];
+ goto done;
}
}
- /* Keep underrun error through the STATUS stage */
- if (urb->status == cc_to_error[TD_DATAUNDERRUN])
- cc = TD_DATAUNDERRUN;
if (cc != TD_CC_NOERROR && cc != TD_NOTACCESSED
&& (++ep->error_count >= 3 || cc == TD_CC_STALL
|| cc == TD_DATAOVERRUN)) {
- if (urb->status == -EINPROGRESS)
- urb->status = cc_to_error[cc];
+ status = cc_to_error[cc];
if (ep->nextpid == USB_PID_ACK)
ep->nextpid = 0;
- spin_unlock(&urb->lock);
- continue;
+ goto done;
}
/* According to usb spec, zero-length Int transfer signals
finishing of the urb. Hey, does this apply only
for IN endpoints? */
if (usb_pipeint(urb->pipe) && !PTD_GET_LEN(ptd)) {
- if (urb->status == -EINPROGRESS)
- urb->status = 0;
- spin_unlock(&urb->lock);
- continue;
+ status = 0;
+ goto done;
}
/* Relax after previously failed, but later succeeded
@@ -381,8 +424,8 @@ static void postproc_atl_queue(struct isp116x *isp116x)
/* All data for this URB is transferred, let's finish */
if (usb_pipecontrol(urb->pipe))
ep->nextpid = USB_PID_ACK;
- else if (urb->status == -EINPROGRESS)
- urb->status = 0;
+ else
+ status = 0;
break;
case USB_PID_SETUP:
if (PTD_GET_ACTIVE(ptd)
@@ -402,69 +445,27 @@ static void postproc_atl_queue(struct isp116x *isp116x)
if (PTD_GET_ACTIVE(ptd)
|| (cc != TD_CC_NOERROR && cc < 0x0E))
break;
- if (urb->status == -EINPROGRESS)
- urb->status = 0;
+ if ((urb->transfer_flags & URB_SHORT_NOT_OK) &&
+ urb->actual_length <
+ urb->transfer_buffer_length)
+ status = -EREMOTEIO;
+ else
+ status = 0;
ep->nextpid = 0;
break;
default:
BUG();
}
- spin_unlock(&urb->lock);
- }
-}
-
-/*
- Take done or failed requests out of schedule. Give back
- processed urbs.
-*/
-static void finish_request(struct isp116x *isp116x, struct isp116x_ep *ep,
- struct urb *urb)
-__releases(isp116x->lock) __acquires(isp116x->lock)
-{
- unsigned i;
-
- urb->hcpriv = NULL;
- ep->error_count = 0;
-
- if (usb_pipecontrol(urb->pipe))
- ep->nextpid = USB_PID_SETUP;
-
- urb_dbg(urb, "Finish");
-
- spin_unlock(&isp116x->lock);
- usb_hcd_giveback_urb(isp116x_to_hcd(isp116x), urb);
- spin_lock(&isp116x->lock);
-
- /* take idle endpoints out of the schedule */
- if (!list_empty(&ep->hep->urb_list))
- return;
-
- /* async deschedule */
- if (!list_empty(&ep->schedule)) {
- list_del_init(&ep->schedule);
- return;
- }
- /* periodic deschedule */
- DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);
- for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {
- struct isp116x_ep *temp;
- struct isp116x_ep **prev = &isp116x->periodic[i];
-
- while (*prev && ((temp = *prev) != ep))
- prev = &temp->next;
- if (*prev)
- *prev = ep->next;
- isp116x->load[i] -= ep->load;
- }
- ep->branch = PERIODIC_SIZE;
- isp116x_to_hcd(isp116x)->self.bandwidth_allocated -=
- ep->load / ep->period;
-
- /* switch irq type? */
- if (!--isp116x->periodic_count) {
- isp116x->irqenb &= ~HCuPINT_SOF;
- isp116x->irqenb |= HCuPINT_ATL;
+ done:
+ if (status != -EINPROGRESS) {
+ spin_lock(&urb->lock);
+ if (urb->status == -EINPROGRESS)
+ urb->status = status;
+ spin_unlock(&urb->lock);
+ }
+ if (urb->status != -EINPROGRESS)
+ finish_request(isp116x, ep, urb);
}
}
@@ -570,9 +571,6 @@ static void start_atl_transfers(struct isp116x *isp116x)
*/
static void finish_atl_transfers(struct isp116x *isp116x)
{
- struct isp116x_ep *ep;
- struct urb *urb;
-
if (!isp116x->atl_active)
return;
/* Fifo not ready? */
@@ -582,16 +580,6 @@ static void finish_atl_transfers(struct isp116x *isp116x)
atomic_inc(&isp116x->atl_finishing);
unpack_fifo(isp116x);
postproc_atl_queue(isp116x);
- for (ep = isp116x->atl_active; ep; ep = ep->active) {
- urb =
- container_of(ep->hep->urb_list.next, struct urb, urb_list);
- /* USB_PID_ACK check here avoids finishing of
- control transfers, for which TD_DATAUNDERRUN
- occured, while URB_SHORT_NOT_OK was set */
- if (urb && urb->status != -EINPROGRESS
- && ep->nextpid != USB_PID_ACK)
- finish_request(isp116x, ep, urb);
- }
atomic_dec(&isp116x->atl_finishing);
}
@@ -821,15 +809,12 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd,
}
/* in case of unlink-during-submit */
- spin_lock(&urb->lock);
if (urb->status != -EINPROGRESS) {
- spin_unlock(&urb->lock);
finish_request(isp116x, ep, urb);
ret = 0;
goto fail;
}
urb->hcpriv = hep;
- spin_unlock(&urb->lock);
start_atl_transfers(isp116x);
fail:
OpenPOWER on IntegriCloud