Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: qmi_wwan: bind to both control and data interface

Always bind to control interface regardless of whether
it is a shared interface or not.

A QMI/wwan function is required to provide both a control
interface (QMI) and a data interface (wwan). All devices
supported by this driver do so. But the vendors may
choose to use different USB descriptor layouts, and some
vendors even allow the same device to present different
layouts.

Most of these devices use a USB descriptor layout with a
single USB interface for both control and data. But some
split control and data into two interfaces, bound together
by a CDC Union descriptor on the control interface. Before
the cdc-wdm subdriver support was added, this split was
used to let cdc-wdm drive the QMI control interface and
qmi_wwan drive the wwna data interface.

This split driver model has a number of issues:
- qmi_wwan must match on the data interface descriptor,
which often are indistiguishable from data interfaces
belonging to other CDC (like) functions like ACM
- supporting a single QMI/wwan function requires adding
the device to two drivers
- syncronizing the probes among a number of drivers, to
ensure selecting the correct driver, is difficult unless
all drivers match on the same interface

This patch resolves these problems by using the same
probing mechanism as cdc-ether for devices with a two-
interface USB descriptor layout. This makes the driver
behave consistently, supporting both the control and data
part of the QMI/wwan function, regardless of the USB
descriptors.

Cc: Thomas Schäfer <tschaefer@t-online.de>
Signed-off-by: Bjørn Mork <bjorn@mork.no>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Bjørn Mork and committed by
David S. Miller
230718bd f47cd136

+63 -68
+63 -68
drivers/net/usb/qmi_wwan.c
··· 1 1 /* 2 2 * Copyright (c) 2012 Bjørn Mork <bjorn@mork.no> 3 3 * 4 + * The probing code is heavily inspired by cdc_ether, which is: 5 + * Copyright (C) 2003-2005 by David Brownell 6 + * Copyright (C) 2006 by Ole Andre Vadla Ravnas (ActiveSync) 7 + * 4 8 * This program is free software; you can redistribute it and/or 5 9 * modify it under the terms of the GNU General Public License 6 10 * version 2 as published by the Free Software Foundation. ··· 19 15 #include <linux/usb/usbnet.h> 20 16 #include <linux/usb/cdc-wdm.h> 21 17 22 - /* The name of the CDC Device Management driver */ 23 - #define DM_DRIVER "cdc_wdm" 24 - 25 - /* 26 - * This driver supports wwan (3G/LTE/?) devices using a vendor 18 + /* This driver supports wwan (3G/LTE/?) devices using a vendor 27 19 * specific management protocol called Qualcomm MSM Interface (QMI) - 28 20 * in addition to the more common AT commands over serial interface 29 21 * management ··· 31 31 * management protocol is used in place of the standard CDC 32 32 * notifications NOTIFY_NETWORK_CONNECTION and NOTIFY_SPEED_CHANGE 33 33 * 34 + * Alternatively, control and data functions can be combined in a 35 + * single USB interface. 36 + * 34 37 * Handling a protocol like QMI is out of the scope for any driver. 35 - * It can be exported as a character device using the cdc-wdm driver, 36 - * which will enable userspace applications ("modem managers") to 37 - * handle it. This may be required to use the network interface 38 - * provided by the driver. 38 + * It is exported as a character device using the cdc-wdm driver as 39 + * a subdriver, enabling userspace applications ("modem managers") to 40 + * handle it. 39 41 * 40 42 * These devices may alternatively/additionally be configured using AT 41 - * commands on any of the serial interfaces driven by the option driver 42 - * 43 - * This driver binds only to the data ("slave") interface to enable 44 - * the cdc-wdm driver to bind to the control interface. It still 45 - * parses the CDC functional descriptors on the control interface to 46 - * a) verify that this is indeed a handled interface (CDC Union 47 - * header lists it as slave) 48 - * b) get MAC address and other ethernet config from the CDC Ethernet 49 - * header 50 - * c) enable user bind requests against the control interface, which 51 - * is the common way to bind to CDC Ethernet Control Model type 52 - * interfaces 53 - * d) provide a hint to the user about which interface is the 54 - * corresponding management interface 43 + * commands on a serial interface 55 44 */ 56 45 57 46 /* driver specific data */ ··· 124 135 static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) 125 136 { 126 137 int status = -1; 127 - struct usb_interface *control = NULL; 128 138 u8 *buf = intf->cur_altsetting->extra; 129 139 int len = intf->cur_altsetting->extralen; 130 140 struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc; ··· 131 143 struct usb_cdc_ether_desc *cdc_ether = NULL; 132 144 u32 required = 1 << USB_CDC_HEADER_TYPE | 1 << USB_CDC_UNION_TYPE; 133 145 u32 found = 0; 146 + struct usb_driver *driver = driver_of(intf); 134 147 struct qmi_wwan_state *info = (void *)&dev->data; 135 148 136 149 BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct qmi_wwan_state))); 137 150 138 - atomic_set(&info->pmcount, 0); 139 - 140 - /* 141 - * assume a data interface has no additional descriptors and 142 - * that the control and data interface are numbered 143 - * consecutively - this holds for the Huawei device at least 144 - */ 145 - if (len == 0 && desc->bInterfaceNumber > 0) { 146 - control = usb_ifnum_to_if(dev->udev, desc->bInterfaceNumber - 1); 147 - if (!control) 148 - goto err; 149 - 150 - buf = control->cur_altsetting->extra; 151 - len = control->cur_altsetting->extralen; 152 - dev_dbg(&intf->dev, "guessing \"control\" => %s, \"data\" => this\n", 153 - dev_name(&control->dev)); 154 - } 151 + /* require a single interrupt status endpoint for subdriver */ 152 + if (intf->cur_altsetting->desc.bNumEndpoints != 1) 153 + goto err; 155 154 156 155 while (len > 3) { 157 156 struct usb_descriptor_header *h = (void *)buf; ··· 202 227 goto err; 203 228 } 204 229 205 - /* give the user a helpful hint if trying to bind to the wrong interface */ 206 - if (cdc_union && desc->bInterfaceNumber == cdc_union->bMasterInterface0) { 207 - dev_err(&intf->dev, "leaving \"control\" interface for " DM_DRIVER " - try binding to %s instead!\n", 208 - dev_name(&usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0)->dev)); 230 + /* verify CDC Union */ 231 + if (desc->bInterfaceNumber != cdc_union->bMasterInterface0) { 232 + dev_err(&intf->dev, "bogus CDC Union: master=%u\n", cdc_union->bMasterInterface0); 233 + goto err; 234 + } 235 + 236 + /* need to save these for unbind */ 237 + info->control = intf; 238 + info->data = usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0); 239 + if (!info->data) { 240 + dev_err(&intf->dev, "bogus CDC Union: slave=%u\n", cdc_union->bSlaveInterface0); 209 241 goto err; 210 242 } 211 243 ··· 222 240 usbnet_get_ethernet_addr(dev, cdc_ether->iMACAddress); 223 241 } 224 242 225 - /* success! point the user to the management interface */ 226 - if (control) 227 - dev_info(&intf->dev, "Use \"" DM_DRIVER "\" for QMI interface %s\n", 228 - dev_name(&control->dev)); 243 + /* claim data interface and set it up */ 244 + status = usb_driver_claim_interface(driver, info->data, dev); 245 + if (status < 0) 246 + goto err; 229 247 230 - /* XXX: add a sysfs symlink somewhere to help management applications find it? */ 231 - 232 - /* collect bulk endpoints now that we know intf == "data" interface */ 233 - status = usbnet_get_endpoints(dev, intf); 248 + status = qmi_wwan_register_subdriver(dev); 249 + if (status < 0) { 250 + usb_set_intfdata(info->data, NULL); 251 + usb_driver_release_interface(driver, info->data); 252 + } 234 253 235 254 err: 236 255 return status; ··· 240 257 /* Some devices combine the "control" and "data" functions into a 241 258 * single interface with all three endpoints: interrupt + bulk in and 242 259 * out 243 - * 244 - * Setting up cdc-wdm as a subdriver owning the interrupt endpoint 245 - * will let it provide userspace access to the encapsulated QMI 246 - * protocol without interfering with the usbnet operations. 247 - */ 260 + */ 248 261 static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf) 249 262 { 250 263 int rv; ··· 292 313 return rv; 293 314 } 294 315 295 - static void qmi_wwan_unbind_shared(struct usbnet *dev, struct usb_interface *intf) 316 + static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf) 296 317 { 297 318 struct qmi_wwan_state *info = (void *)&dev->data; 319 + struct usb_driver *driver = driver_of(intf); 320 + struct usb_interface *other; 298 321 299 322 if (info->subdriver && info->subdriver->disconnect) 300 - info->subdriver->disconnect(intf); 323 + info->subdriver->disconnect(info->control); 324 + 325 + /* allow user to unbind using either control or data */ 326 + if (intf == info->control) 327 + other = info->data; 328 + else 329 + other = info->control; 330 + 331 + /* only if not shared */ 332 + if (other && intf != other) { 333 + usb_set_intfdata(other, NULL); 334 + usb_driver_release_interface(driver, other); 335 + } 301 336 302 337 info->subdriver = NULL; 338 + info->data = NULL; 339 + info->control = NULL; 303 340 } 304 341 305 342 /* suspend/resume wrappers calling both usbnet and the cdc-wdm ··· 359 364 return ret; 360 365 } 361 366 362 - 363 367 static const struct driver_info qmi_wwan_info = { 364 368 .description = "QMI speaking wwan device", 365 369 .flags = FLAG_WWAN, 366 370 .bind = qmi_wwan_bind, 371 + .unbind = qmi_wwan_unbind, 367 372 .manage_power = qmi_wwan_manage_power, 368 373 }; 369 374 ··· 371 376 .description = "QMI speaking wwan device with combined interface", 372 377 .flags = FLAG_WWAN, 373 378 .bind = qmi_wwan_bind_shared, 374 - .unbind = qmi_wwan_unbind_shared, 379 + .unbind = qmi_wwan_unbind, 375 380 .manage_power = qmi_wwan_manage_power, 376 381 }; 377 382 ··· 379 384 .description = "Qualcomm Gobi wwan/QMI device", 380 385 .flags = FLAG_WWAN, 381 386 .bind = qmi_wwan_bind_gobi, 382 - .unbind = qmi_wwan_unbind_shared, 387 + .unbind = qmi_wwan_unbind, 383 388 .manage_power = qmi_wwan_manage_power, 384 389 }; 385 390 ··· 388 393 .description = "Qualcomm WWAN/QMI device", 389 394 .flags = FLAG_WWAN, 390 395 .bind = qmi_wwan_bind_shared, 391 - .unbind = qmi_wwan_unbind_shared, 396 + .unbind = qmi_wwan_unbind, 392 397 .manage_power = qmi_wwan_manage_power, 393 398 .data = BIT(1), /* interface whitelist bitmap */ 394 399 }; ··· 397 402 .description = "Qualcomm WWAN/QMI device", 398 403 .flags = FLAG_WWAN, 399 404 .bind = qmi_wwan_bind_shared, 400 - .unbind = qmi_wwan_unbind_shared, 405 + .unbind = qmi_wwan_unbind, 401 406 .manage_power = qmi_wwan_manage_power, 402 407 .data = BIT(4), /* interface whitelist bitmap */ 403 408 }; ··· 419 424 .description = "Sierra Wireless wwan/QMI device", 420 425 .flags = FLAG_WWAN, 421 426 .bind = qmi_wwan_bind_gobi, 422 - .unbind = qmi_wwan_unbind_shared, 427 + .unbind = qmi_wwan_unbind, 423 428 .manage_power = qmi_wwan_manage_power, 424 429 .data = BIT(8) | BIT(19), /* interface whitelist bitmap */ 425 430 }; ··· 435 440 .idVendor = HUAWEI_VENDOR_ID, 436 441 .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 437 442 .bInterfaceSubClass = 1, 438 - .bInterfaceProtocol = 8, /* NOTE: This is the *slave* interface of the CDC Union! */ 443 + .bInterfaceProtocol = 9, /* CDC Ethernet *control* interface */ 439 444 .driver_info = (unsigned long)&qmi_wwan_info, 440 445 }, 441 446 { /* Vodafone/Huawei K5005 (12d1:14c8) and similar modems */ ··· 443 448 .idVendor = HUAWEI_VENDOR_ID, 444 449 .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 445 450 .bInterfaceSubClass = 1, 446 - .bInterfaceProtocol = 56, /* NOTE: This is the *slave* interface of the CDC Union! */ 451 + .bInterfaceProtocol = 57, /* CDC Ethernet *control* interface */ 447 452 .driver_info = (unsigned long)&qmi_wwan_info, 448 453 }, 449 454 { /* Huawei E392, E398 and possibly others in "Windows mode"