Commit 00516d13 authored by Bjørn Mork's avatar Bjørn Mork Committed by David S. Miller

qmi_wwan: unconditionally reject 2 ep interfaces

We have been using the fact that the QMI and DIAG functions
usually are the only ones with class/subclass/protocol being
ff/ff/ff on Quectel modems. This has allowed us to match the
QMI function without knowing the exact interface number,
which can vary depending on firmware configuration.

The ability to silently reject the DIAG function, which is
usually handled by the option driver, is important for this
method to work.  This is done based on the knowledge that it
has exactly 2 bulk endpoints.  QMI function control interfaces
will have either 3 or 1 endpoint. This rule is universal so
the quirk condition can be removed.

The fixed layouts known from the Gobi1k and Gobi2k modems
have been gradually replaced by more dynamic layouts, and
many vendors now use configurable layouts without changing
device IDs.  Renaming the class/subclass/protocol matching
macro makes it more obvious that this is now not Quectel
specific anymore.

Cc: Kristian Evensen <kristian.evensen@gmail.com>
Cc: Aleksander Morgado <aleksander@aleksander.es>
Signed-off-by: default avatarBjørn Mork <bjorn@mork.no>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5d1fbdf2
...@@ -61,7 +61,6 @@ enum qmi_wwan_flags { ...@@ -61,7 +61,6 @@ enum qmi_wwan_flags {
enum qmi_wwan_quirks { enum qmi_wwan_quirks {
QMI_WWAN_QUIRK_DTR = 1 << 0, /* needs "set DTR" request */ QMI_WWAN_QUIRK_DTR = 1 << 0, /* needs "set DTR" request */
QMI_WWAN_QUIRK_QUECTEL_DYNCFG = 1 << 1, /* check num. endpoints */
}; };
struct qmimux_hdr { struct qmimux_hdr {
...@@ -916,16 +915,6 @@ static const struct driver_info qmi_wwan_info_quirk_dtr = { ...@@ -916,16 +915,6 @@ static const struct driver_info qmi_wwan_info_quirk_dtr = {
.data = QMI_WWAN_QUIRK_DTR, .data = QMI_WWAN_QUIRK_DTR,
}; };
static const struct driver_info qmi_wwan_info_quirk_quectel_dyncfg = {
.description = "WWAN/QMI device",
.flags = FLAG_WWAN | FLAG_SEND_ZLP,
.bind = qmi_wwan_bind,
.unbind = qmi_wwan_unbind,
.manage_power = qmi_wwan_manage_power,
.rx_fixup = qmi_wwan_rx_fixup,
.data = QMI_WWAN_QUIRK_DTR | QMI_WWAN_QUIRK_QUECTEL_DYNCFG,
};
#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 */
...@@ -946,14 +935,18 @@ static const struct driver_info qmi_wwan_info_quirk_quectel_dyncfg = { ...@@ -946,14 +935,18 @@ static const struct driver_info qmi_wwan_info_quirk_quectel_dyncfg = {
#define QMI_GOBI_DEVICE(vend, prod) \ #define QMI_GOBI_DEVICE(vend, prod) \
QMI_FIXED_INTF(vend, prod, 0) QMI_FIXED_INTF(vend, prod, 0)
/* Quectel does not use fixed interface numbers on at least some of their /* Many devices have QMI and DIAG functions which are distinguishable
* devices. We need to check the number of endpoints to ensure that we bind to * from other vendor specific functions by class, subclass and
* the correct interface. * protocol all being 0xff. The DIAG function has exactly 2 endpoints
* and is silently rejected when probed.
*
* This makes it possible to match dynamically numbered QMI functions
* as seen on e.g. many Quectel modems.
*/ */
#define QMI_QUIRK_QUECTEL_DYNCFG(vend, prod) \ #define QMI_MATCH_FF_FF_FF(vend, prod) \
USB_DEVICE_AND_INTERFACE_INFO(vend, prod, USB_CLASS_VENDOR_SPEC, \ USB_DEVICE_AND_INTERFACE_INFO(vend, prod, USB_CLASS_VENDOR_SPEC, \
USB_SUBCLASS_VENDOR_SPEC, 0xff), \ USB_SUBCLASS_VENDOR_SPEC, 0xff), \
.driver_info = (unsigned long)&qmi_wwan_info_quirk_quectel_dyncfg .driver_info = (unsigned long)&qmi_wwan_info_quirk_dtr
static const struct usb_device_id products[] = { static const struct usb_device_id products[] = {
/* 1. CDC ECM like devices match on the control interface */ /* 1. CDC ECM like devices match on the control interface */
...@@ -1059,10 +1052,10 @@ static const struct usb_device_id products[] = { ...@@ -1059,10 +1052,10 @@ static const struct usb_device_id products[] = {
USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7), USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7),
.driver_info = (unsigned long)&qmi_wwan_info, .driver_info = (unsigned long)&qmi_wwan_info,
}, },
{QMI_QUIRK_QUECTEL_DYNCFG(0x2c7c, 0x0125)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0125)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */
{QMI_QUIRK_QUECTEL_DYNCFG(0x2c7c, 0x0306)}, /* Quectel EP06/EG06/EM06 */ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0306)}, /* Quectel EP06/EG06/EM06 */
{QMI_QUIRK_QUECTEL_DYNCFG(0x2c7c, 0x0512)}, /* Quectel EG12/EM12 */ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0512)}, /* Quectel EG12/EM12 */
{QMI_QUIRK_QUECTEL_DYNCFG(0x2c7c, 0x0800)}, /* Quectel RM500Q-GL */ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0800)}, /* Quectel RM500Q-GL */
/* 3. Combined interface devices matching on interface number */ /* 3. Combined interface devices matching on interface number */
{QMI_FIXED_INTF(0x0408, 0xea42, 4)}, /* Yota / Megafon M100-1 */ {QMI_FIXED_INTF(0x0408, 0xea42, 4)}, /* Yota / Megafon M100-1 */
...@@ -1455,7 +1448,6 @@ static int qmi_wwan_probe(struct usb_interface *intf, ...@@ -1455,7 +1448,6 @@ static int qmi_wwan_probe(struct usb_interface *intf,
{ {
struct usb_device_id *id = (struct usb_device_id *)prod; struct usb_device_id *id = (struct usb_device_id *)prod;
struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc; struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc;
const struct driver_info *info;
/* Workaround to enable dynamic IDs. This disables usbnet /* Workaround to enable dynamic IDs. This disables usbnet
* blacklisting functionality. Which, if required, can be * blacklisting functionality. Which, if required, can be
...@@ -1491,12 +1483,8 @@ static int qmi_wwan_probe(struct usb_interface *intf, ...@@ -1491,12 +1483,8 @@ static int qmi_wwan_probe(struct usb_interface *intf,
* different. Ignore the current interface if the number of endpoints * different. Ignore the current interface if the number of endpoints
* equals the number for the diag interface (two). * equals the number for the diag interface (two).
*/ */
info = (void *)id->driver_info; if (desc->bNumEndpoints == 2)
return -ENODEV;
if (info->data & QMI_WWAN_QUIRK_QUECTEL_DYNCFG) {
if (desc->bNumEndpoints == 2)
return -ENODEV;
}
return usbnet_probe(intf, id); return usbnet_probe(intf, id);
} }
......
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