Commit e9c72b55 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] rm "automagic resubmit" for usb interrupt transfers

Here's that promised patch to remove the problematic "automagic
resubmit" mode from the API for interrupt transfers.  It covers
the core (including main HCDs) and a few essential drivers.

All urbs now obey a simple rule:  submit them once, then wait for
some completion callback.  Or unlink the urb if you're impatient,
canceling the i/o request (which may have been partially completed).
Bulk and interrupt transfers now behave the same at the API level,
except that only interrupt transfers have bandwidth failure modes.


Previously, interrupt transfers were different from bulk transfers
in several ways that made limited sense.  The only thing that's
supposed to be special is achieving service latency guarantees by
using the reserved periodic bandwidth.

But there were a lot of other restrictions, plus HCD-dependent
behaviors/bugs.  Doing something like sending a 97 byte message
to a device portably was a thing of pain, since the low-level
"one packet per interval" rule was pushed up to drivers instead
of being handled inside HCDs like it is for bulk, and sending a
final "short" packet meant an urb unlink/relink.  (Fixing this
required UHCI to use a queue of TDs, like EHCI and OHCI; fixed
by 2.5.44, and a small change in this patch.  I'm not sure the
unlink/relink issues have ever been really addressed.)  Neither
1-msec transfer intervals nor USB 2.0 "high bandwidth" mode can
reliably be serviced without a multi-buffered queue of interrupt
transfers.  (Comes almost for free with TD queueing; as of 2.5.44
all HCDs should do this.)

And then there's "automagic resubmission", which made HCDs
keep urbs during their complete() callbacks in a rather curious
state ... half-owned by HCD, half-owned by device driver, not
exactly linked but maybe not unlinked either.  Bug-prone, and
hard to test.


So that's all gone now!  This particular patch

  - updates the main hcds to use normal urb-completion logic
    for interrupt transfers, nothing special. (*)

  - makes usbcore (hub and root hub drivers) expect that, and
    removes an old kernel 2.3 "urb state confusion" workaround.
    (urb->dev is no longer nulled to distinguish unlinked urbs,
    since there's no longer a "half-in/half-out" state.)  also
    the relevent kerneldoc is updated.

  - enables the 'usbtest' support for interrupt transfers, in
    both queued and non-queued modes.  (but I haven't made time
    to test this ... the hcds "should" be fine since they use the
    same code now for bulk and interrupt, and bulk checked out.)

  - teaches hid-core, usbkbd, and usbmouse how to resubmit
    interrupt transfers explicitly. usb keyboards/mice work,
    but some less-common HID devices won't.

  - updated usb/net drivers (catc, kaweth, pegasus, rtl8150)

But it doesn't update all device drivers that use interrupt
transfers.  The failure mode for un-converted drivers will
be that interrupts after the first one get lost, and the
fix for those drivers will be simple (see what the drivers
here do).


(*) It doesn't touch non-{E,O,U}HCI HCDs, like the SL-811HS,
     since those changes will require hardware as well as
     some quality time with 'usbtest'.
parent df0cca21
...@@ -495,25 +495,17 @@ static void rh_report_status (unsigned long ptr) ...@@ -495,25 +495,17 @@ static void rh_report_status (unsigned long ptr)
if (length > 0) { if (length > 0) {
urb->actual_length = length; urb->actual_length = length;
urb->status = 0; urb->status = 0;
urb->hcpriv = 0;
urb->complete (urb); urb->complete (urb);
return;
} }
spin_lock_irqsave (&hcd_data_lock, flags); } else
urb->status = -EINPROGRESS;
if (HCD_IS_RUNNING (hcd->state)
&& rh_status_urb (hcd, urb) != 0) {
/* another driver snuck in? */
dbg ("%s, can't resubmit roothub status urb?",
hcd->self.bus_name);
spin_unlock_irqrestore (&hcd_data_lock, flags);
BUG ();
}
spin_unlock_irqrestore (&hcd_data_lock, flags);
} else {
spin_unlock_irqrestore (&urb->lock, flags); spin_unlock_irqrestore (&urb->lock, flags);
/* retrigger timer until completion: success or unlink */
spin_lock_irqsave (&hcd_data_lock, flags); spin_lock_irqsave (&hcd_data_lock, flags);
rh_status_urb (hcd, urb); rh_status_urb (hcd, urb);
spin_unlock_irqrestore (&hcd_data_lock, flags); spin_unlock_irqrestore (&hcd_data_lock, flags);
}
} else { } else {
/* this urb's been unlinked */ /* this urb's been unlinked */
urb->hcpriv = 0; urb->hcpriv = 0;
...@@ -967,7 +959,6 @@ static void urb_unlink (struct urb *urb) ...@@ -967,7 +959,6 @@ static void urb_unlink (struct urb *urb)
spin_lock_irqsave (&hcd_data_lock, flags); spin_lock_irqsave (&hcd_data_lock, flags);
list_del_init (&urb->urb_list); list_del_init (&urb->urb_list);
dev = urb->dev; dev = urb->dev;
urb->dev = NULL;
usb_put_dev (dev); usb_put_dev (dev);
spin_unlock_irqrestore (&hcd_data_lock, flags); spin_unlock_irqrestore (&hcd_data_lock, flags);
} }
...@@ -1155,7 +1146,10 @@ static int hcd_unlink_urb (struct urb *urb) ...@@ -1155,7 +1146,10 @@ static int hcd_unlink_urb (struct urb *urb)
goto done; goto done;
} }
/* maybe set up to block on completion notification */ /* maybe set up to block until the urb's completion fires. the
* lower level hcd code is always async, locking on urb->status
* updates; an intercepted completion unblocks us.
*/
if ((urb->transfer_flags & USB_TIMEOUT_KILLED)) if ((urb->transfer_flags & USB_TIMEOUT_KILLED))
urb->status = -ETIMEDOUT; urb->status = -ETIMEDOUT;
else if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) { else if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) {
...@@ -1290,11 +1284,6 @@ EXPORT_SYMBOL (usb_hcd_operations); ...@@ -1290,11 +1284,6 @@ EXPORT_SYMBOL (usb_hcd_operations);
* (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.
* Bandwidth and other resources will be deallocated.
*
* HCDs must not use this for periodic URBs that are still scheduled
* and will be reissued. They should just call their completion handlers
* until the urb is returned to the device driver by unlinking.
*/ */
void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb) void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
{ {
......
...@@ -119,6 +119,7 @@ static void hub_irq(struct urb *urb) ...@@ -119,6 +119,7 @@ static void hub_irq(struct urb *urb)
{ {
struct usb_hub *hub = (struct usb_hub *)urb->context; struct usb_hub *hub = (struct usb_hub *)urb->context;
unsigned long flags; unsigned long flags;
int status;
switch (urb->status) { switch (urb->status) {
case -ENOENT: /* synchronous unlink */ case -ENOENT: /* synchronous unlink */
...@@ -131,7 +132,7 @@ static void hub_irq(struct urb *urb) ...@@ -131,7 +132,7 @@ static void hub_irq(struct urb *urb)
dbg("hub '%s' status %d for interrupt transfer", dbg("hub '%s' status %d for interrupt transfer",
urb->dev->devpath, urb->status); urb->dev->devpath, urb->status);
if ((++hub->nerrors < 10) || hub->error) if ((++hub->nerrors < 10) || hub->error)
return; goto resubmit;
hub->error = urb->status; hub->error = urb->status;
/* FALL THROUGH */ /* FALL THROUGH */
...@@ -149,6 +150,12 @@ static void hub_irq(struct urb *urb) ...@@ -149,6 +150,12 @@ static void hub_irq(struct urb *urb)
wake_up(&khubd_wait); wake_up(&khubd_wait);
} }
spin_unlock_irqrestore(&hub_event_lock, flags); spin_unlock_irqrestore(&hub_event_lock, flags);
resubmit:
if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0)
err ("hub '%s-%s' status %d for interrupt resubmit",
urb->dev->bus->bus_name, urb->dev->devpath,
status);
} }
/* USB 2.0 spec Section 11.24.2.3 */ /* USB 2.0 spec Section 11.24.2.3 */
......
...@@ -287,12 +287,6 @@ static void sg_complete (struct urb *urb) ...@@ -287,12 +287,6 @@ static void sg_complete (struct urb *urb)
* *
* The request may be canceled with usb_sg_cancel(), either before or after * The request may be canceled with usb_sg_cancel(), either before or after
* usb_sg_wait() is called. * usb_sg_wait() is called.
*
* NOTE:
*
* At this writing, don't use the interrupt transfer mode, since the old old
* "automagic resubmit" mode hasn't yet been removed. It should be removed
* by the time 2.5 finalizes.
*/ */
int usb_sg_init ( int usb_sg_init (
struct usb_sg_request *io, struct usb_sg_request *io,
......
...@@ -108,45 +108,47 @@ struct urb * usb_get_urb(struct urb *urb) ...@@ -108,45 +108,47 @@ struct urb * usb_get_urb(struct urb *urb)
* *
* Successful submissions return 0; otherwise this routine returns a * Successful submissions return 0; otherwise this routine returns a
* negative error number. If the submission is successful, the complete() * negative error number. If the submission is successful, the complete()
* fuction of the urb will be called when the USB host driver is * callback from the urb will be called exactly once, when the USB core and
* finished with the urb (either a successful transmission, or some * host controller driver are finished with the urb. When the completion
* error case.)
*
* Unreserved Bandwidth Transfers:
*
* Bulk or control requests complete only once. When the completion
* function is called, control of the URB is returned to the device * function is called, control of the URB is returned to the device
* driver which issued the request. The completion handler may then * driver which issued the request. The completion handler may then
* immediately free or reuse that URB. * immediately free or reuse that URB.
* *
* Bulk URBs may be queued by submitting an URB to an endpoint before
* previous ones complete. This can maximize bandwidth utilization by
* letting the USB controller start work on the next URB without any
* delay to report completion (scheduling and processing an interrupt)
* and then submit that next request.
*
* For control endpoints, the synchronous usb_control_msg() call is * For control endpoints, the synchronous usb_control_msg() call is
* often used (in non-interrupt context) instead of this call. * often used (in non-interrupt context) instead of this call.
* That is often used through convenience wrappers, for the requests
* that are standardized in the USB 2.0 specification. For bulk
* endpoints, a synchronous usb_bulk_msg() call is available.
* *
* Reserved Bandwidth Transfers: * Request Queuing:
*
* URBs may be submitted to endpoints before previous ones complete, to
* minimize the impact of interrupt latencies and system overhead on data
* throughput. This is required for continuous isochronous data streams,
* and may also be required for some kinds of interrupt transfers. Such
* queueing also maximizes bandwidth utilization by letting USB controllers
* start work on later requests before driver software has finished the
* completion processing for earlier requests.
* *
* Periodic URBs (interrupt or isochronous) are performed repeatedly. * Bulk and Isochronous URBs may always be queued. At this writing, all
* mainstream host controller drivers support queueing for control and
* interrupt transfer requests.
* *
* For interrupt requests this is (currently) automagically done * Reserved Bandwidth Transfers:
* until the original request is aborted. When the completion callback
* indicates the URB has been unlinked (with a special status code),
* control of that URB returns to the device driver. Otherwise, the
* completion handler does not control the URB, and should not change
* any of its fields.
* *
* For isochronous requests, the completion handler is expected to * Periodic transfers (interrupt or isochronous) are performed repeatedly,
* submit an urb, typically resubmitting its parameter, until drivers * using the interval specified in the urb. Submitting the first urb to
* stop wanting data transfers. (For example, audio playback might have * the endpoint reserves the bandwidth necessary to make those transfers.
* finished, or a webcam turned off.) * If the USB subsystem can't allocate sufficient bandwidth to perform
* the periodic request, submitting such a periodic request should fail.
* *
* If the USB subsystem can't reserve sufficient bandwidth to perform * Device drivers must explicitly request that repetition, by ensuring that
* the periodic request, and bandwidth reservation is being done for * some URB is always on the endpoint's queue (except possibly for short
* this controller, submitting such a periodic request will fail. * periods during completion callacks). When there is no longer an urb
* queued, the endpoint's bandwidth reservation is canceled. This means
* drivers can use their completion handlers to ensure they keep bandwidth
* they need, by reinitializing and resubmitting the just-completed urb
* until the driver longer needs that periodic bandwidth.
* *
* Memory Flags: * Memory Flags:
* *
...@@ -356,8 +358,7 @@ int usb_submit_urb(struct urb *urb, int mem_flags) ...@@ -356,8 +358,7 @@ int usb_submit_urb(struct urb *urb, int mem_flags)
* This routine cancels an in-progress request. The requests's * This routine cancels an in-progress request. The requests's
* completion handler will be called with a status code indicating * completion handler will be called with a status code indicating
* that the request has been canceled, and that control of the URB * that the request has been canceled, and that control of the URB
* has been returned to that device driver. This is the only way * has been returned to that device driver.
* to stop an interrupt transfer, so long as the device is connected.
* *
* When the USB_ASYNC_UNLINK transfer flag for the URB is clear, this * When the USB_ASYNC_UNLINK transfer flag for the URB is clear, this
* request is synchronous. Success is indicated by returning zero, * request is synchronous. Success is indicated by returning zero,
......
...@@ -106,8 +106,6 @@ static const char hcd_name [] = "ehci-hcd"; ...@@ -106,8 +106,6 @@ static const char hcd_name [] = "ehci-hcd";
#define EHCI_STATS #define EHCI_STATS
#endif #endif
#define INTR_AUTOMAGIC /* to be removed later in 2.5 */
/* magic numbers that can affect system performance */ /* magic numbers that can affect system performance */
#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ #define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
#define EHCI_TUNE_RL_HS 0 /* nak throttle; see 4.9 */ #define EHCI_TUNE_RL_HS 0 /* nak throttle; see 4.9 */
......
...@@ -160,13 +160,6 @@ static inline void qtd_copy_status (struct urb *urb, size_t length, u32 token) ...@@ -160,13 +160,6 @@ static inline void qtd_copy_status (struct urb *urb, size_t length, u32 token)
static void ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb) static void ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb)
{ {
#ifdef INTR_AUTOMAGIC
struct urb *resubmit = 0;
struct usb_device *dev = 0;
static int ehci_urb_enqueue (struct usb_hcd *, struct urb *, int);
#endif
if (likely (urb->hcpriv != 0)) { if (likely (urb->hcpriv != 0)) {
struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv;
...@@ -175,14 +168,6 @@ static void ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb) ...@@ -175,14 +168,6 @@ static void ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb)
/* ... update hc-wide periodic stats (for usbfs) */ /* ... update hc-wide periodic stats (for usbfs) */
hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs--; hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs--;
#ifdef INTR_AUTOMAGIC
if (!((urb->status == -ENOENT)
|| (urb->status == -ECONNRESET))) {
resubmit = usb_get_urb (urb);
dev = urb->dev;
}
#endif
} }
qh_put (ehci, qh); qh_put (ehci, qh);
urb->hcpriv = 0; urb->hcpriv = 0;
...@@ -207,25 +192,6 @@ static void ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb) ...@@ -207,25 +192,6 @@ static void ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb)
spin_unlock (&ehci->lock); spin_unlock (&ehci->lock);
usb_hcd_giveback_urb (&ehci->hcd, urb); usb_hcd_giveback_urb (&ehci->hcd, urb);
#ifdef INTR_AUTOMAGIC
if (resubmit && ((urb->status == -ENOENT)
|| (urb->status == -ECONNRESET))) {
usb_put_urb (resubmit);
resubmit = 0;
}
// device drivers will soon be doing something like this
if (resubmit) {
int status;
resubmit->dev = dev;
status = SUBMIT_URB (resubmit, SLAB_KERNEL);
if (status != 0)
err ("can't resubmit interrupt urb %p: status %d",
resubmit, status);
usb_put_urb (resubmit);
}
#endif
spin_lock (&ehci->lock); spin_lock (&ehci->lock);
} }
......
...@@ -62,54 +62,6 @@ static void finish_urb (struct ohci_hcd *ohci, struct urb *urb) ...@@ -62,54 +62,6 @@ static void finish_urb (struct ohci_hcd *ohci, struct urb *urb)
usb_hcd_giveback_urb (&ohci->hcd, urb); usb_hcd_giveback_urb (&ohci->hcd, urb);
} }
static void td_submit_urb (struct ohci_hcd *ohci, struct urb *urb);
/* Report interrupt transfer completion, maybe reissue */
static inline void intr_resub (struct ohci_hcd *hc, struct urb *urb)
{
struct urb_priv *urb_priv = urb->hcpriv;
unsigned long flags;
// FIXME going away along with the rest of interrrupt automagic...
/* FIXME: MP race. If another CPU partially unlinks
* this URB (urb->status was updated, hasn't yet told
* us to dequeue) before we call complete() here, an
* extra "unlinked" completion will be reported...
*/
spin_lock_irqsave (&urb->lock, flags);
if (likely (urb->status == -EINPROGRESS))
urb->status = 0;
spin_unlock_irqrestore (&urb->lock, flags);
if (!(urb->transfer_flags & URB_NO_DMA_MAP)
&& usb_pipein (urb->pipe))
pci_dma_sync_single (hc->hcd.pdev, urb->transfer_dma,
urb->transfer_buffer_length,
PCI_DMA_FROMDEVICE);
#ifdef OHCI_VERBOSE_DEBUG
urb_print (urb, "INTR", usb_pipeout (urb->pipe));
#endif
urb->complete (urb);
/* always requeued, but ED_SKIP if complete() unlinks.
* EDs are removed from periodic table only at SOF intr.
*/
urb->actual_length = 0;
spin_lock_irqsave (&urb->lock, flags);
if (urb_priv->state != URB_DEL)
urb->status = -EINPROGRESS;
spin_unlock (&urb->lock);
/* syncing with PCI_DMA_TODEVICE is evidently trouble... */
spin_lock (&hc->lock);
td_submit_urb (hc, urb);
spin_unlock_irqrestore (&hc->lock, flags);
}
/*-------------------------------------------------------------------------* /*-------------------------------------------------------------------------*
* ED handling functions * ED handling functions
...@@ -1022,21 +974,9 @@ static void dl_done_list (struct ohci_hcd *ohci, struct td *td) ...@@ -1022,21 +974,9 @@ static void dl_done_list (struct ohci_hcd *ohci, struct td *td)
td_done (urb, td); td_done (urb, td);
urb_priv->td_cnt++; urb_priv->td_cnt++;
/* If all this urb's TDs are done, call complete(). /* If all this urb's TDs are done, call complete() */
* Interrupt transfers are the only special case:
* they're reissued, until "deleted" by usb_unlink_urb
* (real work done in a SOF intr, by finish_unlinks).
*/
if (urb_priv->td_cnt == urb_priv->length) { if (urb_priv->td_cnt == urb_priv->length) {
int resubmit;
resubmit = usb_pipeint (urb->pipe)
&& (urb_priv->state != URB_DEL);
spin_unlock_irqrestore (&ohci->lock, flags); spin_unlock_irqrestore (&ohci->lock, flags);
if (resubmit)
intr_resub (ohci, urb);
else
finish_urb (ohci, urb); finish_urb (ohci, urb);
spin_lock_irqsave (&ohci->lock, flags); spin_lock_irqsave (&ohci->lock, flags);
} }
......
...@@ -1247,10 +1247,11 @@ static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struc ...@@ -1247,10 +1247,11 @@ static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struc
static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb) static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
{ {
/* Interrupt-IN can't be more than 1 packet */ /* USB 1.1 interrupt transfers only involve one packet per interval;
if (usb_pipein(urb->pipe) && urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) * that's the uhci_submit_common() "breadth first" policy. Drivers
return -EINVAL; * can submit urbs of any length, but longer ones might need many
* intervals to complete.
*/
return uhci_submit_common(uhci, urb, eurb, uhci->skelqh[__interval_to_skel(urb->interval)]); return uhci_submit_common(uhci, urb, eurb, uhci->skelqh[__interval_to_skel(urb->interval)]);
} }
...@@ -1804,43 +1805,18 @@ static void uhci_free_pending_qhs(struct uhci_hcd *uhci) ...@@ -1804,43 +1805,18 @@ static void uhci_free_pending_qhs(struct uhci_hcd *uhci)
static void uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb) static void uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb)
{ {
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct usb_device *dev = urb->dev;
struct uhci_hcd *uhci = hcd_to_uhci(hcd); struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int killed, resubmit_interrupt, status, ret; int status;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&urb->lock, flags); spin_lock_irqsave(&urb->lock, flags);
killed = (urb->status == -ENOENT || urb->status == -ECONNRESET);
resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT &&
urb->interval && !killed);
status = urbp->status; status = urbp->status;
uhci_destroy_urb_priv(uhci, urb); uhci_destroy_urb_priv(uhci, urb);
if (!killed) if (urb->status != -ENOENT && urb->status != -ECONNRESET)
urb->status = status; urb->status = status;
spin_unlock_irqrestore(&urb->lock, flags); spin_unlock_irqrestore(&urb->lock, flags);
if (resubmit_interrupt) {
urb->complete(urb);
/* Recheck the status. The completion handler may have */
/* unlinked the resubmitting interrupt URB */
/* Note that this doesn't do what usb_hcd_giveback_urb() */
/* normally does, so that doesn't ever get done. */
if (urb->status == -ECONNRESET) {
usb_put_urb(urb);
return;
}
urb->dev = dev;
urb->status = -EINPROGRESS;
urb->actual_length = 0;
urb->bandwidth = 0;
if ((ret = uhci_urb_enqueue(&uhci->hcd, urb, 0)))
printk(KERN_ERR __FILE__ ": could not resubmit interrupt URB : %d\n", ret);
} else
usb_hcd_giveback_urb(hcd, urb); usb_hcd_giveback_urb(hcd, urb);
} }
......
...@@ -900,12 +900,26 @@ static int hid_input_report(int type, struct urb *urb) ...@@ -900,12 +900,26 @@ static int hid_input_report(int type, struct urb *urb)
static void hid_irq_in(struct urb *urb) static void hid_irq_in(struct urb *urb)
{ {
if (urb->status) { struct hid_device *hid = urb->context;
dbg("nonzero status in input irq %d", urb->status); int status;
switch (urb->status) {
case 0: /* success */
hid_input_report(HID_INPUT_REPORT, urb);
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return; return;
default: /* error */
dbg("nonzero status in input irq %d", urb->status);
} }
hid_input_report(HID_INPUT_REPORT, urb); status = usb_submit_urb (urb, SLAB_ATOMIC);
if (status)
err ("can't resubmit intr, %s-%s/input%d, status %d",
hid->dev->bus->bus_name, hid->dev->devpath,
hid->ifnum, status);
} }
/* /*
......
...@@ -87,7 +87,17 @@ static void usb_kbd_irq(struct urb *urb) ...@@ -87,7 +87,17 @@ static void usb_kbd_irq(struct urb *urb)
struct usb_kbd *kbd = urb->context; struct usb_kbd *kbd = urb->context;
int i; int i;
if (urb->status) return; switch (urb->status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
input_report_key(&kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1); input_report_key(&kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
...@@ -112,6 +122,13 @@ static void usb_kbd_irq(struct urb *urb) ...@@ -112,6 +122,13 @@ static void usb_kbd_irq(struct urb *urb)
input_sync(&kbd->dev); input_sync(&kbd->dev);
memcpy(kbd->old, kbd->new, 8); memcpy(kbd->old, kbd->new, 8);
resubmit:
i = usb_submit_urb (urb, SLAB_ATOMIC);
if (i)
err ("can't resubmit intr, %s-%s/input0, status %d",
kbd->usbdev->bus->bus_name,
kbd->usbdev->devpath, i);
} }
int usb_kbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) int usb_kbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
......
...@@ -62,8 +62,20 @@ static void usb_mouse_irq(struct urb *urb) ...@@ -62,8 +62,20 @@ static void usb_mouse_irq(struct urb *urb)
struct usb_mouse *mouse = urb->context; struct usb_mouse *mouse = urb->context;
signed char *data = mouse->data; signed char *data = mouse->data;
struct input_dev *dev = &mouse->dev; struct input_dev *dev = &mouse->dev;
int status;
switch (urb->status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
if (urb->status) return;
input_report_key(dev, BTN_LEFT, data[0] & 0x01); input_report_key(dev, BTN_LEFT, data[0] & 0x01);
input_report_key(dev, BTN_RIGHT, data[0] & 0x02); input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
...@@ -76,6 +88,12 @@ static void usb_mouse_irq(struct urb *urb) ...@@ -76,6 +88,12 @@ static void usb_mouse_irq(struct urb *urb)
input_report_rel(dev, REL_WHEEL, data[3]); input_report_rel(dev, REL_WHEEL, data[3]);
input_sync(dev); input_sync(dev);
resubmit:
status = usb_submit_urb (urb, SLAB_ATOMIC);
if (status)
err ("can't resubmit intr, %s-%s/input0, status %d",
mouse->usbdev->bus->bus_name,
mouse->usbdev->devpath, status);
} }
static int usb_mouse_open(struct input_dev *dev) static int usb_mouse_open(struct input_dev *dev)
......
...@@ -449,7 +449,10 @@ static int ch9_postconfig (struct usbtest_dev *dev) ...@@ -449,7 +449,10 @@ static int ch9_postconfig (struct usbtest_dev *dev)
if (udev->descriptor.bNumConfigurations != 1) { if (udev->descriptor.bNumConfigurations != 1) {
int expected = udev->actconfig->bConfigurationValue; int expected = udev->actconfig->bConfigurationValue;
/* [9.4.2] get_configuration always works */ /* [9.4.2] get_configuration always works
* ... although some cheap devices (like one TI Hub I've got)
* won't return config descriptors except before set_config.
*/
retval = usb_control_msg (udev, usb_rcvctrlpipe (udev, 0), retval = usb_control_msg (udev, usb_rcvctrlpipe (udev, 0),
USB_REQ_GET_CONFIGURATION, USB_RECIP_DEVICE, USB_REQ_GET_CONFIGURATION, USB_RECIP_DEVICE,
0, 0, dev->buf, 1, HZ * USB_CTRL_GET_TIMEOUT); 0, 0, dev->buf, 1, HZ * USB_CTRL_GET_TIMEOUT);
...@@ -543,6 +546,10 @@ static int ch9_postconfig (struct usbtest_dev *dev) ...@@ -543,6 +546,10 @@ static int ch9_postconfig (struct usbtest_dev *dev)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
// control queueing !!
/*-------------------------------------------------------------------------*/
/* We only have this one interface to user space, through usbfs. /* We only have this one interface to user space, through usbfs.
* User mode code can scan usbfs to find N different devices (maybe on * User mode code can scan usbfs to find N different devices (maybe on
* different busses) to use when testing, and allocate one thread per * different busses) to use when testing, and allocate one thread per
...@@ -839,14 +846,6 @@ usbtest_probe (struct usb_interface *intf, const struct usb_device_id *id) ...@@ -839,14 +846,6 @@ usbtest_probe (struct usb_interface *intf, const struct usb_device_id *id)
dev->out_pipe = usb_sndintpipe (udev, info->ep_out); dev->out_pipe = usb_sndintpipe (udev, info->ep_out);
wtest = " intr-out"; wtest = " intr-out";
} }
#if 1
// FIXME disabling this until we finally get rid of
// interrupt "automagic" resubmission
dbg ("%s: no interrupt transfers for now", dev->id);
kfree (dev);
return -ENODEV;
#endif
} else { } else {
if (info->ep_in) { if (info->ep_in) {
dev->in_pipe = usb_rcvbulkpipe (udev, info->ep_in); dev->in_pipe = usb_rcvbulkpipe (udev, info->ep_in);
......
...@@ -308,9 +308,17 @@ static void catc_irq_done(struct urb *urb) ...@@ -308,9 +308,17 @@ static void catc_irq_done(struct urb *urb)
linksts = LinkBad; linksts = LinkBad;
} }
if (urb->status) { switch (urb->status) {
dbg("irq_done, status %d, data %02x %02x.", urb->status, data[0], data[1]); case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return; return;
/* -EPIPE: should clear the halt */
default: /* error */
dbg("irq_done, status %d, data %02x %02x.", urb->status, data[0], data[1]);
goto resubmit;
} }
if (linksts == LinkGood) { if (linksts == LinkGood) {
...@@ -334,6 +342,12 @@ static void catc_irq_done(struct urb *urb) ...@@ -334,6 +342,12 @@ static void catc_irq_done(struct urb *urb)
} }
} }
} }
resubmit:
status = usb_submit_urb (urb, SLAB_ATOMIC);
if (status)
err ("can't resubmit intr, %s-%s, status %d",
catc->usbdev->bus->bus_name,
catc->usbdev->devpath, status);
} }
/* /*
......
...@@ -471,12 +471,24 @@ static int kaweth_resubmit_rx_urb(struct kaweth_device *, int); ...@@ -471,12 +471,24 @@ static int kaweth_resubmit_rx_urb(struct kaweth_device *, int);
static void int_callback(struct urb *u) static void int_callback(struct urb *u)
{ {
struct kaweth_device *kaweth = u->context; struct kaweth_device *kaweth = u->context;
int act_state; int act_state, status;
/* we abuse the interrupt urb for rebsubmitting under low memory saving a timer */ /* we abuse the interrupt urb for rebsubmitting under low memory saving a timer */
if (kaweth->suspend_lowmem) if (kaweth->suspend_lowmem)
kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC); kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
switch (u->status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
/* we check the link state to report changes */ /* we check the link state to report changes */
if (kaweth->linkstate != (act_state = ( kaweth->intbuffer[STATE_OFFSET] | STATE_MASK) >> STATE_SHIFT)) { if (kaweth->linkstate != (act_state = ( kaweth->intbuffer[STATE_OFFSET] | STATE_MASK) >> STATE_SHIFT)) {
if (!act_state) if (!act_state)
...@@ -486,7 +498,12 @@ static void int_callback(struct urb *u) ...@@ -486,7 +498,12 @@ static void int_callback(struct urb *u)
kaweth->linkstate = act_state; kaweth->linkstate = act_state;
} }
resubmit:
status = usb_submit_urb (u, SLAB_ATOMIC);
if (status)
err ("can't resubmit intr, %s-%s, status %d",
kaweth->dev->bus->bus_name,
kaweth->dev->devpath, status);
} }
/**************************************************************** /****************************************************************
......
...@@ -670,6 +670,7 @@ static void intr_callback(struct urb *urb) ...@@ -670,6 +670,7 @@ static void intr_callback(struct urb *urb)
pegasus_t *pegasus = urb->context; pegasus_t *pegasus = urb->context;
struct net_device *net; struct net_device *net;
__u8 *d; __u8 *d;
int status;
if (!pegasus) if (!pegasus)
return; return;
...@@ -677,7 +678,9 @@ static void intr_callback(struct urb *urb) ...@@ -677,7 +678,9 @@ static void intr_callback(struct urb *urb)
switch (urb->status) { switch (urb->status) {
case 0: case 0:
break; break;
case -ECONNRESET: /* unlink */
case -ENOENT: case -ENOENT:
case -ESHUTDOWN:
return; return;
default: default:
info("intr status %d", urb->status); info("intr status %d", urb->status);
...@@ -700,6 +703,11 @@ static void intr_callback(struct urb *urb) ...@@ -700,6 +703,11 @@ static void intr_callback(struct urb *urb)
netif_carrier_on(net); netif_carrier_on(net);
} }
} }
status = usb_submit_urb (urb, SLAB_ATOMIC);
if (status)
err ("%s: can't resubmit interrupt urb, %d",
net->name, status);
} }
static void pegasus_tx_timeout(struct net_device *net) static void pegasus_tx_timeout(struct net_device *net)
......
...@@ -454,18 +454,32 @@ static void write_bulk_callback(struct urb *urb) ...@@ -454,18 +454,32 @@ static void write_bulk_callback(struct urb *urb)
void intr_callback(struct urb *urb) void intr_callback(struct urb *urb)
{ {
rtl8150_t *dev; rtl8150_t *dev;
int status;
dev = urb->context; dev = urb->context;
if (!dev) if (!dev)
return; return;
switch (urb->status) { switch (urb->status) {
case 0: case 0: /* success */
break; break;
case -ECONNRESET: /* unlink */
case -ENOENT: case -ENOENT:
case -ESHUTDOWN:
return; return;
/* -EPIPE: should clear the halt */
default: default:
info("%s: intr status %d", dev->netdev->name, urb->status); info("%s: intr status %d", dev->netdev->name, urb->status);
goto resubmit;
} }
/* FIXME if this doesn't do anything, don't submit the urb! */
resubmit:
status = usb_submit_urb (urb, SLAB_ATOMIC);
if (status)
err ("can't resubmit intr, %s-%s/input0, status %d",
dev->udev->bus->bus_name,
dev->udev->devpath, status);
} }
/* /*
......
...@@ -791,8 +791,7 @@ typedef void (*usb_complete_t)(struct urb *); ...@@ -791,8 +791,7 @@ typedef void (*usb_complete_t)(struct urb *);
* @context: For use in completion functions. This normally points to * @context: For use in completion functions. This normally points to
* request-specific driver context. * request-specific driver context.
* @complete: Completion handler. This URB is passed as the parameter to the * @complete: Completion handler. This URB is passed as the parameter to the
* completion function. Except for interrupt or isochronous transfers * completion function. The completion function may then do what
* that aren't being unlinked, the completion function may then do what
* it likes with the URB, including resubmitting or freeing it. * it likes with the URB, including resubmitting or freeing it.
* @iso_frame_desc: Used to provide arrays of ISO transfer buffers and to * @iso_frame_desc: Used to provide arrays of ISO transfer buffers and to
* collect the transfer status for each buffer. * collect the transfer status for each buffer.
...@@ -885,11 +884,6 @@ typedef void (*usb_complete_t)(struct urb *); ...@@ -885,11 +884,6 @@ typedef void (*usb_complete_t)(struct urb *);
* When completion callback is invoked for non-isochronous URBs, the * When completion callback is invoked for non-isochronous URBs, the
* actual_length field tells how many bytes were transferred. * actual_length field tells how many bytes were transferred.
* *
* For interrupt URBs, the URB provided to the callback
* function is still "owned" by the USB core subsystem unless the status
* indicates that the URB has been unlinked. Completion handlers should
* not modify such URBs until they have been unlinked.
*
* ISO transfer status is reported in the status and actual_length fields * ISO transfer status is reported in the status and actual_length fields
* of the iso_frame_desc array, and the number of errors is reported in * of the iso_frame_desc array, and the number of errors is reported in
* error_count. Completion callbacks for ISO transfers will normally * error_count. Completion callbacks for ISO transfers will normally
......
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