Re: avoid resetting mode-switched devices

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Am Dienstag, 15. Dezember 2009 16:42:54 schrieb Alan Stern:
> On Tue, 15 Dec 2009, Oliver Neukum wrote:
> 
> > Am Montag, 14. Dezember 2009 23:12:06 schrieb Alan Stern:

> > Yes, I have some code to fall back to usb_reset_configuration().
> > Should we do so also for storage?
> 
> I don't think that's such a good idea.  For instance, I doubt
> usb_reset_configuration() will be powerful enough to recover from the
> errors usb-storage encounters.  In fact, even a port reset isn't always
> powerful enough.

True, but we cannot do a port reset for such devices.

> Also it doesn't notify drivers about the reset.  Really it was meant 
> for use only with non-composite devices, where there's never more than 
> one driver involved.

Yes, we'd need an extended version that calls pre/post_reset.
I am attaching the stuff I wrote before I looked closely at the
reset logic.
 
> > > Well, if we don't have any choice then it has to be done.  Obviously a
> > > sysfs attribute is the best way.  However I can't help thinking that
> > > this is only a symptom of something deeper, and it ought to be
> > > generalized.  But it's not clear how...
> > 
> > The obvious alternative is to put mode switching into the kernel.
> > If you don't want to do that, there'll be other drawbacks, we are seeing
> > here.
> 
> Even if mode switching were in the kernel (and in a few cases it is),
> there still would be problems.  Mode switching often involves changing
> descriptors.  The core interprets these changes as meaning that the
> device has disconnected and another device was plugged into the port.

We'd have to save copies of descriptors in both modes, compare,
initiate a reswitch and wait for it. Extremely ugly.

> Face it, mode switching is a bad idea.  These devices should have been
> designed to use multiple configurations instead.  Since they weren't,
> their functionality is severely compromised: It's very difficult to
> reset them for error recovery.

I do face it. I am asking whether the unease you feel is due to sucking
hardware or have I overlooked something?

	Regards
		Oliver

commit d7253d1e33bd46c8237635763a65b5c2ef27486e
Author: Oliver Neukum <oliver@xxxxxxxxxx>
Date:   Fri Nov 6 07:59:32 2009 +0100

    usb:first draft of new quirk

diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 03bd703..7d74b22 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -30,6 +30,7 @@
 #include <linux/workqueue.h>
 
 #include <linux/usb.h>
+#include <linux/usb/quirks.h>
 
 #include <linux/hid.h>
 #include <linux/hiddev.h>
@@ -123,10 +124,14 @@ static void hid_reset(struct work_struct *work)
 	}
 
 	else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
+
 		dev_dbg(&usbhid->intf->dev, "resetting device\n");
 		rc = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf);
 		if (rc == 0) {
-			rc = usb_reset_device(hid_to_usb_dev(hid));
+			if (hid_to_usb_dev(hid)->quirks & USB_QUIRK_RESET_MORPHS)
+				rc = usb_multiple_reset_configuration(hid_to_usb_dev(hid));
+			else
+				rc = usb_reset_device(hid_to_usb_dev(hid));
 			usb_unlock_device(hid_to_usb_dev(hid));
 		}
 		clear_bit(HID_RESET_PENDING, &usbhid->iofl);
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index da718e8..06a856f 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1398,6 +1398,71 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
 EXPORT_SYMBOL_GPL(usb_set_interface);
 
 /**
+ * usb_multiple_reset_configuration - lightweight device reset
+ * @dev: the device whose configuration is being reset
+ *
+ * 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 endpoint state (only for bulk and interrupt
+ * endpoints).  Other usbcore state is unchanged, including bindings of
+ * usb device drivers to interfaces.
+ *
+ * This version notifies other drivers via pre/post_reset if they
+ * support it or else unbinds and rebinds the drivers
+ *
+ * The caller must own the device lock.
+ *
+ * Returns zero on success, else a negative error code.
+ */
+int usb_multiple_reset_configuration(struct usb_device *dev)
+{
+	int result, i;
+	struct usb_host_config *config = dev->actconfig;
+
+	/* Prevent autosuspend during the reset */
+	usb_autoresume_device(dev);
+
+	for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+		struct usb_interface *cintf = config->interface[i];
+		struct usb_driver *drv;
+		int unbind = 0;
+
+		if (cintf->dev.driver) {
+			drv = to_usb_driver(cintf->dev.driver);
+			if (drv->pre_reset && drv->post_reset)
+				unbind = (drv->pre_reset)(cintf);
+			else if (cintf->condition ==
+					USB_INTERFACE_BOUND)
+				unbind = 1;
+			if (unbind)
+				usb_forced_unbind_intf(cintf);
+		}
+	}
+
+	result = usb_reset_configuration(dev);
+
+	for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) {
+		struct usb_interface *cintf = config->interface[i];
+		struct usb_driver *drv;
+		int rebind = cintf->needs_binding;
+
+		if (!rebind && cintf->dev.driver) {
+			drv = to_usb_driver(cintf->dev.driver);
+			if (drv->post_reset)
+				rebind = (drv->post_reset)(cintf);
+			else if (cintf->condition ==
+					USB_INTERFACE_BOUND)
+				rebind = 1;
+		}
+		if (result == 0 && rebind)
+			usb_rebind_intf(cintf);
+	}
+	return result;
+}
+EXPORT_SYMBOL_GPL(usb_multiple_reset_configuration);
+
+/**
  * usb_reset_configuration - lightweight device reset
  * @dev: the device whose configuration is being reset
  *
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index ab93918..0b68922 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -120,6 +120,7 @@ void usb_detect_quirks(struct usb_device *udev)
 	 * for all devices.  It will affect things like hub resets
 	 * and EMF-related port disables.
 	 */
-	udev->persist_enabled = 1;
+	if (!(udev->quirks & USB_QUIRK_RESET_MORPHS))
+		udev->persist_enabled = 1;
 #endif	/* CONFIG_PM */
 }
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 589f6b4..cb12486 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -47,6 +47,8 @@
 #include <linux/errno.h>
 #include <linux/slab.h>
 
+#include <linux/usb/quirks.h>
+
 #include <scsi/scsi.h>
 #include <scsi/scsi_eh.h>
 #include <scsi/scsi_device.h>
@@ -1288,6 +1290,10 @@ int usb_stor_port_reset(struct us_data *us)
 {
 	int result;
 
+	/*for these devices we must use the class specific method */
+	if (us->pusb_dev->quirks & USB_QUIRK_RESET_MORPHS)
+		return -EPERM;
+
 	result = 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);
diff --git a/include/linux/usb.h b/include/linux/usb.h
index a34fa89..ea0eb6f 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -1421,6 +1421,7 @@ extern int usb_string(struct usb_device *dev, int index,
 /* wrappers that also update important state inside usbcore */
 extern int usb_clear_halt(struct usb_device *dev, int pipe);
 extern int usb_reset_configuration(struct usb_device *dev);
+extern int usb_multiple_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);
 
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index 2526f3b..0a555dd 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -19,4 +19,7 @@
 /* device can't handle its Configuration or Interface strings */
 #define USB_QUIRK_CONFIG_INTF_STRINGS	0x00000008
 
+/*device will morph if reset, don't use reset for handling errors */
+#define USB_QUIRK_RESET_MORPHS		0x00000010
+
 #endif /* __LINUX_USB_QUIRKS_H */

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux