You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

388 lines
14 KiB

2 years ago
  1. /******************************************************************************
  2. @file QmiWwanCM.c
  3. @brief QMI WWAN connectivity manager.
  4. DESCRIPTION
  5. Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules.
  6. INITIALIZATION AND SEQUENCING REQUIREMENTS
  7. None.
  8. ---------------------------------------------------------------------------
  9. Copyright (c) 2016 - 2020 Quectel Wireless Solution, Co., Ltd. All Rights Reserved.
  10. Quectel Wireless Solution Proprietary and Confidential.
  11. ---------------------------------------------------------------------------
  12. ******************************************************************************/
  13. #include <stdio.h>
  14. #include <string.h>
  15. #include <termios.h>
  16. #include <stdio.h>
  17. #include <ctype.h>
  18. typedef unsigned short sa_family_t;
  19. #include <linux/un.h>
  20. #include "QMIThread.h"
  21. #ifdef CONFIG_QMIWWAN
  22. static int cdc_wdm_fd = -1;
  23. static UCHAR GetQCTLTransactionId(void) {
  24. static int TransactionId = 0;
  25. if (++TransactionId > 0xFF)
  26. TransactionId = 1;
  27. return TransactionId;
  28. }
  29. typedef USHORT (*CUSTOMQCTL)(PQMICTL_MSG pCTLMsg, void *arg);
  30. static PQCQMIMSG ComposeQCTLMsg(USHORT QMICTLType, CUSTOMQCTL customQctlMsgFunction, void *arg) {
  31. UCHAR QMIBuf[WDM_DEFAULT_BUFSIZE];
  32. PQCQMIMSG pRequest = (PQCQMIMSG)QMIBuf;
  33. int Length;
  34. pRequest->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI;
  35. pRequest->QMIHdr.CtlFlags = 0x00;
  36. pRequest->QMIHdr.QMIType = QMUX_TYPE_CTL;
  37. pRequest->QMIHdr.ClientId= 0x00;
  38. pRequest->CTLMsg.QMICTLMsgHdr.CtlFlags = QMICTL_FLAG_REQUEST;
  39. pRequest->CTLMsg.QMICTLMsgHdr.TransactionId = GetQCTLTransactionId();
  40. pRequest->CTLMsg.QMICTLMsgHdr.QMICTLType = cpu_to_le16(QMICTLType);
  41. if (customQctlMsgFunction)
  42. pRequest->CTLMsg.QMICTLMsgHdr.Length = cpu_to_le16(customQctlMsgFunction(&pRequest->CTLMsg, arg) - sizeof(QCQMICTL_MSG_HDR));
  43. else
  44. pRequest->CTLMsg.QMICTLMsgHdr.Length = cpu_to_le16(0x0000);
  45. pRequest->QMIHdr.Length = cpu_to_le16(le16_to_cpu(pRequest->CTLMsg.QMICTLMsgHdr.Length) + sizeof(QCQMICTL_MSG_HDR) + sizeof(QCQMI_HDR) - 1);
  46. Length = le16_to_cpu(pRequest->QMIHdr.Length) + 1;
  47. pRequest = (PQCQMIMSG)malloc(Length);
  48. if (pRequest == NULL) {
  49. dbg_time("%s fail to malloc", __func__);
  50. } else {
  51. memcpy(pRequest, QMIBuf, Length);
  52. }
  53. return pRequest;
  54. }
  55. static USHORT CtlGetVersionReq(PQMICTL_MSG QCTLMsg, void *arg) {
  56. QCTLMsg->GetVersionReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER;
  57. QCTLMsg->GetVersionReq.TLVLength = cpu_to_le16(0x0001);
  58. QCTLMsg->GetVersionReq.QMUXTypes = QMUX_TYPE_ALL;
  59. return sizeof(QMICTL_GET_VERSION_REQ_MSG);
  60. }
  61. static USHORT CtlGetClientIdReq(PQMICTL_MSG QCTLMsg, void *arg) {
  62. QCTLMsg->GetClientIdReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER;
  63. QCTLMsg->GetClientIdReq.TLVLength = cpu_to_le16(0x0001);
  64. QCTLMsg->GetClientIdReq.QMIType = ((UCHAR *)arg)[0];
  65. return sizeof(QMICTL_GET_CLIENT_ID_REQ_MSG);
  66. }
  67. static USHORT CtlReleaseClientIdReq(PQMICTL_MSG QCTLMsg, void *arg) {
  68. QCTLMsg->ReleaseClientIdReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER;
  69. QCTLMsg->ReleaseClientIdReq.TLVLength = cpu_to_le16(0x0002);
  70. QCTLMsg->ReleaseClientIdReq.QMIType = ((UCHAR *)arg)[0];
  71. QCTLMsg->ReleaseClientIdReq.ClientId = ((UCHAR *)arg)[1] ;
  72. return sizeof(QMICTL_RELEASE_CLIENT_ID_REQ_MSG);
  73. }
  74. static int QmiWwanSendQMI(PQCQMIMSG pRequest) {
  75. struct pollfd pollfds[]= {{cdc_wdm_fd, POLLOUT, 0}};
  76. int ret;
  77. if (cdc_wdm_fd == -1) {
  78. dbg_time("%s cdc_wdm_fd = -1", __func__);
  79. return -ENODEV;
  80. }
  81. if (pRequest->QMIHdr.QMIType == QMUX_TYPE_WDS_IPV6)
  82. pRequest->QMIHdr.QMIType = QMUX_TYPE_WDS;
  83. do {
  84. ret = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), 5000);
  85. } while ((ret < 0) && (errno == EINTR));
  86. if (pollfds[0].revents & POLLOUT) {
  87. ssize_t nwrites = le16_to_cpu(pRequest->QMIHdr.Length) + 1;
  88. ret = write(cdc_wdm_fd, pRequest, nwrites);
  89. if (ret == nwrites) {
  90. ret = 0;
  91. } else {
  92. dbg_time("%s write=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
  93. }
  94. } else {
  95. dbg_time("%s poll=%d, revents = 0x%x, errno: %d (%s)", __func__, ret, pollfds[0].revents, errno, strerror(errno));
  96. }
  97. return ret;
  98. }
  99. static int QmiWwanGetClientID(UCHAR QMIType) {
  100. PQCQMIMSG pResponse;
  101. QmiThreadSendQMI(ComposeQCTLMsg(QMICTL_GET_CLIENT_ID_REQ, CtlGetClientIdReq, &QMIType), &pResponse);
  102. if (pResponse) {
  103. USHORT QMUXResult = cpu_to_le16(pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXResult); // QMI_RESULT_SUCCESS
  104. USHORT QMUXError = cpu_to_le16(pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXError); // QMI_ERR_INVALID_ARG
  105. //UCHAR QMIType = pResponse->CTLMsg.GetClientIdRsp.QMIType;
  106. UCHAR ClientId = pResponse->CTLMsg.GetClientIdRsp.ClientId;
  107. if (!QMUXResult && !QMUXError && (QMIType == pResponse->CTLMsg.GetClientIdRsp.QMIType)) {
  108. switch (QMIType) {
  109. case QMUX_TYPE_WDS: dbg_time("Get clientWDS = %d", ClientId); break;
  110. case QMUX_TYPE_DMS: dbg_time("Get clientDMS = %d", ClientId); break;
  111. case QMUX_TYPE_NAS: dbg_time("Get clientNAS = %d", ClientId); break;
  112. case QMUX_TYPE_QOS: dbg_time("Get clientQOS = %d", ClientId); break;
  113. case QMUX_TYPE_WMS: dbg_time("Get clientWMS = %d", ClientId); break;
  114. case QMUX_TYPE_PDS: dbg_time("Get clientPDS = %d", ClientId); break;
  115. case QMUX_TYPE_UIM: dbg_time("Get clientUIM = %d", ClientId); break;
  116. case QMUX_TYPE_WDS_ADMIN: dbg_time("Get clientWDA = %d", ClientId);
  117. break;
  118. default: break;
  119. }
  120. return ClientId;
  121. }
  122. }
  123. return 0;
  124. }
  125. static int QmiWwanReleaseClientID(QMI_SERVICE_TYPE QMIType, UCHAR ClientId) {
  126. UCHAR argv[] = {QMIType, ClientId};
  127. QmiThreadSendQMI(ComposeQCTLMsg(QMICTL_RELEASE_CLIENT_ID_REQ, CtlReleaseClientIdReq, argv), NULL);
  128. return 0;
  129. }
  130. static int QmiWwanInit(PROFILE_T *profile) {
  131. unsigned i;
  132. int ret;
  133. PQCQMIMSG pResponse;
  134. if (profile->qmap_mode == 0 || profile->qmap_mode == 1)
  135. {
  136. for (i = 0; i < 10; i++) {
  137. ret = QmiThreadSendQMITimeout(ComposeQCTLMsg(QMICTL_SYNC_REQ, NULL, NULL), NULL, 1 * 1000);
  138. if (!ret)
  139. break;
  140. sleep(1);
  141. }
  142. if (ret)
  143. return ret;
  144. }
  145. QmiThreadSendQMI(ComposeQCTLMsg(QMICTL_GET_VERSION_REQ, CtlGetVersionReq, NULL), &pResponse);
  146. if (profile->qmap_mode) {
  147. if (pResponse) {
  148. if (pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXResult == 0 && pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXError == 0) {
  149. uint8_t NumElements = 0;
  150. for (NumElements = 0; NumElements < pResponse->CTLMsg.GetVersionRsp.NumElements; NumElements++) {
  151. #if 0
  152. dbg_time("QMUXType = %02x Version = %d.%d",
  153. pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].QMUXType,
  154. pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MajorVersion,
  155. pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MinorVersion);
  156. #endif
  157. if (pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].QMUXType == QMUX_TYPE_WDS_ADMIN)
  158. profile->qmap_version = (pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MinorVersion > 16);
  159. }
  160. }
  161. }
  162. }
  163. if (pResponse) free(pResponse);
  164. qmiclientId[QMUX_TYPE_WDS] = QmiWwanGetClientID(QMUX_TYPE_WDS);
  165. if (profile->enable_ipv6)
  166. qmiclientId[QMUX_TYPE_WDS_IPV6] = QmiWwanGetClientID(QMUX_TYPE_WDS);
  167. qmiclientId[QMUX_TYPE_DMS] = QmiWwanGetClientID(QMUX_TYPE_DMS);
  168. qmiclientId[QMUX_TYPE_NAS] = QmiWwanGetClientID(QMUX_TYPE_NAS);
  169. qmiclientId[QMUX_TYPE_UIM] = QmiWwanGetClientID(QMUX_TYPE_UIM);
  170. if (profile->qmap_mode == 0 || profile->qmap_mode == 1)
  171. qmiclientId[QMUX_TYPE_WDS_ADMIN] = QmiWwanGetClientID(QMUX_TYPE_WDS_ADMIN);
  172. return 0;
  173. }
  174. static int QmiWwanDeInit(void) {
  175. unsigned int i;
  176. for (i = 0; i < sizeof(qmiclientId)/sizeof(qmiclientId[0]); i++)
  177. {
  178. if (qmiclientId[i] != 0)
  179. {
  180. QmiWwanReleaseClientID(i, qmiclientId[i]);
  181. qmiclientId[i] = 0;
  182. }
  183. }
  184. return 0;
  185. }
  186. #define QUECTEL_QMI_PROXY "quectel-qmi-proxy"
  187. static int qmi_proxy_open(const char *name) {
  188. int sockfd = -1;
  189. int reuse_addr = 1;
  190. struct sockaddr_un sockaddr;
  191. socklen_t alen;
  192. /*Create server socket*/
  193. (sockfd = socket(AF_LOCAL, SOCK_STREAM, 0));
  194. if (sockfd < 0)
  195. return sockfd;
  196. memset(&sockaddr, 0, sizeof(sockaddr));
  197. sockaddr.sun_family = AF_LOCAL;
  198. sockaddr.sun_path[0] = 0;
  199. memcpy(sockaddr.sun_path + 1, name, strlen(name) );
  200. alen = strlen(name) + offsetof(struct sockaddr_un, sun_path) + 1;
  201. if(connect(sockfd, (struct sockaddr *)&sockaddr, alen) < 0) {
  202. close(sockfd);
  203. dbg_time("%s connect %s errno: %d (%s)\n", __func__, name, errno, strerror(errno));
  204. return -1;
  205. }
  206. (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,sizeof(reuse_addr)));
  207. dbg_time("connect to %s sockfd = %d\n", name, sockfd);
  208. return sockfd;
  209. }
  210. static ssize_t qmi_proxy_read (int fd, void *buf, size_t size) {
  211. ssize_t nreads;
  212. PQCQMI_HDR pHdr = (PQCQMI_HDR)buf;
  213. nreads = read(fd, pHdr, sizeof(QCQMI_HDR));
  214. if (nreads == sizeof(QCQMI_HDR)) {
  215. nreads += read(fd, pHdr+1, le16_to_cpu(pHdr->Length) + 1 - sizeof(QCQMI_HDR));
  216. }
  217. return nreads;
  218. }
  219. static void * QmiWwanThread(void *pData) {
  220. PROFILE_T *profile = (PROFILE_T *)pData;
  221. const char *cdc_wdm = (const char *)profile->qmichannel;
  222. int wait_for_request_quit = 0;
  223. char servername[32];
  224. int num = cdc_wdm[strlen(cdc_wdm)-1]-'0';
  225. sprintf(servername, QUECTEL_QMI_PROXY"%d", num);
  226. if (profile->qmap_mode == 0 || profile->qmap_mode == 1)
  227. cdc_wdm_fd = open(cdc_wdm, O_RDWR | O_NONBLOCK | O_NOCTTY);
  228. else
  229. cdc_wdm_fd = qmi_proxy_open(servername);
  230. if (cdc_wdm_fd == -1) {
  231. dbg_time("%s Failed to open %s, errno: %d (%s)", __func__, cdc_wdm, errno, strerror(errno));
  232. qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
  233. pthread_exit(NULL);
  234. return NULL;
  235. }
  236. fcntl(cdc_wdm_fd, F_SETFL, fcntl(cdc_wdm_fd,F_GETFL) | O_NONBLOCK);
  237. fcntl(cdc_wdm_fd, F_SETFD, FD_CLOEXEC);
  238. dbg_time("cdc_wdm_fd = %d", cdc_wdm_fd);
  239. qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED);
  240. while (1) {
  241. struct pollfd pollfds[] = {{qmidevice_control_fd[1], POLLIN, 0}, {cdc_wdm_fd, POLLIN, 0}};
  242. int ne, ret, nevents = sizeof(pollfds)/sizeof(pollfds[0]);
  243. do {
  244. ret = poll(pollfds, nevents, wait_for_request_quit ? 1000 : -1);
  245. } while ((ret < 0) && (errno == EINTR));
  246. if (ret == 0 && wait_for_request_quit) {
  247. QmiThreadRecvQMI(NULL);
  248. continue;
  249. }
  250. if (ret <= 0) {
  251. dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
  252. break;
  253. }
  254. for (ne = 0; ne < nevents; ne++) {
  255. int fd = pollfds[ne].fd;
  256. short revents = pollfds[ne].revents;
  257. //dbg_time("{%d, %x, %x}", pollfds[ne].fd, pollfds[ne].events, pollfds[ne].revents);
  258. if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
  259. dbg_time("%s poll err/hup/inval", __func__);
  260. dbg_time("poll fd = %d, events = 0x%04x", fd, revents);
  261. if (fd == cdc_wdm_fd) {
  262. } else {
  263. }
  264. if (revents & (POLLHUP | POLLNVAL)) //EC20 bug, Can get POLLERR
  265. goto __QmiWwanThread_quit;
  266. }
  267. if ((revents & POLLIN) == 0)
  268. continue;
  269. if (fd == qmidevice_control_fd[1]) {
  270. int triger_event;
  271. if (read(fd, &triger_event, sizeof(triger_event)) == sizeof(triger_event)) {
  272. //DBG("triger_event = 0x%x", triger_event);
  273. switch (triger_event) {
  274. case RIL_REQUEST_QUIT:
  275. goto __QmiWwanThread_quit;
  276. break;
  277. case SIG_EVENT_STOP:
  278. wait_for_request_quit = 1;
  279. break;
  280. default:
  281. break;
  282. }
  283. }
  284. }
  285. if (fd == cdc_wdm_fd) {
  286. ssize_t nreads;
  287. static UCHAR QMIBuf[4096];
  288. PQCQMIMSG pResponse = (PQCQMIMSG)QMIBuf;
  289. if (profile->qmap_mode == 0 || profile->qmap_mode == 1)
  290. nreads = read(fd, QMIBuf, sizeof(QMIBuf));
  291. else
  292. nreads = qmi_proxy_read(fd, QMIBuf, sizeof(QMIBuf));
  293. //dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads, errno, strerror(errno));
  294. if (nreads <= 0) {
  295. dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads, errno, strerror(errno));
  296. break;
  297. }
  298. if (nreads != (le16_to_cpu(pResponse->QMIHdr.Length) + 1)) {
  299. dbg_time("%s nreads=%d, pQCQMI->QMIHdr.Length = %d", __func__, (int)nreads, le16_to_cpu(pResponse->QMIHdr.Length));
  300. continue;
  301. }
  302. QmiThreadRecvQMI(pResponse);
  303. }
  304. }
  305. }
  306. __QmiWwanThread_quit:
  307. if (cdc_wdm_fd != -1) { close(cdc_wdm_fd); cdc_wdm_fd = -1; }
  308. qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
  309. QmiThreadRecvQMI(NULL); //main thread may pending on QmiThreadSendQMI()
  310. dbg_time("%s exit", __func__);
  311. pthread_exit(NULL);
  312. return NULL;
  313. }
  314. #else
  315. static int QmiWwanSendQMI(PQCQMIMSG pRequest) {return -1;}
  316. static int QmiWwanInit(PROFILE_T *profile) {return -1;}
  317. static int QmiWwanDeInit(void) {return -1;}
  318. static void * QmiWwanThread(void *pData) {dbg_time("please set CONFIG_QMIWWAN"); return NULL;}
  319. #endif
  320. const struct qmi_device_ops qmiwwan_qmidev_ops = {
  321. .init = QmiWwanInit,
  322. .deinit = QmiWwanDeInit,
  323. .send = QmiWwanSendQMI,
  324. .read = QmiWwanThread,
  325. };