Commit 1fc4c84d authored by Bjørn Mork's avatar Bjørn Mork Committed by David S. Miller

net: cdc_ether: allow combined control and data interface

Some Icera based Huawei modems handled by this driver are not
completely CDC ECM compliant, using the same USB interface for both
control and data. The CDC functional descriptors include a Union
naming this interface as both master and slave, so it is supportable
by relaxing the descriptor parsing in case these interfaces are
identical.

This has been tested on a Huawei K3806 and verified to add support
for that device.
Reported-and-tested-by: default avatarEnrico Mioso <mrkiko.rs@gmail.com>
Signed-off-by: default avatarBjørn Mork <bjorn@mork.no>
Acked-by: default avatarOliver Neukum <oliver@neukum.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 06efce71
...@@ -215,6 +215,10 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) ...@@ -215,6 +215,10 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
goto bad_desc; goto bad_desc;
} }
/* some devices merge these - skip class check */
if (info->control == info->data)
goto next_desc;
/* a data interface altsetting does the real i/o */ /* a data interface altsetting does the real i/o */
d = &info->data->cur_altsetting->desc; d = &info->data->cur_altsetting->desc;
if (d->bInterfaceClass != USB_CLASS_CDC_DATA) { if (d->bInterfaceClass != USB_CLASS_CDC_DATA) {
...@@ -304,18 +308,22 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) ...@@ -304,18 +308,22 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
/* claim data interface and set it up ... with side effects. /* claim data interface and set it up ... with side effects.
* network traffic can't flow until an altsetting is enabled. * network traffic can't flow until an altsetting is enabled.
*/ */
if (info->data != info->control) {
status = usb_driver_claim_interface(driver, info->data, dev); status = usb_driver_claim_interface(driver, info->data, dev);
if (status < 0) if (status < 0)
return status; return status;
}
status = usbnet_get_endpoints(dev, info->data); status = usbnet_get_endpoints(dev, info->data);
if (status < 0) { if (status < 0) {
/* ensure immediate exit from usbnet_disconnect */ /* ensure immediate exit from usbnet_disconnect */
usb_set_intfdata(info->data, NULL); usb_set_intfdata(info->data, NULL);
if (info->data != info->control)
usb_driver_release_interface(driver, info->data); usb_driver_release_interface(driver, info->data);
return status; return status;
} }
/* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */ /* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */
if (info->data != info->control)
dev->status = NULL; dev->status = NULL;
if (info->control->cur_altsetting->desc.bNumEndpoints == 1) { if (info->control->cur_altsetting->desc.bNumEndpoints == 1) {
struct usb_endpoint_descriptor *desc; struct usb_endpoint_descriptor *desc;
...@@ -349,6 +357,10 @@ void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf) ...@@ -349,6 +357,10 @@ void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf)
struct cdc_state *info = (void *) &dev->data; struct cdc_state *info = (void *) &dev->data;
struct usb_driver *driver = driver_of(intf); struct usb_driver *driver = driver_of(intf);
/* combined interface - nothing to do */
if (info->data == info->control)
return;
/* disconnect master --> disconnect slave */ /* disconnect master --> disconnect slave */
if (intf == info->control && info->data) { if (intf == info->control && info->data) {
/* ensure immediate exit from usbnet_disconnect */ /* ensure immediate exit from usbnet_disconnect */
......
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