xref: /openbmc/linux/drivers/usb/serial/qcserial.c (revision 24f68eb5bf14a74027946970a18bc902e19d986a)
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