diff options
Diffstat (limited to 'drivers/usb/serial/qcserial.c')
-rw-r--r-- | drivers/usb/serial/qcserial.c | 94 |
1 files changed, 74 insertions, 20 deletions
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 5022fcfa0260..9919d2a9faf2 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c | |||
@@ -22,6 +22,8 @@ | |||
22 | #define DRIVER_AUTHOR "Qualcomm Inc" | 22 | #define DRIVER_AUTHOR "Qualcomm Inc" |
23 | #define DRIVER_DESC "Qualcomm USB Serial driver" | 23 | #define DRIVER_DESC "Qualcomm USB Serial driver" |
24 | 24 | ||
25 | #define QUECTEL_EC20_PID 0x9215 | ||
26 | |||
25 | /* standard device layouts supported by this driver */ | 27 | /* standard device layouts supported by this driver */ |
26 | enum qcserial_layouts { | 28 | enum qcserial_layouts { |
27 | QCSERIAL_G2K = 0, /* Gobi 2000 */ | 29 | QCSERIAL_G2K = 0, /* Gobi 2000 */ |
@@ -171,6 +173,38 @@ static const struct usb_device_id id_table[] = { | |||
171 | }; | 173 | }; |
172 | MODULE_DEVICE_TABLE(usb, id_table); | 174 | MODULE_DEVICE_TABLE(usb, id_table); |
173 | 175 | ||
176 | static int handle_quectel_ec20(struct device *dev, int ifnum) | ||
177 | { | ||
178 | int altsetting = 0; | ||
179 | |||
180 | /* | ||
181 | * Quectel EC20 Mini PCIe LTE module layout: | ||
182 | * 0: DM/DIAG (use libqcdm from ModemManager for communication) | ||
183 | * 1: NMEA | ||
184 | * 2: AT-capable modem port | ||
185 | * 3: Modem interface | ||
186 | * 4: NDIS | ||
187 | */ | ||
188 | switch (ifnum) { | ||
189 | case 0: | ||
190 | dev_dbg(dev, "Quectel EC20 DM/DIAG interface found\n"); | ||
191 | break; | ||
192 | case 1: | ||
193 | dev_dbg(dev, "Quectel EC20 NMEA GPS interface found\n"); | ||
194 | break; | ||
195 | case 2: | ||
196 | case 3: | ||
197 | dev_dbg(dev, "Quectel EC20 Modem port found\n"); | ||
198 | break; | ||
199 | case 4: | ||
200 | /* Don't claim the QMI/net interface */ | ||
201 | altsetting = -1; | ||
202 | break; | ||
203 | } | ||
204 | |||
205 | return altsetting; | ||
206 | } | ||
207 | |||
174 | static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) | 208 | static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) |
175 | { | 209 | { |
176 | struct usb_host_interface *intf = serial->interface->cur_altsetting; | 210 | struct usb_host_interface *intf = serial->interface->cur_altsetting; |
@@ -181,6 +215,10 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) | |||
181 | int altsetting = -1; | 215 | int altsetting = -1; |
182 | bool sendsetup = false; | 216 | bool sendsetup = false; |
183 | 217 | ||
218 | /* we only support vendor specific functions */ | ||
219 | if (intf->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC) | ||
220 | goto done; | ||
221 | |||
184 | nintf = serial->dev->actconfig->desc.bNumInterfaces; | 222 | nintf = serial->dev->actconfig->desc.bNumInterfaces; |
185 | dev_dbg(dev, "Num Interfaces = %d\n", nintf); | 223 | dev_dbg(dev, "Num Interfaces = %d\n", nintf); |
186 | ifnum = intf->desc.bInterfaceNumber; | 224 | ifnum = intf->desc.bInterfaceNumber; |
@@ -240,6 +278,12 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) | |||
240 | altsetting = -1; | 278 | altsetting = -1; |
241 | break; | 279 | break; |
242 | case QCSERIAL_G2K: | 280 | case QCSERIAL_G2K: |
281 | /* handle non-standard layouts */ | ||
282 | if (nintf == 5 && id->idProduct == QUECTEL_EC20_PID) { | ||
283 | altsetting = handle_quectel_ec20(dev, ifnum); | ||
284 | goto done; | ||
285 | } | ||
286 | |||
243 | /* | 287 | /* |
244 | * Gobi 2K+ USB layout: | 288 | * Gobi 2K+ USB layout: |
245 | * 0: QMI/net | 289 | * 0: QMI/net |
@@ -301,29 +345,39 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) | |||
301 | break; | 345 | break; |
302 | case QCSERIAL_HWI: | 346 | case QCSERIAL_HWI: |
303 | /* | 347 | /* |
304 | * Huawei layout: | 348 | * Huawei devices map functions by subclass + protocol |
305 | * 0: AT-capable modem port | 349 | * instead of interface numbers. The protocol identify |
306 | * 1: DM/DIAG | 350 | * a specific function, while the subclass indicate a |
307 | * 2: AT-capable modem port | 351 | * specific firmware source |
308 | * 3: CCID-compatible PCSC interface | 352 | * |
309 | * 4: QMI/net | 353 | * This is a blacklist of functions known to be |
310 | * 5: NMEA | 354 | * non-serial. The rest are assumed to be serial and |
355 | * will be handled by this driver | ||
311 | */ | 356 | */ |
312 | switch (ifnum) { | 357 | switch (intf->desc.bInterfaceProtocol) { |
313 | case 0: | 358 | /* QMI combined (qmi_wwan) */ |
314 | case 2: | 359 | case 0x07: |
315 | dev_dbg(dev, "Modem port found\n"); | 360 | case 0x37: |
316 | break; | 361 | case 0x67: |
317 | case 1: | 362 | /* QMI data (qmi_wwan) */ |
318 | dev_dbg(dev, "DM/DIAG interface found\n"); | 363 | case 0x08: |
319 | break; | 364 | case 0x38: |
320 | case 5: | 365 | case 0x68: |
321 | dev_dbg(dev, "NMEA GPS interface found\n"); | 366 | /* QMI control (qmi_wwan) */ |
322 | break; | 367 | case 0x09: |
323 | default: | 368 | case 0x39: |
324 | /* don't claim any unsupported interface */ | 369 | case 0x69: |
370 | /* NCM like (huawei_cdc_ncm) */ | ||
371 | case 0x16: | ||
372 | case 0x46: | ||
373 | case 0x76: | ||
325 | altsetting = -1; | 374 | altsetting = -1; |
326 | break; | 375 | break; |
376 | default: | ||
377 | dev_dbg(dev, "Huawei type serial port found (%02x/%02x/%02x)\n", | ||
378 | intf->desc.bInterfaceClass, | ||
379 | intf->desc.bInterfaceSubClass, | ||
380 | intf->desc.bInterfaceProtocol); | ||
327 | } | 381 | } |
328 | break; | 382 | break; |
329 | default: | 383 | default: |