aboutsummaryrefslogtreecommitdiffstats
path: root/usb
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2006-06-02 15:05:31 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2006-06-02 15:05:31 -0700
commit824837789fc2c31455d2faf32d259f2b3d25a73d (patch)
tree5e99c7d880b473595733c9608a764e5671945bb6 /usb
parentce6089ada2fea84bef30a839543688664859162b (diff)
downloadpatches-824837789fc2c31455d2faf32d259f2b3d25a73d.tar.gz
more usb patches
Diffstat (limited to 'usb')
-rw-r--r--usb/always-announce-new-usb-devices.patch6
-rw-r--r--usb/usb-hub-use-usb_reset_composite_device.patch134
-rw-r--r--usb/usb-new-devices-for-the-option-driver.patch275
-rw-r--r--usb/usb-serial-dynamic-id.patch6
-rw-r--r--usb/usb-storage-use-usb_reset_composite_device.patch161
-rw-r--r--usb/usbcore-port-reset-for-composite-devices.patch177
-rw-r--r--usb/usbcore-recovery-from-set-configuration-failure.patch199
-rw-r--r--usb/usbhid-use-usb_reset_composite_device.patch93
8 files changed, 1045 insertions, 6 deletions
diff --git a/usb/always-announce-new-usb-devices.patch b/usb/always-announce-new-usb-devices.patch
index 6f0cb775b4392..92c215956b3f3 100644
--- a/usb/always-announce-new-usb-devices.patch
+++ b/usb/always-announce-new-usb-devices.patch
@@ -14,7 +14,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
--- gregkh-2.6.orig/drivers/usb/core/hub.c
+++ gregkh-2.6/drivers/usb/core/hub.c
-@@ -1285,7 +1285,6 @@ static int choose_configuration(struct u
+@@ -1289,7 +1289,6 @@ static int choose_configuration(struct u
return i;
}
@@ -22,7 +22,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
static void show_string(struct usb_device *udev, char *id, char *string)
{
if (!string)
-@@ -1293,10 +1292,6 @@ static void show_string(struct usb_devic
+@@ -1297,10 +1296,6 @@ static void show_string(struct usb_devic
dev_printk(KERN_INFO, &udev->dev, "%s: %s\n", id, string);
}
@@ -33,7 +33,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
#ifdef CONFIG_USB_OTG
-@@ -1341,7 +1336,10 @@ int usb_new_device(struct usb_device *ud
+@@ -1345,7 +1340,10 @@ int usb_new_device(struct usb_device *ud
udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
/* Tell the world! */
diff --git a/usb/usb-hub-use-usb_reset_composite_device.patch b/usb/usb-hub-use-usb_reset_composite_device.patch
new file mode 100644
index 0000000000000..365d1fe34df73
--- /dev/null
+++ b/usb/usb-hub-use-usb_reset_composite_device.patch
@@ -0,0 +1,134 @@
+From stern@rowland.harvard.edu Thu Jun 1 10:37:33 2006
+Date: Thu, 1 Jun 2006 13:37:24 -0400 (EDT)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+cc: USB development list <linux-usb-devel@lists.sourceforge.net>
+Subject: USB hub: use usb_reset_composite_device
+Message-ID: <Pine.LNX.4.44L0.0606011333440.5835-100000@iolanthe.rowland.org>
+
+This patch (as700) modifies the hub driver to take advantage of the new
+usb_reset_composite_device API. The existing code had special-case
+calls stuck into usb_reset_device, just before and after the reset.
+With the new version there's no need for special-case stuff; it all
+happens naturally in the form of pre_reset and post_reset notifications.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/core/hub.c | 34 +++++++++++++++-------------------
+ 1 file changed, 15 insertions(+), 19 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/core/hub.c
++++ gregkh-2.6/drivers/usb/core/hub.c
+@@ -525,15 +525,16 @@ static int hub_port_disable(struct usb_h
+
+
+ /* caller has locked the hub device */
+-static void hub_pre_reset(struct usb_hub *hub, int disable_ports)
++static void hub_pre_reset(struct usb_interface *intf)
+ {
++ struct usb_hub *hub = usb_get_intfdata(intf);
+ struct usb_device *hdev = hub->hdev;
+ int port1;
+
+ for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
+ if (hdev->children[port1 - 1]) {
+ usb_disconnect(&hdev->children[port1 - 1]);
+- if (disable_ports)
++ if (hub->error == 0)
+ hub_port_disable(hub, port1, 0);
+ }
+ }
+@@ -541,8 +542,10 @@ static void hub_pre_reset(struct usb_hub
+ }
+
+ /* caller has locked the hub device */
+-static void hub_post_reset(struct usb_hub *hub)
++static void hub_post_reset(struct usb_interface *intf)
+ {
++ struct usb_hub *hub = usb_get_intfdata(intf);
++
+ hub_activate(hub);
+ hub_power_on(hub);
+ }
+@@ -802,15 +805,16 @@ static void hub_disconnect(struct usb_in
+ struct usb_hub *hub = usb_get_intfdata (intf);
+ struct usb_device *hdev;
+
++ /* Disconnect all children and quiesce the hub */
++ hub->error = 0;
++ hub_pre_reset(intf);
++
+ usb_set_intfdata (intf, NULL);
+ hdev = hub->hdev;
+
+ if (hdev->speed == USB_SPEED_HIGH)
+ highspeed_hubs--;
+
+- /* Disconnect all children and quiesce the hub */
+- hub_pre_reset(hub, 1);
+-
+ usb_free_urb(hub->urb);
+ hub->urb = NULL;
+
+@@ -2747,7 +2751,8 @@ static void hub_events(void)
+
+ /* If the hub has died, clean up after it */
+ if (hdev->state == USB_STATE_NOTATTACHED) {
+- hub_pre_reset(hub, 0);
++ hub->error = -ENODEV;
++ hub_pre_reset(intf);
+ goto loop;
+ }
+
+@@ -2759,7 +2764,7 @@ static void hub_events(void)
+ dev_dbg (hub_dev, "resetting for error %d\n",
+ hub->error);
+
+- ret = usb_reset_device(hdev);
++ ret = usb_reset_composite_device(hdev, intf);
+ if (ret) {
+ dev_dbg (hub_dev,
+ "error resetting hub: %d\n", ret);
+@@ -2928,6 +2933,8 @@ static struct usb_driver hub_driver = {
+ .disconnect = hub_disconnect,
+ .suspend = hub_suspend,
+ .resume = hub_resume,
++ .pre_reset = hub_pre_reset,
++ .post_reset = hub_post_reset,
+ .ioctl = hub_ioctl,
+ .id_table = hub_id_table,
+ };
+@@ -3033,7 +3040,6 @@ int usb_reset_device(struct usb_device *
+ struct usb_device *parent_hdev = udev->parent;
+ struct usb_hub *parent_hub;
+ struct usb_device_descriptor descriptor = udev->descriptor;
+- struct usb_hub *hub = NULL;
+ int i, ret = 0;
+ int port1 = udev->portnum;
+
+@@ -3051,14 +3057,6 @@ int usb_reset_device(struct usb_device *
+ }
+ parent_hub = hdev_to_hub(parent_hdev);
+
+- /* If we're resetting an active hub, take some special actions */
+- if (udev->actconfig && udev->actconfig->desc.bNumInterfaces > 0 &&
+- udev->actconfig->interface[0]->dev.driver ==
+- &hub_driver.driver &&
+- (hub = hdev_to_hub(udev)) != NULL) {
+- hub_pre_reset(hub, 0);
+- }
+-
+ set_bit(port1, parent_hub->busy_bits);
+ for (i = 0; i < SET_CONFIG_TRIES; ++i) {
+
+@@ -3117,8 +3115,6 @@ int usb_reset_device(struct usb_device *
+ }
+
+ done:
+- if (hub)
+- hub_post_reset(hub);
+ return 0;
+
+ re_enumerate:
diff --git a/usb/usb-new-devices-for-the-option-driver.patch b/usb/usb-new-devices-for-the-option-driver.patch
new file mode 100644
index 0000000000000..dd6e2f011613c
--- /dev/null
+++ b/usb/usb-new-devices-for-the-option-driver.patch
@@ -0,0 +1,275 @@
+From smurf@smurf.noris.de Fri Jun 2 03:16:29 2006
+Date: Fri, 2 Jun 2006 11:48:56 +0200
+From: Matthias Urlichs <smurf@smurf.noris.de>
+Cc: greg@kroah.com
+Subject: USB: new devices for the Option driver
+Message-ID: <20060602094856.GD406@kiste.smurf.noris.de>
+Content-Disposition: inline
+
+This patch extends the "option" driver with a few more devices, some of
+which are actually connected to USB the "right" way -- as opposed to
+doing it via PCMCIA and OHCI.
+
+Signed-Off-By: Matthias Urlichs <smurf@debian.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/usb/serial/Kconfig | 28 +++++++--
+ drivers/usb/serial/option.c | 136 ++++++++++++++++++++++++++++++++++----------
+ 2 files changed, 130 insertions(+), 34 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/serial/Kconfig
++++ gregkh-2.6/drivers/usb/serial/Kconfig
+@@ -491,12 +491,30 @@ config USB_SERIAL_XIRCOM
+ module will be called keyspan_pda.
+
+ config USB_SERIAL_OPTION
+- tristate "USB Option PCMCIA serial driver"
+- depends on USB_SERIAL && USB_OHCI_HCD && PCCARD
++ tristate "USB driver for GSM modems"
++ depends on USB_SERIAL
+ help
+- Say Y here if you want to use an Option card. This is a
+- GSM card, controlled by three serial ports which are connected
+- via an OHCI adapter located on a PC card.
++ Say Y here if you have a GSM modem that's connected to USB.
++
++ To compile this driver as a module, choose M here: the
++ module will be called option.
++
++ If this driver doesn't recognize your device,
++ it might be accessible via the FTDI_SIO driver.
++
++config USB_SERIAL_OPTION_PCCARD
++ tristate "driver for GSM modems on PCMCIA cards"
++ depends on USB_SERIAL
++ select PCCARD
++ select USB_OHCI_HCD
++ select USB_SERIAL_OPTION
++ help
++ Say Y here if you have an "Option" GSM PCMCIA card
++ (or an OEM version: branded Huawei, Audiovox, or Novatel).
++
++ These cards feature a built-in OHCI-USB adapter and an
++ internally-connected GSM modem. The USB bus is not
++ accessible externally.
+
+ To compile this driver as a module, choose M here: the
+ module will be called option.
+--- gregkh-2.6.orig/drivers/usb/serial/option.c
++++ gregkh-2.6/drivers/usb/serial/option.c
+@@ -1,5 +1,5 @@
+ /*
+- Option Card (PCMCIA to) USB to Serial Driver
++ USB Driver for GSM modems
+
+ Copyright (C) 2005 Matthias Urlichs <smurf@smurf.noris.de>
+
+@@ -28,15 +28,34 @@
+ 2005-09-10 v0.4.3 added HUAWEI E600 card and Audiovox AirCard
+ 2005-09-20 v0.4.4 increased recv buffer size: the card sometimes
+ wants to send >2000 bytes.
+- 2006-04-10 v0.4.2 fixed two array overrun errors :-/
++ 2006-04-10 v0.5 fixed two array overrun errors :-/
++ 2006-04-21 v0.5.1 added support for Sierra Wireless MC8755
++ 2006-05-15 v0.6 re-enable multi-port support
++ 2006-06-01 v0.6.1 add COBRA
++ 2006-06-01 v0.6.2 add backwards-compatibility stuff
++ 2006-06-01 v0.6.3 add Novatel Wireless
++ 2006-06-01 v0.7 Option => GSM
+
+ Work sponsored by: Sigos GmbH, Germany <info@sigos.de>
+
++ This driver exists because the "normal" serial driver doesn't work too well
++ with GSM modems. Issues:
++ - data loss -- one single Receive URB is not nearly enough
++ - nonstandard flow (Option devices) and multiplex (Sierra) control
++ - controlling the baud rate doesn't make sense
++
++ This driver is named "option" because the most common device it's
++ used for is a PC-Card (with an internal OHCI-USB interface, behind
++ which the GSM interface sits), made by Option Inc.
++
++ Some of the "one port" devices actually exhibit multiple USB instances
++ on the USB bus. This is not a bug, these ports are used for different
++ device features.
+ */
+
+-#define DRIVER_VERSION "v0.4"
++#define DRIVER_VERSION "v0.7.0"
+ #define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"
+-#define DRIVER_DESC "Option Card (PC-Card to) USB to Serial Driver"
++#define DRIVER_DESC "USB Driver for GSM modems"
+
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+@@ -74,22 +93,45 @@ static int option_tiocmset(struct usb_s
+ static int option_send_setup(struct usb_serial_port *port);
+
+ /* Vendor and product IDs */
+-#define OPTION_VENDOR_ID 0x0AF0
+-#define HUAWEI_VENDOR_ID 0x12D1
+-#define AUDIOVOX_VENDOR_ID 0x0F3D
+-
+-#define OPTION_PRODUCT_OLD 0x5000
+-#define OPTION_PRODUCT_FUSION 0x6000
+-#define OPTION_PRODUCT_FUSION2 0x6300
+-#define HUAWEI_PRODUCT_E600 0x1001
+-#define AUDIOVOX_PRODUCT_AIRCARD 0x0112
++#define OPTION_VENDOR_ID 0x0AF0
++#define HUAWEI_VENDOR_ID 0x12D1
++#define AUDIOVOX_VENDOR_ID 0x0F3D
++#define SIERRAWIRELESS_VENDOR_ID 0x1199
++#define NOVATELWIRELESS_VENDOR_ID 0x1410
++
++#define OPTION_PRODUCT_OLD 0x5000
++#define OPTION_PRODUCT_FUSION 0x6000
++#define OPTION_PRODUCT_FUSION2 0x6300
++#define OPTION_PRODUCT_COBRA 0x6500
++#define HUAWEI_PRODUCT_E600 0x1001
++#define AUDIOVOX_PRODUCT_AIRCARD 0x0112
++#define SIERRAWIRELESS_PRODUCT_MC8755 0x6802
++#define NOVATELWIRELESS_PRODUCT_U740 0x1400
+
+ static struct usb_device_id option_ids[] = {
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
++ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
+ { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
+ { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) },
++ { USB_DEVICE(SIERRAWIRELESS_VENDOR_ID, SIERRAWIRELESS_PRODUCT_MC8755) },
++ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
++ { } /* Terminating entry */
++};
++
++static struct usb_device_id option_ids1[] = {
++ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
++ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) },
++ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) },
++ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
++ { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) },
++ { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) },
++ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) },
++ { } /* Terminating entry */
++};
++static struct usb_device_id option_ids3[] = {
++ { USB_DEVICE(SIERRAWIRELESS_VENDOR_ID, SIERRAWIRELESS_PRODUCT_MC8755) },
+ { } /* Terminating entry */
+ };
+
+@@ -111,12 +153,39 @@ static struct usb_serial_driver option_3
+ .owner = THIS_MODULE,
+ .name = "option",
+ },
+- .description = "Option 3G data card",
+- .id_table = option_ids,
++ .description = "GSM modem (3-port)",
++ .id_table = option_ids3,
++ .num_interrupt_in = NUM_DONT_CARE,
++ .num_bulk_in = NUM_DONT_CARE,
++ .num_bulk_out = NUM_DONT_CARE,
++ .num_ports = 3,
++ .open = option_open,
++ .close = option_close,
++ .write = option_write,
++ .write_room = option_write_room,
++ .chars_in_buffer = option_chars_in_buffer,
++ .throttle = option_rx_throttle,
++ .unthrottle = option_rx_unthrottle,
++ .set_termios = option_set_termios,
++ .break_ctl = option_break_ctl,
++ .tiocmget = option_tiocmget,
++ .tiocmset = option_tiocmset,
++ .attach = option_startup,
++ .shutdown = option_shutdown,
++ .read_int_callback = option_instat_callback,
++};
++
++static struct usb_serial_driver option_1port_device = {
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "option",
++ },
++ .description = "GSM modem (1-port)",
++ .id_table = option_ids1,
+ .num_interrupt_in = NUM_DONT_CARE,
+ .num_bulk_in = NUM_DONT_CARE,
+ .num_bulk_out = NUM_DONT_CARE,
+- .num_ports = 1, /* 3, but the card reports its ports separately */
++ .num_ports = 1,
+ .open = option_open,
+ .close = option_close,
+ .write = option_write,
+@@ -170,6 +239,9 @@ struct option_port_private {
+ static int __init option_init(void)
+ {
+ int retval;
++ retval = usb_serial_register(&option_1port_device);
++ if (retval)
++ goto failed_1port_device_register;
+ retval = usb_serial_register(&option_3port_device);
+ if (retval)
+ goto failed_3port_device_register;
+@@ -184,6 +256,8 @@ static int __init option_init(void)
+ failed_driver_register:
+ usb_serial_deregister (&option_3port_device);
+ failed_3port_device_register:
++ usb_serial_deregister (&option_1port_device);
++failed_1port_device_register:
+ return retval;
+ }
+
+@@ -191,6 +265,7 @@ static void __exit option_exit(void)
+ {
+ usb_deregister (&option_driver);
+ usb_serial_deregister (&option_3port_device);
++ usb_serial_deregister (&option_1port_device);
+ }
+
+ module_init(option_init);
+@@ -572,27 +647,30 @@ static struct urb *option_setup_urb(stru
+ /* Setup urbs */
+ static void option_setup_urbs(struct usb_serial *serial)
+ {
+- int j;
++ int i,j;
+ struct usb_serial_port *port;
+ struct option_port_private *portdata;
+
+ dbg("%s", __FUNCTION__);
+
+- port = serial->port[0];
+- portdata = usb_get_serial_port_data(port);
++
++ for (i = 0; i < serial->num_ports; i++) {
++ port = serial->port[i];
++ portdata = usb_get_serial_port_data(port);
+
+ /* Do indat endpoints first */
+- for (j = 0; j < N_IN_URB; ++j) {
+- portdata->in_urbs[j] = option_setup_urb (serial,
+- port->bulk_in_endpointAddress, USB_DIR_IN, port,
+- portdata->in_buffer[j], IN_BUFLEN, option_indat_callback);
+- }
++ for (j = 0; j < N_IN_URB; ++j) {
++ portdata->in_urbs[j] = option_setup_urb (serial,
++ port->bulk_in_endpointAddress, USB_DIR_IN, port,
++ portdata->in_buffer[j], IN_BUFLEN, option_indat_callback);
++ }
+
+- /* outdat endpoints */
+- for (j = 0; j < N_OUT_URB; ++j) {
+- portdata->out_urbs[j] = option_setup_urb (serial,
+- port->bulk_out_endpointAddress, USB_DIR_OUT, port,
+- portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback);
++ /* outdat endpoints */
++ for (j = 0; j < N_OUT_URB; ++j) {
++ portdata->out_urbs[j] = option_setup_urb (serial,
++ port->bulk_out_endpointAddress, USB_DIR_OUT, port,
++ portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback);
++ }
+ }
+ }
+
diff --git a/usb/usb-serial-dynamic-id.patch b/usb/usb-serial-dynamic-id.patch
index 238c06f401f86..64abcdb9f292f 100644
--- a/usb/usb-serial-dynamic-id.patch
+++ b/usb/usb-serial-dynamic-id.patch
@@ -284,7 +284,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
--- gregkh-2.6.orig/include/linux/usb.h
+++ gregkh-2.6/include/linux/usb.h
-@@ -417,6 +417,8 @@ extern void usb_driver_release_interface
+@@ -419,6 +419,8 @@ extern void usb_driver_release_interface
struct usb_interface *iface);
const struct usb_device_id *usb_match_id(struct usb_interface *interface,
const struct usb_device_id *id);
@@ -293,7 +293,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
extern struct usb_interface *usb_find_interface(struct usb_driver *drv,
int minor);
-@@ -531,11 +533,21 @@ static inline int usb_make_path (struct
+@@ -533,11 +535,21 @@ static inline int usb_make_path (struct
/* ----------------------------------------------------------------------- */
@@ -315,7 +315,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
/**
* struct usb_driver - identifies USB driver to usbcore
* @name: The driver name should be unique among USB drivers,
-@@ -1192,6 +1204,7 @@ usb_maxpacket(struct usb_device *udev, i
+@@ -1201,6 +1213,7 @@ usb_maxpacket(struct usb_device *udev, i
extern void usb_register_notify(struct notifier_block *nb);
extern void usb_unregister_notify(struct notifier_block *nb);
diff --git a/usb/usb-storage-use-usb_reset_composite_device.patch b/usb/usb-storage-use-usb_reset_composite_device.patch
new file mode 100644
index 0000000000000..7d071bf4e8db8
--- /dev/null
+++ b/usb/usb-storage-use-usb_reset_composite_device.patch
@@ -0,0 +1,161 @@
+From linux-usb-devel-bounces@lists.sourceforge.net Thu Jun 1 10:53:25 2006
+Date: Thu, 1 Jun 2006 13:52:56 -0400 (EDT)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+Message-ID: <Pine.LNX.4.44L0.0606011337250.5835-100000@iolanthe.rowland.org>
+Cc: Matthew Dharm <mdharm-usb@one-eyed-alien.net>
+Subject: usb-storage: use usb_reset_composite_device
+
+This patch (as701) modifies usb-storage to take advantage of the new
+usb_reset_composite_device() API. Now we will be able to safely request
+port resets even if other drivers are bound to a mass-storage device.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/storage/scsiglue.c | 4 ---
+ drivers/usb/storage/transport.c | 50 ++++++++++++++++++++--------------------
+ drivers/usb/storage/usb.c | 33 ++++++++++++++++++++++++++
+ 3 files changed, 59 insertions(+), 28 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/storage/scsiglue.c
++++ gregkh-2.6/drivers/usb/storage/scsiglue.c
+@@ -286,11 +286,7 @@ static int bus_reset(struct scsi_cmnd *s
+ int result;
+
+ US_DEBUGP("%s called\n", __FUNCTION__);
+-
+- mutex_lock(&(us->dev_mutex));
+ result = usb_stor_port_reset(us);
+- mutex_unlock(&us->dev_mutex);
+-
+ return result < 0 ? FAILED : SUCCESS;
+ }
+
+--- gregkh-2.6.orig/drivers/usb/storage/transport.c
++++ gregkh-2.6/drivers/usb/storage/transport.c
+@@ -703,16 +703,19 @@ void usb_stor_invoke_transport(struct sc
+ * device reset. */
+ Handle_Errors:
+
+- /* Let the SCSI layer know we are doing a reset, set the
+- * RESETTING bit, and clear the ABORTING bit so that the reset
+- * may proceed. */
++ /* Set the RESETTING bit, and clear the ABORTING bit so that
++ * the reset may proceed. */
+ scsi_lock(us_to_host(us));
+- usb_stor_report_bus_reset(us);
+ set_bit(US_FLIDX_RESETTING, &us->flags);
+ clear_bit(US_FLIDX_ABORTING, &us->flags);
+ scsi_unlock(us_to_host(us));
+
++ /* We must release the device lock because the pre_reset routine
++ * will want to acquire it. */
++ mutex_unlock(&us->dev_mutex);
+ result = usb_stor_port_reset(us);
++ mutex_lock(&us->dev_mutex);
++
+ if (result < 0) {
+ scsi_lock(us_to_host(us));
+ usb_stor_report_device_reset(us);
+@@ -1196,31 +1199,30 @@ int usb_stor_Bulk_reset(struct us_data *
+ 0, us->ifnum, NULL, 0);
+ }
+
+-/* Issue a USB port reset to the device. But don't do anything if
+- * there's more than one interface in the device, so that other users
+- * are not affected. */
++/* Issue a USB port reset to the device. The caller must not hold
++ * us->dev_mutex.
++ */
+ int usb_stor_port_reset(struct us_data *us)
+ {
+- int result, rc;
++ int result, rc_lock;
+
+- if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+- result = -EIO;
+- US_DEBUGP("No reset during disconnect\n");
+- } else if (us->pusb_dev->actconfig->desc.bNumInterfaces != 1) {
+- result = -EBUSY;
+- US_DEBUGP("Refusing to reset a multi-interface device\n");
+- } else {
+- result = rc =
+- usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
+- if (result < 0) {
+- US_DEBUGP("unable to lock device for reset: %d\n",
+- result);
++ result = rc_lock =
++ usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
++ if (result < 0)
++ US_DEBUGP("unable to lock device for reset: %d\n", result);
++ else {
++ /* Were we disconnected while waiting for the lock? */
++ if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
++ result = -EIO;
++ US_DEBUGP("No reset during disconnect\n");
+ } else {
+- result = usb_reset_device(us->pusb_dev);
+- if (rc)
+- usb_unlock_device(us->pusb_dev);
+- US_DEBUGP("usb_reset_device returns %d\n", result);
++ result = usb_reset_composite_device(
++ us->pusb_dev, us->pusb_intf);
++ US_DEBUGP("usb_reset_composite_device returns %d\n",
++ result);
+ }
++ if (rc_lock)
++ usb_unlock_device(us->pusb_dev);
+ }
+ return result;
+ }
+--- gregkh-2.6.orig/drivers/usb/storage/usb.c
++++ gregkh-2.6/drivers/usb/storage/usb.c
+@@ -221,6 +221,37 @@ static int storage_resume(struct usb_int
+ #endif /* CONFIG_PM */
+
+ /*
++ * The next two routines get called just before and just after
++ * a USB port reset, whether from this driver or a different one.
++ */
++
++static void storage_pre_reset(struct usb_interface *iface)
++{
++ struct us_data *us = usb_get_intfdata(iface);
++
++ US_DEBUGP("%s\n", __FUNCTION__);
++
++ /* Make sure no command runs during the reset */
++ mutex_lock(&us->dev_mutex);
++}
++
++static void storage_post_reset(struct usb_interface *iface)
++{
++ struct us_data *us = usb_get_intfdata(iface);
++
++ US_DEBUGP("%s\n", __FUNCTION__);
++
++ /* Report the reset to the SCSI core */
++ scsi_lock(us_to_host(us));
++ usb_stor_report_bus_reset(us);
++ scsi_unlock(us_to_host(us));
++
++ /* FIXME: Notify the subdrivers that they need to reinitialize
++ * the device */
++ mutex_unlock(&us->dev_mutex);
++}
++
++/*
+ * fill_inquiry_response takes an unsigned char array (which must
+ * be at least 36 characters) and populates the vendor name,
+ * product name, and revision fields. Then the array is copied
+@@ -1002,6 +1033,8 @@ static struct usb_driver usb_storage_dri
+ .suspend = storage_suspend,
+ .resume = storage_resume,
+ #endif
++ .pre_reset = storage_pre_reset,
++ .post_reset = storage_post_reset,
+ .id_table = storage_usb_ids,
+ };
+
diff --git a/usb/usbcore-port-reset-for-composite-devices.patch b/usb/usbcore-port-reset-for-composite-devices.patch
new file mode 100644
index 0000000000000..29a95ad337fe1
--- /dev/null
+++ b/usb/usbcore-port-reset-for-composite-devices.patch
@@ -0,0 +1,177 @@
+From stern@rowland.harvard.edu Thu Jun 1 10:33:52 2006
+Date: Thu, 1 Jun 2006 13:33:42 -0400 (EDT)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+Subject: usbcore: port reset for composite devices
+Message-ID: <Pine.LNX.4.44L0.0606011328470.5835-100000@iolanthe.rowland.org>
+
+This patch (as699) adds usb_reset_composite_device(), a routine for
+sending a USB port reset to a device with multiple interfaces owned by
+different drivers. Drivers are notified about impending and completed
+resets through two new methods in the usb_driver structure.
+
+The patch modifieds the usbfs ioctl code to make it use the new routine
+instead of usb_reset_device(). Follow-up patches will modify the hub,
+usb-storage, and usbhid drivers so they can utilize this new API.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/core/devio.c | 3 -
+ drivers/usb/core/hub.c | 84 +++++++++++++++++++++++++++++++++++++++++++++--
+ drivers/usb/core/usb.c | 1
+ include/linux/usb.h | 9 +++++
+ 4 files changed, 92 insertions(+), 5 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/core/devio.c
++++ gregkh-2.6/drivers/usb/core/devio.c
+@@ -823,8 +823,7 @@ static int proc_connectinfo(struct dev_s
+
+ static int proc_resetdevice(struct dev_state *ps)
+ {
+- return usb_reset_device(ps->dev);
+-
++ return usb_reset_composite_device(ps->dev, NULL);
+ }
+
+ static int proc_setintf(struct dev_state *ps, void __user *arg)
+--- gregkh-2.6.orig/drivers/usb/core/hub.c
++++ gregkh-2.6/drivers/usb/core/hub.c
+@@ -3007,9 +3007,9 @@ static int config_descriptors_changed(st
+ * usb_reset_device - perform a USB port reset to reinitialize a device
+ * @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
+ *
+- * WARNING - don't reset any device unless drivers for all of its
+- * interfaces are expecting that reset! Maybe some driver->reset()
+- * method should eventually help ensure sufficient cooperation.
++ * WARNING - don't use this routine to reset a composite device
++ * (one with multiple interfaces owned by separate drivers)!
++ * Use usb_reset_composite_device() instead.
+ *
+ * Do a port reset, reassign the device's address, and establish its
+ * former operating configuration. If the reset fails, or the device's
+@@ -3125,3 +3125,81 @@ re_enumerate:
+ hub_port_logical_disconnect(parent_hub, port1);
+ return -ENODEV;
+ }
++
++/**
++ * usb_reset_composite_device - warn interface drivers and perform a USB port reset
++ * @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
++ * @iface: interface bound to the driver making the request (optional)
++ *
++ * Warns all drivers bound to registered interfaces (using their pre_reset
++ * method), performs the port reset, and then lets the drivers know that
++ * the reset is over (using their post_reset method).
++ *
++ * Return value is the same as for usb_reset_device().
++ *
++ * The caller must own the device lock. For example, it's safe to use
++ * this from a driver probe() routine after downloading new firmware.
++ * For calls that might not occur during probe(), drivers should lock
++ * the device using usb_lock_device_for_reset().
++ *
++ * The interface locks are acquired during the pre_reset stage and released
++ * during the post_reset stage. However if iface is not NULL and is
++ * currently being probed, we assume that the caller already owns its
++ * lock.
++ */
++int usb_reset_composite_device(struct usb_device *udev,
++ struct usb_interface *iface)
++{
++ int ret;
++ struct usb_host_config *config = udev->actconfig;
++
++ if (udev->state == USB_STATE_NOTATTACHED ||
++ udev->state == USB_STATE_SUSPENDED) {
++ dev_dbg(&udev->dev, "device reset not allowed in state %d\n",
++ udev->state);
++ return -EINVAL;
++ }
++
++ if (iface && iface->condition != USB_INTERFACE_BINDING)
++ iface = NULL;
++
++ if (config) {
++ int i;
++ struct usb_interface *cintf;
++ struct usb_driver *drv;
++
++ for (i = 0; i < config->desc.bNumInterfaces; ++i) {
++ cintf = config->interface[i];
++ if (cintf != iface)
++ down(&cintf->dev.sem);
++ if (device_is_registered(&cintf->dev) &&
++ cintf->dev.driver) {
++ drv = to_usb_driver(cintf->dev.driver);
++ if (drv->pre_reset)
++ (drv->pre_reset)(cintf);
++ }
++ }
++ }
++
++ ret = usb_reset_device(udev);
++
++ if (config) {
++ int i;
++ struct usb_interface *cintf;
++ struct usb_driver *drv;
++
++ for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) {
++ cintf = config->interface[i];
++ if (device_is_registered(&cintf->dev) &&
++ cintf->dev.driver) {
++ drv = to_usb_driver(cintf->dev.driver);
++ if (drv->post_reset)
++ (drv->post_reset)(cintf);
++ }
++ if (cintf != iface)
++ up(&cintf->dev.sem);
++ }
++ }
++
++ return ret;
++}
+--- gregkh-2.6.orig/drivers/usb/core/usb.c
++++ gregkh-2.6/drivers/usb/core/usb.c
+@@ -1207,6 +1207,7 @@ EXPORT_SYMBOL(usb_ifnum_to_if);
+ EXPORT_SYMBOL(usb_altnum_to_altsetting);
+
+ EXPORT_SYMBOL(usb_reset_device);
++EXPORT_SYMBOL(usb_reset_composite_device);
+
+ EXPORT_SYMBOL(__usb_get_extra_descriptor);
+
+--- gregkh-2.6.orig/include/linux/usb.h
++++ gregkh-2.6/include/linux/usb.h
+@@ -387,6 +387,8 @@ extern int usb_lock_device_for_reset(str
+
+ /* USB port reset for device reinitialization */
+ extern int usb_reset_device(struct usb_device *dev);
++extern int usb_reset_composite_device(struct usb_device *dev,
++ struct usb_interface *iface);
+
+ extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id);
+
+@@ -555,6 +557,10 @@ struct usb_dynids {
+ * do (or don't) show up otherwise in the filesystem.
+ * @suspend: Called when the device is going to be suspended by the system.
+ * @resume: Called when the device is being resumed by the system.
++ * @pre_reset: Called by usb_reset_composite_device() when the device
++ * is about to be reset.
++ * @post_reset: Called by usb_reset_composite_device() after the device
++ * has been reset.
+ * @id_table: USB drivers use ID table to support hotplugging.
+ * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set
+ * or your driver's probe function will never get called.
+@@ -593,6 +599,9 @@ struct usb_driver {
+ int (*suspend) (struct usb_interface *intf, pm_message_t message);
+ int (*resume) (struct usb_interface *intf);
+
++ void (*pre_reset) (struct usb_interface *intf);
++ void (*post_reset) (struct usb_interface *intf);
++
+ const struct usb_device_id *id_table;
+
+ struct usb_dynids dynids;
diff --git a/usb/usbcore-recovery-from-set-configuration-failure.patch b/usb/usbcore-recovery-from-set-configuration-failure.patch
new file mode 100644
index 0000000000000..66c534a1b2215
--- /dev/null
+++ b/usb/usbcore-recovery-from-set-configuration-failure.patch
@@ -0,0 +1,199 @@
+From stern@rowland.harvard.edu Thu Jun 1 10:59:20 2006
+Date: Thu, 1 Jun 2006 13:59:16 -0400 (EDT)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>
+Subject: usbcore: recovery from Set-Configuration failure
+Message-ID: <Pine.LNX.4.44L0.0606011355330.7209-100000@iolanthe.rowland.org>
+
+This patch (as703) improves the error handling when a Set-Configuration
+request fails. The old interfaces are all unregistered before the
+request is sent, and if the request fails then we don't know what config
+the device is using. So it makes no sense to leave actconfig pointing
+to the old configuration with its invalid interfaces.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/usb/core/message.c | 151 ++++++++++++++++++++++-----------------------
+ 1 file changed, 75 insertions(+), 76 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/core/message.c
++++ gregkh-2.6/drivers/usb/core/message.c
+@@ -1411,15 +1411,7 @@ free_interfaces:
+ return ret;
+ }
+ }
+- }
+-
+- /* if it's already configured, clear out old state first.
+- * getting rid of old interfaces means unbinding their drivers.
+- */
+- if (dev->state != USB_STATE_ADDRESS)
+- usb_disable_device (dev, 1); // Skip ep0
+
+- if (cp) {
+ i = dev->bus_mA - cp->desc.bMaxPower * 2;
+ if (i < 0)
+ dev_warn(&dev->dev, "new config #%d exceeds power "
+@@ -1427,84 +1419,91 @@ free_interfaces:
+ configuration, -i);
+ }
+
++ /* if it's already configured, clear out old state first.
++ * getting rid of old interfaces means unbinding their drivers.
++ */
++ if (dev->state != USB_STATE_ADDRESS)
++ usb_disable_device (dev, 1); // Skip ep0
++
+ if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
+- NULL, 0, USB_CTRL_SET_TIMEOUT)) < 0)
+- goto free_interfaces;
++ NULL, 0, USB_CTRL_SET_TIMEOUT)) < 0) {
++
++ /* All the old state is gone, so what else can we do?
++ * The device is probably useless now anyway.
++ */
++ cp = NULL;
++ }
+
+ dev->actconfig = cp;
+- if (!cp)
++ if (!cp) {
+ usb_set_device_state(dev, USB_STATE_ADDRESS);
+- else {
+- usb_set_device_state(dev, USB_STATE_CONFIGURED);
++ goto free_interfaces;
++ }
++ usb_set_device_state(dev, USB_STATE_CONFIGURED);
+
+- /* Initialize the new interface structures and the
+- * hc/hcd/usbcore interface/endpoint state.
++ /* Initialize the new interface structures and the
++ * hc/hcd/usbcore interface/endpoint state.
++ */
++ for (i = 0; i < nintf; ++i) {
++ struct usb_interface_cache *intfc;
++ struct usb_interface *intf;
++ struct usb_host_interface *alt;
++
++ cp->interface[i] = intf = new_interfaces[i];
++ intfc = cp->intf_cache[i];
++ intf->altsetting = intfc->altsetting;
++ intf->num_altsetting = intfc->num_altsetting;
++ kref_get(&intfc->ref);
++
++ alt = usb_altnum_to_altsetting(intf, 0);
++
++ /* No altsetting 0? We'll assume the first altsetting.
++ * We could use a GetInterface call, but if a device is
++ * so non-compliant that it doesn't have altsetting 0
++ * then I wouldn't trust its reply anyway.
+ */
+- for (i = 0; i < nintf; ++i) {
+- struct usb_interface_cache *intfc;
+- struct usb_interface *intf;
+- struct usb_host_interface *alt;
+-
+- cp->interface[i] = intf = new_interfaces[i];
+- intfc = cp->intf_cache[i];
+- intf->altsetting = intfc->altsetting;
+- intf->num_altsetting = intfc->num_altsetting;
+- kref_get(&intfc->ref);
+-
+- alt = usb_altnum_to_altsetting(intf, 0);
+-
+- /* No altsetting 0? We'll assume the first altsetting.
+- * We could use a GetInterface call, but if a device is
+- * so non-compliant that it doesn't have altsetting 0
+- * then I wouldn't trust its reply anyway.
+- */
+- if (!alt)
+- alt = &intf->altsetting[0];
+-
+- intf->cur_altsetting = alt;
+- usb_enable_interface(dev, intf);
+- intf->dev.parent = &dev->dev;
+- intf->dev.driver = NULL;
+- intf->dev.bus = &usb_bus_type;
+- intf->dev.dma_mask = dev->dev.dma_mask;
+- intf->dev.release = release_interface;
+- device_initialize (&intf->dev);
+- mark_quiesced(intf);
+- sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
+- dev->bus->busnum, dev->devpath,
+- configuration,
+- alt->desc.bInterfaceNumber);
+- }
+- kfree(new_interfaces);
++ if (!alt)
++ alt = &intf->altsetting[0];
+
+- if (cp->string == NULL)
+- cp->string = usb_cache_string(dev,
+- cp->desc.iConfiguration);
+-
+- /* Now that all the interfaces are set up, register them
+- * to trigger binding of drivers to interfaces. probe()
+- * routines may install different altsettings and may
+- * claim() any interfaces not yet bound. Many class drivers
+- * need that: CDC, audio, video, etc.
+- */
+- for (i = 0; i < nintf; ++i) {
+- struct usb_interface *intf = cp->interface[i];
++ intf->cur_altsetting = alt;
++ usb_enable_interface(dev, intf);
++ intf->dev.parent = &dev->dev;
++ intf->dev.driver = NULL;
++ intf->dev.bus = &usb_bus_type;
++ intf->dev.dma_mask = dev->dev.dma_mask;
++ intf->dev.release = release_interface;
++ device_initialize (&intf->dev);
++ mark_quiesced(intf);
++ sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
++ dev->bus->busnum, dev->devpath,
++ configuration, alt->desc.bInterfaceNumber);
++ }
++ kfree(new_interfaces);
+
+- dev_dbg (&dev->dev,
+- "adding %s (config #%d, interface %d)\n",
+- intf->dev.bus_id, configuration,
+- intf->cur_altsetting->desc.bInterfaceNumber);
+- ret = device_add (&intf->dev);
+- if (ret != 0) {
+- dev_err(&dev->dev,
+- "device_add(%s) --> %d\n",
+- intf->dev.bus_id,
+- ret);
+- continue;
+- }
+- usb_create_sysfs_intf_files (intf);
++ if (cp->string == NULL)
++ cp->string = usb_cache_string(dev, cp->desc.iConfiguration);
++
++ /* Now that all the interfaces are set up, register them
++ * to trigger binding of drivers to interfaces. probe()
++ * routines may install different altsettings and may
++ * claim() any interfaces not yet bound. Many class drivers
++ * need that: CDC, audio, video, etc.
++ */
++ for (i = 0; i < nintf; ++i) {
++ struct usb_interface *intf = cp->interface[i];
++
++ dev_dbg (&dev->dev,
++ "adding %s (config #%d, interface %d)\n",
++ intf->dev.bus_id, configuration,
++ intf->cur_altsetting->desc.bInterfaceNumber);
++ ret = device_add (&intf->dev);
++ if (ret != 0) {
++ dev_err(&dev->dev, "device_add(%s) --> %d\n",
++ intf->dev.bus_id, ret);
++ continue;
+ }
++ usb_create_sysfs_intf_files (intf);
+ }
+
+ return 0;
diff --git a/usb/usbhid-use-usb_reset_composite_device.patch b/usb/usbhid-use-usb_reset_composite_device.patch
new file mode 100644
index 0000000000000..a80e7e02b0adf
--- /dev/null
+++ b/usb/usbhid-use-usb_reset_composite_device.patch
@@ -0,0 +1,93 @@
+From stern@rowland.harvard.edu Thu Jun 1 10:55:36 2006
+Date: Thu, 1 Jun 2006 13:55:28 -0400 (EDT)
+From: Alan Stern <stern@rowland.harvard.edu>
+To: Greg KH <greg@kroah.com>, Dmitry Torokhov <dtor_core@ameritech.net>, Vojtech Pavlik <vojtech@suse.cz>
+Subject: usbhid: use usb_reset_composite_device
+Message-ID: <Pine.LNX.4.44L0.0606011350500.7209-100000@iolanthe.rowland.org>
+
+This patch (as702) makes usbhid use the new usb_reset_composite_device
+API. Now HID interfaces can coexist with other interfaces on the same
+device, and a reset can safely be requested by any of the drivers.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/input/hid-core.c | 37 ++++++++++++++++++++++++++++++++-----
+ 1 file changed, 32 insertions(+), 5 deletions(-)
+
+--- gregkh-2.6.orig/drivers/usb/input/hid-core.c
++++ gregkh-2.6/drivers/usb/input/hid-core.c
+@@ -944,21 +944,28 @@ static void hid_reset(void *_hid)
+ dev_dbg(&hid->intf->dev, "resetting device\n");
+ rc = rc_lock = usb_lock_device_for_reset(hid->dev, hid->intf);
+ if (rc_lock >= 0) {
+- rc = usb_reset_device(hid->dev);
++ rc = usb_reset_composite_device(hid->dev, hid->intf);
+ if (rc_lock)
+ usb_unlock_device(hid->dev);
+ }
+ clear_bit(HID_RESET_PENDING, &hid->iofl);
+
+- if (rc == 0) {
+- hid->retry_delay = 0;
+- if (hid_start_in(hid))
++ switch (rc) {
++ case 0:
++ if (!test_bit(HID_IN_RUNNING, &hid->iofl))
+ hid_io_error(hid);
+- } else if (!(rc == -ENODEV || rc == -EHOSTUNREACH || rc == -EINTR))
++ break;
++ default:
+ err("can't reset device, %s-%s/input%d, status %d",
+ hid->dev->bus->bus_name,
+ hid->dev->devpath,
+ hid->ifnum, rc);
++ /* FALLTHROUGH */
++ case -EHOSTUNREACH:
++ case -ENODEV:
++ case -EINTR:
++ break;
++ }
+ }
+
+ /* Main I/O error handler */
+@@ -2063,11 +2070,29 @@ static int hid_resume(struct usb_interfa
+ int status;
+
+ clear_bit(HID_SUSPENDED, &hid->iofl);
++ hid->retry_delay = 0;
+ status = hid_start_in(hid);
+ dev_dbg(&intf->dev, "resume status %d\n", status);
+ return status;
+ }
+
++/* Treat USB reset pretty much the same as suspend/resume */
++static void hid_pre_reset(struct usb_interface *intf)
++{
++ /* FIXME: What if the interface is already suspended? */
++ hid_suspend(intf, PMSG_ON);
++}
++
++static void hid_post_reset(struct usb_interface *intf)
++{
++ struct usb_device *dev = interface_to_usbdev (intf);
++
++ hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
++ /* FIXME: Any more reinitialization needed? */
++
++ hid_resume(intf);
++}
++
+ static struct usb_device_id hid_usb_ids [] = {
+ { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
+ .bInterfaceClass = USB_INTERFACE_CLASS_HID },
+@@ -2082,6 +2107,8 @@ static struct usb_driver hid_driver = {
+ .disconnect = hid_disconnect,
+ .suspend = hid_suspend,
+ .resume = hid_resume,
++ .pre_reset = hid_pre_reset,
++ .post_reset = hid_post_reset,
+ .id_table = hid_usb_ids,
+ };
+