Commit 32ebbe7b authored by Josua Dietze's avatar Josua Dietze Committed by Greg Kroah-Hartman

USB: usb-storage: add filter to "option_ms" to leave unrecognized devices alone

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: default avatarJosua Dietze <digidietze@draisberghof.de>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 68335e81
...@@ -37,7 +37,7 @@ MODULE_PARM_DESC(option_zero_cd, "ZeroCD mode (1=Force Modem (default)," ...@@ -37,7 +37,7 @@ MODULE_PARM_DESC(option_zero_cd, "ZeroCD mode (1=Force Modem (default),"
#define RESPONSE_LEN 1024 #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[] = { const unsigned char rezero_msg[] = {
0x55, 0x53, 0x42, 0x43, 0x78, 0x56, 0x34, 0x12, 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) ...@@ -54,10 +54,10 @@ static int option_rezero(struct us_data *us, int ep_in, int ep_out)
if (buffer == NULL) if (buffer == NULL)
return USB_STOR_TRANSPORT_ERROR; 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, result = usb_stor_bulk_transfer_buf(us,
usb_sndbulkpipe(us->pusb_dev, ep_out), us->send_bulk_pipe,
buffer, sizeof (rezero_msg), NULL); buffer, sizeof(rezero_msg), NULL);
if (result != USB_STOR_XFER_GOOD) { if (result != USB_STOR_XFER_GOOD) {
result = USB_STOR_XFER_ERROR; result = USB_STOR_XFER_ERROR;
goto out; goto out;
...@@ -66,9 +66,15 @@ static int option_rezero(struct us_data *us, int ep_in, int ep_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 /* Some of the devices need to be asked for a response, but we don't
* care what that response is. * care what that response is.
*/ */
result = usb_stor_bulk_transfer_buf(us, usb_stor_bulk_transfer_buf(us,
usb_sndbulkpipe(us->pusb_dev, ep_out), us->recv_bulk_pipe,
buffer, RESPONSE_LEN, NULL); 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; result = USB_STOR_XFER_GOOD;
out: out:
...@@ -76,64 +82,75 @@ out: ...@@ -76,64 +82,75 @@ out:
return result; return result;
} }
int option_ms_init(struct us_data *us) static int option_inquiry(struct us_data *us)
{ {
struct usb_device *udev; const unsigned char inquiry_msg[] = {
struct usb_interface *intf; 0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78,
struct usb_host_interface *iface_desc; 0x24, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x12,
struct usb_endpoint_descriptor *endpoint = NULL; 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00,
u8 ep_in = 0, ep_out = 0; 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
int ep_in_size = 0, ep_out_size = 0; };
int i, result; char *buffer;
int 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;
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 */ buffer = kzalloc(0x24, GFP_KERNEL);
iface_desc = intf->cur_altsetting; if (buffer == NULL)
if (iface_desc->desc.bInterfaceClass != 0x8 || return USB_STOR_TRANSPORT_ERROR;
iface_desc->desc.bInterfaceSubClass != 0x6 ||
iface_desc->desc.bInterfaceProtocol != 0x50) { memcpy(buffer, inquiry_msg, sizeof(inquiry_msg));
US_DEBUGP("Option MS: mass storage interface not found, no action " result = usb_stor_bulk_transfer_buf(us,
"required\n"); us->send_bulk_pipe,
return 0; buffer, sizeof(inquiry_msg), NULL);
if (result != USB_STOR_XFER_GOOD) {
result = USB_STOR_XFER_ERROR;
goto out;
} }
/* Find the mass storage bulk endpoints */ result = usb_stor_bulk_transfer_buf(us,
for (i = 0; i < iface_desc->desc.bNumEndpoints && (!ep_in_size || !ep_out_size); ++i) { us->recv_bulk_pipe,
endpoint = &iface_desc->endpoint[i].desc; buffer, 0x24, NULL);
if (result != USB_STOR_XFER_GOOD) {
if (usb_endpoint_is_bulk_in(endpoint)) { result = USB_STOR_XFER_ERROR;
ep_in = usb_endpoint_num(endpoint); goto out;
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);
}
} }
/* Can't find the mass storage endpoints */ result = memcmp(buffer+8, "Option", 6);
if (!ep_in_size || !ep_out_size) {
US_DEBUGP("Option MS: mass storage endpoints not found, no action " /* Read the CSW */
"required\n"); 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; return 0;
} } else
US_DEBUGP("Option MS: this is a genuine Option device,"
" proceeding\n");
/* Force Modem mode */ /* Force Modem mode */
if (option_zero_cd == ZCD_FORCE_MODEM) { if (option_zero_cd == ZCD_FORCE_MODEM) {
US_DEBUGP("Option MS: %s", "Forcing Modem Mode\n"); 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) if (result != USB_STOR_XFER_GOOD)
US_DEBUGP("Option MS: Failed to switch to modem mode.\n"); US_DEBUGP("Option MS: Failed to switch to modem mode.\n");
return -EIO; return -EIO;
......
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