15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2a78b4282SGreg Kroah-Hartman /*
3a78b4282SGreg Kroah-Hartman * Qualcomm Serial USB driver
4a78b4282SGreg Kroah-Hartman *
5a78b4282SGreg Kroah-Hartman * Copyright (c) 2008 QUALCOMM Incorporated.
6a78b4282SGreg Kroah-Hartman * Copyright (c) 2009 Greg Kroah-Hartman <gregkh@suse.de>
7a78b4282SGreg Kroah-Hartman * Copyright (c) 2009 Novell Inc.
8a78b4282SGreg Kroah-Hartman */
9a78b4282SGreg Kroah-Hartman
10a78b4282SGreg Kroah-Hartman #include <linux/tty.h>
11a78b4282SGreg Kroah-Hartman #include <linux/tty_flip.h>
126eb0de82SPaul Gortmaker #include <linux/module.h>
13a78b4282SGreg Kroah-Hartman #include <linux/usb.h>
14a78b4282SGreg Kroah-Hartman #include <linux/usb/serial.h>
153d7e59adSMatthew Garrett #include <linux/slab.h>
163d7e59adSMatthew Garrett #include "usb-wwan.h"
17a78b4282SGreg Kroah-Hartman
18a78b4282SGreg Kroah-Hartman #define DRIVER_AUTHOR "Qualcomm Inc"
19a78b4282SGreg Kroah-Hartman #define DRIVER_DESC "Qualcomm USB Serial driver"
20a78b4282SGreg Kroah-Hartman
219d5b5ed7SPetr Štetiar #define QUECTEL_EC20_PID 0x9215
229d5b5ed7SPetr Štetiar
23d712ca91SBjørn Mork /* standard device layouts supported by this driver */
24d712ca91SBjørn Mork enum qcserial_layouts {
25d712ca91SBjørn Mork QCSERIAL_G2K = 0, /* Gobi 2000 */
26d712ca91SBjørn Mork QCSERIAL_G1K = 1, /* Gobi 1000 */
278bc7a069SBjørn Mork QCSERIAL_SWI = 2, /* Sierra Wireless */
28e7181d00SMartin Hauke QCSERIAL_HWI = 3, /* Huawei */
29d712ca91SBjørn Mork };
30d712ca91SBjørn Mork
31c192c8e7SDan Williams #define DEVICE_G1K(v, p) \
32d712ca91SBjørn Mork USB_DEVICE(v, p), .driver_info = QCSERIAL_G1K
338bc7a069SBjørn Mork #define DEVICE_SWI(v, p) \
348bc7a069SBjørn Mork USB_DEVICE(v, p), .driver_info = QCSERIAL_SWI
35e7181d00SMartin Hauke #define DEVICE_HWI(v, p) \
36e7181d00SMartin Hauke USB_DEVICE(v, p), .driver_info = QCSERIAL_HWI
37c192c8e7SDan Williams
387d40d7e8SNémeth Márton static const struct usb_device_id id_table[] = {
39c192c8e7SDan Williams /* Gobi 1000 devices */
40c192c8e7SDan Williams {DEVICE_G1K(0x05c6, 0x9211)}, /* Acer Gobi QDL device */
41c192c8e7SDan Williams {DEVICE_G1K(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
42c192c8e7SDan Williams {DEVICE_G1K(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */
43c192c8e7SDan Williams {DEVICE_G1K(0x03f0, 0x201d)}, /* HP un2400 Gobi QDL Device */
44c192c8e7SDan Williams {DEVICE_G1K(0x04da, 0x250d)}, /* Panasonic Gobi Modem device */
45c192c8e7SDan Williams {DEVICE_G1K(0x04da, 0x250c)}, /* Panasonic Gobi QDL device */
46c192c8e7SDan Williams {DEVICE_G1K(0x413c, 0x8172)}, /* Dell Gobi Modem device */
47c192c8e7SDan Williams {DEVICE_G1K(0x413c, 0x8171)}, /* Dell Gobi QDL device */
48a254810aSDan Williams {DEVICE_G1K(0x1410, 0xa001)}, /* Novatel/Verizon USB-1000 */
49a254810aSDan Williams {DEVICE_G1K(0x1410, 0xa002)}, /* Novatel Gobi Modem device */
50a254810aSDan Williams {DEVICE_G1K(0x1410, 0xa003)}, /* Novatel Gobi Modem device */
51a254810aSDan Williams {DEVICE_G1K(0x1410, 0xa004)}, /* Novatel Gobi Modem device */
52a254810aSDan Williams {DEVICE_G1K(0x1410, 0xa005)}, /* Novatel Gobi Modem device */
53a254810aSDan Williams {DEVICE_G1K(0x1410, 0xa006)}, /* Novatel Gobi Modem device */
54a254810aSDan Williams {DEVICE_G1K(0x1410, 0xa007)}, /* Novatel Gobi Modem device */
55c192c8e7SDan Williams {DEVICE_G1K(0x1410, 0xa008)}, /* Novatel Gobi QDL device */
56c192c8e7SDan Williams {DEVICE_G1K(0x0b05, 0x1776)}, /* Asus Gobi Modem device */
57c192c8e7SDan Williams {DEVICE_G1K(0x0b05, 0x1774)}, /* Asus Gobi QDL device */
58c192c8e7SDan Williams {DEVICE_G1K(0x19d2, 0xfff3)}, /* ONDA Gobi Modem device */
59c192c8e7SDan Williams {DEVICE_G1K(0x19d2, 0xfff2)}, /* ONDA Gobi QDL device */
60c192c8e7SDan Williams {DEVICE_G1K(0x1557, 0x0a80)}, /* OQO Gobi QDL device */
61c192c8e7SDan Williams {DEVICE_G1K(0x05c6, 0x9001)}, /* Generic Gobi Modem device */
62c192c8e7SDan Williams {DEVICE_G1K(0x05c6, 0x9002)}, /* Generic Gobi Modem device */
63c192c8e7SDan Williams {DEVICE_G1K(0x05c6, 0x9202)}, /* Generic Gobi Modem device */
64c192c8e7SDan Williams {DEVICE_G1K(0x05c6, 0x9203)}, /* Generic Gobi Modem device */
65c192c8e7SDan Williams {DEVICE_G1K(0x05c6, 0x9222)}, /* Generic Gobi Modem device */
66c192c8e7SDan Williams {DEVICE_G1K(0x05c6, 0x9008)}, /* Generic Gobi QDL device */
67c192c8e7SDan Williams {DEVICE_G1K(0x05c6, 0x9009)}, /* Generic Gobi Modem device */
68c192c8e7SDan Williams {DEVICE_G1K(0x05c6, 0x9201)}, /* Generic Gobi QDL device */
69c192c8e7SDan Williams {DEVICE_G1K(0x05c6, 0x9221)}, /* Generic Gobi QDL device */
70c192c8e7SDan Williams {DEVICE_G1K(0x05c6, 0x9231)}, /* Generic Gobi QDL device */
71c192c8e7SDan Williams {DEVICE_G1K(0x1f45, 0x0001)}, /* Unknown Gobi QDL device */
7278796ae1SDaniele Palmas {DEVICE_G1K(0x1bc7, 0x900e)}, /* Telit Gobi QDL device */
73c192c8e7SDan Williams
74c192c8e7SDan Williams /* Gobi 2000 devices */
75c192c8e7SDan Williams {USB_DEVICE(0x1410, 0xa010)}, /* Novatel Gobi 2000 QDL device */
76c192c8e7SDan Williams {USB_DEVICE(0x1410, 0xa011)}, /* Novatel Gobi 2000 QDL device */
77c192c8e7SDan Williams {USB_DEVICE(0x1410, 0xa012)}, /* Novatel Gobi 2000 QDL device */
78c192c8e7SDan Williams {USB_DEVICE(0x1410, 0xa013)}, /* Novatel Gobi 2000 QDL device */
79c192c8e7SDan Williams {USB_DEVICE(0x1410, 0xa014)}, /* Novatel Gobi 2000 QDL device */
800725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x413c, 0x8185)}, /* Dell Gobi 2000 QDL device (N0218, VU936) */
810725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x413c, 0x8186)}, /* Dell Gobi 2000 Modem device (N0218, VU936) */
82bec25b89SAndrew Bird {USB_DEVICE(0x05c6, 0x9208)}, /* Generic Gobi 2000 QDL device */
83bec25b89SAndrew Bird {USB_DEVICE(0x05c6, 0x920b)}, /* Generic Gobi 2000 Modem device */
840725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x05c6, 0x9224)}, /* Sony Gobi 2000 QDL device (N0279, VU730) */
850725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x05c6, 0x9225)}, /* Sony Gobi 2000 Modem device (N0279, VU730) */
860725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x05c6, 0x9244)}, /* Samsung Gobi 2000 QDL device (VL176) */
870725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x05c6, 0x9245)}, /* Samsung Gobi 2000 Modem device (VL176) */
880725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x03f0, 0x241d)}, /* HP Gobi 2000 QDL device (VP412) */
890725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x03f0, 0x251d)}, /* HP Gobi 2000 Modem device (VP412) */
900725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x05c6, 0x9214)}, /* Acer Gobi 2000 QDL device (VP413) */
910725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */
920725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x05c6, 0x9264)}, /* Asus Gobi 2000 QDL device (VR305) */
930725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x05c6, 0x9265)}, /* Asus Gobi 2000 Modem device (VR305) */
940725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x05c6, 0x9234)}, /* Top Global Gobi 2000 QDL device (VR306) */
950725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x05c6, 0x9235)}, /* Top Global Gobi 2000 Modem device (VR306) */
960725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x05c6, 0x9274)}, /* iRex Technologies Gobi 2000 QDL device (VR307) */
970725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x05c6, 0x9275)}, /* iRex Technologies Gobi 2000 Modem device (VR307) */
980725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x1199, 0x9000)}, /* Sierra Wireless Gobi 2000 QDL device (VT773) */
990725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x1199, 0x9001)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
1000725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x1199, 0x9002)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
1010725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x1199, 0x9003)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
1020725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x1199, 0x9004)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
1030725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x1199, 0x9005)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
1040725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x1199, 0x9006)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
1050725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x1199, 0x9007)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
1060725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x1199, 0x9008)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
1070725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x1199, 0x9009)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
1080725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x1199, 0x900a)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
1092f1def26SFlorian Echtler {USB_DEVICE(0x1199, 0x9011)}, /* Sierra Wireless Gobi 2000 Modem device (MC8305) */
1100725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x16d8, 0x8001)}, /* CMDTech Gobi 2000 QDL device (VU922) */
1110725e95eSBernhard Rosenkraenzer {USB_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */
112e07896e6SAnssi Hannula {USB_DEVICE(0x05c6, 0x9204)}, /* Gobi 2000 QDL device */
113e07896e6SAnssi Hannula {USB_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */
1142db4d870SThomas Tuttle
115c192c8e7SDan Williams /* Gobi 3000 devices */
116c192c8e7SDan Williams {USB_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Gobi 3000 QDL */
1172db4d870SThomas Tuttle {USB_DEVICE(0x05c6, 0x920c)}, /* Gobi 3000 QDL */
1182db4d870SThomas Tuttle {USB_DEVICE(0x05c6, 0x920d)}, /* Gobi 3000 Composite */
1192db4d870SThomas Tuttle {USB_DEVICE(0x1410, 0xa020)}, /* Novatel Gobi 3000 QDL */
1202db4d870SThomas Tuttle {USB_DEVICE(0x1410, 0xa021)}, /* Novatel Gobi 3000 Composite */
1212db4d870SThomas Tuttle {USB_DEVICE(0x413c, 0x8193)}, /* Dell Gobi 3000 QDL */
1222db4d870SThomas Tuttle {USB_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */
12324d615a6SAleksander Morgado {USB_DEVICE(0x413c, 0x81a6)}, /* Dell DW5570 QDL (MC8805) */
1241937131aSBjørn Mork {USB_DEVICE(0x1199, 0x68a4)}, /* Sierra Wireless QDL */
1251937131aSBjørn Mork {USB_DEVICE(0x1199, 0x68a5)}, /* Sierra Wireless Modem */
1261937131aSBjørn Mork {USB_DEVICE(0x1199, 0x68a8)}, /* Sierra Wireless QDL */
1271937131aSBjørn Mork {USB_DEVICE(0x1199, 0x68a9)}, /* Sierra Wireless Modem */
128c41444ccSBjørn Mork {USB_DEVICE(0x1199, 0x9010)}, /* Sierra Wireless Gobi 3000 QDL */
129c41444ccSBjørn Mork {USB_DEVICE(0x1199, 0x9012)}, /* Sierra Wireless Gobi 3000 QDL */
13068c79e57SRichard Hartmann {USB_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */
131c41444ccSBjørn Mork {USB_DEVICE(0x1199, 0x9014)}, /* Sierra Wireless Gobi 3000 QDL */
132c41444ccSBjørn Mork {USB_DEVICE(0x1199, 0x9015)}, /* Sierra Wireless Gobi 3000 Modem device */
133c41444ccSBjørn Mork {USB_DEVICE(0x1199, 0x9018)}, /* Sierra Wireless Gobi 3000 QDL */
134c41444ccSBjørn Mork {USB_DEVICE(0x1199, 0x9019)}, /* Sierra Wireless Gobi 3000 Modem device */
1351937131aSBjørn Mork {USB_DEVICE(0x1199, 0x901b)}, /* Sierra Wireless MC7770 */
1362db4d870SThomas Tuttle {USB_DEVICE(0x12D1, 0x14F0)}, /* Sony Gobi 3000 QDL */
1372db4d870SThomas Tuttle {USB_DEVICE(0x12D1, 0x14F1)}, /* Sony Gobi 3000 Composite */
1388a2f132aSRichard Weinberger {USB_DEVICE(0x0AF0, 0x8120)}, /* Option GTM681W */
1391937131aSBjørn Mork
1408bc7a069SBjørn Mork /* non-Gobi Sierra Wireless devices */
14144840decSDavid Ward {DEVICE_SWI(0x03f0, 0x4e1d)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */
1428bc7a069SBjørn Mork {DEVICE_SWI(0x0f3d, 0x68a2)}, /* Sierra Wireless MC7700 */
1438bc7a069SBjørn Mork {DEVICE_SWI(0x114f, 0x68a2)}, /* Sierra Wireless MC7750 */
1448bc7a069SBjørn Mork {DEVICE_SWI(0x1199, 0x68a2)}, /* Sierra Wireless MC7710 */
14592944c45SDavid Ward {DEVICE_SWI(0x1199, 0x68c0)}, /* Sierra Wireless MC7304/MC7354 */
1468bc7a069SBjørn Mork {DEVICE_SWI(0x1199, 0x901c)}, /* Sierra Wireless EM7700 */
147771394a5SDouglas Fischer {DEVICE_SWI(0x1199, 0x901e)}, /* Sierra Wireless EM7355 QDL */
1488bc7a069SBjørn Mork {DEVICE_SWI(0x1199, 0x901f)}, /* Sierra Wireless EM7355 */
1490ce5fb58SAleksander Morgado {DEVICE_SWI(0x1199, 0x9040)}, /* Sierra Wireless Modem */
1503d07984bSDavid Ward {DEVICE_SWI(0x1199, 0x9041)}, /* Sierra Wireless MC7305/MC7355 */
1518bc7a069SBjørn Mork {DEVICE_SWI(0x1199, 0x9051)}, /* Netgear AirCard 340U */
1520ce5fb58SAleksander Morgado {DEVICE_SWI(0x1199, 0x9053)}, /* Sierra Wireless Modem */
1530ce5fb58SAleksander Morgado {DEVICE_SWI(0x1199, 0x9054)}, /* Sierra Wireless Modem */
154ff1fcd50SAleksander Morgado {DEVICE_SWI(0x1199, 0x9055)}, /* Netgear AirCard 341U */
1550ce5fb58SAleksander Morgado {DEVICE_SWI(0x1199, 0x9056)}, /* Sierra Wireless Modem */
1560ce5fb58SAleksander Morgado {DEVICE_SWI(0x1199, 0x9060)}, /* Sierra Wireless Modem */
1570ce5fb58SAleksander Morgado {DEVICE_SWI(0x1199, 0x9061)}, /* Sierra Wireless Modem */
158d2a4309cSErik Ekman {DEVICE_SWI(0x1199, 0x9062)}, /* Sierra Wireless EM7305 QDL */
159996fab55SBjørn Mork {DEVICE_SWI(0x1199, 0x9063)}, /* Sierra Wireless EM7305 */
16004fdbc82SBjørn Mork {DEVICE_SWI(0x1199, 0x9070)}, /* Sierra Wireless MC74xx */
16104fdbc82SBjørn Mork {DEVICE_SWI(0x1199, 0x9071)}, /* Sierra Wireless MC74xx */
16204fdbc82SBjørn Mork {DEVICE_SWI(0x1199, 0x9078)}, /* Sierra Wireless EM74xx */
16304fdbc82SBjørn Mork {DEVICE_SWI(0x1199, 0x9079)}, /* Sierra Wireless EM74xx */
1648d7a10ddSBjørn Mork {DEVICE_SWI(0x1199, 0x907a)}, /* Sierra Wireless EM74xx QDL */
1658d7a10ddSBjørn Mork {DEVICE_SWI(0x1199, 0x907b)}, /* Sierra Wireless EM74xx */
16692a18a65SReinhard Speyerer {DEVICE_SWI(0x1199, 0x9090)}, /* Sierra Wireless EM7565 QDL */
16792a18a65SReinhard Speyerer {DEVICE_SWI(0x1199, 0x9091)}, /* Sierra Wireless EM7565 */
16811c52d25SAleksander Morgado {DEVICE_SWI(0x1199, 0x90d2)}, /* Sierra Wireless EM9191 QDL */
169*4ffcb9d7SJack Wu {DEVICE_SWI(0x1199, 0x90e4)}, /* Sierra Wireless EM86xx QDL*/
170*4ffcb9d7SJack Wu {DEVICE_SWI(0x1199, 0x90e5)}, /* Sierra Wireless EM86xx */
171870b1eeeSEthan Yang {DEVICE_SWI(0x1199, 0xc080)}, /* Sierra Wireless EM7590 QDL */
172870b1eeeSEthan Yang {DEVICE_SWI(0x1199, 0xc081)}, /* Sierra Wireless EM7590 */
1738bc7a069SBjørn Mork {DEVICE_SWI(0x413c, 0x81a2)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */
1748bc7a069SBjørn Mork {DEVICE_SWI(0x413c, 0x81a3)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */
1758bc7a069SBjørn Mork {DEVICE_SWI(0x413c, 0x81a4)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
1768bc7a069SBjørn Mork {DEVICE_SWI(0x413c, 0x81a8)}, /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */
1778bc7a069SBjørn Mork {DEVICE_SWI(0x413c, 0x81a9)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
1786da3700cSPieter Hollants {DEVICE_SWI(0x413c, 0x81b1)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */
179013dd239SPatrik Halfar {DEVICE_SWI(0x413c, 0x81b3)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */
180436ecf55SBjørn Mork {DEVICE_SWI(0x413c, 0x81b5)}, /* Dell Wireless 5811e QDL */
181436ecf55SBjørn Mork {DEVICE_SWI(0x413c, 0x81b6)}, /* Dell Wireless 5811e QDL */
182eee48781SFrank Wunderlich {DEVICE_SWI(0x413c, 0x81c2)}, /* Dell Wireless 5811e */
1833429444aSMatt Jolly {DEVICE_SWI(0x413c, 0x81cb)}, /* Dell Wireless 5816e QDL */
18478d6de3cSMatt Jolly {DEVICE_SWI(0x413c, 0x81cc)}, /* Dell Wireless 5816e */
185f5d9644cSShrirang Bagul {DEVICE_SWI(0x413c, 0x81cf)}, /* Dell Wireless 5819 */
186f5d9644cSShrirang Bagul {DEVICE_SWI(0x413c, 0x81d0)}, /* Dell Wireless 5819 */
187f5d9644cSShrirang Bagul {DEVICE_SWI(0x413c, 0x81d1)}, /* Dell Wireless 5818 */
188f5d9644cSShrirang Bagul {DEVICE_SWI(0x413c, 0x81d2)}, /* Dell Wireless 5818 */
1891f2c1cf4SJackBB Wu {DEVICE_SWI(0x413c, 0x8217)}, /* Dell Wireless DW5826e */
1901f2c1cf4SJackBB Wu {DEVICE_SWI(0x413c, 0x8218)}, /* Dell Wireless DW5826e QDL */
1911937131aSBjørn Mork
192e7181d00SMartin Hauke /* Huawei devices */
193e7181d00SMartin Hauke {DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */
194e7181d00SMartin Hauke
195a78b4282SGreg Kroah-Hartman { } /* Terminating entry */
196a78b4282SGreg Kroah-Hartman };
197a78b4282SGreg Kroah-Hartman MODULE_DEVICE_TABLE(usb, id_table);
198a78b4282SGreg Kroah-Hartman
handle_quectel_ec20(struct device * dev,int ifnum)1999d5b5ed7SPetr Štetiar static int handle_quectel_ec20(struct device *dev, int ifnum)
2009d5b5ed7SPetr Štetiar {
2019d5b5ed7SPetr Štetiar int altsetting = 0;
2029d5b5ed7SPetr Štetiar
2039d5b5ed7SPetr Štetiar /*
2049d5b5ed7SPetr Štetiar * Quectel EC20 Mini PCIe LTE module layout:
2059d5b5ed7SPetr Štetiar * 0: DM/DIAG (use libqcdm from ModemManager for communication)
2069d5b5ed7SPetr Štetiar * 1: NMEA
2079d5b5ed7SPetr Štetiar * 2: AT-capable modem port
2089d5b5ed7SPetr Štetiar * 3: Modem interface
2099d5b5ed7SPetr Štetiar * 4: NDIS
2109d5b5ed7SPetr Štetiar */
2119d5b5ed7SPetr Štetiar switch (ifnum) {
2129d5b5ed7SPetr Štetiar case 0:
2139d5b5ed7SPetr Štetiar dev_dbg(dev, "Quectel EC20 DM/DIAG interface found\n");
2149d5b5ed7SPetr Štetiar break;
2159d5b5ed7SPetr Štetiar case 1:
2169d5b5ed7SPetr Štetiar dev_dbg(dev, "Quectel EC20 NMEA GPS interface found\n");
2179d5b5ed7SPetr Štetiar break;
2189d5b5ed7SPetr Štetiar case 2:
2199d5b5ed7SPetr Štetiar case 3:
2209d5b5ed7SPetr Štetiar dev_dbg(dev, "Quectel EC20 Modem port found\n");
2219d5b5ed7SPetr Štetiar break;
2229d5b5ed7SPetr Štetiar case 4:
2239d5b5ed7SPetr Štetiar /* Don't claim the QMI/net interface */
2249d5b5ed7SPetr Štetiar altsetting = -1;
2259d5b5ed7SPetr Štetiar break;
2269d5b5ed7SPetr Štetiar }
2279d5b5ed7SPetr Štetiar
2289d5b5ed7SPetr Štetiar return altsetting;
2299d5b5ed7SPetr Štetiar }
2309d5b5ed7SPetr Štetiar
qcprobe(struct usb_serial * serial,const struct usb_device_id * id)231a78b4282SGreg Kroah-Hartman static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
232a78b4282SGreg Kroah-Hartman {
233e07896e6SAnssi Hannula struct usb_host_interface *intf = serial->interface->cur_altsetting;
2349760b283SGreg Kroah-Hartman struct device *dev = &serial->dev->dev;
235a78b4282SGreg Kroah-Hartman int retval = -ENODEV;
236a78b4282SGreg Kroah-Hartman __u8 nintf;
237a78b4282SGreg Kroah-Hartman __u8 ifnum;
2382cf69930SBjørn Mork int altsetting = -1;
239efd3e915SDavid Ward bool sendsetup = false;
240a78b4282SGreg Kroah-Hartman
24159536da3SBjørn Mork /* we only support vendor specific functions */
24259536da3SBjørn Mork if (intf->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
24359536da3SBjørn Mork goto done;
24459536da3SBjørn Mork
245a78b4282SGreg Kroah-Hartman nintf = serial->dev->actconfig->desc.bNumInterfaces;
2469760b283SGreg Kroah-Hartman dev_dbg(dev, "Num Interfaces = %d\n", nintf);
247e07896e6SAnssi Hannula ifnum = intf->desc.bInterfaceNumber;
2489760b283SGreg Kroah-Hartman dev_dbg(dev, "This Interface = %d\n", ifnum);
249a78b4282SGreg Kroah-Hartman
2500dfbf65eSBjørn Mork if (nintf == 1) {
251a78b4282SGreg Kroah-Hartman /* QDL mode */
252e07896e6SAnssi Hannula /* Gobi 2000 has a single altsetting, older ones have two */
253e07896e6SAnssi Hannula if (serial->interface->num_altsetting == 2)
254be4c5eb2SJohan Hovold intf = usb_altnum_to_altsetting(serial->interface, 1);
255e07896e6SAnssi Hannula else if (serial->interface->num_altsetting > 2)
2560dfbf65eSBjørn Mork goto done;
257e07896e6SAnssi Hannula
258be4c5eb2SJohan Hovold if (intf && intf->desc.bNumEndpoints == 2 &&
259e07896e6SAnssi Hannula usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) &&
260a78b4282SGreg Kroah-Hartman usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) {
2619760b283SGreg Kroah-Hartman dev_dbg(dev, "QDL port found\n");
262e07896e6SAnssi Hannula
263d25d5f28SBjørn Mork if (serial->interface->num_altsetting == 1)
26499ab3f9eSSteven Hardy retval = 0; /* Success */
265d25d5f28SBjørn Mork else
2662cf69930SBjørn Mork altsetting = 1;
267a78b4282SGreg Kroah-Hartman }
2680dfbf65eSBjørn Mork goto done;
269a78b4282SGreg Kroah-Hartman
2700dfbf65eSBjørn Mork }
2710dfbf65eSBjørn Mork
2720dfbf65eSBjørn Mork /* default to enabling interface */
2730dfbf65eSBjørn Mork altsetting = 0;
274731879f8SDan Williams
275ce1b0661SBjørn Mork /*
276ce1b0661SBjørn Mork * Composite mode; don't bind to the QMI/net interface as that
277c192c8e7SDan Williams * gets handled by other drivers.
2781992de83SMatthias G. Eckermann */
279c192c8e7SDan Williams
280d712ca91SBjørn Mork switch (id->driver_info) {
281d712ca91SBjørn Mork case QCSERIAL_G1K:
282ce1b0661SBjørn Mork /*
283ce1b0661SBjørn Mork * Gobi 1K USB layout:
2843f8bc5e4SDan Williams * 0: DM/DIAG (use libqcdm from ModemManager for communication)
285c192c8e7SDan Williams * 1: serial port (doesn't respond)
286c192c8e7SDan Williams * 2: AT-capable modem port
287c192c8e7SDan Williams * 3: QMI/net
288731879f8SDan Williams */
2898bc7a069SBjørn Mork if (nintf < 3 || nintf > 4) {
2908bc7a069SBjørn Mork dev_err(dev, "unknown number of interfaces: %d\n", nintf);
2918bc7a069SBjørn Mork altsetting = -1;
2928bc7a069SBjørn Mork goto done;
2938bc7a069SBjørn Mork }
2948bc7a069SBjørn Mork
2953f8bc5e4SDan Williams if (ifnum == 0) {
2963f8bc5e4SDan Williams dev_dbg(dev, "Gobi 1K DM/DIAG interface found\n");
2973f8bc5e4SDan Williams altsetting = 1;
2983f8bc5e4SDan Williams } else if (ifnum == 2)
299731879f8SDan Williams dev_dbg(dev, "Modem port found\n");
300731879f8SDan Williams else
301731879f8SDan Williams altsetting = -1;
302d712ca91SBjørn Mork break;
303d712ca91SBjørn Mork case QCSERIAL_G2K:
3049d5b5ed7SPetr Štetiar /* handle non-standard layouts */
3059d5b5ed7SPetr Štetiar if (nintf == 5 && id->idProduct == QUECTEL_EC20_PID) {
3069d5b5ed7SPetr Štetiar altsetting = handle_quectel_ec20(dev, ifnum);
3079d5b5ed7SPetr Štetiar goto done;
3089d5b5ed7SPetr Štetiar }
3099d5b5ed7SPetr Štetiar
310ce1b0661SBjørn Mork /*
311ce1b0661SBjørn Mork * Gobi 2K+ USB layout:
312c192c8e7SDan Williams * 0: QMI/net
313c192c8e7SDan Williams * 1: DM/DIAG (use libqcdm from ModemManager for communication)
314c192c8e7SDan Williams * 2: AT-capable modem port
315c192c8e7SDan Williams * 3: NMEA
316c192c8e7SDan Williams */
3178bc7a069SBjørn Mork if (nintf < 3 || nintf > 4) {
3188bc7a069SBjørn Mork dev_err(dev, "unknown number of interfaces: %d\n", nintf);
3198bc7a069SBjørn Mork altsetting = -1;
3208bc7a069SBjørn Mork goto done;
3218bc7a069SBjørn Mork }
3228bc7a069SBjørn Mork
323731879f8SDan Williams switch (ifnum) {
324731879f8SDan Williams case 0:
325731879f8SDan Williams /* Don't claim the QMI/net interface */
3260dfbf65eSBjørn Mork altsetting = -1;
327731879f8SDan Williams break;
328731879f8SDan Williams case 1:
3299760b283SGreg Kroah-Hartman dev_dbg(dev, "Gobi 2K+ DM/DIAG interface found\n");
3300dfbf65eSBjørn Mork break;
3310dfbf65eSBjørn Mork case 2:
3329760b283SGreg Kroah-Hartman dev_dbg(dev, "Modem port found\n");
3330dfbf65eSBjørn Mork break;
3340dfbf65eSBjørn Mork case 3:
3351992de83SMatthias G. Eckermann /*
3361992de83SMatthias G. Eckermann * NMEA (serial line 9600 8N1)
3371992de83SMatthias G. Eckermann * # echo "\$GPS_START" > /dev/ttyUSBx
3381992de83SMatthias G. Eckermann * # echo "\$GPS_STOP" > /dev/ttyUSBx
3391992de83SMatthias G. Eckermann */
3409760b283SGreg Kroah-Hartman dev_dbg(dev, "Gobi 2K+ NMEA GPS interface found\n");
341731879f8SDan Williams break;
342731879f8SDan Williams }
343d712ca91SBjørn Mork break;
3448bc7a069SBjørn Mork case QCSERIAL_SWI:
3458bc7a069SBjørn Mork /*
3468bc7a069SBjørn Mork * Sierra Wireless layout:
3478bc7a069SBjørn Mork * 0: DM/DIAG (use libqcdm from ModemManager for communication)
3488bc7a069SBjørn Mork * 2: NMEA
3498bc7a069SBjørn Mork * 3: AT-capable modem port
3508bc7a069SBjørn Mork * 8: QMI/net
3518bc7a069SBjørn Mork */
3528bc7a069SBjørn Mork switch (ifnum) {
3538bc7a069SBjørn Mork case 0:
3548bc7a069SBjørn Mork dev_dbg(dev, "DM/DIAG interface found\n");
3558bc7a069SBjørn Mork break;
3568bc7a069SBjørn Mork case 2:
3578bc7a069SBjørn Mork dev_dbg(dev, "NMEA GPS interface found\n");
35892a18a65SReinhard Speyerer sendsetup = true;
3598bc7a069SBjørn Mork break;
3608bc7a069SBjørn Mork case 3:
3618bc7a069SBjørn Mork dev_dbg(dev, "Modem port found\n");
362efd3e915SDavid Ward sendsetup = true;
3638bc7a069SBjørn Mork break;
3648bc7a069SBjørn Mork default:
3658bc7a069SBjørn Mork /* don't claim any unsupported interface */
3668bc7a069SBjørn Mork altsetting = -1;
3678bc7a069SBjørn Mork break;
3688bc7a069SBjørn Mork }
3698bc7a069SBjørn Mork break;
370e7181d00SMartin Hauke case QCSERIAL_HWI:
371e7181d00SMartin Hauke /*
37259536da3SBjørn Mork * Huawei devices map functions by subclass + protocol
37359536da3SBjørn Mork * instead of interface numbers. The protocol identify
37459536da3SBjørn Mork * a specific function, while the subclass indicate a
37559536da3SBjørn Mork * specific firmware source
37659536da3SBjørn Mork *
3774a7375edSGreg Kroah-Hartman * This is a list of functions known to be non-serial. The rest
3784a7375edSGreg Kroah-Hartman * are assumed to be serial and will be handled by this driver
379e7181d00SMartin Hauke */
38059536da3SBjørn Mork switch (intf->desc.bInterfaceProtocol) {
38159536da3SBjørn Mork /* QMI combined (qmi_wwan) */
38259536da3SBjørn Mork case 0x07:
38359536da3SBjørn Mork case 0x37:
38459536da3SBjørn Mork case 0x67:
38559536da3SBjørn Mork /* QMI data (qmi_wwan) */
38659536da3SBjørn Mork case 0x08:
38759536da3SBjørn Mork case 0x38:
38859536da3SBjørn Mork case 0x68:
38959536da3SBjørn Mork /* QMI control (qmi_wwan) */
39059536da3SBjørn Mork case 0x09:
39159536da3SBjørn Mork case 0x39:
39259536da3SBjørn Mork case 0x69:
39359536da3SBjørn Mork /* NCM like (huawei_cdc_ncm) */
39459536da3SBjørn Mork case 0x16:
39559536da3SBjørn Mork case 0x46:
39659536da3SBjørn Mork case 0x76:
397e7181d00SMartin Hauke altsetting = -1;
398e7181d00SMartin Hauke break;
39959536da3SBjørn Mork default:
40059536da3SBjørn Mork dev_dbg(dev, "Huawei type serial port found (%02x/%02x/%02x)\n",
40159536da3SBjørn Mork intf->desc.bInterfaceClass,
40259536da3SBjørn Mork intf->desc.bInterfaceSubClass,
40359536da3SBjørn Mork intf->desc.bInterfaceProtocol);
404e7181d00SMartin Hauke }
405e7181d00SMartin Hauke break;
406d712ca91SBjørn Mork default:
407d712ca91SBjørn Mork dev_err(dev, "unsupported device layout type: %lu\n",
408d712ca91SBjørn Mork id->driver_info);
409d712ca91SBjørn Mork break;
410a78b4282SGreg Kroah-Hartman }
411a78b4282SGreg Kroah-Hartman
4120dfbf65eSBjørn Mork done:
4132cf69930SBjørn Mork if (altsetting >= 0) {
4142cf69930SBjørn Mork retval = usb_set_interface(serial->dev, ifnum, altsetting);
4152cf69930SBjørn Mork if (retval < 0) {
4162cf69930SBjørn Mork dev_err(dev,
4172cf69930SBjørn Mork "Could not set interface, error %d\n",
4182cf69930SBjørn Mork retval);
4192cf69930SBjørn Mork retval = -ENODEV;
4202cf69930SBjørn Mork }
4212cf69930SBjørn Mork }
4222cf69930SBjørn Mork
423efd3e915SDavid Ward if (!retval)
424efd3e915SDavid Ward usb_set_serial_data(serial, (void *)(unsigned long)sendsetup);
425efd3e915SDavid Ward
426a78b4282SGreg Kroah-Hartman return retval;
427a78b4282SGreg Kroah-Hartman }
428a78b4282SGreg Kroah-Hartman
qc_attach(struct usb_serial * serial)429961be09eSJohan Hovold static int qc_attach(struct usb_serial *serial)
430961be09eSJohan Hovold {
431961be09eSJohan Hovold struct usb_wwan_intf_private *data;
432efd3e915SDavid Ward bool sendsetup;
433961be09eSJohan Hovold
434961be09eSJohan Hovold data = kzalloc(sizeof(*data), GFP_KERNEL);
435961be09eSJohan Hovold if (!data)
436961be09eSJohan Hovold return -ENOMEM;
437961be09eSJohan Hovold
438efd3e915SDavid Ward sendsetup = !!(unsigned long)(usb_get_serial_data(serial));
439efd3e915SDavid Ward if (sendsetup)
440efd3e915SDavid Ward data->use_send_setup = 1;
441efd3e915SDavid Ward
442961be09eSJohan Hovold spin_lock_init(&data->susp_lock);
443961be09eSJohan Hovold
444961be09eSJohan Hovold usb_set_serial_data(serial, data);
445961be09eSJohan Hovold
446961be09eSJohan Hovold return 0;
447961be09eSJohan Hovold }
448961be09eSJohan Hovold
qc_release(struct usb_serial * serial)44910c9ab15SSteven Hardy static void qc_release(struct usb_serial *serial)
45010c9ab15SSteven Hardy {
45110c9ab15SSteven Hardy struct usb_wwan_intf_private *priv = usb_get_serial_data(serial);
45210c9ab15SSteven Hardy
45310c9ab15SSteven Hardy usb_set_serial_data(serial, NULL);
45410c9ab15SSteven Hardy kfree(priv);
45510c9ab15SSteven Hardy }
45610c9ab15SSteven Hardy
457a78b4282SGreg Kroah-Hartman static struct usb_serial_driver qcdevice = {
458a78b4282SGreg Kroah-Hartman .driver = {
459a78b4282SGreg Kroah-Hartman .owner = THIS_MODULE,
460a78b4282SGreg Kroah-Hartman .name = "qcserial",
461a78b4282SGreg Kroah-Hartman },
462a78b4282SGreg Kroah-Hartman .description = "Qualcomm USB modem",
463a78b4282SGreg Kroah-Hartman .id_table = id_table,
464a78b4282SGreg Kroah-Hartman .num_ports = 1,
465a78b4282SGreg Kroah-Hartman .probe = qcprobe,
4663d7e59adSMatthew Garrett .open = usb_wwan_open,
4673d7e59adSMatthew Garrett .close = usb_wwan_close,
468efd3e915SDavid Ward .dtr_rts = usb_wwan_dtr_rts,
4693d7e59adSMatthew Garrett .write = usb_wwan_write,
4703d7e59adSMatthew Garrett .write_room = usb_wwan_write_room,
4713d7e59adSMatthew Garrett .chars_in_buffer = usb_wwan_chars_in_buffer,
47208f741a9SMagnus Lynch .tiocmget = usb_wwan_tiocmget,
47308f741a9SMagnus Lynch .tiocmset = usb_wwan_tiocmset,
474961be09eSJohan Hovold .attach = qc_attach,
47510c9ab15SSteven Hardy .release = qc_release,
476b8f0e820SJohan Hovold .port_probe = usb_wwan_port_probe,
477a1028f0aSBjørn Mork .port_remove = usb_wwan_port_remove,
4783d7e59adSMatthew Garrett #ifdef CONFIG_PM
4793d7e59adSMatthew Garrett .suspend = usb_wwan_suspend,
4803d7e59adSMatthew Garrett .resume = usb_wwan_resume,
4813d7e59adSMatthew Garrett #endif
482a78b4282SGreg Kroah-Hartman };
483a78b4282SGreg Kroah-Hartman
484d860322fSAlan Stern static struct usb_serial_driver * const serial_drivers[] = {
485d860322fSAlan Stern &qcdevice, NULL
486d860322fSAlan Stern };
487d860322fSAlan Stern
48868e24113SGreg Kroah-Hartman module_usb_serial_driver(serial_drivers, id_table);
489a78b4282SGreg Kroah-Hartman
490a78b4282SGreg Kroah-Hartman MODULE_AUTHOR(DRIVER_AUTHOR);
491a78b4282SGreg Kroah-Hartman MODULE_DESCRIPTION(DRIVER_DESC);
492a78b4282SGreg Kroah-Hartman MODULE_LICENSE("GPL v2");
493