1 /******************************************************************************
2 @file QmiWwanCM.c
3 @brief QMI WWAN connectivity manager.
4
5 DESCRIPTION
6 Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules.
7
8 INITIALIZATION AND SEQUENCING REQUIREMENTS
9 None.
10
11 ---------------------------------------------------------------------------
12 Copyright (c) 2016 - 2020 Quectel Wireless Solution, Co., Ltd. All Rights Reserved.
13 Quectel Wireless Solution Proprietary and Confidential.
14 ---------------------------------------------------------------------------
15 ******************************************************************************/
16
17 #include <stdio.h>
18 #include <string.h>
19 #include <termios.h>
20 #include <stdio.h>
21 #include <ctype.h>
22 #include "QMIThread.h"
23
24 #ifdef CONFIG_QMIWWAN
25 static int cdc_wdm_fd = -1;
26 static UCHAR qmiclientId[QMUX_TYPE_WDS_ADMIN + 1];
27
GetQCTLTransactionId(void)28 static UCHAR GetQCTLTransactionId(void) {
29 static int TransactionId = 0;
30 if (++TransactionId > 0xFF)
31 TransactionId = 1;
32 return TransactionId;
33 }
34
35 typedef USHORT (*CUSTOMQCTL)(PQMICTL_MSG pCTLMsg, void *arg);
36
ComposeQCTLMsg(USHORT QMICTLType,CUSTOMQCTL customQctlMsgFunction,void * arg)37 static PQCQMIMSG ComposeQCTLMsg(USHORT QMICTLType, CUSTOMQCTL customQctlMsgFunction, void *arg) {
38 UCHAR QMIBuf[WDM_DEFAULT_BUFSIZE];
39 PQCQMIMSG pRequest = (PQCQMIMSG)QMIBuf;
40 int Length;
41
42 pRequest->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI;
43 pRequest->QMIHdr.CtlFlags = 0x00;
44 pRequest->QMIHdr.QMIType = QMUX_TYPE_CTL;
45 pRequest->QMIHdr.ClientId= 0x00;
46
47 pRequest->CTLMsg.QMICTLMsgHdr.CtlFlags = QMICTL_FLAG_REQUEST;
48 pRequest->CTLMsg.QMICTLMsgHdr.TransactionId = GetQCTLTransactionId();
49 pRequest->CTLMsg.QMICTLMsgHdr.QMICTLType = cpu_to_le16(QMICTLType);
50 if (customQctlMsgFunction)
51 pRequest->CTLMsg.QMICTLMsgHdr.Length = cpu_to_le16(customQctlMsgFunction(&pRequest->CTLMsg, arg) - sizeof(QCQMICTL_MSG_HDR));
52 else
53 pRequest->CTLMsg.QMICTLMsgHdr.Length = cpu_to_le16(0x0000);
54
55 pRequest->QMIHdr.Length = cpu_to_le16(le16_to_cpu(pRequest->CTLMsg.QMICTLMsgHdr.Length) + sizeof(QCQMICTL_MSG_HDR) + sizeof(QCQMI_HDR) - 1);
56 Length = le16_to_cpu(pRequest->QMIHdr.Length) + 1;
57
58 pRequest = (PQCQMIMSG)malloc(Length);
59 if (pRequest == NULL) {
60 dbg_time("%s fail to malloc", __func__);
61 } else {
62 memcpy(pRequest, QMIBuf, Length);
63 }
64
65 return pRequest;
66 }
67
CtlGetVersionReq(PQMICTL_MSG QCTLMsg,void * arg)68 static USHORT CtlGetVersionReq(PQMICTL_MSG QCTLMsg, void *arg)
69 {
70 (void)arg;
71 QCTLMsg->GetVersionReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER;
72 QCTLMsg->GetVersionReq.TLVLength = cpu_to_le16(0x0001);
73 QCTLMsg->GetVersionReq.QMUXTypes = QMUX_TYPE_ALL;
74 return sizeof(QMICTL_GET_VERSION_REQ_MSG);
75 }
76
CtlGetClientIdReq(PQMICTL_MSG QCTLMsg,void * arg)77 static USHORT CtlGetClientIdReq(PQMICTL_MSG QCTLMsg, void *arg) {
78 QCTLMsg->GetClientIdReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER;
79 QCTLMsg->GetClientIdReq.TLVLength = cpu_to_le16(0x0001);
80 QCTLMsg->GetClientIdReq.QMIType = ((UCHAR *)arg)[0];
81 return sizeof(QMICTL_GET_CLIENT_ID_REQ_MSG);
82 }
83
CtlReleaseClientIdReq(PQMICTL_MSG QCTLMsg,void * arg)84 static USHORT CtlReleaseClientIdReq(PQMICTL_MSG QCTLMsg, void *arg) {
85 QCTLMsg->ReleaseClientIdReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER;
86 QCTLMsg->ReleaseClientIdReq.TLVLength = cpu_to_le16(0x0002);
87 QCTLMsg->ReleaseClientIdReq.QMIType = ((UCHAR *)arg)[0];
88 QCTLMsg->ReleaseClientIdReq.ClientId = ((UCHAR *)arg)[1] ;
89 return sizeof(QMICTL_RELEASE_CLIENT_ID_REQ_MSG);
90 }
91
CtlLibQmiProxyOpenReq(PQMICTL_MSG QCTLMsg,void * arg)92 static USHORT CtlLibQmiProxyOpenReq(PQMICTL_MSG QCTLMsg, void *arg)
93 {
94 (void)arg;
95 const char *device_path = (const char *)(arg);
96 QCTLMsg->LibQmiProxyOpenReq.TLVType = 0x01;
97 QCTLMsg->LibQmiProxyOpenReq.TLVLength = cpu_to_le16(strlen(device_path));
98 //strcpy(QCTLMsg->LibQmiProxyOpenReq.device_path, device_path);
99 //__builtin___strcpy_chk
100 memcpy(QCTLMsg->LibQmiProxyOpenReq.device_path, device_path, strlen(device_path));
101 return sizeof(QMICTL_LIBQMI_PROXY_OPEN_MSG) + (strlen(device_path));
102 }
103
libqmi_proxy_open(const char * cdc_wdm)104 static int libqmi_proxy_open(const char *cdc_wdm) {
105 int ret;
106 PQCQMIMSG pResponse;
107
108 ret = QmiThreadSendQMI(ComposeQCTLMsg(QMI_MESSAGE_CTL_INTERNAL_PROXY_OPEN,
109 CtlLibQmiProxyOpenReq, (void *)cdc_wdm), &pResponse);
110 if (!ret && pResponse
111 && pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXResult == 0
112 && pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXError == 0) {
113 ret = 0;
114 }
115 else {
116 return -1;
117 }
118
119 if (pResponse)
120 free(pResponse);
121
122 return ret;
123 }
124
QmiWwanSendQMI(PQCQMIMSG pRequest)125 static int QmiWwanSendQMI(PQCQMIMSG pRequest) {
126 struct pollfd pollfds[]= {{cdc_wdm_fd, POLLOUT, 0}};
127 int ret;
128
129 if (cdc_wdm_fd == -1) {
130 dbg_time("%s cdc_wdm_fd = -1", __func__);
131 return -ENODEV;
132 }
133
134 if (pRequest->QMIHdr.QMIType != QMUX_TYPE_CTL) {
135 pRequest->QMIHdr.ClientId = qmiclientId[pRequest->QMIHdr.QMIType];
136 if (pRequest->QMIHdr.ClientId == 0) {
137 dbg_time("QMIType %d has no clientID", pRequest->QMIHdr.QMIType);
138 return -ENODEV;
139 }
140
141 if (pRequest->QMIHdr.QMIType == QMUX_TYPE_WDS_IPV6)
142 pRequest->QMIHdr.QMIType = QMUX_TYPE_WDS;
143 }
144
145 do {
146 ret = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), 5000);
147 } while ((ret < 0) && (errno == EINTR));
148
149 if (pollfds[0].revents & POLLOUT) {
150 ssize_t nwrites = le16_to_cpu(pRequest->QMIHdr.Length) + 1;
151 ret = write(cdc_wdm_fd, pRequest, nwrites);
152 if (ret == nwrites) {
153 ret = 0;
154 } else {
155 dbg_time("%s write=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
156 }
157 } else {
158 dbg_time("%s poll=%d, revents = 0x%x, errno: %d (%s)", __func__, ret, pollfds[0].revents, errno, strerror(errno));
159 }
160
161 return ret;
162 }
163
QmiWwanGetClientID(UCHAR QMIType)164 static UCHAR QmiWwanGetClientID(UCHAR QMIType) {
165 PQCQMIMSG pResponse;
166
167 QmiThreadSendQMI(ComposeQCTLMsg(QMICTL_GET_CLIENT_ID_REQ, CtlGetClientIdReq, &QMIType), &pResponse);
168
169 if (pResponse) {
170 USHORT QMUXResult = cpu_to_le16(pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXResult); // QMI_RESULT_SUCCESS
171 USHORT QMUXError = cpu_to_le16(pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXError); // QMI_ERR_INVALID_ARG
172 //UCHAR QMIType = pResponse->CTLMsg.GetClientIdRsp.QMIType;
173 UCHAR ClientId = pResponse->CTLMsg.GetClientIdRsp.ClientId;
174
175 if (!QMUXResult && !QMUXError && (QMIType == pResponse->CTLMsg.GetClientIdRsp.QMIType)) {
176 switch (QMIType) {
177 case QMUX_TYPE_WDS: dbg_time("Get clientWDS = %d", ClientId); break;
178 case QMUX_TYPE_DMS: dbg_time("Get clientDMS = %d", ClientId); break;
179 case QMUX_TYPE_NAS: dbg_time("Get clientNAS = %d", ClientId); break;
180 case QMUX_TYPE_QOS: dbg_time("Get clientQOS = %d", ClientId); break;
181 case QMUX_TYPE_WMS: dbg_time("Get clientWMS = %d", ClientId); break;
182 case QMUX_TYPE_PDS: dbg_time("Get clientPDS = %d", ClientId); break;
183 case QMUX_TYPE_UIM: dbg_time("Get clientUIM = %d", ClientId); break;
184 case QMUX_TYPE_WDS_ADMIN: dbg_time("Get clientWDA = %d", ClientId);
185 break;
186 default: break;
187 }
188 return ClientId;
189 }
190 }
191 return 0;
192 }
193
QmiWwanReleaseClientID(QMI_SERVICE_TYPE QMIType,UCHAR ClientId)194 static int QmiWwanReleaseClientID(QMI_SERVICE_TYPE QMIType, UCHAR ClientId) {
195 UCHAR argv[] = {QMIType, ClientId};
196 QmiThreadSendQMI(ComposeQCTLMsg(QMICTL_RELEASE_CLIENT_ID_REQ, CtlReleaseClientIdReq, argv), NULL);
197 return 0;
198 }
199
QmiWwanInit(PROFILE_T * profile)200 static int QmiWwanInit(PROFILE_T *profile) {
201 unsigned i;
202 int ret;
203 PQCQMIMSG pResponse;
204
205 if (profile->proxy[0] && !strcmp(profile->proxy, LIBQMI_PROXY)) {
206 ret = libqmi_proxy_open(profile->qmichannel);
207 if (ret)
208 return ret;
209 }
210
211 if (!profile->proxy[0]) {
212 for (i = 0; i < 10; i++) {
213 ret = QmiThreadSendQMITimeout(ComposeQCTLMsg(QMICTL_SYNC_REQ, NULL, NULL), NULL, 1 * 1000, __func__);
214 if (!ret)
215 break;
216 sleep(1);
217 }
218 if (ret)
219 return ret;
220 }
221
222 QmiThreadSendQMI(ComposeQCTLMsg(QMICTL_GET_VERSION_REQ, CtlGetVersionReq, NULL), &pResponse);
223 if (profile->qmap_mode) {
224 if (pResponse) {
225 if (pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXResult == 0 && pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXError == 0) {
226 uint8_t NumElements = 0;
227
228 for (NumElements = 0; NumElements < pResponse->CTLMsg.GetVersionRsp.NumElements; NumElements++) {
229 #if 0
230 dbg_time("QMUXType = %02x Version = %d.%d",
231 pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].QMUXType,
232 pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MajorVersion,
233 pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MinorVersion);
234 #endif
235 if (pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].QMUXType == QMUX_TYPE_WDS_ADMIN)
236 profile->qmap_version = (pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MinorVersion > 16);
237 }
238 }
239 }
240 }
241 if (pResponse) free(pResponse);
242 qmiclientId[QMUX_TYPE_WDS] = QmiWwanGetClientID(QMUX_TYPE_WDS);
243 if (profile->enable_ipv6)
244 qmiclientId[QMUX_TYPE_WDS_IPV6] = QmiWwanGetClientID(QMUX_TYPE_WDS);
245 qmiclientId[QMUX_TYPE_DMS] = QmiWwanGetClientID(QMUX_TYPE_DMS);
246 qmiclientId[QMUX_TYPE_NAS] = QmiWwanGetClientID(QMUX_TYPE_NAS);
247 qmiclientId[QMUX_TYPE_UIM] = QmiWwanGetClientID(QMUX_TYPE_UIM);
248 qmiclientId[QMUX_TYPE_WDS_ADMIN] = QmiWwanGetClientID(QMUX_TYPE_WDS_ADMIN);
249 profile->wda_client = qmiclientId[QMUX_TYPE_WDS_ADMIN];
250
251 return 0;
252 }
253
QmiWwanDeInit(void)254 static int QmiWwanDeInit(void) {
255 unsigned int i;
256 for (i = 0; i < sizeof(qmiclientId)/sizeof(qmiclientId[0]); i++)
257 {
258 if (qmiclientId[i] != 0)
259 {
260 QmiWwanReleaseClientID(i, qmiclientId[i]);
261 qmiclientId[i] = 0;
262 }
263 }
264
265 return 0;
266 }
267
qmi_proxy_read(int fd,void * buf,size_t size)268 static ssize_t qmi_proxy_read (int fd, void *buf, size_t size) {
269 ssize_t nreads;
270 PQCQMI_HDR pHdr = (PQCQMI_HDR)buf;
271
272 nreads = read(fd, pHdr, sizeof(QCQMI_HDR));
273 if (nreads == sizeof(QCQMI_HDR) && le16_to_cpu(pHdr->Length) < size) {
274 nreads += read(fd, pHdr+1, le16_to_cpu(pHdr->Length) + 1 - sizeof(QCQMI_HDR));
275 }
276
277 return nreads;
278 }
279
280 #ifdef QUECTEL_QMI_MERGE
QmiWwanMergeQmiRsp(void * buf,ssize_t * src_size)281 static int QmiWwanMergeQmiRsp(void *buf, ssize_t *src_size) {
282 static QMI_MSG_PACKET s_QMIPacket;
283 QMI_MSG_HEADER *header = NULL;
284 ssize_t size = *src_size;
285
286 if((uint16_t)size < sizeof(QMI_MSG_HEADER))
287 return -1;
288
289 header = (QMI_MSG_HEADER *)buf;
290 if(le16_to_cpu(header->idenity) != MERGE_PACKET_IDENTITY || le16_to_cpu(header->version) != MERGE_PACKET_VERSION || le16_to_cpu(header->cur_len) > le16_to_cpu(header->total_len))
291 return -1;
292
293 if(le16_to_cpu(header->cur_len) == le16_to_cpu(header->total_len)) {
294 *src_size = le16_to_cpu(header->total_len);
295 memcpy(buf, buf + sizeof(QMI_MSG_HEADER), *src_size);
296 s_QMIPacket.len = 0;
297 return 0;
298 }
299
300 memcpy(s_QMIPacket.buf + s_QMIPacket.len, buf + sizeof(QMI_MSG_HEADER), le16_to_cpu(header->cur_len));
301 s_QMIPacket.len += le16_to_cpu(header->cur_len);
302
303 if (le16_to_cpu(header->cur_len) < MERGE_PACKET_MAX_PAYLOAD_SIZE || s_QMIPacket.len >= le16_to_cpu(header->total_len)) {
304 memcpy(buf, s_QMIPacket.buf, s_QMIPacket.len);
305 *src_size = s_QMIPacket.len;
306 s_QMIPacket.len = 0;
307 return 0;
308 }
309
310 return -1;
311 }
312 #endif
313
QmiWwanThread(void * pData)314 static void * QmiWwanThread(void *pData) {
315 PROFILE_T *profile = (PROFILE_T *)pData;
316 const char *cdc_wdm = (const char *)profile->qmichannel;
317 int wait_for_request_quit = 0;
318 char num = cdc_wdm[strlen(cdc_wdm)-1];
319
320 if (profile->proxy[0]) {
321 if (!strncmp(profile->proxy, QUECTEL_QMI_PROXY, strlen(QUECTEL_QMI_PROXY))) {
322 snprintf(profile->proxy, sizeof(profile->proxy), "%s%c", QUECTEL_QMI_PROXY, num);
323 }
324 }
325 else if (profile->qmap_mode > 1) {
326 snprintf(profile->proxy, sizeof(profile->proxy), "%s%c", QUECTEL_QMI_PROXY, num);
327 }
328
329 if (profile->proxy[0])
330 cdc_wdm_fd = cm_open_proxy(profile->proxy);
331 else
332 cdc_wdm_fd = cm_open_dev(cdc_wdm);
333
334 if (cdc_wdm_fd == -1) {
335 dbg_time("%s Failed to open %s, errno: %d (%s)", __func__, cdc_wdm, errno, strerror(errno));
336 qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
337 pthread_exit(NULL);
338 return NULL;
339 }
340
341 dbg_time("cdc_wdm_fd = %d", cdc_wdm_fd);
342
343 qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED);
344 while (1) {
345 struct pollfd pollfds[] = {{qmidevice_control_fd[1], POLLIN, 0}, {cdc_wdm_fd, POLLIN, 0}};
346 int ne, ret, nevents = sizeof(pollfds)/sizeof(pollfds[0]);
347
348 do {
349 ret = poll(pollfds, nevents, wait_for_request_quit ? 1000 : -1);
350 } while ((ret < 0) && (errno == EINTR));
351
352 if (ret == 0 && wait_for_request_quit) {
353 QmiThreadRecvQMI(NULL);
354 continue;
355 }
356
357 if (ret <= 0) {
358 dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
359 break;
360 }
361
362 for (ne = 0; ne < nevents; ne++) {
363 int fd = pollfds[ne].fd;
364 short revents = pollfds[ne].revents;
365
366 //dbg_time("{%d, %x, %x}", pollfds[ne].fd, pollfds[ne].events, pollfds[ne].revents);
367
368 if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
369 dbg_time("%s poll err/hup/inval", __func__);
370 dbg_time("poll fd = %d, events = 0x%04x", fd, revents);
371 if (fd == cdc_wdm_fd) {
372 } else {
373 }
374 if (revents & (POLLHUP | POLLNVAL)) //EC20 bug, Can get POLLERR
375 goto __QmiWwanThread_quit;
376 }
377
378 if ((revents & POLLIN) == 0)
379 continue;
380
381 if (fd == qmidevice_control_fd[1]) {
382 int triger_event;
383 if (read(fd, &triger_event, sizeof(triger_event)) == sizeof(triger_event)) {
384 //DBG("triger_event = 0x%x", triger_event);
385 switch (triger_event) {
386 case RIL_REQUEST_QUIT:
387 goto __QmiWwanThread_quit;
388 break;
389 case SIG_EVENT_STOP:
390 wait_for_request_quit = 1;
391 break;
392 default:
393 break;
394 }
395 }
396 }
397
398 if (fd == cdc_wdm_fd) {
399 ssize_t nreads;
400 PQCQMIMSG pResponse = (PQCQMIMSG)cm_recv_buf;
401
402 if (!profile->proxy[0])
403 nreads = read(fd, cm_recv_buf, sizeof(cm_recv_buf));
404 else
405 nreads = qmi_proxy_read(fd, cm_recv_buf, sizeof(cm_recv_buf));
406 //dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads, errno, strerror(errno));
407 if (nreads <= 0) {
408 dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads, errno, strerror(errno));
409 break;
410 }
411 #ifdef QUECTEL_QMI_MERGE
412 if((profile->qmap_mode == 0 || profile->qmap_mode == 1) && QmiWwanMergeQmiRsp(cm_recv_buf, &nreads))
413 continue;
414 #endif
415 if (nreads != (le16_to_cpu(pResponse->QMIHdr.Length) + 1)) {
416 dbg_time("%s nreads=%d, pQCQMI->QMIHdr.Length = %d", __func__, (int)nreads, le16_to_cpu(pResponse->QMIHdr.Length));
417 continue;
418 }
419
420 QmiThreadRecvQMI(pResponse);
421 }
422 }
423 }
424
425 __QmiWwanThread_quit:
426 if (cdc_wdm_fd != -1) { close(cdc_wdm_fd); cdc_wdm_fd = -1; }
427 qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
428 QmiThreadRecvQMI(NULL); //main thread may pending on QmiThreadSendQMI()
429 dbg_time("%s exit", __func__);
430 pthread_exit(NULL);
431 return NULL;
432 }
433
434 const struct qmi_device_ops qmiwwan_qmidev_ops = {
435 .init = QmiWwanInit,
436 .deinit = QmiWwanDeInit,
437 .send = QmiWwanSendQMI,
438 .read = QmiWwanThread,
439 };
440 #endif
441
442