USB: usb-storage: add filter to "option_ms" to leave unrecognized devices alone
authorJosua Dietze <digidietze@draisberghof.de>
Sun, 24 May 2009 21:21:42 +0000 (23:21 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 16 Jun 2009 04:44:46 +0000 (21:44 -0700)
Some unusual usb devices from the maker "Option" are switched from
storage to serial/modem mode by sending a SCSI REZERO command. In one
case a fairly common vendor/device ID is affected which led to problems
for users of other modems or phones which are not supposed to be
switched.

The patch adds a filter by reading the vendor name with the SCSI INQUIRY
command, and skips the switching code for all unrecognized entries.

Further changes are cleanups and corrections pointed out by Alan Stern.

Tested with two devices with the IDs 05c6:1000, one from "Option" and
switchable, and one from Samsung (cell phone).

Signed-off-by: Josua Dietze <digidietze@draisberghof.de>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/storage/option_ms.c

index 0d8853339bb613f590c5eb981c4520e4674118ba..d41cc0a970f79a147660da239bdc014612396354 100644 (file)
@@ -37,7 +37,7 @@ MODULE_PARM_DESC(option_zero_cd, "ZeroCD mode (1=Force Modem (default),"
 
 #define RESPONSE_LEN 1024
 
-static int option_rezero(struct us_data *us, int ep_in, int ep_out)
+static int option_rezero(struct us_data *us)
 {
        const unsigned char rezero_msg[] = {
          0x55, 0x53, 0x42, 0x43, 0x78, 0x56, 0x34, 0x12,
@@ -54,10 +54,10 @@ static int option_rezero(struct us_data *us, int ep_in, int ep_out)
        if (buffer == NULL)
                return USB_STOR_TRANSPORT_ERROR;
 
-       memcpy(buffer, rezero_msg, sizeof (rezero_msg));
+       memcpy(buffer, rezero_msg, sizeof(rezero_msg));
        result = usb_stor_bulk_transfer_buf(us,
-                       usb_sndbulkpipe(us->pusb_dev, ep_out),
-                       buffer, sizeof (rezero_msg), NULL);
+                       us->send_bulk_pipe,
+                       buffer, sizeof(rezero_msg), NULL);
        if (result != USB_STOR_XFER_GOOD) {
                result = USB_STOR_XFER_ERROR;
                goto out;
@@ -66,9 +66,15 @@ static int option_rezero(struct us_data *us, int ep_in, int ep_out)
        /* Some of the devices need to be asked for a response, but we don't
         * care what that response is.
         */
-       result = usb_stor_bulk_transfer_buf(us,
-                       usb_sndbulkpipe(us->pusb_dev, ep_out),
+       usb_stor_bulk_transfer_buf(us,
+                       us->recv_bulk_pipe,
                        buffer, RESPONSE_LEN, NULL);
+
+       /* Read the CSW */
+       usb_stor_bulk_transfer_buf(us,
+                       us->recv_bulk_pipe,
+                       buffer, 13, NULL);
+
        result = USB_STOR_XFER_GOOD;
 
 out:
@@ -76,64 +82,75 @@ out:
        return result;
 }
 
-int option_ms_init(struct us_data *us)
+static int option_inquiry(struct us_data *us)
 {
-       struct usb_device *udev;
-       struct usb_interface *intf;
-       struct usb_host_interface *iface_desc;
-       struct usb_endpoint_descriptor *endpoint = NULL;
-       u8 ep_in = 0, ep_out = 0;
-       int ep_in_size = 0, ep_out_size = 0;
-       int i, result;
-
-       udev = us->pusb_dev;
-       intf = us->pusb_intf;
-
-       /* Ensure it's really a ZeroCD device; devices that are already
-        * in modem mode return 0xFF for class, subclass, and protocol.
-        */
-       if (udev->descriptor.bDeviceClass != 0 ||
-           udev->descriptor.bDeviceSubClass != 0 ||
-           udev->descriptor.bDeviceProtocol != 0 ||
-           udev->actconfig->desc.bNumInterfaces == 3)
-               return 0;
+       const unsigned char inquiry_msg[] = {
+         0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78,
+         0x24, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x12,
+         0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+       char *buffer;
+       int result;
 
-       US_DEBUGP("Option MS: option_ms_init called\n");
+       US_DEBUGP("Option MS: %s", "device inquiry for vendor name\n");
 
-       /* Find the right mass storage interface */
-       iface_desc = intf->cur_altsetting;
-       if (iface_desc->desc.bInterfaceClass != 0x8 ||
-           iface_desc->desc.bInterfaceSubClass != 0x6 ||
-           iface_desc->desc.bInterfaceProtocol != 0x50) {
-               US_DEBUGP("Option MS: mass storage interface not found, no action "
-                         "required\n");
-               return 0;
+       buffer = kzalloc(0x24, GFP_KERNEL);
+       if (buffer == NULL)
+               return USB_STOR_TRANSPORT_ERROR;
+
+       memcpy(buffer, inquiry_msg, sizeof(inquiry_msg));
+       result = usb_stor_bulk_transfer_buf(us,
+                       us->send_bulk_pipe,
+                       buffer, sizeof(inquiry_msg), NULL);
+       if (result != USB_STOR_XFER_GOOD) {
+               result = USB_STOR_XFER_ERROR;
+               goto out;
        }
 
-       /* Find the mass storage bulk endpoints */
-       for (i = 0; i < iface_desc->desc.bNumEndpoints && (!ep_in_size || !ep_out_size); ++i) {
-               endpoint = &iface_desc->endpoint[i].desc;
-
-               if (usb_endpoint_is_bulk_in(endpoint)) {
-                       ep_in = usb_endpoint_num(endpoint);
-                       ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize);
-               } else if (usb_endpoint_is_bulk_out(endpoint)) {
-                       ep_out = usb_endpoint_num(endpoint);
-                       ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize);
-               }
+       result = usb_stor_bulk_transfer_buf(us,
+                       us->recv_bulk_pipe,
+                       buffer, 0x24, NULL);
+       if (result != USB_STOR_XFER_GOOD) {
+               result = USB_STOR_XFER_ERROR;
+               goto out;
        }
 
-       /* Can't find the mass storage endpoints */
-       if (!ep_in_size || !ep_out_size) {
-               US_DEBUGP("Option MS: mass storage endpoints not found, no action "
-                         "required\n");
+       result = memcmp(buffer+8, "Option", 6);
+
+       /* Read the CSW */
+       usb_stor_bulk_transfer_buf(us,
+                       us->recv_bulk_pipe,
+                       buffer, 13, NULL);
+
+out:
+       kfree(buffer);
+       return result;
+}
+
+
+int option_ms_init(struct us_data *us)
+{
+       int result;
+
+       US_DEBUGP("Option MS: option_ms_init called\n");
+
+       /* Additional test for vendor information via INQUIRY,
+        * because some vendor/product IDs are ambiguous
+        */
+       result = option_inquiry(us);
+       if (result != 0) {
+               US_DEBUGP("Option MS: vendor is not Option or not determinable,"
+                         " no action taken\n");
                return 0;
-       }
+       } else
+               US_DEBUGP("Option MS: this is a genuine Option device,"
+                         " proceeding\n");
 
        /* Force Modem mode */
        if (option_zero_cd == ZCD_FORCE_MODEM) {
                US_DEBUGP("Option MS: %s", "Forcing Modem Mode\n");
-               result = option_rezero(us, ep_in, ep_out);
+               result = option_rezero(us);
                if (result != USB_STOR_XFER_GOOD)
                        US_DEBUGP("Option MS: Failed to switch to modem mode.\n");
                return -EIO;