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