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: Josua Dietze <digidietze@draisberghof.de>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Josua Dietze 2009-05-24 23:21:42 +02:00 committed by Greg Kroah-Hartman
parent 68335e816a
commit 32ebbe7b6a
1 changed files with 71 additions and 54 deletions

View 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,
@ -56,7 +56,7 @@ static int option_rezero(struct us_data *us, int ep_in, int ep_out)
memcpy(buffer, rezero_msg, sizeof(rezero_msg));
result = usb_stor_bulk_transfer_buf(us,
usb_sndbulkpipe(us->pusb_dev, ep_out),
us->send_bulk_pipe,
buffer, sizeof(rezero_msg), NULL);
if (result != USB_STOR_XFER_GOOD) {
result = USB_STOR_XFER_ERROR;
@ -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 @@ static int option_rezero(struct us_data *us, int ep_in, int ep_out)
return result;
}
static int option_inquiry(struct us_data *us)
{
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: %s", "device inquiry for vendor name\n");
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;
}
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;
}
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)
{
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;
int result;
US_DEBUGP("Option MS: option_ms_init called\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");
/* 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;
}
/* 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);
}
}
/* 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");
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;