Skip to content

Commit e4bf634

Browse files
kristrevdavem330
authored andcommitted
qmi_wwan: Add quirk for Quectel dynamic config
Most, if not all, Quectel devices use dynamic interface numbers, and users are able to change the USB configuration at will. Matching on for example interface number is therefore not possible. Instead, the QMI device can be identified by looking at the interface class, subclass and protocol (all 0xff), as well as the number of endpoints. The reason we need to look at the number of endpoints, is that the diagnostic port interface has the same class, subclass and protocol as QMI. However, the diagnostic port only has two endpoints, while QMI has three. Until now, we have identified the QMI device by combining a match on class, subclass and protocol, with a call to the function quectel_diag_detect(). In quectel_diag_detect(), we check if the number of endpoints matches for known Quectel vendor/product ids. Adding new vendor/product ids to quectel_diag_detect() is not a good long-term solution. This commit replaces the function with a quirk, and applies the quirk to affected Quectel devices that I have been able to test the change with (EP06, EM12 and EC25). If the quirk is set and the number of endpoints equal two, we return from qmi_wwan_probe() with -ENODEV. Signed-off-by: Kristian Evensen <[email protected]> Acked-by: Bjørn Mork <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 22b56e8 commit e4bf634

File tree

1 file changed

+31
-34
lines changed

1 file changed

+31
-34
lines changed

drivers/net/usb/qmi_wwan.c

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ enum qmi_wwan_flags {
6363

6464
enum qmi_wwan_quirks {
6565
QMI_WWAN_QUIRK_DTR = 1 << 0, /* needs "set DTR" request */
66+
QMI_WWAN_QUIRK_QUECTEL_DYNCFG = 1 << 1, /* check num. endpoints */
6667
};
6768

6869
struct qmimux_hdr {
@@ -845,6 +846,16 @@ static const struct driver_info qmi_wwan_info_quirk_dtr = {
845846
.data = QMI_WWAN_QUIRK_DTR,
846847
};
847848

849+
static const struct driver_info qmi_wwan_info_quirk_quectel_dyncfg = {
850+
.description = "WWAN/QMI device",
851+
.flags = FLAG_WWAN | FLAG_SEND_ZLP,
852+
.bind = qmi_wwan_bind,
853+
.unbind = qmi_wwan_unbind,
854+
.manage_power = qmi_wwan_manage_power,
855+
.rx_fixup = qmi_wwan_rx_fixup,
856+
.data = QMI_WWAN_QUIRK_DTR | QMI_WWAN_QUIRK_QUECTEL_DYNCFG,
857+
};
858+
848859
#define HUAWEI_VENDOR_ID 0x12D1
849860

850861
/* map QMI/wwan function by a fixed interface number */
@@ -865,6 +876,15 @@ static const struct driver_info qmi_wwan_info_quirk_dtr = {
865876
#define QMI_GOBI_DEVICE(vend, prod) \
866877
QMI_FIXED_INTF(vend, prod, 0)
867878

879+
/* Quectel does not use fixed interface numbers on at least some of their
880+
* devices. We need to check the number of endpoints to ensure that we bind to
881+
* the correct interface.
882+
*/
883+
#define QMI_QUIRK_QUECTEL_DYNCFG(vend, prod) \
884+
USB_DEVICE_AND_INTERFACE_INFO(vend, prod, USB_CLASS_VENDOR_SPEC, \
885+
USB_SUBCLASS_VENDOR_SPEC, 0xff), \
886+
.driver_info = (unsigned long)&qmi_wwan_info_quirk_quectel_dyncfg
887+
868888
static const struct usb_device_id products[] = {
869889
/* 1. CDC ECM like devices match on the control interface */
870890
{ /* Huawei E392, E398 and possibly others sharing both device id and more... */
@@ -969,20 +989,9 @@ static const struct usb_device_id products[] = {
969989
USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7),
970990
.driver_info = (unsigned long)&qmi_wwan_info,
971991
},
972-
{ /* Quectel EP06/EG06/EM06 */
973-
USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x0306,
974-
USB_CLASS_VENDOR_SPEC,
975-
USB_SUBCLASS_VENDOR_SPEC,
976-
0xff),
977-
.driver_info = (unsigned long)&qmi_wwan_info_quirk_dtr,
978-
},
979-
{ /* Quectel EG12/EM12 */
980-
USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x0512,
981-
USB_CLASS_VENDOR_SPEC,
982-
USB_SUBCLASS_VENDOR_SPEC,
983-
0xff),
984-
.driver_info = (unsigned long)&qmi_wwan_info_quirk_dtr,
985-
},
992+
{QMI_QUIRK_QUECTEL_DYNCFG(0x2c7c, 0x0125)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */
993+
{QMI_QUIRK_QUECTEL_DYNCFG(0x2c7c, 0x0306)}, /* Quectel EP06/EG06/EM06 */
994+
{QMI_QUIRK_QUECTEL_DYNCFG(0x2c7c, 0x0512)}, /* Quectel EG12/EM12 */
986995

987996
/* 3. Combined interface devices matching on interface number */
988997
{QMI_FIXED_INTF(0x0408, 0xea42, 4)}, /* Yota / Megafon M100-1 */
@@ -1271,7 +1280,6 @@ static const struct usb_device_id products[] = {
12711280
{QMI_FIXED_INTF(0x03f0, 0x9d1d, 1)}, /* HP lt4120 Snapdragon X5 LTE */
12721281
{QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */
12731282
{QMI_QUIRK_SET_DTR(0x1e0e, 0x9001, 5)}, /* SIMCom 7100E, 7230E, 7600E ++ */
1274-
{QMI_QUIRK_SET_DTR(0x2c7c, 0x0125, 4)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */
12751283
{QMI_QUIRK_SET_DTR(0x2c7c, 0x0121, 4)}, /* Quectel EC21 Mini PCIe */
12761284
{QMI_QUIRK_SET_DTR(0x2c7c, 0x0191, 4)}, /* Quectel EG91 */
12771285
{QMI_FIXED_INTF(0x2c7c, 0x0296, 4)}, /* Quectel BG96 */
@@ -1351,27 +1359,12 @@ static bool quectel_ec20_detected(struct usb_interface *intf)
13511359
return false;
13521360
}
13531361

1354-
static bool quectel_diag_detected(struct usb_interface *intf)
1355-
{
1356-
struct usb_device *dev = interface_to_usbdev(intf);
1357-
struct usb_interface_descriptor intf_desc = intf->cur_altsetting->desc;
1358-
u16 id_vendor = le16_to_cpu(dev->descriptor.idVendor);
1359-
u16 id_product = le16_to_cpu(dev->descriptor.idProduct);
1360-
1361-
if (id_vendor != 0x2c7c || intf_desc.bNumEndpoints != 2)
1362-
return false;
1363-
1364-
if (id_product == 0x0306 || id_product == 0x0512)
1365-
return true;
1366-
else
1367-
return false;
1368-
}
1369-
13701362
static int qmi_wwan_probe(struct usb_interface *intf,
13711363
const struct usb_device_id *prod)
13721364
{
13731365
struct usb_device_id *id = (struct usb_device_id *)prod;
13741366
struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc;
1367+
const struct driver_info *info;
13751368

13761369
/* Workaround to enable dynamic IDs. This disables usbnet
13771370
* blacklisting functionality. Which, if required, can be
@@ -1405,10 +1398,14 @@ static int qmi_wwan_probe(struct usb_interface *intf,
14051398
* we need to match on class/subclass/protocol. These values are
14061399
* identical for the diagnostic- and QMI-interface, but bNumEndpoints is
14071400
* different. Ignore the current interface if the number of endpoints
1408-
* the number for the diag interface (two).
1401+
* equals the number for the diag interface (two).
14091402
*/
1410-
if (quectel_diag_detected(intf))
1411-
return -ENODEV;
1403+
info = (void *)&id->driver_info;
1404+
1405+
if (info->data & QMI_WWAN_QUIRK_QUECTEL_DYNCFG) {
1406+
if (desc->bNumEndpoints == 2)
1407+
return -ENODEV;
1408+
}
14121409

14131410
return usbnet_probe(intf, id);
14141411
}

0 commit comments

Comments
 (0)