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