aboutsummaryrefslogtreecommitdiffstats
path: root/usb.current
diff options
authorGreg Kroah-Hartman <gregkh@suse.de>2009-04-15 21:35:14 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2009-04-15 21:35:14 -0700
commit4cc7aece738b4f3c8ab49a170297f871e1c5a804 (patch)
treeb29d5cb0e2021992e2a07b22858cf1f4d618130a /usb.current
parent98dc1382efb52ca1423870b2b0673eac399857c1 (diff)
downloadpatches-4cc7aece738b4f3c8ab49a170297f871e1c5a804.tar.gz
usb patches
Diffstat (limited to 'usb.current')
-rw-r--r--usb.current/usb-add-reset-endpoint-operations.patch398
-rw-r--r--usb.current/usb-serial-ti_usb_3410_5052.c-ti_usb-returns-eio-when-reopening-the-device.patch39
-rw-r--r--usb.current/usb-whci-hcd-check-return-value-of-usb_hcd_link_urb_to_ep.patch94
-rw-r--r--usb.current/usb-whci-hcd-provide-a-endpoint_reset-method.patch167
-rw-r--r--usb.current/wusb-correct-format-of-wusb_chid-sysfs-file.patch69
-rw-r--r--usb.current/wusb-disconnect-all-devices-when-stopping-a-wusb-hcd.patch58
-rw-r--r--usb.current/wusb-fix-oops-when-completing-urbs-for-disconnected-devices.patch53
7 files changed, 878 insertions, 0 deletions
diff --git a/usb.current/usb-add-reset-endpoint-operations.patch b/usb.current/usb-add-reset-endpoint-operations.patch
new file mode 100644
index 00000000000000..6f1fb8654577dc
--- /dev/null
+++ b/usb.current/usb-add-reset-endpoint-operations.patch
@@ -0,0 +1,398 @@
+From dvrabel@hera.kernel.org Wed Apr 15 20:47:43 2009
+From: David Vrabel <david.vrabel@csr.com>
+Date: Wed, 8 Apr 2009 17:36:28 +0000
+Subject: USB: add reset endpoint operations
+To: Greg KH <gregkh@suse.de>
+Cc: David Vrabel <david.vrabel@csr.com>
+Message-ID: <1239212193-27618-2-git-send-email-david.vrabel@csr.com>
+
+
+Wireless USB endpoint state has a sequence number and a current
+window and not just a single toggle bit. So allow HCDs to provide a
+endpoint_reset method and call this or clear the software toggles as
+required (after a clear halt, set configuration etc.).
+
+usb_settoggle() and friends are then HCD internal and are moved into
+core/hcd.h and all device drivers call usb_reset_endpoint() instead.
+
+If the device endpoint state has been reset (with a clear halt) but
+the host endpoint state has not then subsequent data transfers will
+not complete. The device will only work again after it is reset or
+disconnected.
+
+Signed-off-by: David Vrabel <david.vrabel@csr.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+---
+ drivers/block/ub.c | 20 ++++------
+ drivers/isdn/hisax/st5481_usb.c | 9 ----
+ drivers/media/video/pvrusb2/pvrusb2-hdw.c | 1
+ drivers/usb/core/devio.c | 2 -
+ drivers/usb/core/hcd.c | 26 +++++++++++++
+ drivers/usb/core/hcd.h | 14 +++++++
+ drivers/usb/core/message.c | 58 +++++++++++++++++++-----------
+ drivers/usb/core/usb.c | 2 -
+ drivers/usb/storage/transport.c | 4 --
+ include/linux/usb.h | 9 ----
+ 10 files changed, 91 insertions(+), 54 deletions(-)
+
+--- a/drivers/block/ub.c
++++ b/drivers/block/ub.c
+@@ -1025,6 +1025,7 @@ static void ub_scsi_urb_compl(struct ub_
+ {
+ struct urb *urb = &sc->work_urb;
+ struct bulk_cs_wrap *bcs;
++ int endp;
+ int len;
+ int rc;
+
+@@ -1033,6 +1034,10 @@ static void ub_scsi_urb_compl(struct ub_
+ return;
+ }
+
++ endp = usb_pipeendpoint(sc->last_pipe);
++ if (usb_pipein(sc->last_pipe))
++ endp |= USB_DIR_IN;
++
+ if (cmd->state == UB_CMDST_CLEAR) {
+ if (urb->status == -EPIPE) {
+ /*
+@@ -1048,9 +1053,7 @@ static void ub_scsi_urb_compl(struct ub_
+ * We ignore the result for the halt clear.
+ */
+
+- /* reset the endpoint toggle */
+- usb_settoggle(sc->dev, usb_pipeendpoint(sc->last_pipe),
+- usb_pipeout(sc->last_pipe), 0);
++ usb_reset_endpoint(sc->dev, endp);
+
+ ub_state_sense(sc, cmd);
+
+@@ -1065,9 +1068,7 @@ static void ub_scsi_urb_compl(struct ub_
+ * We ignore the result for the halt clear.
+ */
+
+- /* reset the endpoint toggle */
+- usb_settoggle(sc->dev, usb_pipeendpoint(sc->last_pipe),
+- usb_pipeout(sc->last_pipe), 0);
++ usb_reset_endpoint(sc->dev, endp);
+
+ ub_state_stat(sc, cmd);
+
+@@ -1082,9 +1083,7 @@ static void ub_scsi_urb_compl(struct ub_
+ * We ignore the result for the halt clear.
+ */
+
+- /* reset the endpoint toggle */
+- usb_settoggle(sc->dev, usb_pipeendpoint(sc->last_pipe),
+- usb_pipeout(sc->last_pipe), 0);
++ usb_reset_endpoint(sc->dev, endp);
+
+ ub_state_stat_counted(sc, cmd);
+
+@@ -2119,8 +2118,7 @@ static int ub_probe_clear_stall(struct u
+ del_timer_sync(&timer);
+ usb_kill_urb(&sc->work_urb);
+
+- /* reset the endpoint toggle */
+- usb_settoggle(sc->dev, endp, usb_pipeout(sc->last_pipe), 0);
++ usb_reset_endpoint(sc->dev, endp);
+
+ return 0;
+ }
+--- a/drivers/isdn/hisax/st5481_usb.c
++++ b/drivers/isdn/hisax/st5481_usb.c
+@@ -149,14 +149,7 @@ static void usb_ctrl_complete(struct urb
+ if (ctrl_msg->dr.bRequest == USB_REQ_CLEAR_FEATURE) {
+ /* Special case handling for pipe reset */
+ le16_to_cpus(&ctrl_msg->dr.wIndex);
+-
+- /* toggle is reset on clear */
+- usb_settoggle(adapter->usb_dev,
+- ctrl_msg->dr.wIndex & ~USB_DIR_IN,
+- (ctrl_msg->dr.wIndex & USB_DIR_IN) == 0,
+- 0);
+-
+-
++ usb_reset_endpoint(adapter->usb_dev, ctrl_msg->dr.wIndex);
+ }
+
+ if (ctrl_msg->complete)
+--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
++++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+@@ -1461,7 +1461,6 @@ static int pvr2_upload_firmware1(struct
+ return ret;
+ }
+
+- usb_settoggle(hdw->usb_dev, 0 & 0xf, !(0 & USB_DIR_IN), 0);
+ usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f));
+
+ pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
+--- a/drivers/usb/core/devio.c
++++ b/drivers/usb/core/devio.c
+@@ -841,7 +841,7 @@ static int proc_resetep(struct dev_state
+ ret = checkintf(ps, ret);
+ if (ret)
+ return ret;
+- usb_settoggle(ps->dev, ep & 0xf, !(ep & USB_DIR_IN), 0);
++ usb_reset_endpoint(ps->dev, ep);
+ return 0;
+ }
+
+--- a/drivers/usb/core/hcd.c
++++ b/drivers/usb/core/hcd.c
+@@ -1539,6 +1539,32 @@ void usb_hcd_disable_endpoint(struct usb
+ hcd->driver->endpoint_disable(hcd, ep);
+ }
+
++/**
++ * usb_hcd_reset_endpoint - reset host endpoint state
++ * @udev: USB device.
++ * @ep: the endpoint to reset.
++ *
++ * Resets any host endpoint state such as the toggle bit, sequence
++ * number and current window.
++ */
++void usb_hcd_reset_endpoint(struct usb_device *udev,
++ struct usb_host_endpoint *ep)
++{
++ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
++
++ if (hcd->driver->endpoint_reset)
++ hcd->driver->endpoint_reset(hcd, ep);
++ else {
++ int epnum = usb_endpoint_num(&ep->desc);
++ int is_out = usb_endpoint_dir_out(&ep->desc);
++ int is_control = usb_endpoint_xfer_control(&ep->desc);
++
++ usb_settoggle(udev, epnum, is_out, 0);
++ if (is_control)
++ usb_settoggle(udev, epnum, !is_out, 0);
++ }
++}
++
+ /* Protect against drivers that try to unlink URBs after the device
+ * is gone, by waiting until all unlinks for @udev are finished.
+ * Since we don't currently track URBs by device, simply wait until
+--- a/drivers/usb/core/hcd.h
++++ b/drivers/usb/core/hcd.h
+@@ -206,6 +206,11 @@ struct hc_driver {
+ void (*endpoint_disable)(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep);
+
++ /* (optional) reset any endpoint state such as sequence number
++ and current window */
++ void (*endpoint_reset)(struct usb_hcd *hcd,
++ struct usb_host_endpoint *ep);
++
+ /* root hub support */
+ int (*hub_status_data) (struct usb_hcd *hcd, char *buf);
+ int (*hub_control) (struct usb_hcd *hcd,
+@@ -234,6 +239,8 @@ extern void usb_hcd_flush_endpoint(struc
+ struct usb_host_endpoint *ep);
+ extern void usb_hcd_disable_endpoint(struct usb_device *udev,
+ struct usb_host_endpoint *ep);
++extern void usb_hcd_reset_endpoint(struct usb_device *udev,
++ struct usb_host_endpoint *ep);
+ extern void usb_hcd_synchronize_unlinks(struct usb_device *udev);
+ extern int usb_hcd_get_frame_number(struct usb_device *udev);
+
+@@ -279,6 +286,13 @@ extern irqreturn_t usb_hcd_irq(int irq,
+ extern void usb_hc_died(struct usb_hcd *hcd);
+ extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd);
+
++/* The D0/D1 toggle bits ... USE WITH CAUTION (they're almost hcd-internal) */
++#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1)
++#define usb_dotoggle(dev, ep, out) ((dev)->toggle[out] ^= (1 << (ep)))
++#define usb_settoggle(dev, ep, out, bit) \
++ ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << (ep))) | \
++ ((bit) << (ep)))
++
+ /* -------------------------------------------------------------------------- */
+
+ /* Enumeration is only for the hub driver, or HCD virtual root hubs */
+--- a/drivers/usb/core/message.c
++++ b/drivers/usb/core/message.c
+@@ -1002,8 +1002,7 @@ int usb_clear_halt(struct usb_device *de
+ * the copy in usb-storage, for as long as we need two copies.
+ */
+
+- /* toggle was reset by the clear */
+- usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0);
++ usb_reset_endpoint(dev, endp);
+
+ return 0;
+ }
+@@ -1076,6 +1075,30 @@ void usb_disable_endpoint(struct usb_dev
+ }
+
+ /**
++ * usb_reset_endpoint - Reset an endpoint's state.
++ * @dev: the device whose endpoint is to be reset
++ * @epaddr: the endpoint's address. Endpoint number for output,
++ * endpoint number + USB_DIR_IN for input
++ *
++ * Resets any host-side endpoint state such as the toggle bit,
++ * sequence number or current window.
++ */
++void usb_reset_endpoint(struct usb_device *dev, unsigned int epaddr)
++{
++ unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
++ struct usb_host_endpoint *ep;
++
++ if (usb_endpoint_out(epaddr))
++ ep = dev->ep_out[epnum];
++ else
++ ep = dev->ep_in[epnum];
++ if (ep)
++ usb_hcd_reset_endpoint(dev, ep);
++}
++EXPORT_SYMBOL_GPL(usb_reset_endpoint);
++
++
++/**
+ * usb_disable_interface -- Disable all endpoints for an interface
+ * @dev: the device whose interface is being disabled
+ * @intf: pointer to the interface descriptor
+@@ -1117,7 +1140,6 @@ void usb_disable_device(struct usb_devic
+ usb_disable_endpoint(dev, i, true);
+ usb_disable_endpoint(dev, i + USB_DIR_IN, true);
+ }
+- dev->toggle[0] = dev->toggle[1] = 0;
+
+ /* getting rid of interfaces will disconnect
+ * any drivers bound to them (a key side effect)
+@@ -1154,28 +1176,24 @@ void usb_disable_device(struct usb_devic
+ * usb_enable_endpoint - Enable an endpoint for USB communications
+ * @dev: the device whose interface is being enabled
+ * @ep: the endpoint
+- * @reset_toggle: flag to set the endpoint's toggle back to 0
++ * @reset_ep: flag to reset the endpoint state
+ *
+- * Resets the endpoint toggle if asked, and sets dev->ep_{in,out} pointers.
++ * Resets the endpoint state if asked, and sets dev->ep_{in,out} pointers.
+ * For control endpoints, both the input and output sides are handled.
+ */
+ void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep,
+- bool reset_toggle)
++ bool reset_ep)
+ {
+ int epnum = usb_endpoint_num(&ep->desc);
+ int is_out = usb_endpoint_dir_out(&ep->desc);
+ int is_control = usb_endpoint_xfer_control(&ep->desc);
+
+- if (is_out || is_control) {
+- if (reset_toggle)
+- usb_settoggle(dev, epnum, 1, 0);
++ if (reset_ep)
++ usb_hcd_reset_endpoint(dev, ep);
++ if (is_out || is_control)
+ dev->ep_out[epnum] = ep;
+- }
+- if (!is_out || is_control) {
+- if (reset_toggle)
+- usb_settoggle(dev, epnum, 0, 0);
++ if (!is_out || is_control)
+ dev->ep_in[epnum] = ep;
+- }
+ ep->enabled = 1;
+ }
+
+@@ -1183,18 +1201,18 @@ void usb_enable_endpoint(struct usb_devi
+ * usb_enable_interface - Enable all the endpoints for an interface
+ * @dev: the device whose interface is being enabled
+ * @intf: pointer to the interface descriptor
+- * @reset_toggles: flag to set the endpoints' toggles back to 0
++ * @reset_eps: flag to reset the endpoints' state
+ *
+ * Enables all the endpoints for the interface's current altsetting.
+ */
+ void usb_enable_interface(struct usb_device *dev,
+- struct usb_interface *intf, bool reset_toggles)
++ struct usb_interface *intf, bool reset_eps)
+ {
+ struct usb_host_interface *alt = intf->cur_altsetting;
+ int i;
+
+ for (i = 0; i < alt->desc.bNumEndpoints; ++i)
+- usb_enable_endpoint(dev, &alt->endpoint[i], reset_toggles);
++ usb_enable_endpoint(dev, &alt->endpoint[i], reset_eps);
+ }
+
+ /**
+@@ -1335,7 +1353,7 @@ EXPORT_SYMBOL_GPL(usb_set_interface);
+ * This issues a standard SET_CONFIGURATION request to the device using
+ * the current configuration. The effect is to reset most USB-related
+ * state in the device, including interface altsettings (reset to zero),
+- * endpoint halts (cleared), and data toggle (only for bulk and interrupt
++ * endpoint halts (cleared), and endpoint state (only for bulk and interrupt
+ * endpoints). Other usbcore state is unchanged, including bindings of
+ * usb device drivers to interfaces.
+ *
+@@ -1343,7 +1361,7 @@ EXPORT_SYMBOL_GPL(usb_set_interface);
+ * (multi-interface) devices. Instead, the driver for each interface may
+ * use usb_set_interface() on the interfaces it claims. Be careful though;
+ * some devices don't support the SET_INTERFACE request, and others won't
+- * reset all the interface state (notably data toggles). Resetting the whole
++ * reset all the interface state (notably endpoint state). Resetting the whole
+ * configuration would affect other drivers' interfaces.
+ *
+ * The caller must own the device lock.
+@@ -1376,8 +1394,6 @@ int usb_reset_configuration(struct usb_d
+ if (retval < 0)
+ return retval;
+
+- dev->toggle[0] = dev->toggle[1] = 0;
+-
+ /* re-init hc/hcd interface/endpoint state */
+ for (i = 0; i < config->desc.bNumInterfaces; i++) {
+ struct usb_interface *intf = config->interface[i];
+--- a/drivers/usb/core/usb.c
++++ b/drivers/usb/core/usb.c
+@@ -362,7 +362,7 @@ struct usb_device *usb_alloc_dev(struct
+ dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
+ dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
+ /* ep0 maxpacket comes later, from device descriptor */
+- usb_enable_endpoint(dev, &dev->ep0, true);
++ usb_enable_endpoint(dev, &dev->ep0, false);
+ dev->can_submit = 1;
+
+ /* Save readable and stable topology id, distinguishing devices
+--- a/drivers/usb/storage/transport.c
++++ b/drivers/usb/storage/transport.c
+@@ -247,10 +247,8 @@ int usb_stor_clear_halt(struct us_data *
+ USB_ENDPOINT_HALT, endp,
+ NULL, 0, 3*HZ);
+
+- /* reset the endpoint toggle */
+ if (result >= 0)
+- usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe),
+- usb_pipeout(pipe), 0);
++ usb_reset_endpoint(us->pusb_dev, endp);
+
+ US_DEBUGP("%s: result = %d\n", __func__, result);
+ return result;
+--- a/include/linux/usb.h
++++ b/include/linux/usb.h
+@@ -1387,6 +1387,7 @@ extern int usb_string(struct usb_device
+ extern int usb_clear_halt(struct usb_device *dev, int pipe);
+ extern int usb_reset_configuration(struct usb_device *dev);
+ extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate);
++extern void usb_reset_endpoint(struct usb_device *dev, unsigned int epaddr);
+
+ /* this request isn't really synchronous, but it belongs with the others */
+ extern int usb_driver_set_configuration(struct usb_device *udev, int config);
+@@ -1491,14 +1492,6 @@ void usb_sg_wait(struct usb_sg_request *
+ #define usb_pipecontrol(pipe) (usb_pipetype((pipe)) == PIPE_CONTROL)
+ #define usb_pipebulk(pipe) (usb_pipetype((pipe)) == PIPE_BULK)
+
+-/* The D0/D1 toggle bits ... USE WITH CAUTION (they're almost hcd-internal) */
+-#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1)
+-#define usb_dotoggle(dev, ep, out) ((dev)->toggle[out] ^= (1 << (ep)))
+-#define usb_settoggle(dev, ep, out, bit) \
+- ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << (ep))) | \
+- ((bit) << (ep)))
+-
+-
+ static inline unsigned int __create_pipe(struct usb_device *dev,
+ unsigned int endpoint)
+ {
diff --git a/usb.current/usb-serial-ti_usb_3410_5052.c-ti_usb-returns-eio-when-reopening-the-device.patch b/usb.current/usb-serial-ti_usb_3410_5052.c-ti_usb-returns-eio-when-reopening-the-device.patch
new file mode 100644
index 00000000000000..77f7bcde8b53b0
--- /dev/null
+++ b/usb.current/usb-serial-ti_usb_3410_5052.c-ti_usb-returns-eio-when-reopening-the-device.patch
@@ -0,0 +1,39 @@
+From akpm@linux-foundation.org Wed Apr 15 21:31:15 2009
+From: Christoph Mair <christoph.mair@gmail.com>
+Date: Wed, 15 Apr 2009 16:41:32 -0700
+Subject: USB: serial: ti_usb_3410_5052.c: ti_usb returns EIO when reopening the device
+To: mm-commits@vger.kernel.org
+Cc: christoph.mair@gmail.com, alan@lxorguk.ukuu.org.uk, greg@kroah.com, stable@kernel.org
+Message-ID: <200904152341.n3FNfWj2010587@imap1.linux-foundation.org>
+
+From: Christoph Mair <christoph.mair@gmail.com>
+
+Fix regression introduced by 4a90f09b20f4622dcbff1f0e1e6bae1704f8ad8c
+("tty: usb-serial krefs").
+
+The driver works as expected until you close the device and try to reopen
+it. All you get from now on, is an I/O error. It seems that something
+isn't released correctly, because the refcount of the usbserial module
+does not drop to zero, even when unloading all dependent drivers.
+
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
+Cc: <stable@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/ti_usb_3410_5052.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/serial/ti_usb_3410_5052.c
++++ b/drivers/usb/serial/ti_usb_3410_5052.c
+@@ -1231,8 +1231,8 @@ static void ti_bulk_in_callback(struct u
+ tport->tp_icount.rx += urb->actual_length;
+ spin_unlock(&tport->tp_lock);
+ }
+- tty_kref_put(tty);
+ }
++ tty_kref_put(tty);
+
+ exit:
+ /* continue to read unless stopping */
diff --git a/usb.current/usb-whci-hcd-check-return-value-of-usb_hcd_link_urb_to_ep.patch b/usb.current/usb-whci-hcd-check-return-value-of-usb_hcd_link_urb_to_ep.patch
new file mode 100644
index 00000000000000..d14e9083ef77a0
--- /dev/null
+++ b/usb.current/usb-whci-hcd-check-return-value-of-usb_hcd_link_urb_to_ep.patch
@@ -0,0 +1,94 @@
+From dvrabel@hera.kernel.org Wed Apr 15 21:02:24 2009
+From: David Vrabel <david.vrabel@csr.com>
+Date: Wed, 8 Apr 2009 17:36:31 +0000
+Subject: USB: whci-hcd: check return value of usb_hcd_link_urb_to_ep()
+To: Greg KH <gregkh@suse.de>
+Cc: David Vrabel <david.vrabel@csr.com>
+Message-ID: <1239212193-27618-5-git-send-email-david.vrabel@csr.com>
+
+
+Check the return value of usb_hcd_link_urb_to_ep() and do not add the
+urb to the ASL/PZL if it returns an error.
+
+Omitting the check results in urbs that appear to be submitted
+successfully but then cannot be unliked (because
+usb_hcd_check_unlink_urb() returns an error). This can cause khubd (for
+example) to block forever in usb_kill_urb().
+
+Signed-off-by: David Vrabel <david.vrabel@csr.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+---
+ drivers/usb/host/whci/asl.c | 12 +++++++++---
+ drivers/usb/host/whci/pzl.c | 12 +++++++++---
+ 2 files changed, 18 insertions(+), 6 deletions(-)
+
+--- a/drivers/usb/host/whci/asl.c
++++ b/drivers/usb/host/whci/asl.c
+@@ -255,23 +255,29 @@ int asl_urb_enqueue(struct whc *whc, str
+
+ spin_lock_irqsave(&whc->lock, flags);
+
++ err = usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb);
++ if (err < 0) {
++ spin_unlock_irqrestore(&whc->lock, flags);
++ return err;
++ }
++
+ qset = get_qset(whc, urb, GFP_ATOMIC);
+ if (qset == NULL)
+ err = -ENOMEM;
+ else
+ err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
+ if (!err) {
+- usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb);
+ if (!qset->in_sw_list)
+ asl_qset_insert_begin(whc, qset);
+- }
++ } else
++ usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb);
+
+ spin_unlock_irqrestore(&whc->lock, flags);
+
+ if (!err)
+ queue_work(whc->workqueue, &whc->async_work);
+
+- return 0;
++ return err;
+ }
+
+ /**
+--- a/drivers/usb/host/whci/pzl.c
++++ b/drivers/usb/host/whci/pzl.c
+@@ -283,23 +283,29 @@ int pzl_urb_enqueue(struct whc *whc, str
+
+ spin_lock_irqsave(&whc->lock, flags);
+
++ err = usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb);
++ if (err < 0) {
++ spin_unlock_irqrestore(&whc->lock, flags);
++ return err;
++ }
++
+ qset = get_qset(whc, urb, GFP_ATOMIC);
+ if (qset == NULL)
+ err = -ENOMEM;
+ else
+ err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
+ if (!err) {
+- usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb);
+ if (!qset->in_sw_list)
+ qset_insert_in_sw_list(whc, qset);
+- }
++ } else
++ usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb);
+
+ spin_unlock_irqrestore(&whc->lock, flags);
+
+ if (!err)
+ queue_work(whc->workqueue, &whc->periodic_work);
+
+- return 0;
++ return err;
+ }
+
+ /**
diff --git a/usb.current/usb-whci-hcd-provide-a-endpoint_reset-method.patch b/usb.current/usb-whci-hcd-provide-a-endpoint_reset-method.patch
new file mode 100644
index 00000000000000..07384f3ba9d568
--- /dev/null
+++ b/usb.current/usb-whci-hcd-provide-a-endpoint_reset-method.patch
@@ -0,0 +1,167 @@
+From dvrabel@hera.kernel.org Wed Apr 15 21:01:59 2009
+From: David Vrabel <david.vrabel@csr.com>
+Date: Wed, 8 Apr 2009 17:36:29 +0000
+Subject: USB: whci-hcd: provide a endpoint_reset method
+To: Greg KH <gregkh@suse.de>
+Cc: David Vrabel <david.vrabel@csr.com>
+Message-ID: <1239212193-27618-3-git-send-email-david.vrabel@csr.com>
+
+
+Provide a endpoint_reset method to reset sequence number and current
+window. This QHead information can only be changed while the qset is
+not in a schedule.
+
+Signed-off-by: David Vrabel <david.vrabel@csr.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+---
+ drivers/usb/host/whci/asl.c | 3 ++-
+ drivers/usb/host/whci/hcd.c | 23 +++++++++++++++++++++++
+ drivers/usb/host/whci/pzl.c | 4 ++--
+ drivers/usb/host/whci/qset.c | 24 +++++++++++++++++++++---
+ drivers/usb/host/whci/whcd.h | 1 +
+ drivers/usb/host/whci/whci-hc.h | 1 +
+ 6 files changed, 50 insertions(+), 6 deletions(-)
+
+--- a/drivers/usb/host/whci/asl.c
++++ b/drivers/usb/host/whci/asl.c
+@@ -122,7 +122,8 @@ static uint32_t process_qset(struct whc
+ process_inactive_qtd(whc, qset, td);
+ }
+
+- update |= qset_add_qtds(whc, qset);
++ if (!qset->remove)
++ update |= qset_add_qtds(whc, qset);
+
+ done:
+ /*
+--- a/drivers/usb/host/whci/hcd.c
++++ b/drivers/usb/host/whci/hcd.c
+@@ -186,6 +186,28 @@ static void whc_endpoint_disable(struct
+ }
+ }
+
++static void whc_endpoint_reset(struct usb_hcd *usb_hcd,
++ struct usb_host_endpoint *ep)
++{
++ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
++ struct whc *whc = wusbhc_to_whc(wusbhc);
++ struct whc_qset *qset;
++
++ qset = ep->hcpriv;
++ if (qset) {
++ qset->remove = 1;
++
++ if (usb_endpoint_xfer_bulk(&ep->desc)
++ || usb_endpoint_xfer_control(&ep->desc))
++ queue_work(whc->workqueue, &whc->async_work);
++ else
++ queue_work(whc->workqueue, &whc->periodic_work);
++
++ qset_reset(whc, qset);
++ }
++}
++
++
+ static struct hc_driver whc_hc_driver = {
+ .description = "whci-hcd",
+ .product_desc = "Wireless host controller",
+@@ -200,6 +222,7 @@ static struct hc_driver whc_hc_driver =
+ .urb_enqueue = whc_urb_enqueue,
+ .urb_dequeue = whc_urb_dequeue,
+ .endpoint_disable = whc_endpoint_disable,
++ .endpoint_reset = whc_endpoint_reset,
+
+ .hub_status_data = wusbhc_rh_status_data,
+ .hub_control = wusbhc_rh_control,
+--- a/drivers/usb/host/whci/pzl.c
++++ b/drivers/usb/host/whci/pzl.c
+@@ -128,7 +128,8 @@ static enum whc_update pzl_process_qset(
+ process_inactive_qtd(whc, qset, td);
+ }
+
+- update |= qset_add_qtds(whc, qset);
++ if (!qset->remove)
++ update |= qset_add_qtds(whc, qset);
+
+ done:
+ /*
+@@ -353,7 +354,6 @@ void pzl_qset_delete(struct whc *whc, st
+ qset_delete(whc, qset);
+ }
+
+-
+ /**
+ * pzl_init - initialize the periodic zone list
+ * @whc: the WHCI host controller
+--- a/drivers/usb/host/whci/qset.c
++++ b/drivers/usb/host/whci/qset.c
+@@ -89,11 +89,16 @@ static void qset_fill_qh(struct whc_qset
+ QH_INFO3_TX_RATE_53_3
+ | QH_INFO3_TX_PWR(0) /* 0 == max power */
+ );
++
++ qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1);
+ }
+
+ /**
+ * qset_clear - clear fields in a qset so it may be reinserted into a
+- * schedule
++ * schedule.
++ *
++ * The sequence number and current window are not cleared (see
++ * qset_reset()).
+ */
+ void qset_clear(struct whc *whc, struct whc_qset *qset)
+ {
+@@ -101,9 +106,8 @@ void qset_clear(struct whc *whc, struct
+ qset->remove = 0;
+
+ qset->qh.link = cpu_to_le32(QH_LINK_NTDS(8) | QH_LINK_T);
+- qset->qh.status = cpu_to_le16(QH_STATUS_ICUR(qset->td_start));
++ qset->qh.status = qset->qh.status & QH_STATUS_SEQ_MASK;
+ qset->qh.err_count = 0;
+- qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1);
+ qset->qh.scratch[0] = 0;
+ qset->qh.scratch[1] = 0;
+ qset->qh.scratch[2] = 0;
+@@ -114,6 +118,20 @@ void qset_clear(struct whc *whc, struct
+ }
+
+ /**
++ * qset_reset - reset endpoint state in a qset.
++ *
++ * Clears the sequence number and current window. This qset must not
++ * be in the ASL or PZL.
++ */
++void qset_reset(struct whc *whc, struct whc_qset *qset)
++{
++ wait_for_completion(&qset->remove_complete);
++
++ qset->qh.status &= ~QH_STATUS_SEQ_MASK;
++ qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1);
++}
++
++/**
+ * get_qset - get the qset for an async endpoint
+ *
+ * A new qset is created if one does not already exist.
+--- a/drivers/usb/host/whci/whcd.h
++++ b/drivers/usb/host/whci/whcd.h
+@@ -184,6 +184,7 @@ void qset_free(struct whc *whc, struct w
+ struct whc_qset *get_qset(struct whc *whc, struct urb *urb, gfp_t mem_flags);
+ void qset_delete(struct whc *whc, struct whc_qset *qset);
+ void qset_clear(struct whc *whc, struct whc_qset *qset);
++void qset_reset(struct whc *whc, struct whc_qset *qset);
+ int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb,
+ gfp_t mem_flags);
+ void qset_free_std(struct whc *whc, struct whc_std *std);
+--- a/drivers/usb/host/whci/whci-hc.h
++++ b/drivers/usb/host/whci/whci-hc.h
+@@ -185,6 +185,7 @@ struct whc_qhead {
+ #define QH_STATUS_FLOW_CTRL (1 << 15)
+ #define QH_STATUS_ICUR(i) ((i) << 5)
+ #define QH_STATUS_TO_ICUR(s) (((s) >> 5) & 0x7)
++#define QH_STATUS_SEQ_MASK 0x1f
+
+ /**
+ * usb_pipe_to_qh_type - USB core pipe type to QH transfer type
diff --git a/usb.current/wusb-correct-format-of-wusb_chid-sysfs-file.patch b/usb.current/wusb-correct-format-of-wusb_chid-sysfs-file.patch
new file mode 100644
index 00000000000000..d87cee2c2b2d58
--- /dev/null
+++ b/usb.current/wusb-correct-format-of-wusb_chid-sysfs-file.patch
@@ -0,0 +1,69 @@
+From dvrabel@hera.kernel.org Wed Apr 15 21:03:49 2009
+From: David Vrabel <david.vrabel@csr.com>
+Date: Wed, 8 Apr 2009 17:36:30 +0000
+Subject: WUSB: correct format of wusb_chid sysfs file
+To: Greg KH <gregkh@suse.de>
+Cc: David Vrabel <david.vrabel@csr.com>
+Message-ID: <1239212193-27618-4-git-send-email-david.vrabel@csr.com>
+
+
+Make the wusb_chid sysfs file match the ABI documentation.
+
+Print all zeros if the WUSB host is stopped (instead of an empty file)
+and end the file with a newline.
+
+Signed-off-by: David Vrabel <david.vrabel@csr.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/wusbcore/wusbhc.c | 26 ++++++++++++--------------
+ 1 file changed, 12 insertions(+), 14 deletions(-)
+
+--- a/drivers/usb/wusbcore/wusbhc.c
++++ b/drivers/usb/wusbcore/wusbhc.c
+@@ -88,33 +88,31 @@ static DEVICE_ATTR(wusb_trust_timeout, 0
+ wusb_trust_timeout_store);
+
+ /*
+- * Show & store the current WUSB CHID
++ * Show the current WUSB CHID.
+ */
+ static ssize_t wusb_chid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+ {
+ struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
++ const struct wusb_ckhdid *chid;
+ ssize_t result = 0;
+
+ if (wusbhc->wuie_host_info != NULL)
+- result += ckhdid_printf(buf, PAGE_SIZE,
+- &wusbhc->wuie_host_info->CHID);
++ chid = &wusbhc->wuie_host_info->CHID;
++ else
++ chid = &wusb_ckhdid_zero;
++
++ result += ckhdid_printf(buf, PAGE_SIZE, chid);
++ result += sprintf(buf + result, "\n");
++
+ return result;
+ }
+
+ /*
+- * Store a new CHID
+- *
+- * This will (FIXME) trigger many changes.
++ * Store a new CHID.
+ *
+- * - Send an all zeros CHID and it will stop the controller
+- * - Send a non-zero CHID and it will start it
+- * (unless it was started, it will just change the CHID,
+- * diconnecting all devices first).
+- *
+- * So first we scan the MMC we are sent and then we act on it. We
+- * read it in the same format as we print it, an ASCII string of 16
+- * hex bytes.
++ * - Write an all zeros CHID and it will stop the controller
++ * - Write a non-zero CHID and it will start it.
+ *
+ * See wusbhc_chid_set() for more info.
+ */
diff --git a/usb.current/wusb-disconnect-all-devices-when-stopping-a-wusb-hcd.patch b/usb.current/wusb-disconnect-all-devices-when-stopping-a-wusb-hcd.patch
new file mode 100644
index 00000000000000..1ec1895f5641d1
--- /dev/null
+++ b/usb.current/wusb-disconnect-all-devices-when-stopping-a-wusb-hcd.patch
@@ -0,0 +1,58 @@
+From dvrabel@hera.kernel.org Wed Apr 15 21:02:54 2009
+From: David Vrabel <david.vrabel@csr.com>
+Date: Wed, 8 Apr 2009 17:36:33 +0000
+Subject: WUSB: disconnect all devices when stopping a WUSB HCD
+To: Greg KH <gregkh@suse.de>
+Cc: David Vrabel <david.vrabel@csr.com>
+Message-ID: <1239212193-27618-7-git-send-email-david.vrabel@csr.com>
+
+
+Make sure all WUSB devices are disconnected when stopping a WUSB HCD so
+that we don't leak the devices' wusb_dev structures.
+
+Signed-off-by: David Vrabel <david.vrabel@csr.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/wusbcore/devconnect.c | 19 +++++++++++++------
+ 1 file changed, 13 insertions(+), 6 deletions(-)
+
+--- a/drivers/usb/wusbcore/devconnect.c
++++ b/drivers/usb/wusbcore/devconnect.c
+@@ -396,7 +396,8 @@ static void __wusbhc_dev_disconnect(stru
+
+ /* After a device disconnects, change the GTK (see [WUSB]
+ * section 6.2.11.2). */
+- wusbhc_gtk_rekey(wusbhc);
++ if (wusbhc->active)
++ wusbhc_gtk_rekey(wusbhc);
+
+ /* The Wireless USB part has forgotten about the device already; now
+ * khubd's timer will pick up the disconnection and remove the USB
+@@ -1084,15 +1085,21 @@ error_mmcie_set:
+ * wusbhc_devconnect_stop - stop managing connected devices
+ * @wusbhc: the WUSB HC
+ *
+- * Removes the Host Info IE and stops the keep alives.
+- *
+- * FIXME: should this disconnect all devices?
++ * Disconnects any devices still connected, stops the keep alives and
++ * removes the Host Info IE.
+ */
+ void wusbhc_devconnect_stop(struct wusbhc *wusbhc)
+ {
+- cancel_delayed_work_sync(&wusbhc->keep_alive_timer);
+- WARN_ON(!list_empty(&wusbhc->cack_list));
++ int i;
+
++ mutex_lock(&wusbhc->mutex);
++ for (i = 0; i < wusbhc->ports_max; i++) {
++ if (wusbhc->port[i].wusb_dev)
++ __wusbhc_dev_disconnect(wusbhc, &wusbhc->port[i]);
++ }
++ mutex_unlock(&wusbhc->mutex);
++
++ cancel_delayed_work_sync(&wusbhc->keep_alive_timer);
+ wusbhc_mmcie_rm(wusbhc, &wusbhc->wuie_host_info->hdr);
+ kfree(wusbhc->wuie_host_info);
+ wusbhc->wuie_host_info = NULL;
diff --git a/usb.current/wusb-fix-oops-when-completing-urbs-for-disconnected-devices.patch b/usb.current/wusb-fix-oops-when-completing-urbs-for-disconnected-devices.patch
new file mode 100644
index 00000000000000..f388a25bb3c792
--- /dev/null
+++ b/usb.current/wusb-fix-oops-when-completing-urbs-for-disconnected-devices.patch
@@ -0,0 +1,53 @@
+From dvrabel@hera.kernel.org Wed Apr 15 21:03:18 2009
+From: David Vrabel <david.vrabel@csr.com>
+Date: Wed, 8 Apr 2009 17:36:32 +0000
+Subject: WUSB: fix oops when completing URBs for disconnected devices
+To: Greg KH <gregkh@suse.de>
+Cc: David Vrabel <david.vrabel@csr.com>
+Message-ID: <1239212193-27618-6-git-send-email-david.vrabel@csr.com>
+
+
+Fix an oops in wusbhc_giveback_urb() if the wusb device had disconnected
+while an urb was in progress. Also release the ref count obtained here.
+
+Signed-off-by: David Vrabel <david.vrabel@csr.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/wusbcore/devconnect.c | 2 ++
+ drivers/usb/wusbcore/wusbhc.c | 8 +++++---
+ 2 files changed, 7 insertions(+), 3 deletions(-)
+
+--- a/drivers/usb/wusbcore/devconnect.c
++++ b/drivers/usb/wusbcore/devconnect.c
+@@ -267,6 +267,8 @@ static void wusbhc_devconnect_acked_work
+ mutex_lock(&wusbhc->mutex);
+ wusbhc_devconnect_acked(wusbhc, wusb_dev);
+ mutex_unlock(&wusbhc->mutex);
++
++ wusb_dev_put(wusb_dev);
+ }
+
+ /*
+--- a/drivers/usb/wusbcore/wusbhc.c
++++ b/drivers/usb/wusbcore/wusbhc.c
+@@ -338,14 +338,16 @@ EXPORT_SYMBOL_GPL(wusb_cluster_id_put);
+ void wusbhc_giveback_urb(struct wusbhc *wusbhc, struct urb *urb, int status)
+ {
+ struct wusb_dev *wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, urb->dev);
+-
+- if (status == 0) {
++
++ if (status == 0 && wusb_dev) {
+ wusb_dev->entry_ts = jiffies;
+
+- /* wusbhc_devconnect_acked() can't be called from from
++ /* wusbhc_devconnect_acked() can't be called from
+ atomic context so defer it to a work queue. */
+ if (!list_empty(&wusb_dev->cack_node))
+ queue_work(wusbd, &wusb_dev->devconnect_acked_work);
++ else
++ wusb_dev_put(wusb_dev);
+ }
+
+ usb_hcd_giveback_urb(&wusbhc->usb_hcd, urb, status);