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.

241 lines
8.5 KiB

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