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