| From ec28752505ed38b95d5a644957af2e3a2a3e3bb0 Mon Sep 17 00:00:00 2001 |
| From: Kristian Evensen <kristian.evensen@gmail.com> |
| Date: Thu, 1 Nov 2018 20:52:46 +0100 |
| Subject: USB: serial: option: improve Quectel EP06 detection |
| |
| commit 36cae568404a298a19a6e8a3f18641075d4cab04 upstream |
| |
| The Quectel EP06 (and EM06/EG06) LTE modem supports updating the USB |
| configuration, without the VID/PID or configuration number changing. |
| When the configuration is updated and interfaces are added/removed, the |
| interface numbers are updated. This causes our current code for matching |
| EP06 not to work as intended, as the assumption about reserved |
| interfaces no longer holds. If for example the diagnostic (first) |
| interface is removed, option will (try to) bind to the QMI interface. |
| |
| This patch improves EP06 detection by replacing the current match with |
| two matches, and those matches check class, subclass and protocol as |
| well as VID and PID. The diag interface exports class, subclass and |
| protocol as 0xff. For the other serial interfaces, class is 0xff and |
| subclass and protocol are both 0x0. |
| |
| The modem can export the following devices and always in this order: |
| diag, nmea, at, ppp. qmi and adb. This means that diag can only ever be |
| interface 0, and interface numbers 1-5 should be marked as reserved. The |
| three other serial devices can have interface numbers 0-3, but I have |
| not marked any interfaces as reserved. The reason is that the serial |
| devices are the only interfaces exported by the device where subclass |
| and protocol is 0x0. |
| |
| QMI exports the same class, subclass and protocol values as the diag |
| interface. However, the two interfaces have different number of |
| endpoints, QMI has three and diag two. I have added a check for number |
| of interfaces if VID/PID matches the EP06, and we ignore the device if |
| number of interfaces equals three (and subclass is set). |
| |
| The upstream commit does not apply cleanly to the 4.14-tree because of |
| differences in option_probe(). In order to make the commit apply, a |
| slight reshuffeling of the code was needed. |
| |
| Signed-off-by: Kristian Evensen <kristian.evensen@gmail.com> |
| Acked-by: Dan Williams <dcbw@redhat.com> |
| [ johan: drop uneeded RSVD(5) for ADB ] |
| Cc: stable <stable@vger.kernel.org> |
| Signed-off-by: Johan Hovold <johan@kernel.org> |
| Signed-off-by: Kristian Evensen <kristian.evensen@gmail.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/usb/serial/option.c | 17 +++++++++++++++-- |
| 1 file changed, 15 insertions(+), 2 deletions(-) |
| |
| diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c |
| index 0600dadd6a0c..d8d3cb18e9ea 100644 |
| --- a/drivers/usb/serial/option.c |
| +++ b/drivers/usb/serial/option.c |
| @@ -1084,8 +1084,9 @@ static const struct usb_device_id option_ids[] = { |
| .driver_info = RSVD(4) }, |
| { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96), |
| .driver_info = RSVD(4) }, |
| - { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06), |
| - .driver_info = RSVD(4) | RSVD(5) }, |
| + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0xff, 0xff), |
| + .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) }, |
| + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0, 0) }, |
| { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) }, |
| { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) }, |
| { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6003), |
| @@ -2010,6 +2011,18 @@ static int option_probe(struct usb_serial *serial, |
| iface_desc->bInterfaceClass != USB_CLASS_CDC_DATA) |
| return -ENODEV; |
| |
| + /* |
| + * Don't bind to the QMI device of the Quectel EP06/EG06/EM06. Class, |
| + * subclass and protocol is 0xff for both the diagnostic port and the |
| + * QMI interface, but the diagnostic port only has two endpoints (QMI |
| + * has three). |
| + */ |
| + if (dev_desc->idVendor == cpu_to_le16(QUECTEL_VENDOR_ID) && |
| + dev_desc->idProduct == cpu_to_le16(QUECTEL_PRODUCT_EP06) && |
| + iface_desc->bInterfaceSubClass && iface_desc->bNumEndpoints == 3) { |
| + return -ENODEV; |
| + } |
| + |
| /* Store the device flags so we can use them during attach. */ |
| usb_set_serial_data(serial, (void *)device_flags); |
| |
| -- |
| 2.17.1 |
| |