18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Qualcomm Serial USB driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *	Copyright (c) 2008 QUALCOMM Incorporated.
68c2ecf20Sopenharmony_ci *	Copyright (c) 2009 Greg Kroah-Hartman <gregkh@suse.de>
78c2ecf20Sopenharmony_ci *	Copyright (c) 2009 Novell Inc.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/tty.h>
118c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/usb.h>
148c2ecf20Sopenharmony_ci#include <linux/usb/serial.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include "usb-wwan.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Qualcomm Inc"
198c2ecf20Sopenharmony_ci#define DRIVER_DESC "Qualcomm USB Serial driver"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define QUECTEL_EC20_PID	0x9215
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* standard device layouts supported by this driver */
248c2ecf20Sopenharmony_cienum qcserial_layouts {
258c2ecf20Sopenharmony_ci	QCSERIAL_G2K = 0,	/* Gobi 2000 */
268c2ecf20Sopenharmony_ci	QCSERIAL_G1K = 1,	/* Gobi 1000 */
278c2ecf20Sopenharmony_ci	QCSERIAL_SWI = 2,	/* Sierra Wireless */
288c2ecf20Sopenharmony_ci	QCSERIAL_HWI = 3,	/* Huawei */
298c2ecf20Sopenharmony_ci};
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define DEVICE_G1K(v, p) \
328c2ecf20Sopenharmony_ci	USB_DEVICE(v, p), .driver_info = QCSERIAL_G1K
338c2ecf20Sopenharmony_ci#define DEVICE_SWI(v, p) \
348c2ecf20Sopenharmony_ci	USB_DEVICE(v, p), .driver_info = QCSERIAL_SWI
358c2ecf20Sopenharmony_ci#define DEVICE_HWI(v, p) \
368c2ecf20Sopenharmony_ci	USB_DEVICE(v, p), .driver_info = QCSERIAL_HWI
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table[] = {
398c2ecf20Sopenharmony_ci	/* Gobi 1000 devices */
408c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x05c6, 0x9211)},	/* Acer Gobi QDL device */
418c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x05c6, 0x9212)},	/* Acer Gobi Modem Device */
428c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x03f0, 0x1f1d)},	/* HP un2400 Gobi Modem Device */
438c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x03f0, 0x201d)},	/* HP un2400 Gobi QDL Device */
448c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x04da, 0x250d)},	/* Panasonic Gobi Modem device */
458c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x04da, 0x250c)},	/* Panasonic Gobi QDL device */
468c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x413c, 0x8172)},	/* Dell Gobi Modem device */
478c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x413c, 0x8171)},	/* Dell Gobi QDL device */
488c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x1410, 0xa001)},	/* Novatel/Verizon USB-1000 */
498c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x1410, 0xa002)},	/* Novatel Gobi Modem device */
508c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x1410, 0xa003)},	/* Novatel Gobi Modem device */
518c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x1410, 0xa004)},	/* Novatel Gobi Modem device */
528c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x1410, 0xa005)},	/* Novatel Gobi Modem device */
538c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x1410, 0xa006)},	/* Novatel Gobi Modem device */
548c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x1410, 0xa007)},	/* Novatel Gobi Modem device */
558c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x1410, 0xa008)},	/* Novatel Gobi QDL device */
568c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x0b05, 0x1776)},	/* Asus Gobi Modem device */
578c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x0b05, 0x1774)},	/* Asus Gobi QDL device */
588c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x19d2, 0xfff3)},	/* ONDA Gobi Modem device */
598c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x19d2, 0xfff2)},	/* ONDA Gobi QDL device */
608c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x1557, 0x0a80)},	/* OQO Gobi QDL device */
618c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x05c6, 0x9001)},   /* Generic Gobi Modem device */
628c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x05c6, 0x9002)},	/* Generic Gobi Modem device */
638c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x05c6, 0x9202)},	/* Generic Gobi Modem device */
648c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x05c6, 0x9203)},	/* Generic Gobi Modem device */
658c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x05c6, 0x9222)},	/* Generic Gobi Modem device */
668c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x05c6, 0x9008)},	/* Generic Gobi QDL device */
678c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x05c6, 0x9009)},	/* Generic Gobi Modem device */
688c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x05c6, 0x9201)},	/* Generic Gobi QDL device */
698c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x05c6, 0x9221)},	/* Generic Gobi QDL device */
708c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x05c6, 0x9231)},	/* Generic Gobi QDL device */
718c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x1f45, 0x0001)},	/* Unknown Gobi QDL device */
728c2ecf20Sopenharmony_ci	{DEVICE_G1K(0x1bc7, 0x900e)},	/* Telit Gobi QDL device */
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	/* Gobi 2000 devices */
758c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1410, 0xa010)},	/* Novatel Gobi 2000 QDL device */
768c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1410, 0xa011)},	/* Novatel Gobi 2000 QDL device */
778c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1410, 0xa012)},	/* Novatel Gobi 2000 QDL device */
788c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1410, 0xa013)},	/* Novatel Gobi 2000 QDL device */
798c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1410, 0xa014)},	/* Novatel Gobi 2000 QDL device */
808c2ecf20Sopenharmony_ci	{USB_DEVICE(0x413c, 0x8185)},	/* Dell Gobi 2000 QDL device (N0218, VU936) */
818c2ecf20Sopenharmony_ci	{USB_DEVICE(0x413c, 0x8186)},	/* Dell Gobi 2000 Modem device (N0218, VU936) */
828c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x9208)},	/* Generic Gobi 2000 QDL device */
838c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x920b)},	/* Generic Gobi 2000 Modem device */
848c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x9224)},	/* Sony Gobi 2000 QDL device (N0279, VU730) */
858c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x9225)},	/* Sony Gobi 2000 Modem device (N0279, VU730) */
868c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x9244)},	/* Samsung Gobi 2000 QDL device (VL176) */
878c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x9245)},	/* Samsung Gobi 2000 Modem device (VL176) */
888c2ecf20Sopenharmony_ci	{USB_DEVICE(0x03f0, 0x241d)},	/* HP Gobi 2000 QDL device (VP412) */
898c2ecf20Sopenharmony_ci	{USB_DEVICE(0x03f0, 0x251d)},	/* HP Gobi 2000 Modem device (VP412) */
908c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x9214)},	/* Acer Gobi 2000 QDL device (VP413) */
918c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x9215)},	/* Acer Gobi 2000 Modem device (VP413) */
928c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x9264)},	/* Asus Gobi 2000 QDL device (VR305) */
938c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x9265)},	/* Asus Gobi 2000 Modem device (VR305) */
948c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x9234)},	/* Top Global Gobi 2000 QDL device (VR306) */
958c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x9235)},	/* Top Global Gobi 2000 Modem device (VR306) */
968c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x9274)},	/* iRex Technologies Gobi 2000 QDL device (VR307) */
978c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x9275)},	/* iRex Technologies Gobi 2000 Modem device (VR307) */
988c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9000)},	/* Sierra Wireless Gobi 2000 QDL device (VT773) */
998c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9001)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
1008c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9002)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
1018c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9003)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
1028c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9004)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
1038c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9005)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
1048c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9006)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
1058c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9007)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
1068c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9008)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
1078c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9009)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
1088c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x900a)},	/* Sierra Wireless Gobi 2000 Modem device (VT773) */
1098c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9011)},   /* Sierra Wireless Gobi 2000 Modem device (MC8305) */
1108c2ecf20Sopenharmony_ci	{USB_DEVICE(0x16d8, 0x8001)},	/* CMDTech Gobi 2000 QDL device (VU922) */
1118c2ecf20Sopenharmony_ci	{USB_DEVICE(0x16d8, 0x8002)},	/* CMDTech Gobi 2000 Modem device (VU922) */
1128c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x9204)},	/* Gobi 2000 QDL device */
1138c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x9205)},	/* Gobi 2000 Modem device */
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* Gobi 3000 devices */
1168c2ecf20Sopenharmony_ci	{USB_DEVICE(0x03f0, 0x371d)},	/* HP un2430 Gobi 3000 QDL */
1178c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x920c)},	/* Gobi 3000 QDL */
1188c2ecf20Sopenharmony_ci	{USB_DEVICE(0x05c6, 0x920d)},	/* Gobi 3000 Composite */
1198c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1410, 0xa020)},   /* Novatel Gobi 3000 QDL */
1208c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1410, 0xa021)},	/* Novatel Gobi 3000 Composite */
1218c2ecf20Sopenharmony_ci	{USB_DEVICE(0x413c, 0x8193)},	/* Dell Gobi 3000 QDL */
1228c2ecf20Sopenharmony_ci	{USB_DEVICE(0x413c, 0x8194)},	/* Dell Gobi 3000 Composite */
1238c2ecf20Sopenharmony_ci	{USB_DEVICE(0x413c, 0x81a6)},	/* Dell DW5570 QDL (MC8805) */
1248c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x68a4)},	/* Sierra Wireless QDL */
1258c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x68a5)},	/* Sierra Wireless Modem */
1268c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x68a8)},	/* Sierra Wireless QDL */
1278c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x68a9)},	/* Sierra Wireless Modem */
1288c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9010)},	/* Sierra Wireless Gobi 3000 QDL */
1298c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9012)},	/* Sierra Wireless Gobi 3000 QDL */
1308c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9013)},	/* Sierra Wireless Gobi 3000 Modem device (MC8355) */
1318c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9014)},	/* Sierra Wireless Gobi 3000 QDL */
1328c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9015)},	/* Sierra Wireless Gobi 3000 Modem device */
1338c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9018)},	/* Sierra Wireless Gobi 3000 QDL */
1348c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x9019)},	/* Sierra Wireless Gobi 3000 Modem device */
1358c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1199, 0x901b)},	/* Sierra Wireless MC7770 */
1368c2ecf20Sopenharmony_ci	{USB_DEVICE(0x12D1, 0x14F0)},	/* Sony Gobi 3000 QDL */
1378c2ecf20Sopenharmony_ci	{USB_DEVICE(0x12D1, 0x14F1)},	/* Sony Gobi 3000 Composite */
1388c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0AF0, 0x8120)},	/* Option GTM681W */
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* non-Gobi Sierra Wireless devices */
1418c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x03f0, 0x4e1d)},	/* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */
1428c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x0f3d, 0x68a2)},	/* Sierra Wireless MC7700 */
1438c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x114f, 0x68a2)},	/* Sierra Wireless MC7750 */
1448c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x68a2)},	/* Sierra Wireless MC7710 */
1458c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x68c0)},	/* Sierra Wireless MC7304/MC7354 */
1468c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x901c)},	/* Sierra Wireless EM7700 */
1478c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x901e)},	/* Sierra Wireless EM7355 QDL */
1488c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x901f)},	/* Sierra Wireless EM7355 */
1498c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9040)},	/* Sierra Wireless Modem */
1508c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9041)},	/* Sierra Wireless MC7305/MC7355 */
1518c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9051)},	/* Netgear AirCard 340U */
1528c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9053)},	/* Sierra Wireless Modem */
1538c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9054)},	/* Sierra Wireless Modem */
1548c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9055)},	/* Netgear AirCard 341U */
1558c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9056)},	/* Sierra Wireless Modem */
1568c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9060)},	/* Sierra Wireless Modem */
1578c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9061)},	/* Sierra Wireless Modem */
1588c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9062)},	/* Sierra Wireless EM7305 QDL */
1598c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9063)},	/* Sierra Wireless EM7305 */
1608c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9070)},	/* Sierra Wireless MC74xx */
1618c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9071)},	/* Sierra Wireless MC74xx */
1628c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9078)},	/* Sierra Wireless EM74xx */
1638c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9079)},	/* Sierra Wireless EM74xx */
1648c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x907a)},	/* Sierra Wireless EM74xx QDL */
1658c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x907b)},	/* Sierra Wireless EM74xx */
1668c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9090)},	/* Sierra Wireless EM7565 QDL */
1678c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x9091)},	/* Sierra Wireless EM7565 */
1688c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0x90d2)},	/* Sierra Wireless EM9191 QDL */
1698c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0xc080)},	/* Sierra Wireless EM7590 QDL */
1708c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x1199, 0xc081)},	/* Sierra Wireless EM7590 */
1718c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x81a2)},	/* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */
1728c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x81a3)},	/* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */
1738c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x81a4)},	/* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
1748c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x81a8)},	/* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */
1758c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x81a9)},	/* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
1768c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x81b1)},	/* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */
1778c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x81b3)},	/* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */
1788c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x81b5)},	/* Dell Wireless 5811e QDL */
1798c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x81b6)},	/* Dell Wireless 5811e QDL */
1808c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x81c2)},	/* Dell Wireless 5811e */
1818c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x81cb)},	/* Dell Wireless 5816e QDL */
1828c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x81cc)},	/* Dell Wireless 5816e */
1838c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x81cf)},   /* Dell Wireless 5819 */
1848c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x81d0)},   /* Dell Wireless 5819 */
1858c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x81d1)},   /* Dell Wireless 5818 */
1868c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x81d2)},   /* Dell Wireless 5818 */
1878c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x8217)},	/* Dell Wireless DW5826e */
1888c2ecf20Sopenharmony_ci	{DEVICE_SWI(0x413c, 0x8218)},	/* Dell Wireless DW5826e QDL */
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	/* Huawei devices */
1918c2ecf20Sopenharmony_ci	{DEVICE_HWI(0x03f0, 0x581d)},	/* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	{ }				/* Terminating entry */
1948c2ecf20Sopenharmony_ci};
1958c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic int handle_quectel_ec20(struct device *dev, int ifnum)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	int altsetting = 0;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/*
2028c2ecf20Sopenharmony_ci	 * Quectel EC20 Mini PCIe LTE module layout:
2038c2ecf20Sopenharmony_ci	 * 0: DM/DIAG (use libqcdm from ModemManager for communication)
2048c2ecf20Sopenharmony_ci	 * 1: NMEA
2058c2ecf20Sopenharmony_ci	 * 2: AT-capable modem port
2068c2ecf20Sopenharmony_ci	 * 3: Modem interface
2078c2ecf20Sopenharmony_ci	 * 4: NDIS
2088c2ecf20Sopenharmony_ci	 */
2098c2ecf20Sopenharmony_ci	switch (ifnum) {
2108c2ecf20Sopenharmony_ci	case 0:
2118c2ecf20Sopenharmony_ci		dev_dbg(dev, "Quectel EC20 DM/DIAG interface found\n");
2128c2ecf20Sopenharmony_ci		break;
2138c2ecf20Sopenharmony_ci	case 1:
2148c2ecf20Sopenharmony_ci		dev_dbg(dev, "Quectel EC20 NMEA GPS interface found\n");
2158c2ecf20Sopenharmony_ci		break;
2168c2ecf20Sopenharmony_ci	case 2:
2178c2ecf20Sopenharmony_ci	case 3:
2188c2ecf20Sopenharmony_ci		dev_dbg(dev, "Quectel EC20 Modem port found\n");
2198c2ecf20Sopenharmony_ci		break;
2208c2ecf20Sopenharmony_ci	case 4:
2218c2ecf20Sopenharmony_ci		/* Don't claim the QMI/net interface */
2228c2ecf20Sopenharmony_ci		altsetting = -1;
2238c2ecf20Sopenharmony_ci		break;
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	return altsetting;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	struct usb_host_interface *intf = serial->interface->cur_altsetting;
2328c2ecf20Sopenharmony_ci	struct device *dev = &serial->dev->dev;
2338c2ecf20Sopenharmony_ci	int retval = -ENODEV;
2348c2ecf20Sopenharmony_ci	__u8 nintf;
2358c2ecf20Sopenharmony_ci	__u8 ifnum;
2368c2ecf20Sopenharmony_ci	int altsetting = -1;
2378c2ecf20Sopenharmony_ci	bool sendsetup = false;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* we only support vendor specific functions */
2408c2ecf20Sopenharmony_ci	if (intf->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
2418c2ecf20Sopenharmony_ci		goto done;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	nintf = serial->dev->actconfig->desc.bNumInterfaces;
2448c2ecf20Sopenharmony_ci	dev_dbg(dev, "Num Interfaces = %d\n", nintf);
2458c2ecf20Sopenharmony_ci	ifnum = intf->desc.bInterfaceNumber;
2468c2ecf20Sopenharmony_ci	dev_dbg(dev, "This Interface = %d\n", ifnum);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (nintf == 1) {
2498c2ecf20Sopenharmony_ci		/* QDL mode */
2508c2ecf20Sopenharmony_ci		/* Gobi 2000 has a single altsetting, older ones have two */
2518c2ecf20Sopenharmony_ci		if (serial->interface->num_altsetting == 2)
2528c2ecf20Sopenharmony_ci			intf = usb_altnum_to_altsetting(serial->interface, 1);
2538c2ecf20Sopenharmony_ci		else if (serial->interface->num_altsetting > 2)
2548c2ecf20Sopenharmony_ci			goto done;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		if (intf && intf->desc.bNumEndpoints == 2 &&
2578c2ecf20Sopenharmony_ci		    usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) &&
2588c2ecf20Sopenharmony_ci		    usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) {
2598c2ecf20Sopenharmony_ci			dev_dbg(dev, "QDL port found\n");
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci			if (serial->interface->num_altsetting == 1)
2628c2ecf20Sopenharmony_ci				retval = 0; /* Success */
2638c2ecf20Sopenharmony_ci			else
2648c2ecf20Sopenharmony_ci				altsetting = 1;
2658c2ecf20Sopenharmony_ci		}
2668c2ecf20Sopenharmony_ci		goto done;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	/* default to enabling interface */
2718c2ecf20Sopenharmony_ci	altsetting = 0;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/*
2748c2ecf20Sopenharmony_ci	 * Composite mode; don't bind to the QMI/net interface as that
2758c2ecf20Sopenharmony_ci	 * gets handled by other drivers.
2768c2ecf20Sopenharmony_ci	 */
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	switch (id->driver_info) {
2798c2ecf20Sopenharmony_ci	case QCSERIAL_G1K:
2808c2ecf20Sopenharmony_ci		/*
2818c2ecf20Sopenharmony_ci		 * Gobi 1K USB layout:
2828c2ecf20Sopenharmony_ci		 * 0: DM/DIAG (use libqcdm from ModemManager for communication)
2838c2ecf20Sopenharmony_ci		 * 1: serial port (doesn't respond)
2848c2ecf20Sopenharmony_ci		 * 2: AT-capable modem port
2858c2ecf20Sopenharmony_ci		 * 3: QMI/net
2868c2ecf20Sopenharmony_ci		 */
2878c2ecf20Sopenharmony_ci		if (nintf < 3 || nintf > 4) {
2888c2ecf20Sopenharmony_ci			dev_err(dev, "unknown number of interfaces: %d\n", nintf);
2898c2ecf20Sopenharmony_ci			altsetting = -1;
2908c2ecf20Sopenharmony_ci			goto done;
2918c2ecf20Sopenharmony_ci		}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		if (ifnum == 0) {
2948c2ecf20Sopenharmony_ci			dev_dbg(dev, "Gobi 1K DM/DIAG interface found\n");
2958c2ecf20Sopenharmony_ci			altsetting = 1;
2968c2ecf20Sopenharmony_ci		} else if (ifnum == 2)
2978c2ecf20Sopenharmony_ci			dev_dbg(dev, "Modem port found\n");
2988c2ecf20Sopenharmony_ci		else
2998c2ecf20Sopenharmony_ci			altsetting = -1;
3008c2ecf20Sopenharmony_ci		break;
3018c2ecf20Sopenharmony_ci	case QCSERIAL_G2K:
3028c2ecf20Sopenharmony_ci		/* handle non-standard layouts */
3038c2ecf20Sopenharmony_ci		if (nintf == 5 && id->idProduct == QUECTEL_EC20_PID) {
3048c2ecf20Sopenharmony_ci			altsetting = handle_quectel_ec20(dev, ifnum);
3058c2ecf20Sopenharmony_ci			goto done;
3068c2ecf20Sopenharmony_ci		}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		/*
3098c2ecf20Sopenharmony_ci		 * Gobi 2K+ USB layout:
3108c2ecf20Sopenharmony_ci		 * 0: QMI/net
3118c2ecf20Sopenharmony_ci		 * 1: DM/DIAG (use libqcdm from ModemManager for communication)
3128c2ecf20Sopenharmony_ci		 * 2: AT-capable modem port
3138c2ecf20Sopenharmony_ci		 * 3: NMEA
3148c2ecf20Sopenharmony_ci		 */
3158c2ecf20Sopenharmony_ci		if (nintf < 3 || nintf > 4) {
3168c2ecf20Sopenharmony_ci			dev_err(dev, "unknown number of interfaces: %d\n", nintf);
3178c2ecf20Sopenharmony_ci			altsetting = -1;
3188c2ecf20Sopenharmony_ci			goto done;
3198c2ecf20Sopenharmony_ci		}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci		switch (ifnum) {
3228c2ecf20Sopenharmony_ci		case 0:
3238c2ecf20Sopenharmony_ci			/* Don't claim the QMI/net interface */
3248c2ecf20Sopenharmony_ci			altsetting = -1;
3258c2ecf20Sopenharmony_ci			break;
3268c2ecf20Sopenharmony_ci		case 1:
3278c2ecf20Sopenharmony_ci			dev_dbg(dev, "Gobi 2K+ DM/DIAG interface found\n");
3288c2ecf20Sopenharmony_ci			break;
3298c2ecf20Sopenharmony_ci		case 2:
3308c2ecf20Sopenharmony_ci			dev_dbg(dev, "Modem port found\n");
3318c2ecf20Sopenharmony_ci			break;
3328c2ecf20Sopenharmony_ci		case 3:
3338c2ecf20Sopenharmony_ci			/*
3348c2ecf20Sopenharmony_ci			 * NMEA (serial line 9600 8N1)
3358c2ecf20Sopenharmony_ci			 * # echo "\$GPS_START" > /dev/ttyUSBx
3368c2ecf20Sopenharmony_ci			 * # echo "\$GPS_STOP"  > /dev/ttyUSBx
3378c2ecf20Sopenharmony_ci			 */
3388c2ecf20Sopenharmony_ci			dev_dbg(dev, "Gobi 2K+ NMEA GPS interface found\n");
3398c2ecf20Sopenharmony_ci			break;
3408c2ecf20Sopenharmony_ci		}
3418c2ecf20Sopenharmony_ci		break;
3428c2ecf20Sopenharmony_ci	case QCSERIAL_SWI:
3438c2ecf20Sopenharmony_ci		/*
3448c2ecf20Sopenharmony_ci		 * Sierra Wireless layout:
3458c2ecf20Sopenharmony_ci		 * 0: DM/DIAG (use libqcdm from ModemManager for communication)
3468c2ecf20Sopenharmony_ci		 * 2: NMEA
3478c2ecf20Sopenharmony_ci		 * 3: AT-capable modem port
3488c2ecf20Sopenharmony_ci		 * 8: QMI/net
3498c2ecf20Sopenharmony_ci		 */
3508c2ecf20Sopenharmony_ci		switch (ifnum) {
3518c2ecf20Sopenharmony_ci		case 0:
3528c2ecf20Sopenharmony_ci			dev_dbg(dev, "DM/DIAG interface found\n");
3538c2ecf20Sopenharmony_ci			break;
3548c2ecf20Sopenharmony_ci		case 2:
3558c2ecf20Sopenharmony_ci			dev_dbg(dev, "NMEA GPS interface found\n");
3568c2ecf20Sopenharmony_ci			sendsetup = true;
3578c2ecf20Sopenharmony_ci			break;
3588c2ecf20Sopenharmony_ci		case 3:
3598c2ecf20Sopenharmony_ci			dev_dbg(dev, "Modem port found\n");
3608c2ecf20Sopenharmony_ci			sendsetup = true;
3618c2ecf20Sopenharmony_ci			break;
3628c2ecf20Sopenharmony_ci		default:
3638c2ecf20Sopenharmony_ci			/* don't claim any unsupported interface */
3648c2ecf20Sopenharmony_ci			altsetting = -1;
3658c2ecf20Sopenharmony_ci			break;
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci		break;
3688c2ecf20Sopenharmony_ci	case QCSERIAL_HWI:
3698c2ecf20Sopenharmony_ci		/*
3708c2ecf20Sopenharmony_ci		 * Huawei devices map functions by subclass + protocol
3718c2ecf20Sopenharmony_ci		 * instead of interface numbers. The protocol identify
3728c2ecf20Sopenharmony_ci		 * a specific function, while the subclass indicate a
3738c2ecf20Sopenharmony_ci		 * specific firmware source
3748c2ecf20Sopenharmony_ci		 *
3758c2ecf20Sopenharmony_ci		 * This is a list of functions known to be non-serial.  The rest
3768c2ecf20Sopenharmony_ci		 * are assumed to be serial and will be handled by this driver
3778c2ecf20Sopenharmony_ci		 */
3788c2ecf20Sopenharmony_ci		switch (intf->desc.bInterfaceProtocol) {
3798c2ecf20Sopenharmony_ci			/* QMI combined (qmi_wwan) */
3808c2ecf20Sopenharmony_ci		case 0x07:
3818c2ecf20Sopenharmony_ci		case 0x37:
3828c2ecf20Sopenharmony_ci		case 0x67:
3838c2ecf20Sopenharmony_ci			/* QMI data (qmi_wwan) */
3848c2ecf20Sopenharmony_ci		case 0x08:
3858c2ecf20Sopenharmony_ci		case 0x38:
3868c2ecf20Sopenharmony_ci		case 0x68:
3878c2ecf20Sopenharmony_ci			/* QMI control (qmi_wwan) */
3888c2ecf20Sopenharmony_ci		case 0x09:
3898c2ecf20Sopenharmony_ci		case 0x39:
3908c2ecf20Sopenharmony_ci		case 0x69:
3918c2ecf20Sopenharmony_ci			/* NCM like (huawei_cdc_ncm) */
3928c2ecf20Sopenharmony_ci		case 0x16:
3938c2ecf20Sopenharmony_ci		case 0x46:
3948c2ecf20Sopenharmony_ci		case 0x76:
3958c2ecf20Sopenharmony_ci			altsetting = -1;
3968c2ecf20Sopenharmony_ci			break;
3978c2ecf20Sopenharmony_ci		default:
3988c2ecf20Sopenharmony_ci			dev_dbg(dev, "Huawei type serial port found (%02x/%02x/%02x)\n",
3998c2ecf20Sopenharmony_ci				intf->desc.bInterfaceClass,
4008c2ecf20Sopenharmony_ci				intf->desc.bInterfaceSubClass,
4018c2ecf20Sopenharmony_ci				intf->desc.bInterfaceProtocol);
4028c2ecf20Sopenharmony_ci		}
4038c2ecf20Sopenharmony_ci		break;
4048c2ecf20Sopenharmony_ci	default:
4058c2ecf20Sopenharmony_ci		dev_err(dev, "unsupported device layout type: %lu\n",
4068c2ecf20Sopenharmony_ci			id->driver_info);
4078c2ecf20Sopenharmony_ci		break;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cidone:
4118c2ecf20Sopenharmony_ci	if (altsetting >= 0) {
4128c2ecf20Sopenharmony_ci		retval = usb_set_interface(serial->dev, ifnum, altsetting);
4138c2ecf20Sopenharmony_ci		if (retval < 0) {
4148c2ecf20Sopenharmony_ci			dev_err(dev,
4158c2ecf20Sopenharmony_ci				"Could not set interface, error %d\n",
4168c2ecf20Sopenharmony_ci				retval);
4178c2ecf20Sopenharmony_ci			retval = -ENODEV;
4188c2ecf20Sopenharmony_ci		}
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	if (!retval)
4228c2ecf20Sopenharmony_ci		usb_set_serial_data(serial, (void *)(unsigned long)sendsetup);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	return retval;
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic int qc_attach(struct usb_serial *serial)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	struct usb_wwan_intf_private *data;
4308c2ecf20Sopenharmony_ci	bool sendsetup;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_KERNEL);
4338c2ecf20Sopenharmony_ci	if (!data)
4348c2ecf20Sopenharmony_ci		return -ENOMEM;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	sendsetup = !!(unsigned long)(usb_get_serial_data(serial));
4378c2ecf20Sopenharmony_ci	if (sendsetup)
4388c2ecf20Sopenharmony_ci		data->use_send_setup = 1;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	spin_lock_init(&data->susp_lock);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	usb_set_serial_data(serial, data);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	return 0;
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic void qc_release(struct usb_serial *serial)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	struct usb_wwan_intf_private *priv = usb_get_serial_data(serial);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	usb_set_serial_data(serial, NULL);
4528c2ecf20Sopenharmony_ci	kfree(priv);
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic struct usb_serial_driver qcdevice = {
4568c2ecf20Sopenharmony_ci	.driver = {
4578c2ecf20Sopenharmony_ci		.owner     = THIS_MODULE,
4588c2ecf20Sopenharmony_ci		.name      = "qcserial",
4598c2ecf20Sopenharmony_ci	},
4608c2ecf20Sopenharmony_ci	.description         = "Qualcomm USB modem",
4618c2ecf20Sopenharmony_ci	.id_table            = id_table,
4628c2ecf20Sopenharmony_ci	.num_ports           = 1,
4638c2ecf20Sopenharmony_ci	.probe               = qcprobe,
4648c2ecf20Sopenharmony_ci	.open		     = usb_wwan_open,
4658c2ecf20Sopenharmony_ci	.close		     = usb_wwan_close,
4668c2ecf20Sopenharmony_ci	.dtr_rts	     = usb_wwan_dtr_rts,
4678c2ecf20Sopenharmony_ci	.write		     = usb_wwan_write,
4688c2ecf20Sopenharmony_ci	.write_room	     = usb_wwan_write_room,
4698c2ecf20Sopenharmony_ci	.chars_in_buffer     = usb_wwan_chars_in_buffer,
4708c2ecf20Sopenharmony_ci	.tiocmget            = usb_wwan_tiocmget,
4718c2ecf20Sopenharmony_ci	.tiocmset            = usb_wwan_tiocmset,
4728c2ecf20Sopenharmony_ci	.attach              = qc_attach,
4738c2ecf20Sopenharmony_ci	.release	     = qc_release,
4748c2ecf20Sopenharmony_ci	.port_probe          = usb_wwan_port_probe,
4758c2ecf20Sopenharmony_ci	.port_remove	     = usb_wwan_port_remove,
4768c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4778c2ecf20Sopenharmony_ci	.suspend	     = usb_wwan_suspend,
4788c2ecf20Sopenharmony_ci	.resume		     = usb_wwan_resume,
4798c2ecf20Sopenharmony_ci#endif
4808c2ecf20Sopenharmony_ci};
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
4838c2ecf20Sopenharmony_ci	&qcdevice, NULL
4848c2ecf20Sopenharmony_ci};
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
4898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
4908c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
491