|
@@ -139,10 +139,18 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
|
|
|
|
|
|
BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct qmi_wwan_state)));
|
|
BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct qmi_wwan_state)));
|
|
|
|
|
|
- /* require a single interrupt status endpoint for subdriver */
|
|
|
|
|
|
+ /* control and data is shared? */
|
|
|
|
+ if (intf->cur_altsetting->desc.bNumEndpoints == 3) {
|
|
|
|
+ info->control = intf;
|
|
|
|
+ info->data = intf;
|
|
|
|
+ goto shared;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* else require a single interrupt status endpoint on control intf */
|
|
if (intf->cur_altsetting->desc.bNumEndpoints != 1)
|
|
if (intf->cur_altsetting->desc.bNumEndpoints != 1)
|
|
goto err;
|
|
goto err;
|
|
|
|
|
|
|
|
+ /* and a number of CDC descriptors */
|
|
while (len > 3) {
|
|
while (len > 3) {
|
|
struct usb_descriptor_header *h = (void *)buf;
|
|
struct usb_descriptor_header *h = (void *)buf;
|
|
|
|
|
|
@@ -231,8 +239,9 @@ next_desc:
|
|
if (status < 0)
|
|
if (status < 0)
|
|
goto err;
|
|
goto err;
|
|
|
|
|
|
|
|
+shared:
|
|
status = qmi_wwan_register_subdriver(dev);
|
|
status = qmi_wwan_register_subdriver(dev);
|
|
- if (status < 0) {
|
|
|
|
|
|
+ if (status < 0 && info->control != info->data) {
|
|
usb_set_intfdata(info->data, NULL);
|
|
usb_set_intfdata(info->data, NULL);
|
|
usb_driver_release_interface(driver, info->data);
|
|
usb_driver_release_interface(driver, info->data);
|
|
}
|
|
}
|
|
@@ -241,20 +250,6 @@ err:
|
|
return status;
|
|
return status;
|
|
}
|
|
}
|
|
|
|
|
|
-/* Some devices combine the "control" and "data" functions into a
|
|
|
|
- * single interface with all three endpoints: interrupt + bulk in and
|
|
|
|
- * out
|
|
|
|
- */
|
|
|
|
-static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf)
|
|
|
|
-{
|
|
|
|
- struct qmi_wwan_state *info = (void *)&dev->data;
|
|
|
|
-
|
|
|
|
- /* control and data is shared */
|
|
|
|
- info->control = intf;
|
|
|
|
- info->data = intf;
|
|
|
|
- return qmi_wwan_register_subdriver(dev);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf)
|
|
static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf)
|
|
{
|
|
{
|
|
struct qmi_wwan_state *info = (void *)&dev->data;
|
|
struct qmi_wwan_state *info = (void *)&dev->data;
|
|
@@ -330,20 +325,12 @@ static const struct driver_info qmi_wwan_info = {
|
|
.manage_power = qmi_wwan_manage_power,
|
|
.manage_power = qmi_wwan_manage_power,
|
|
};
|
|
};
|
|
|
|
|
|
-static const struct driver_info qmi_wwan_shared = {
|
|
|
|
- .description = "WWAN/QMI device",
|
|
|
|
- .flags = FLAG_WWAN,
|
|
|
|
- .bind = qmi_wwan_bind_shared,
|
|
|
|
- .unbind = qmi_wwan_unbind,
|
|
|
|
- .manage_power = qmi_wwan_manage_power,
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
#define HUAWEI_VENDOR_ID 0x12D1
|
|
#define HUAWEI_VENDOR_ID 0x12D1
|
|
|
|
|
|
/* map QMI/wwan function by a fixed interface number */
|
|
/* map QMI/wwan function by a fixed interface number */
|
|
#define QMI_FIXED_INTF(vend, prod, num) \
|
|
#define QMI_FIXED_INTF(vend, prod, num) \
|
|
USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \
|
|
USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \
|
|
- .driver_info = (unsigned long)&qmi_wwan_shared
|
|
|
|
|
|
+ .driver_info = (unsigned long)&qmi_wwan_info
|
|
|
|
|
|
/* Gobi 1000 QMI/wwan interface number is 3 according to qcserial */
|
|
/* Gobi 1000 QMI/wwan interface number is 3 according to qcserial */
|
|
#define QMI_GOBI1K_DEVICE(vend, prod) \
|
|
#define QMI_GOBI1K_DEVICE(vend, prod) \
|
|
@@ -367,15 +354,15 @@ static const struct usb_device_id products[] = {
|
|
/* 2. Combined interface devices matching on class+protocol */
|
|
/* 2. Combined interface devices matching on class+protocol */
|
|
{ /* Huawei E392, E398 and possibly others in "Windows mode" */
|
|
{ /* Huawei E392, E398 and possibly others in "Windows mode" */
|
|
USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 17),
|
|
USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 17),
|
|
- .driver_info = (unsigned long)&qmi_wwan_shared,
|
|
|
|
|
|
+ .driver_info = (unsigned long)&qmi_wwan_info,
|
|
},
|
|
},
|
|
{ /* Pantech UML290 */
|
|
{ /* Pantech UML290 */
|
|
USB_DEVICE_AND_INTERFACE_INFO(0x106c, 0x3718, USB_CLASS_VENDOR_SPEC, 0xf0, 0xff),
|
|
USB_DEVICE_AND_INTERFACE_INFO(0x106c, 0x3718, USB_CLASS_VENDOR_SPEC, 0xf0, 0xff),
|
|
- .driver_info = (unsigned long)&qmi_wwan_shared,
|
|
|
|
|
|
+ .driver_info = (unsigned long)&qmi_wwan_info,
|
|
},
|
|
},
|
|
{ /* Pantech UML290 - newer firmware */
|
|
{ /* Pantech UML290 - newer firmware */
|
|
USB_DEVICE_AND_INTERFACE_INFO(0x106c, 0x3718, USB_CLASS_VENDOR_SPEC, 0xf1, 0xff),
|
|
USB_DEVICE_AND_INTERFACE_INFO(0x106c, 0x3718, USB_CLASS_VENDOR_SPEC, 0xf1, 0xff),
|
|
- .driver_info = (unsigned long)&qmi_wwan_shared,
|
|
|
|
|
|
+ .driver_info = (unsigned long)&qmi_wwan_info,
|
|
},
|
|
},
|
|
|
|
|
|
/* 3. Combined interface devices matching on interface number */
|
|
/* 3. Combined interface devices matching on interface number */
|
|
@@ -457,7 +444,7 @@ static int qmi_wwan_probe(struct usb_interface *intf, const struct usb_device_id
|
|
*/
|
|
*/
|
|
if (!id->driver_info) {
|
|
if (!id->driver_info) {
|
|
dev_dbg(&intf->dev, "setting defaults for dynamic device id\n");
|
|
dev_dbg(&intf->dev, "setting defaults for dynamic device id\n");
|
|
- id->driver_info = (unsigned long)&qmi_wwan_shared;
|
|
|
|
|
|
+ id->driver_info = (unsigned long)&qmi_wwan_info;
|
|
}
|
|
}
|
|
|
|
|
|
return usbnet_probe(intf, id);
|
|
return usbnet_probe(intf, id);
|