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