From: Pete Zaitcev <zaitcev@redhat.com> Date: Tue, 5 May 2009 15:37:30 -0600 Subject: [usb] support Huawei's mode switch in kernel Message-id: 20090505153730.110a77e8.zaitcev@redhat.com O-Subject: [Patch RHEL 5.4] Support Huawei's mode switch in kernel Bugzilla: 485182 RH-Acked-by: Anton Arapov <aarapov@redhat.com> RH-Acked-by: Peter Martuccelli <peterm@redhat.com> RH-Acked-by: John W. Linville <linville@redhat.com> This a fix for bz#485182, where Huawei modem cannot be used because by default it comes up as a storage device instead of a modem. The issue was "addressed" before with a purely user-mode tool, but the experience with it was awful. It turned out that preventing usb-storage from doing the doomed SCSI scanning was much better. So, we add an initializer that _fails_ the usb-storage probe if it _succeeded_ in initializing. The other part of the patch adds a primitive so the serial driver would bind to serial side only. This way usb-storage can bind again simultaneously with option.c and thus the internal storage on the modem remains accessible, in case an ISP recorded dialout parameters there. This whole thing is an upstream solution. A good example of how pushing drivers to the userland is not always the best approach, too. Our support tested a kernel I built for them in Brew (Huawei is a rarity in America, and it's somewhat expensive, so I don't have one in my lab). I think the risk is low. It's not zero since I merged the ID tables and this affects unrelated devices (such as Dell's cell modems). Still, please ACK. Our colleagues from Europe asked for this. -- Pete diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 804dddd..aec6f2f 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -79,7 +79,27 @@ static int option_send_setup(struct usb_serial_port *port); #define OPTION_PRODUCT_FUSION2 0x6300 #define OPTION_PRODUCT_COBRA 0x6500 #define OPTION_PRODUCT_COBRA2 0x6600 + #define HUAWEI_PRODUCT_E600 0x1001 +#define HUAWEI_PRODUCT_E220 0x1003 +#define HUAWEI_PRODUCT_E220BIS 0x1004 +#define HUAWEI_PRODUCT_E1401 0x1401 +#define HUAWEI_PRODUCT_E1403 0x1403 +#define HUAWEI_PRODUCT_E1405 0x1405 +#define HUAWEI_PRODUCT_E1406 0x1406 +#define HUAWEI_PRODUCT_E1408 0x1408 +#define HUAWEI_PRODUCT_E1409 0x1409 +#define HUAWEI_PRODUCT_E1410 0x1410 +#define HUAWEI_PRODUCT_E1411 0x1411 +#define HUAWEI_PRODUCT_E1412 0x1412 +#define HUAWEI_PRODUCT_E1413 0x1413 +#define HUAWEI_PRODUCT_E1414 0x1414 +#define HUAWEI_PRODUCT_E1415 0x1415 +#define HUAWEI_PRODUCT_E1416 0x1416 +#define HUAWEI_PRODUCT_E1417 0x1417 +#define HUAWEI_PRODUCT_E1418 0x1418 +#define HUAWEI_PRODUCT_E1419 0x1419 + #define AUDIOVOX_PRODUCT_AIRCARD 0x0112 #define NOVATELWIRELESS_PRODUCT_U740 0x1400 #define ANYDATA_PRODUCT_ID 0x6501 @@ -90,7 +110,25 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) }, - { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220BIS, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1401, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1403, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1405, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1406, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1408, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1409, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1410, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1411, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1412, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1413, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1414, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1415, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1416, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1417, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1418, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1419, 0xff, 0xff, 0xff) }, { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) }, { USB_DEVICE(DELL_VENDOR_ID, 0x8114) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite EV620 CDMA/EV-DO */ @@ -108,19 +146,6 @@ static struct usb_device_id option_ids[] = { { } /* 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(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) }, - { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) }, - { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) }, - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) }, - { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) }, - { } /* Terminating entry */ -}; - MODULE_DEVICE_TABLE(usb, option_ids); static struct usb_driver option_driver = { @@ -141,7 +166,7 @@ static struct usb_serial_driver option_1port_device = { .name = "option1", }, .description = "GSM modem (1-port)", - .id_table = option_ids1, + .id_table = option_ids, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, .num_bulk_out = NUM_DONT_CARE, diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c index ab173b3..9941ce5 100644 --- a/drivers/usb/storage/initializers.c +++ b/drivers/usb/storage/initializers.c @@ -98,6 +98,20 @@ int usb_stor_ucr61s2b_init(struct us_data *us) return (res ? -1 : 0); } +/* This places the HUAWEI E220 devices in multi-port mode */ +int usb_stor_huawei_e220_init(struct us_data *us) +{ + int result; + + us->iobuf[0] = 0x1; + result = usb_stor_control_msg(us, us->send_ctrl_pipe, + USB_REQ_SET_FEATURE, + USB_TYPE_STANDARD | USB_RECIP_DEVICE, + 0x01, 0x0, us->iobuf, 0x1, 1000); + US_DEBUGP("usb_control_msg performing result is %d\n", result); + return (result ? 0 : -1); +} + /* Place the Rio Karma into mass storage mode. * * The initialization begins by sending 40 bytes starting diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h index 927f778..ae2f111 100644 --- a/drivers/usb/storage/initializers.h +++ b/drivers/usb/storage/initializers.h @@ -47,4 +47,7 @@ int usb_stor_euscsi_init(struct us_data *us); /* This function is required to activate all four slots on the UCR-61S2B * flash reader */ int usb_stor_ucr61s2b_init(struct us_data *us); +/* This places the HUAWEI E220 devices in multi-port mode */ +int usb_stor_huawei_e220_init(struct us_data *us); + int rio_karma_init(struct us_data *us); diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 8486747..93759b9 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1286,6 +1286,101 @@ UNUSUAL_DEV( 0x1019, 0x0c55, 0x0110, 0x0110, US_SC_DEVICE, US_PR_DEVICE, usb_stor_ucr61s2b_init, 0 ), +UNUSUAL_DEV( 0x12d1, 0x1001, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1003, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1004, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1401, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1403, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1405, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1406, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1408, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1409, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1410, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1411, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1412, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1413, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1414, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1415, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1416, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1417, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1418, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), +UNUSUAL_DEV( 0x12d1, 0x1419, 0x0000, 0x0000, + "HUAWEI MOBILE", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, + 0), /* Reported by Vilius Bilinkevicius <vilisas AT xxx DOT lt) */ UNUSUAL_DEV( 0x132b, 0x000b, 0x0001, 0x0001, "Minolta", diff --git a/include/linux/usb.h b/include/linux/usb.h index 3d6e55b..0983c54 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -678,6 +678,30 @@ static inline int usb_endpoint_is_isoc_out(const struct usb_endpoint_descriptor .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), \ .bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr) +/** + * USB_DEVICE_AND_INTERFACE_INFO - + * describe a specific usb device with a class of usb interfaces + * @vend: the 16 bit USB Vendor ID + * @prod: the 16 bit USB Product ID + * @cl: bInterfaceClass value + * @sc: bInterfaceSubClass value + * @pr: bInterfaceProtocol value + * + * This macro is used to create a struct usb_device_id that matches a + * specific device with a specific class of interfaces. + * + * This is especially useful when explicitly matching devices that have + * vendor specific bDeviceClass values, but standards-compliant interfaces. + */ +#define USB_DEVICE_AND_INTERFACE_INFO(vend, prod, cl, sc, pr) \ + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO \ + | USB_DEVICE_ID_MATCH_DEVICE, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bInterfaceClass = (cl), \ + .bInterfaceSubClass = (sc), \ + .bInterfaceProtocol = (pr) + /* ----------------------------------------------------------------------- */ struct usb_dynids {