1*4882a593Smuzhiyun /******************************************************************************
2*4882a593Smuzhiyun @file GobiNetCM.c
3*4882a593Smuzhiyun @brief GobiNet driver.
4*4882a593Smuzhiyun
5*4882a593Smuzhiyun DESCRIPTION
6*4882a593Smuzhiyun Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules.
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun INITIALIZATION AND SEQUENCING REQUIREMENTS
9*4882a593Smuzhiyun None.
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun ---------------------------------------------------------------------------
12*4882a593Smuzhiyun Copyright (c) 2016 - 2020 Quectel Wireless Solution, Co., Ltd. All Rights Reserved.
13*4882a593Smuzhiyun Quectel Wireless Solution Proprietary and Confidential.
14*4882a593Smuzhiyun ---------------------------------------------------------------------------
15*4882a593Smuzhiyun ******************************************************************************/
16*4882a593Smuzhiyun #include <stdio.h>
17*4882a593Smuzhiyun #include <string.h>
18*4882a593Smuzhiyun #include <termios.h>
19*4882a593Smuzhiyun #include <stdio.h>
20*4882a593Smuzhiyun #include <ctype.h>
21*4882a593Smuzhiyun #include "QMIThread.h"
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #ifdef CONFIG_GOBINET
24*4882a593Smuzhiyun static int qmiclientId[QMUX_TYPE_WDS_ADMIN + 1];
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun // IOCTL to generate a client ID for this service type
27*4882a593Smuzhiyun #define IOCTL_QMI_GET_SERVICE_FILE 0x8BE0 + 1
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun // IOCTL to get the VIDPID of the device
30*4882a593Smuzhiyun #define IOCTL_QMI_GET_DEVICE_VIDPID 0x8BE0 + 2
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun // IOCTL to get the MEID of the device
33*4882a593Smuzhiyun #define IOCTL_QMI_GET_DEVICE_MEID 0x8BE0 + 3
34*4882a593Smuzhiyun
GobiNetSendQMI(PQCQMIMSG pRequest)35*4882a593Smuzhiyun static int GobiNetSendQMI(PQCQMIMSG pRequest) {
36*4882a593Smuzhiyun int ret, fd;
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun fd = qmiclientId[pRequest->QMIHdr.QMIType];
39*4882a593Smuzhiyun pRequest->QMIHdr.ClientId = (fd&0xFF) ? fd&0xFF : pRequest->QMIHdr.QMIType;
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun if (fd <= 0) {
42*4882a593Smuzhiyun dbg_time("%s QMIType: %d has no clientID", __func__, pRequest->QMIHdr.QMIType);
43*4882a593Smuzhiyun return -ENODEV;
44*4882a593Smuzhiyun }
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun // Always ready to write
47*4882a593Smuzhiyun if (1 == 1) {
48*4882a593Smuzhiyun ssize_t nwrites = le16_to_cpu(pRequest->QMIHdr.Length) + 1 - sizeof(QCQMI_HDR);
49*4882a593Smuzhiyun ret = write(fd, &pRequest->MUXMsg, nwrites);
50*4882a593Smuzhiyun if (ret == nwrites) {
51*4882a593Smuzhiyun ret = 0;
52*4882a593Smuzhiyun } else {
53*4882a593Smuzhiyun dbg_time("%s write=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun } else {
56*4882a593Smuzhiyun dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun return ret;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun
GobiNetGetClientID(const char * qcqmi,UCHAR QMIType)62*4882a593Smuzhiyun static int GobiNetGetClientID(const char *qcqmi, UCHAR QMIType) {
63*4882a593Smuzhiyun int ClientId;
64*4882a593Smuzhiyun ClientId = cm_open_dev(qcqmi);
65*4882a593Smuzhiyun if (ClientId == -1) {
66*4882a593Smuzhiyun dbg_time("failed to open %s, errno: %d (%s)", qcqmi, errno, strerror(errno));
67*4882a593Smuzhiyun return -1;
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun if (ioctl(ClientId, IOCTL_QMI_GET_SERVICE_FILE, QMIType) != 0) {
71*4882a593Smuzhiyun dbg_time("failed to get ClientID for 0x%02x errno: %d (%s)", QMIType, errno, strerror(errno));
72*4882a593Smuzhiyun close(ClientId);
73*4882a593Smuzhiyun ClientId = 0;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun switch (QMIType) {
77*4882a593Smuzhiyun case QMUX_TYPE_WDS: dbg_time("Get clientWDS = %d", ClientId); break;
78*4882a593Smuzhiyun case QMUX_TYPE_DMS: dbg_time("Get clientDMS = %d", ClientId); break;
79*4882a593Smuzhiyun case QMUX_TYPE_NAS: dbg_time("Get clientNAS = %d", ClientId); break;
80*4882a593Smuzhiyun case QMUX_TYPE_QOS: dbg_time("Get clientQOS = %d", ClientId); break;
81*4882a593Smuzhiyun case QMUX_TYPE_WMS: dbg_time("Get clientWMS = %d", ClientId); break;
82*4882a593Smuzhiyun case QMUX_TYPE_PDS: dbg_time("Get clientPDS = %d", ClientId); break;
83*4882a593Smuzhiyun case QMUX_TYPE_UIM: dbg_time("Get clientUIM = %d", ClientId); break;
84*4882a593Smuzhiyun case QMUX_TYPE_WDS_ADMIN: dbg_time("Get clientWDA = %d", ClientId);
85*4882a593Smuzhiyun break;
86*4882a593Smuzhiyun default: break;
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun return ClientId;
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun
GobiNetDeInit(void)92*4882a593Smuzhiyun static int GobiNetDeInit(void) {
93*4882a593Smuzhiyun unsigned int i;
94*4882a593Smuzhiyun for (i = 0; i < sizeof(qmiclientId)/sizeof(qmiclientId[0]); i++)
95*4882a593Smuzhiyun {
96*4882a593Smuzhiyun if (qmiclientId[i] != 0)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun close(qmiclientId[i]);
99*4882a593Smuzhiyun qmiclientId[i] = 0;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun return 0;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
GobiNetThread(void * pData)106*4882a593Smuzhiyun static void * GobiNetThread(void *pData) {
107*4882a593Smuzhiyun PROFILE_T *profile = (PROFILE_T *)pData;
108*4882a593Smuzhiyun const char *qcqmi = (const char *)profile->qmichannel;
109*4882a593Smuzhiyun int wait_for_request_quit = 0;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun qmiclientId[QMUX_TYPE_WDS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS);
112*4882a593Smuzhiyun if (profile->enable_ipv6)
113*4882a593Smuzhiyun qmiclientId[QMUX_TYPE_WDS_IPV6] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS);
114*4882a593Smuzhiyun qmiclientId[QMUX_TYPE_DMS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_DMS);
115*4882a593Smuzhiyun qmiclientId[QMUX_TYPE_NAS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_NAS);
116*4882a593Smuzhiyun qmiclientId[QMUX_TYPE_UIM] = GobiNetGetClientID(qcqmi, QMUX_TYPE_UIM);
117*4882a593Smuzhiyun if (profile->qmap_mode == 0 || profile->loopback_state) {//when QMAP enabled, set data format in GobiNet Driver
118*4882a593Smuzhiyun qmiclientId[QMUX_TYPE_WDS_ADMIN] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS_ADMIN);
119*4882a593Smuzhiyun profile->wda_client = qmiclientId[QMUX_TYPE_WDS_ADMIN];
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun //donot check clientWDA, there is only one client for WDA, if quectel-CM is killed by SIGKILL, i cannot get client ID for WDA again!
123*4882a593Smuzhiyun if (qmiclientId[QMUX_TYPE_WDS] == 0) /*|| (clientWDA == -1)*/ {
124*4882a593Smuzhiyun GobiNetDeInit();
125*4882a593Smuzhiyun dbg_time("%s Failed to open %s, errno: %d (%s)", __func__, qcqmi, errno, strerror(errno));
126*4882a593Smuzhiyun qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
127*4882a593Smuzhiyun pthread_exit(NULL);
128*4882a593Smuzhiyun return NULL;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED);
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun while (1) {
134*4882a593Smuzhiyun struct pollfd pollfds[16] = {{qmidevice_control_fd[1], POLLIN, 0}};
135*4882a593Smuzhiyun int ne, ret, nevents = 1;
136*4882a593Smuzhiyun unsigned int i;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun for (i = 0; i < sizeof(qmiclientId)/sizeof(qmiclientId[0]); i++)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun if (qmiclientId[i] != 0)
141*4882a593Smuzhiyun {
142*4882a593Smuzhiyun pollfds[nevents].fd = qmiclientId[i];
143*4882a593Smuzhiyun pollfds[nevents].events = POLLIN;
144*4882a593Smuzhiyun pollfds[nevents].revents = 0;
145*4882a593Smuzhiyun nevents++;
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun do {
150*4882a593Smuzhiyun ret = poll(pollfds, nevents, wait_for_request_quit ? 1000: -1);
151*4882a593Smuzhiyun } while ((ret < 0) && (errno == EINTR));
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun if (ret == 0 && wait_for_request_quit) {
154*4882a593Smuzhiyun QmiThreadRecvQMI(NULL); //main thread may pending on QmiThreadSendQMI()
155*4882a593Smuzhiyun continue;
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun if (ret <= 0) {
159*4882a593Smuzhiyun dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
160*4882a593Smuzhiyun break;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun for (ne = 0; ne < nevents; ne++) {
164*4882a593Smuzhiyun int fd = pollfds[ne].fd;
165*4882a593Smuzhiyun short revents = pollfds[ne].revents;
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
168*4882a593Smuzhiyun dbg_time("%s poll err/hup/inval", __func__);
169*4882a593Smuzhiyun dbg_time("epoll fd = %d, events = 0x%04x", fd, revents);
170*4882a593Smuzhiyun if (fd == qmidevice_control_fd[1]) {
171*4882a593Smuzhiyun } else {
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun if (revents & (POLLERR | POLLHUP | POLLNVAL))
174*4882a593Smuzhiyun goto __GobiNetThread_quit;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun if ((revents & POLLIN) == 0)
178*4882a593Smuzhiyun continue;
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun if (fd == qmidevice_control_fd[1]) {
181*4882a593Smuzhiyun int triger_event;
182*4882a593Smuzhiyun if (read(fd, &triger_event, sizeof(triger_event)) == sizeof(triger_event)) {
183*4882a593Smuzhiyun //DBG("triger_event = 0x%x", triger_event);
184*4882a593Smuzhiyun switch (triger_event) {
185*4882a593Smuzhiyun case RIL_REQUEST_QUIT:
186*4882a593Smuzhiyun goto __GobiNetThread_quit;
187*4882a593Smuzhiyun break;
188*4882a593Smuzhiyun case SIG_EVENT_STOP:
189*4882a593Smuzhiyun wait_for_request_quit = 1;
190*4882a593Smuzhiyun break;
191*4882a593Smuzhiyun default:
192*4882a593Smuzhiyun break;
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun continue;
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun ssize_t nreads;
200*4882a593Smuzhiyun PQCQMIMSG pResponse = (PQCQMIMSG)cm_recv_buf;
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun nreads = read(fd, &pResponse->MUXMsg, sizeof(cm_recv_buf) - sizeof(QCQMI_HDR));
203*4882a593Smuzhiyun if (nreads <= 0)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads, errno, strerror(errno));
206*4882a593Smuzhiyun break;
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun for (i = 0; i < sizeof(qmiclientId)/sizeof(qmiclientId[0]); i++)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun if (qmiclientId[i] == fd)
212*4882a593Smuzhiyun {
213*4882a593Smuzhiyun pResponse->QMIHdr.QMIType = i;
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun pResponse->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI;
218*4882a593Smuzhiyun pResponse->QMIHdr.Length = cpu_to_le16(nreads + sizeof(QCQMI_HDR) - 1);
219*4882a593Smuzhiyun pResponse->QMIHdr.CtlFlags = 0x00;
220*4882a593Smuzhiyun pResponse->QMIHdr.ClientId = (fd&0xFF) ? fd&0xFF : pResponse->QMIHdr.QMIType;;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun QmiThreadRecvQMI(pResponse);
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun __GobiNetThread_quit:
228*4882a593Smuzhiyun GobiNetDeInit();
229*4882a593Smuzhiyun qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
230*4882a593Smuzhiyun QmiThreadRecvQMI(NULL); //main thread may pending on QmiThreadSendQMI()
231*4882a593Smuzhiyun dbg_time("%s exit", __func__);
232*4882a593Smuzhiyun pthread_exit(NULL);
233*4882a593Smuzhiyun return NULL;
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun const struct qmi_device_ops gobi_qmidev_ops = {
237*4882a593Smuzhiyun .deinit = GobiNetDeInit,
238*4882a593Smuzhiyun .send = GobiNetSendQMI,
239*4882a593Smuzhiyun .read = GobiNetThread,
240*4882a593Smuzhiyun };
241*4882a593Smuzhiyun #endif
242*4882a593Smuzhiyun
243