#include #include #include #include #include "QMIThread.h" #ifdef CONFIG_GOBINET // IOCTL to generate a client ID for this service type #define IOCTL_QMI_GET_SERVICE_FILE 0x8BE0 + 1 // IOCTL to get the VIDPID of the device #define IOCTL_QMI_GET_DEVICE_VIDPID 0x8BE0 + 2 // IOCTL to get the MEID of the device #define IOCTL_QMI_GET_DEVICE_MEID 0x8BE0 + 3 static int GobiNetSendQMI(PQCQMIMSG pRequest) { int ret, fd; static int send_count = 0; fd = qmiclientId[pRequest->QMIHdr.QMIType]; if (fd <= 0) { dbg_time("%s QMIType: %d has no clientID", __func__, pRequest->QMIHdr.QMIType); return -ENODEV; } // Always ready to write re_write: if (1 == 1) { ssize_t nwrites = le16_to_cpu(pRequest->QMIHdr.Length) + 1 - sizeof(QCQMI_HDR); ret = write(fd, &pRequest->MUXMsg, nwrites); if (ret == nwrites) { ret = 0; send_count = 0; } else { send_count++; dbg_time("%s write=%d, errno: %d (%s) send_count %d", __func__, ret, errno, strerror(errno), send_count); if (send_count < 3) { sleep(1); goto re_write; } } } else { dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno)); } return ret; } static int GobiNetGetClientID(const char *qcqmi, UCHAR QMIType) { int ClientId; ClientId = open(qcqmi, O_RDWR | O_NONBLOCK | O_NOCTTY); if (ClientId == -1) { dbg_time("failed to open %s, errno: %d (%s)", qcqmi, errno, strerror(errno)); return -1; } if (ioctl(ClientId, IOCTL_QMI_GET_SERVICE_FILE, QMIType) != 0) { dbg_time("failed to get ClientID for 0x%02x errno: %d (%s)", QMIType, errno, strerror(errno)); close(ClientId); ClientId = 0; } dbg_time("%s: QMIType = %d clientid %d", __func__, QMIType, ClientId); switch (QMIType) { case QMUX_TYPE_WDS: dbg_time("Get clientWDS = %d", ClientId); break; case QMUX_TYPE_DMS: dbg_time("Get clientDMS = %d", ClientId); break; case QMUX_TYPE_NAS: dbg_time("Get clientNAS = %d", ClientId); break; case QMUX_TYPE_QOS: dbg_time("Get clientQOS = %d", ClientId); break; case QMUX_TYPE_WMS: dbg_time("Get clientWMS = %d", ClientId); break; case QMUX_TYPE_PDS: dbg_time("Get clientPDS = %d", ClientId); break; case QMUX_TYPE_UIM: dbg_time("Get clientUIM = %d", ClientId); break; case QMUX_TYPE_WDS_ADMIN: dbg_time("Get clientWDA = %d", ClientId); break; default: break; } return ClientId; } static int GobiNetDeInit(void) { unsigned int i; for (i = 0; i < sizeof(qmiclientId) / sizeof(qmiclientId[0]); i++) { if (qmiclientId[i] != 0) { close(qmiclientId[i]); qmiclientId[i] = 0; } } return 0; } static void *GobiNetThread(void *pData) { PROFILE_T *profile = (PROFILE_T *)pData; const char *qcqmi = (const char *)profile->qmichannel; int wait_for_request_quit = 0; dbg_time("%s %d", __func__, __LINE__); if (profile->ipv4_flag) qmiclientId[QMUX_TYPE_WDS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS); if (profile->ipv6_flag) qmiclientId[QMUX_TYPE_WDS_IPV6] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS); qmiclientId[QMUX_TYPE_DMS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_DMS); qmiclientId[QMUX_TYPE_NAS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_NAS); qmiclientId[QMUX_TYPE_UIM] = GobiNetGetClientID(qcqmi, QMUX_TYPE_UIM); // qmiclientId[QMUX_TYPE_WDS_ADMIN] = // GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS_ADMIN); //if ((qmiclientId[QMUX_TYPE_WDS] == 0) && (qmiclientId[QMUX_TYPE_WDS_IPV6] == 0)) /*|| (clientWDA == -1)*/ { if ((qmiclientId[QMUX_TYPE_DMS] == 0) || (qmiclientId[QMUX_TYPE_NAS] == 0) || (qmiclientId[QMUX_TYPE_UIM] == 0) || (profile->ipv4_flag ? ((qmiclientId[QMUX_TYPE_WDS] == 0) ? 1 : 0):0)|| (profile->ipv6_flag ? ((qmiclientId[QMUX_TYPE_WDS_IPV6] == 0) ? 1 : 0):0)) { GobiNetDeInit(); dbg_time("%s Failed to open %s, errno: %d (%s)", __func__, qcqmi, errno,strerror(errno)); qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED); pthread_exit(NULL); return NULL; } qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED); while (1) { struct pollfd pollfds[16] = {{qmidevice_control_fd[1], POLLIN, 0}}; int ne, ret, nevents = 1; unsigned int i; for (i = 0; i < sizeof(qmiclientId) / sizeof(qmiclientId[0]); i++) { if (qmiclientId[i] != 0) { pollfds[nevents].fd = qmiclientId[i]; pollfds[nevents].events = POLLIN; pollfds[nevents].revents = 0; nevents++; } } do { ret = poll(pollfds, nevents, wait_for_request_quit ? 1000 : -1); } while ((ret < 0) && (errno == EINTR)); if (ret == 0 && wait_for_request_quit) { QmiThreadRecvQMI( NULL); // main thread may pending on QmiThreadSendQMI() continue; } if (ret <= 0) { dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno)); break; } for (ne = 0; ne < nevents; ne++) { int fd = pollfds[ne].fd; short revents = pollfds[ne].revents; if (revents & (POLLERR | POLLHUP | POLLNVAL)) { dbg_time("%s poll err/hup/inval", __func__); dbg_time("epoll fd = %d, events = 0x%04x", fd, revents); if (fd == qmidevice_control_fd[1]) { } else { } if (revents & (POLLERR | POLLHUP | POLLNVAL)) goto __GobiNetThread_quit; } if ((revents & POLLIN) == 0) continue; if (fd == qmidevice_control_fd[1]) { int triger_event; if (read(fd, &triger_event, sizeof(triger_event)) == sizeof(triger_event)) { // DBG("triger_event = 0x%x", triger_event); switch (triger_event) { case RIL_REQUEST_QUIT: goto __GobiNetThread_quit; break; case SIGTERM: wait_for_request_quit = 1; break; default: break; } } continue; } { ssize_t nreads; static UCHAR QMIBuf[4096]; PQCQMIMSG pResponse = (PQCQMIMSG)QMIBuf; nreads = read(fd, &pResponse->MUXMsg, sizeof(QMIBuf) - sizeof(QCQMI_HDR)); if (nreads <= 0) { dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads, errno, strerror(errno)); break; } for (i = 0; i < sizeof(qmiclientId) / sizeof(qmiclientId[0]); i++) { if (qmiclientId[i] == fd) { pResponse->QMIHdr.QMIType = i; } } pResponse->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI; pResponse->QMIHdr.Length = cpu_to_le16(nreads + sizeof(QCQMI_HDR) - 1); pResponse->QMIHdr.CtlFlags = 0x00; pResponse->QMIHdr.ClientId = fd & 0xFF; QmiThreadRecvQMI(pResponse); } } } __GobiNetThread_quit: GobiNetDeInit(); qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED); QmiThreadRecvQMI(NULL); // main thread may pending on QmiThreadSendQMI() dbg_time("%s exit", __func__); pthread_exit(NULL); return NULL; } #else static int GobiNetSendQMI(PQCQMIMSG pRequest) { return -1; } static void *GobiNetThread(void *pData) { dbg_time("please set CONFIG_GOBINET"); return NULL; } #endif const struct qmi_device_ops gobi_qmidev_ops = { .deinit = GobiNetDeInit, .send = GobiNetSendQMI, .read = GobiNetThread, };