xref: /OK3568_Linux_fs/app/forlinx/quectelCM/QmiWwanCM.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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