From b550deb1440790e0614a0b03d426dfce0e74ef24 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 4 Jun 2026 16:32:58 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=97=BD=20Sync=202026-06-04=2016:32:58?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fibocom-dial/Makefile | 22 + fibocom-dial/patches/100-rename-binary.patch | 42 + fibocom-dial/src/GobiNetCM.c | 275 ++ fibocom-dial/src/MPQCTL.h | 363 ++ fibocom-dial/src/MPQMI.h | 287 ++ fibocom-dial/src/MPQMUX.c | 437 +++ fibocom-dial/src/MPQMUX.h | 3671 ++++++++++++++++++ fibocom-dial/src/Makefile | 42 + fibocom-dial/src/QMIThread.c | 2551 ++++++++++++ fibocom-dial/src/QMIThread.h | 260 ++ fibocom-dial/src/QmiWwanCM.c | 426 ++ fibocom-dial/src/default.script | 63 + fibocom-dial/src/fibo_qmimsg_server.c | 1331 +++++++ fibocom-dial/src/libmnl/README | 28 + fibocom-dial/src/libmnl/attr.c | 722 ++++ fibocom-dial/src/libmnl/callback.c | 167 + fibocom-dial/src/libmnl/dhcp/dhcp.h | 5 + fibocom-dial/src/libmnl/dhcp/dhcpclient.c | 515 +++ fibocom-dial/src/libmnl/dhcp/dhcpmsg.c | 100 + fibocom-dial/src/libmnl/dhcp/dhcpmsg.h | 106 + fibocom-dial/src/libmnl/dhcp/packet.c | 247 ++ fibocom-dial/src/libmnl/dhcp/packet.h | 25 + fibocom-dial/src/libmnl/ifutils.c | 748 ++++ fibocom-dial/src/libmnl/ifutils.h | 53 + fibocom-dial/src/libmnl/libmnl.h | 202 + fibocom-dial/src/libmnl/nlmsg.c | 556 +++ fibocom-dial/src/libmnl/socket.c | 351 ++ fibocom-dial/src/main.c | 1770 +++++++++ fibocom-dial/src/multi-pdn-manager.c | 1019 +++++ fibocom-dial/src/multi-pdn.ini | 67 + fibocom-dial/src/qmap_bridge_mode.c | 438 +++ fibocom-dial/src/query_pcie_mode.c | 160 + fibocom-dial/src/query_pcie_mode.h | 17 + fibocom-dial/src/udhcpc.c | 289 ++ fibocom-dial/src/udhcpc_netlink.c | 421 ++ fibocom-dial/src/util.c | 142 + fibocom-dial/src/util.h | 53 + fibocom_QMI_WWAN/Makefile | 47 - fibocom_QMI_WWAN/src/Makefile | 38 - fibocom_QMI_WWAN/src/qmi_wwan_f.c | 2534 ------------ quectel_MHI/Makefile | 47 - quectel_MHI/src/Makefile | 34 - quectel_MHI/src/README | 36 - quectel_MHI/src/ReleaseNote.txt | 132 - quectel_MHI/src/controllers/Kconfig | 13 - quectel_MHI/src/controllers/Makefile | 1 - quectel_MHI/src/controllers/mhi_arch_qti.c | 275 -- quectel_MHI/src/controllers/mhi_qcom.c | 715 ---- quectel_MHI/src/controllers/mhi_qcom.h | 92 - quectel_MHI/src/controllers/mhi_qti.c | 1309 ------- quectel_MHI/src/controllers/mhi_qti.h | 44 - quectel_MHI/src/core/Makefile | 1 - quectel_MHI/src/core/mhi.h | 908 ----- quectel_MHI/src/core/mhi_boot.c | 860 ---- quectel_MHI/src/core/mhi_dtr.c | 274 -- quectel_MHI/src/core/mhi_init.c | 2776 ------------- quectel_MHI/src/core/mhi_internal.h | 1190 ------ quectel_MHI/src/core/mhi_main.c | 2722 ------------- quectel_MHI/src/core/mhi_pm.c | 1255 ------ quectel_MHI/src/core/mhi_sdx20.h | 362 -- quectel_MHI/src/core/sdx20_mhi.h | 426 -- quectel_MHI/src/devices/Kconfig | 33 - quectel_MHI/src/devices/Makefile | 3 - quectel_MHI/src/devices/mhi_netdev.c | 1063 ----- quectel_MHI/src/devices/mhi_netdev_quectel.c | 3421 ---------------- quectel_MHI/src/devices/mhi_satellite.c | 1153 ------ quectel_MHI/src/devices/mhi_uci.c | 981 ----- quectel_MHI/src/log/AT_OVER_PCIE.txt | 31 - quectel_MHI/src/log/MBIM_OVER_PCIE.txt | 145 - quectel_MHI/src/log/QMI_OVER_PCIE.txt | 134 - quectel_MHI/src/log/QXDM_OVER_PCIE.txt | 14 - quectel_QMI_WWAN/Makefile | 47 - quectel_QMI_WWAN/src/Makefile | 36 - quectel_QMI_WWAN/src/ReleaseNote.txt | 146 - quectel_QMI_WWAN/src/qmi_wwan_q.c | 2626 ------------- quectel_QMI_WWAN/src/rmnet_nss.c | 424 -- 76 files changed, 17971 insertions(+), 26348 deletions(-) create mode 100644 fibocom-dial/Makefile create mode 100644 fibocom-dial/patches/100-rename-binary.patch create mode 100644 fibocom-dial/src/GobiNetCM.c create mode 100644 fibocom-dial/src/MPQCTL.h create mode 100644 fibocom-dial/src/MPQMI.h create mode 100644 fibocom-dial/src/MPQMUX.c create mode 100644 fibocom-dial/src/MPQMUX.h create mode 100644 fibocom-dial/src/Makefile create mode 100644 fibocom-dial/src/QMIThread.c create mode 100644 fibocom-dial/src/QMIThread.h create mode 100644 fibocom-dial/src/QmiWwanCM.c create mode 100644 fibocom-dial/src/default.script create mode 100644 fibocom-dial/src/fibo_qmimsg_server.c create mode 100644 fibocom-dial/src/libmnl/README create mode 100644 fibocom-dial/src/libmnl/attr.c create mode 100644 fibocom-dial/src/libmnl/callback.c create mode 100644 fibocom-dial/src/libmnl/dhcp/dhcp.h create mode 100644 fibocom-dial/src/libmnl/dhcp/dhcpclient.c create mode 100644 fibocom-dial/src/libmnl/dhcp/dhcpmsg.c create mode 100644 fibocom-dial/src/libmnl/dhcp/dhcpmsg.h create mode 100644 fibocom-dial/src/libmnl/dhcp/packet.c create mode 100644 fibocom-dial/src/libmnl/dhcp/packet.h create mode 100644 fibocom-dial/src/libmnl/ifutils.c create mode 100644 fibocom-dial/src/libmnl/ifutils.h create mode 100644 fibocom-dial/src/libmnl/libmnl.h create mode 100644 fibocom-dial/src/libmnl/nlmsg.c create mode 100644 fibocom-dial/src/libmnl/socket.c create mode 100644 fibocom-dial/src/main.c create mode 100644 fibocom-dial/src/multi-pdn-manager.c create mode 100644 fibocom-dial/src/multi-pdn.ini create mode 100644 fibocom-dial/src/qmap_bridge_mode.c create mode 100644 fibocom-dial/src/query_pcie_mode.c create mode 100644 fibocom-dial/src/query_pcie_mode.h create mode 100644 fibocom-dial/src/udhcpc.c create mode 100644 fibocom-dial/src/udhcpc_netlink.c create mode 100644 fibocom-dial/src/util.c create mode 100644 fibocom-dial/src/util.h delete mode 100644 fibocom_QMI_WWAN/Makefile delete mode 100644 fibocom_QMI_WWAN/src/Makefile delete mode 100644 fibocom_QMI_WWAN/src/qmi_wwan_f.c delete mode 100644 quectel_MHI/Makefile delete mode 100644 quectel_MHI/src/Makefile delete mode 100644 quectel_MHI/src/README delete mode 100644 quectel_MHI/src/ReleaseNote.txt delete mode 100644 quectel_MHI/src/controllers/Kconfig delete mode 100644 quectel_MHI/src/controllers/Makefile delete mode 100644 quectel_MHI/src/controllers/mhi_arch_qti.c delete mode 100644 quectel_MHI/src/controllers/mhi_qcom.c delete mode 100644 quectel_MHI/src/controllers/mhi_qcom.h delete mode 100644 quectel_MHI/src/controllers/mhi_qti.c delete mode 100644 quectel_MHI/src/controllers/mhi_qti.h delete mode 100644 quectel_MHI/src/core/Makefile delete mode 100644 quectel_MHI/src/core/mhi.h delete mode 100644 quectel_MHI/src/core/mhi_boot.c delete mode 100644 quectel_MHI/src/core/mhi_dtr.c delete mode 100644 quectel_MHI/src/core/mhi_init.c delete mode 100644 quectel_MHI/src/core/mhi_internal.h delete mode 100644 quectel_MHI/src/core/mhi_main.c delete mode 100644 quectel_MHI/src/core/mhi_pm.c delete mode 100644 quectel_MHI/src/core/mhi_sdx20.h delete mode 100644 quectel_MHI/src/core/sdx20_mhi.h delete mode 100644 quectel_MHI/src/devices/Kconfig delete mode 100644 quectel_MHI/src/devices/Makefile delete mode 100644 quectel_MHI/src/devices/mhi_netdev.c delete mode 100644 quectel_MHI/src/devices/mhi_netdev_quectel.c delete mode 100644 quectel_MHI/src/devices/mhi_satellite.c delete mode 100644 quectel_MHI/src/devices/mhi_uci.c delete mode 100644 quectel_MHI/src/log/AT_OVER_PCIE.txt delete mode 100644 quectel_MHI/src/log/MBIM_OVER_PCIE.txt delete mode 100644 quectel_MHI/src/log/QMI_OVER_PCIE.txt delete mode 100644 quectel_MHI/src/log/QXDM_OVER_PCIE.txt delete mode 100644 quectel_QMI_WWAN/Makefile delete mode 100644 quectel_QMI_WWAN/src/Makefile delete mode 100644 quectel_QMI_WWAN/src/ReleaseNote.txt delete mode 100644 quectel_QMI_WWAN/src/qmi_wwan_q.c delete mode 100644 quectel_QMI_WWAN/src/rmnet_nss.c diff --git a/fibocom-dial/Makefile b/fibocom-dial/Makefile new file mode 100644 index 000000000..390e643d6 --- /dev/null +++ b/fibocom-dial/Makefile @@ -0,0 +1,22 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=fibocom-dial +PKG_RELEASE:=3 + +include $(INCLUDE_DIR)/package.mk + +define Package/fibocom-dial + SECTION:=net + CATEGORY:=Network + SUBMENU:=WWAN + TITLE:=Dialing tool for Fibocom WWAN modules +endef + +define Package/fibocom-dial/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/fibocom-dial $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/fibo_qmimsg_server $(1)/usr/bin/fibocom-qmimsg-server + $(INSTALL_BIN) $(PKG_BUILD_DIR)/multi-pdn-manager $(1)/usr/bin/fibocom-multi-pdn-manager +endef + +$(eval $(call BuildPackage,fibocom-dial)) diff --git a/fibocom-dial/patches/100-rename-binary.patch b/fibocom-dial/patches/100-rename-binary.patch new file mode 100644 index 000000000..eef4a5cb6 --- /dev/null +++ b/fibocom-dial/patches/100-rename-binary.patch @@ -0,0 +1,42 @@ +--- a/QmiWwanCM.c ++++ b/QmiWwanCM.c +@@ -8,7 +8,7 @@ typedef unsigned short sa_family_t; + #include "QMIThread.h" + + #ifdef CONFIG_QMIWWAN +-#define FIBO_QMI_PROXY "fibo_qmimsg_server" ++#define FIBO_QMI_PROXY "fibocom-qmimsg-server" + static int cdc_wdm_fd = -1; + + static UCHAR GetQCTLTransactionId(void) +--- a/fibo_qmimsg_server.c ++++ b/fibo_qmimsg_server.c +@@ -984,7 +984,7 @@ qmi_proxy_init_fail: + } + //2021-06-03 willa.liu@fibocom.com changed begin + static void qmi_start_server(void) { +- qmi_proxy_server_fd = create_local_server("fibo_qmimsg_server"); ++ qmi_proxy_server_fd = create_local_server("fibocom-qmimsg-server"); + printf("%s: qmi_proxy_server_fd = %d\n", __func__, qmi_proxy_server_fd); + if (qmi_proxy_server_fd == -1) { + dprintf("%s Failed to create %s, errno: %d (%s)\n", __func__, "fibocom-qmi-proxy", errno, strerror(errno)); +--- a/multi-pdn-manager.c ++++ b/multi-pdn-manager.c +@@ -812,7 +812,7 @@ int main(int argc, char *argv[]) + { + dbg_time("The Fibocom multi-pdn-manager is already running! So killall multi-pdn-manager!"); + system("killall -9 fibocom-dial"); +- system("killall -9 multi-pdn-manager"); ++ system("killall -9 fibocom-multi-pdn-manager"); + } + } + else +@@ -846,7 +846,7 @@ int main(int argc, char *argv[]) + if (is_pcie_dial()) + { + dbg_time("start qmi proxy server"); +- //system("./fibo_qmimsg_server -d /dev/mhi_QMI0 &"); ++ //system("./fibocom-qmimsg-server -d /dev/mhi_QMI0 &"); + } + while (1) + { diff --git a/fibocom-dial/src/GobiNetCM.c b/fibocom-dial/src/GobiNetCM.c new file mode 100644 index 000000000..d1f646420 --- /dev/null +++ b/fibocom-dial/src/GobiNetCM.c @@ -0,0 +1,275 @@ +#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, +}; diff --git a/fibocom-dial/src/MPQCTL.h b/fibocom-dial/src/MPQCTL.h new file mode 100644 index 000000000..d9b426820 --- /dev/null +++ b/fibocom-dial/src/MPQCTL.h @@ -0,0 +1,363 @@ +/*=========================================================================== + + M P Q C T L. H +DESCRIPTION: + + This module contains QMI QCTL module. + +INITIALIZATION AND SEQUENCING REQUIREMENTS: + +Copyright (C) 2011 by Qualcomm Technologies, Incorporated. All Rights Reserved. +===========================================================================*/ + +#ifndef MPQCTL_H +#define MPQCTL_H + +#include "MPQMI.h" + +#pragma pack(push, 1) + +// ================= QMICTL ================== + +// QMICTL Control Flags +#define QMICTL_CTL_FLAG_CMD 0x00 +#define QMICTL_CTL_FLAG_RSP 0x01 +#define QMICTL_CTL_FLAG_IND 0x02 + +#if 0 +typedef struct _QMICTL_TRANSACTION_ITEM +{ + LIST_ENTRY List; + UCHAR TransactionId; // QMICTL transaction id + PVOID Context; // Adapter or IocDev + PIRP Irp; +} QMICTL_TRANSACTION_ITEM, *PQMICTL_TRANSACTION_ITEM; +#endif + +typedef struct _QCQMICTL_MSG_HDR { + UCHAR CtlFlags; // 00-cmd, 01-rsp, 10-ind + UCHAR TransactionId; + USHORT QMICTLType; + USHORT Length; +} __attribute__((packed)) QCQMICTL_MSG_HDR, *PQCQMICTL_MSG_HDR; + +#define QCQMICTL_MSG_HDR_SIZE sizeof(QCQMICTL_MSG_HDR) + +typedef struct _QCQMICTL_MSG_HDR_RESP { + UCHAR CtlFlags; // 00-cmd, 01-rsp, 10-ind + UCHAR TransactionId; + USHORT QMICTLType; + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} __attribute__((packed)) QCQMICTL_MSG_HDR_RESP, *PQCQMICTL_MSG_HDR_RESP; + +typedef struct _QCQMICTL_MSG { + UCHAR CtlFlags; // 00-cmd, 01-rsp, 10-ind + UCHAR TransactionId; + USHORT QMICTLType; + USHORT Length; + UCHAR Payload; +} __attribute__((packed)) QCQMICTL_MSG, *PQCQMICTL_MSG; + +// TLV Header +typedef struct _QCQMICTL_TLV_HDR { + UCHAR TLVType; + USHORT TLVLength; +} __attribute__((packed)) QCQMICTL_TLV_HDR, *PQCQMICTL_TLV_HDR; + +#define QCQMICTL_TLV_HDR_SIZE sizeof(QCQMICTL_TLV_HDR) + +// QMICTL Type +#define QMICTL_SET_INSTANCE_ID_REQ 0x0020 +#define QMICTL_SET_INSTANCE_ID_RESP 0x0020 +#define QMICTL_GET_VERSION_REQ 0x0021 +#define QMICTL_GET_VERSION_RESP 0x0021 +#define QMICTL_GET_CLIENT_ID_REQ 0x0022 +#define QMICTL_GET_CLIENT_ID_RESP 0x0022 +#define QMICTL_RELEASE_CLIENT_ID_REQ 0x0023 +#define QMICTL_RELEASE_CLIENT_ID_RESP 0x0023 +#define QMICTL_REVOKE_CLIENT_ID_IND 0x0024 +#define QMICTL_INVALID_CLIENT_ID_IND 0x0025 +#define QMICTL_SET_DATA_FORMAT_REQ 0x0026 +#define QMICTL_SET_DATA_FORMAT_RESP 0x0026 +#define QMICTL_SYNC_REQ 0x0027 +#define QMICTL_SYNC_RESP 0x0027 +#define QMICTL_SYNC_IND 0x0027 + +#define QMICTL_FLAG_REQUEST 0x00 +#define QMICTL_FLAG_RESPONSE 0x01 +#define QMICTL_FLAG_INDICATION 0x02 + +// QMICTL Message Definitions + +typedef struct _QMICTL_SET_INSTANCE_ID_REQ_MSG { + UCHAR CtlFlags; // QMICTL_FLAG_REQUEST + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_SET_INSTANCE_ID_REQ + USHORT Length; // 4 + UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLVLength; // 1 + UCHAR Value; // Host-unique QMI instance for this device driver +} __attribute__((packed)) QMICTL_SET_INSTANCE_ID_REQ_MSG, + *PQMICTL_SET_INSTANCE_ID_REQ_MSG; + +typedef struct _QMICTL_SET_INSTANCE_ID_RESP_MSG { + UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_SET_INSTANCE_ID_RESP + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE + USHORT TLVLength; // 0x0004 + USHORT QMIResult; + USHORT QMIError; + UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLV2Length; // 0x0002 + USHORT QMI_ID; // Upper byte is assigned by MSM, + // lower assigned by host +} __attribute__((packed)) QMICTL_SET_INSTANCE_ID_RESP_MSG, + *PQMICTL_SET_INSTANCE_ID_RESP_MSG; + +typedef struct _QMICTL_GET_VERSION_REQ_MSG { + UCHAR CtlFlags; // QMICTL_FLAG_REQUEST + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_GET_VERSION_REQ + USHORT Length; // 0 + UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLVLength; // var + UCHAR QMUXTypes; // List of one byte QMUX_TYPE values + // 0xFF returns a list of versions for all + // QMUX_TYPEs implemented on the device +} __attribute__((packed)) QMICTL_GET_VERSION_REQ_MSG, + *PQMICTL_GET_VERSION_REQ_MSG; + +typedef struct _QMUX_TYPE_VERSION_STRUCT { + UCHAR QMUXType; + USHORT MajorVersion; + USHORT MinorVersion; +} __attribute__((packed)) QMUX_TYPE_VERSION_STRUCT, *PQMUX_TYPE_VERSION_STRUCT; + +typedef struct _ADDENDUM_VERSION_PREAMBLE { + UCHAR LabelLength; + UCHAR Label; +} __attribute__((packed)) ADDENDUM_VERSION_PREAMBLE, + *PADDENDUM_VERSION_PREAMBLE; + +#define QMICTL_GET_VERSION_RSP_TLV_TYPE_VERSION 0x01 +#define QMICTL_GET_VERSION_RSP_TLV_TYPE_ADD_VERSION 0x10 + +typedef struct _QMICTL_GET_VERSION_RESP_MSG { + UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_GET_VERSION_RESP + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE + USHORT TLVLength; // 0x0004 + USHORT QMIResult; + USHORT QMIError; + UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLV2Length; // var + UCHAR NumElements; // Num of QMUX_TYPE_VERSION_STRUCT + QMUX_TYPE_VERSION_STRUCT TypeVersion[0]; +} __attribute__((packed)) QMICTL_GET_VERSION_RESP_MSG, + *PQMICTL_GET_VERSION_RESP_MSG; + +typedef struct _QMICTL_GET_CLIENT_ID_REQ_MSG { + UCHAR CtlFlags; // QMICTL_FLAG_REQUEST + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_GET_CLIENT_ID_REQ + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLVLength; // 1 + UCHAR QMIType; // QMUX type +} __attribute__((packed)) QMICTL_GET_CLIENT_ID_REQ_MSG, + *PQMICTL_GET_CLIENT_ID_REQ_MSG; + +typedef struct _QMICTL_GET_CLIENT_ID_RESP_MSG { + UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_GET_CLIENT_ID_RESP + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE + USHORT TLVLength; // 0x0004 + USHORT QMIResult; // result code + USHORT QMIError; // error code + UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLV2Length; // 2 + UCHAR QMIType; + UCHAR ClientId; +} __attribute__((packed)) QMICTL_GET_CLIENT_ID_RESP_MSG, + *PQMICTL_GET_CLIENT_ID_RESP_MSG; + +typedef struct _QMICTL_RELEASE_CLIENT_ID_REQ_MSG { + UCHAR CtlFlags; // QMICTL_FLAG_REQUEST + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_RELEASE_CLIENT_ID_REQ + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLVLength; // 0x0002 + UCHAR QMIType; + UCHAR ClientId; +} __attribute__((packed)) QMICTL_RELEASE_CLIENT_ID_REQ_MSG, + *PQMICTL_RELEASE_CLIENT_ID_REQ_MSG; + +typedef struct _QMICTL_RELEASE_CLIENT_ID_RESP_MSG { + UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_RELEASE_CLIENT_ID_RESP + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE + USHORT TLVLength; // 0x0004 + USHORT QMIResult; // result code + USHORT QMIError; // error code + UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLV2Length; // 2 + UCHAR QMIType; + UCHAR ClientId; +} __attribute__((packed)) QMICTL_RELEASE_CLIENT_ID_RESP_MSG, + *PQMICTL_RELEASE_CLIENT_ID_RESP_MSG; + +typedef struct _QMICTL_REVOKE_CLIENT_ID_IND_MSG { + UCHAR CtlFlags; // QMICTL_FLAG_INDICATION + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLVLength; // 0x0002 + UCHAR QMIType; + UCHAR ClientId; +} __attribute__((packed)) QMICTL_REVOKE_CLIENT_ID_IND_MSG, + *PQMICTL_REVOKE_CLIENT_ID_IND_MSG; + +typedef struct _QMICTL_INVALID_CLIENT_ID_IND_MSG { + UCHAR CtlFlags; // QMICTL_FLAG_INDICATION + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLVLength; // 0x0002 + UCHAR QMIType; + UCHAR ClientId; +} __attribute__((packed)) QMICTL_INVALID_CLIENT_ID_IND_MSG, + *PQMICTL_INVALID_CLIENT_ID_IND_MSG; + +typedef struct _QMICTL_SET_DATA_FORMAT_REQ_MSG { + UCHAR CtlFlags; // QMICTL_FLAG_REQUEST + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_SET_DATA_FORMAT_REQ + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLVLength; // 1 + UCHAR DataFormat; // 0-default; 1-QoS hdr present +} __attribute__((packed)) QMICTL_SET_DATA_FORMAT_REQ_MSG, + *PQMICTL_SET_DATA_FORMAT_REQ_MSG; + +#ifdef QC_IP_MODE +#define SET_DATA_FORMAT_TLV_TYPE_LINK_PROTO 0x10 +#define SET_DATA_FORMAT_LINK_PROTO_ETH 0x0001 +#define SET_DATA_FORMAT_LINK_PROTO_IP 0x0002 +typedef struct _QMICTL_SET_DATA_FORMAT_TLV_LINK_PROT { + UCHAR TLVType; // Link-Layer Protocol + USHORT TLVLength; // 2 + USHORT LinkProt; // 0x1: ETH; 0x2: IP +} QMICTL_SET_DATA_FORMAT_TLV_LINK_PROT, *PQMICTL_SET_DATA_FORMAT_TLV_LINK_PROT; + +#ifdef QCMP_UL_TLP +#define SET_DATA_FORMAT_TLV_TYPE_UL_TLP 0x11 +typedef struct _QMICTL_SET_DATA_FORMAT_TLV_UL_TLP { + UCHAR TLVType; // 0x11, Uplink TLP Setting + USHORT TLVLength; // 1 + UCHAR UlTlpSetting; // 0x0: Disable; 0x01: Enable +} QMICTL_SET_DATA_FORMAT_TLV_UL_TLP, *PQMICTL_SET_DATA_FORMAT_TLV_UL_TLP; +#endif // QCMP_UL_TLP + +#ifdef QCMP_DL_TLP +#define SET_DATA_FORMAT_TLV_TYPE_DL_TLP 0x13 +typedef struct _QMICTL_SET_DATA_FORMAT_TLV_DL_TLP { + UCHAR TLVType; // 0x11, Uplink TLP Setting + USHORT TLVLength; // 1 + UCHAR DlTlpSetting; // 0x0: Disable; 0x01: Enable +} QMICTL_SET_DATA_FORMAT_TLV_DL_TLP, *PQMICTL_SET_DATA_FORMAT_TLV_DL_TLP; +#endif // QCMP_DL_TLP + +#endif // QC_IP_MODE + +#ifdef MP_QCQOS_ENABLED +#define SET_DATA_FORMAT_TLV_TYPE_QOS_SETTING 0x12 +typedef struct _QMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING { + UCHAR TLVType; // 0x12, QoS setting + USHORT TLVLength; // 1 + UCHAR QosSetting; // 0x0: Disable; 0x01: Enable +} QMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING, + *PQMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING; +#endif // MP_QCQOS_ENABLED + +typedef struct _QMICTL_SET_DATA_FORMAT_RESP_MSG { + UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_SET_DATA_FORMAT_RESP + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE + USHORT TLVLength; // 0x0004 + USHORT QMIResult; // result code + USHORT QMIError; // error code +} __attribute__((packed)) QMICTL_SET_DATA_FORMAT_RESP_MSG, + *PQMICTL_SET_DATA_FORMAT_RESP_MSG; + +typedef struct _QMICTL_SYNC_REQ_MSG { + UCHAR CtlFlags; // QMICTL_FLAG_REQUEST + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_CTL_SYNC_REQ + USHORT Length; // 0 +} __attribute__((packed)) QMICTL_SYNC_REQ_MSG, *PQMICTL_SYNC_REQ_MSG; + +typedef struct _QMICTL_SYNC_RESP_MSG { + UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_CTL_SYNC_RESP + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE + USHORT TLVLength; // 0x0004 + USHORT QMIResult; + USHORT QMIError; +} __attribute__((packed)) QMICTL_SYNC_RESP_MSG, *PQMICTL_SYNC_RESP_MSG; + +typedef struct _QMICTL_SYNC_IND_MSG { + UCHAR CtlFlags; // QMICTL_FLAG_INDICATION + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND + USHORT Length; +} __attribute__((packed)) QMICTL_SYNC_IND_MSG, *PQMICTL_SYNC_IND_MSG; + +typedef struct _QMICTL_MSG { + union { + // Message Header + QCQMICTL_MSG_HDR QMICTLMsgHdr; + QCQMICTL_MSG_HDR_RESP QMICTLMsgHdrRsp; + + // QMICTL Message + QMICTL_SET_INSTANCE_ID_REQ_MSG SetInstanceIdReq; + QMICTL_SET_INSTANCE_ID_RESP_MSG SetInstanceIdRsp; + QMICTL_GET_VERSION_REQ_MSG GetVersionReq; + QMICTL_GET_VERSION_RESP_MSG GetVersionRsp; + QMICTL_GET_CLIENT_ID_REQ_MSG GetClientIdReq; + QMICTL_GET_CLIENT_ID_RESP_MSG GetClientIdRsp; + QMICTL_RELEASE_CLIENT_ID_REQ_MSG ReleaseClientIdReq; + QMICTL_RELEASE_CLIENT_ID_RESP_MSG ReleaseClientIdRsp; + QMICTL_REVOKE_CLIENT_ID_IND_MSG RevokeClientIdInd; + QMICTL_INVALID_CLIENT_ID_IND_MSG InvalidClientIdInd; + QMICTL_SET_DATA_FORMAT_REQ_MSG SetDataFormatReq; + QMICTL_SET_DATA_FORMAT_RESP_MSG SetDataFormatRsp; + QMICTL_SYNC_REQ_MSG SyncReq; + QMICTL_SYNC_RESP_MSG SyncRsp; + QMICTL_SYNC_IND_MSG SyncInd; + }; +} __attribute__((packed)) QMICTL_MSG, *PQMICTL_MSG; + +#endif // MPQCTL_H diff --git a/fibocom-dial/src/MPQMI.h b/fibocom-dial/src/MPQMI.h new file mode 100644 index 000000000..f0063af26 --- /dev/null +++ b/fibocom-dial/src/MPQMI.h @@ -0,0 +1,287 @@ +/*=========================================================================== + + M P Q M I. H +DESCRIPTION: + + This module contains forward references to the QMI module. + +INITIALIZATION AND SEQUENCING REQUIREMENTS: + +Copyright (C) 2011 by Qualcomm Technologies, Incorporated. All Rights Reserved. +===========================================================================*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + $Header: //depot/QMI/win/qcdrivers/ndis/MPQMI.h#3 $ + +when who what, where, why +-------- --- ---------------------------------------------------------- +11/20/04 hg Initial version. +===========================================================================*/ + +#ifndef USBQMI_H +#define USBQMI_H + +typedef char CHAR; +typedef unsigned char UCHAR; +typedef unsigned short USHORT; +typedef int INT; +typedef unsigned int UINT; +typedef long LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef char *PCHAR; +typedef unsigned char *PUCHAR; +typedef int *PINT; +typedef int BOOL; + +#define TRUE (1 == 1) +#define FALSE (1 != 1) + +#define QMICTL_SUPPORTED_MAJOR_VERSION 1 +#define QMICTL_SUPPORTED_MINOR_VERSION 0 + +#pragma pack(push, 1) + +// ========= USB Control Message ========== + +#define USB_CTL_MSG_TYPE_QMI 0x01 + +// USB Control Message +typedef struct _QCUSB_CTL_MSG_HDR { + UCHAR IFType; +} __attribute__((packed)) QCUSB_CTL_MSG_HDR, *PQCUSB_CTL_MSG_HDR; + +#define QCUSB_CTL_MSG_HDR_SIZE sizeof(QCUSB_CTL_MSG_HDR) + +typedef struct _QCUSB_CTL_MSG { + UCHAR IFType; + UCHAR Message; +} __attribute__((packed)) QCUSB_CTL_MSG, *PQCUSB_CTL_MSG; + +#define QCTLV_TYPE_REQUIRED_PARAMETER 0x01 +#define QCTLV_TYPE_RESULT_CODE 0x02 + +// ================= QMI ================== + +// Define QMI Type +typedef enum _QMI_SERVICE_TYPE { + QMUX_TYPE_CTL = 0x00, + QMUX_TYPE_WDS = 0x01, + QMUX_TYPE_DMS = 0x02, + QMUX_TYPE_NAS = 0x03, + QMUX_TYPE_QOS = 0x04, + QMUX_TYPE_WMS = 0x05, + QMUX_TYPE_PDS = 0x06, + QMUX_TYPE_UIM = 0x0B, + QMUX_TYPE_WDS_IPV6 = 0x11, + QMUX_TYPE_WDS_ADMIN = 0x1A, + QMUX_TYPE_MAX = 0xFF, + QMUX_TYPE_ALL = 0xFF +} QMI_SERVICE_TYPE; + +typedef enum _QMI_RESULT_CODE_TYPE { + QMI_RESULT_SUCCESS = 0x0000, + QMI_RESULT_FAILURE = 0x0001 +} QMI_RESULT_CODE_TYPE; + +typedef enum _QMI_ERROR_CODE_TYPE { + QMI_ERR_NONE = 0x0000, + QMI_ERR_MALFORMED_MSG = 0x0001, + QMI_ERR_NO_MEMORY = 0x0002, + QMI_ERR_INTERNAL = 0x0003, + QMI_ERR_ABORTED = 0x0004, + QMI_ERR_CLIENT_IDS_EXHAUSTED = 0x0005, + QMI_ERR_UNABORTABLE_TRANSACTION = 0x0006, + QMI_ERR_INVALID_CLIENT_ID = 0x0007, + QMI_ERR_NO_THRESHOLDS = 0x0008, + QMI_ERR_INVALID_HANDLE = 0x0009, + QMI_ERR_INVALID_PROFILE = 0x000A, + QMI_ERR_INVALID_PINID = 0x000B, + QMI_ERR_INCORRECT_PIN = 0x000C, + QMI_ERR_NO_NETWORK_FOUND = 0x000D, + QMI_ERR_CALL_FAILED = 0x000E, + QMI_ERR_OUT_OF_CALL = 0x000F, + QMI_ERR_NOT_PROVISIONED = 0x0010, + QMI_ERR_MISSING_ARG = 0x0011, + QMI_ERR_ARG_TOO_LONG = 0x0013, + QMI_ERR_INVALID_TX_ID = 0x0016, + QMI_ERR_DEVICE_IN_USE = 0x0017, + QMI_ERR_OP_NETWORK_UNSUPPORTED = 0x0018, + QMI_ERR_OP_DEVICE_UNSUPPORTED = 0x0019, + QMI_ERR_NO_EFFECT = 0x001A, + QMI_ERR_NO_FREE_PROFILE = 0x001B, + QMI_ERR_INVALID_PDP_TYPE = 0x001C, + QMI_ERR_INVALID_TECH_PREF = 0x001D, + QMI_ERR_INVALID_PROFILE_TYPE = 0x001E, + QMI_ERR_INVALID_SERVICE_TYPE = 0x001F, + QMI_ERR_INVALID_REGISTER_ACTION = 0x0020, + QMI_ERR_INVALID_PS_ATTACH_ACTION = 0x0021, + QMI_ERR_AUTHENTICATION_FAILED = 0x0022, + QMI_ERR_PIN_BLOCKED = 0x0023, + QMI_ERR_PIN_PERM_BLOCKED = 0x0024, + QMI_ERR_SIM_NOT_INITIALIZED = 0x0025, + QMI_ERR_MAX_QOS_REQUESTS_IN_USE = 0x0026, + QMI_ERR_INCORRECT_FLOW_FILTER = 0x0027, + QMI_ERR_NETWORK_QOS_UNAWARE = 0x0028, + QMI_ERR_INVALID_QOS_ID = 0x0029, + QMI_ERR_INVALID_ID = 0x0029, + QMI_ERR_REQUESTED_NUM_UNSUPPORTED = 0x002A, + QMI_ERR_INTERFACE_NOT_FOUND = 0x002B, + QMI_ERR_FLOW_SUSPENDED = 0x002C, + QMI_ERR_INVALID_DATA_FORMAT = 0x002D, + QMI_ERR_GENERAL = 0x002E, + QMI_ERR_UNKNOWN = 0x002F, + QMI_ERR_INVALID_ARG = 0x0030, + QMI_ERR_INVALID_INDEX = 0x0031, + QMI_ERR_NO_ENTRY = 0x0032, + QMI_ERR_DEVICE_STORAGE_FULL = 0x0033, + QMI_ERR_DEVICE_NOT_READY = 0x0034, + QMI_ERR_NETWORK_NOT_READY = 0x0035, + QMI_ERR_CAUSE_CODE = 0x0036, + QMI_ERR_MESSAGE_NOT_SENT = 0x0037, + QMI_ERR_MESSAGE_DELIVERY_FAILURE = 0x0038, + QMI_ERR_INVALID_MESSAGE_ID = 0x0039, + QMI_ERR_ENCODING = 0x003A, + QMI_ERR_AUTHENTICATION_LOCK = 0x003B, + QMI_ERR_INVALID_TRANSITION = 0x003C, + QMI_ERR_NOT_A_MCAST_IFACE = 0x003D, + QMI_ERR_MAX_MCAST_REQUESTS_IN_USE = 0x003E, + QMI_ERR_INVALID_MCAST_HANDLE = 0x003F, + QMI_ERR_INVALID_IP_FAMILY_PREF = 0x0040, + QMI_ERR_SESSION_INACTIVE = 0x0041, + QMI_ERR_SESSION_INVALID = 0x0042, + QMI_ERR_SESSION_OWNERSHIP = 0x0043, + QMI_ERR_INSUFFICIENT_RESOURCES = 0x0044, + QMI_ERR_DISABLED = 0x0045, + QMI_ERR_INVALID_OPERATION = 0x0046, + QMI_ERR_INVALID_QMI_CMD = 0x0047, + QMI_ERR_TPDU_TYPE = 0x0048, + QMI_ERR_SMSC_ADDR = 0x0049, + QMI_ERR_INFO_UNAVAILABLE = 0x004A, + QMI_ERR_SEGMENT_TOO_LONG = 0x004B, + QMI_ERR_SEGMENT_ORDER = 0x004C, + QMI_ERR_BUNDLING_NOT_SUPPORTED = 0x004D, + QMI_ERR_OP_PARTIAL_FAILURE = 0x004E, + QMI_ERR_POLICY_MISMATCH = 0x004F, + QMI_ERR_SIM_FILE_NOT_FOUND = 0x0050, + QMI_ERR_EXTENDED_INTERNAL = 0x0051, + QMI_ERR_ACCESS_DENIED = 0x0052, + QMI_ERR_HARDWARE_RESTRICTED = 0x0053, + QMI_ERR_ACK_NOT_SENT = 0x0054, + QMI_ERR_INJECT_TIMEOUT = 0x0055, + QMI_ERR_INCOMPATIBLE_STATE = 0x005A, + QMI_ERR_FDN_RESTRICT = 0x005B, + QMI_ERR_SUPS_FAILURE_CAUSE = 0x005C, + QMI_ERR_NO_RADIO = 0x005D, + QMI_ERR_NOT_SUPPORTED = 0x005E, + QMI_ERR_NO_SUBSCRIPTION = 0x005F, + QMI_ERR_CARD_CALL_CONTROL_FAILED = 0x0060, + QMI_ERR_NETWORK_ABORTED = 0x0061, + QMI_ERR_MSG_BLOCKED = 0x0062, + QMI_ERR_INVALID_SESSION_TYPE = 0x0064, + QMI_ERR_INVALID_PB_TYPE = 0x0065, + QMI_ERR_NO_SIM = 0x0066, + QMI_ERR_PB_NOT_READY = 0x0067, + QMI_ERR_PIN_RESTRICTION = 0x0068, + QMI_ERR_PIN2_RESTRICTION = 0x0069, + QMI_ERR_PUK_RESTRICTION = 0x006A, + QMI_ERR_PUK2_RESTRICTION = 0x006B, + QMI_ERR_PB_ACCESS_RESTRICTED = 0x006C, + QMI_ERR_PB_DELETE_IN_PROG = 0x006D, + QMI_ERR_PB_TEXT_TOO_LONG = 0x006E, + QMI_ERR_PB_NUMBER_TOO_LONG = 0x006F, + QMI_ERR_PB_HIDDEN_KEY_RESTRICTION = 0x0070 +} QMI_ERROR_CODE_TYPE; + +#define QCQMI_CTL_FLAG_SERVICE 0x80 +#define QCQMI_CTL_FLAG_CTL_POINT 0x00 + +typedef struct _QCQMI_HDR { + UCHAR IFType; + USHORT Length; + UCHAR CtlFlags; // reserved + UCHAR QMIType; + UCHAR ClientId; +} __attribute__((packed)) QCQMI_HDR, *PQCQMI_HDR; + +#define QCQMI_HDR_SIZE (sizeof(QCQMI_HDR) - 1) + +typedef struct _QCQMI { + UCHAR IFType; + USHORT Length; + UCHAR CtlFlags; // reserved + UCHAR QMIType; + UCHAR ClientId; + UCHAR SDU; +} __attribute__((packed)) QCQMI, *PQCQMI; + +typedef struct _QMI_SERVICE_VERSION { + USHORT Major; + USHORT Minor; + USHORT AddendumMajor; + USHORT AddendumMinor; +} __attribute__((packed)) QMI_SERVICE_VERSION, *PQMI_SERVICE_VERSION; + +// ================= QMUX ================== + +#define QMUX_MSG_OVERHEAD_BYTES 4 // Type(USHORT) Length(USHORT) -- header + +#define QMUX_BROADCAST_CID 0xFF + +typedef struct _QCQMUX_HDR { + UCHAR CtlFlags; // 0: single QMUX Msg; 1: + USHORT TransactionId; +} __attribute__((packed)) QCQMUX_HDR, *PQCQMUX_HDR; + +typedef struct _QCQMUX { + UCHAR CtlFlags; // 0: single QMUX Msg; 1: + USHORT TransactionId; + UCHAR Message; // Type(2), Length(2), Value +} __attribute__((packed)) QCQMUX, *PQCQMUX; + +#define QCQMUX_HDR_SIZE sizeof(QCQMUX_HDR) + +typedef struct _QCQMUX_MSG_HDR { + USHORT Type; + USHORT Length; +} __attribute__((packed)) QCQMUX_MSG_HDR, *PQCQMUX_MSG_HDR; + +#define QCQMUX_MSG_HDR_SIZE sizeof(QCQMUX_MSG_HDR) + +typedef struct _QCQMUX_MSG_HDR_RESP { + USHORT Type; + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} __attribute__((packed)) QCQMUX_MSG_HDR_RESP, *PQCQMUX_MSG_HDR_RESP; + +typedef struct _QCQMUX_TLV { + UCHAR Type; + USHORT Length; + UCHAR Value; +} __attribute__((packed)) QCQMUX_TLV, *PQCQMUX_TLV; + +typedef struct _QMI_TLV_HDR { + UCHAR TLVType; + USHORT TLVLength; +} __attribute__((packed)) QMI_TLV_HDR, *PQMI_TLV_HDR; + +// QMUX Message Definitions -- QMI SDU +#define QMUX_CTL_FLAG_SINGLE_MSG 0x00 +#define QMUX_CTL_FLAG_COMPOUND_MSG 0x01 +#define QMUX_CTL_FLAG_TYPE_CMD 0x00 +#define QMUX_CTL_FLAG_TYPE_RSP 0x02 +#define QMUX_CTL_FLAG_TYPE_IND 0x04 +#define QMUX_CTL_FLAG_MASK_COMPOUND 0x01 +#define QMUX_CTL_FLAG_MASK_TYPE 0x06 // 00-cmd, 01-rsp, 10-ind + +#pragma pack(pop) + +#endif // USBQMI_H diff --git a/fibocom-dial/src/MPQMUX.c b/fibocom-dial/src/MPQMUX.c new file mode 100644 index 000000000..2fd59e501 --- /dev/null +++ b/fibocom-dial/src/MPQMUX.c @@ -0,0 +1,437 @@ +#include "QMIThread.h" +static char line[1024]; +static pthread_mutex_t dumpQMIMutex = PTHREAD_MUTEX_INITIALIZER; +#undef dbg +#define dbg(format, arg...) \ + do { \ + if (strlen(line) < sizeof(line)) \ + snprintf(&line[strlen(line)], sizeof(line) - strlen(line), format, \ + ##arg); \ + } while (0) + +PQMI_TLV_HDR GetTLV(PQCQMUX_MSG_HDR pQMUXMsgHdr, int TLVType); + +typedef struct { + UINT type; + const char *name; +} QMI_NAME_T; + +#define qmi_name_item(type) \ + { \ + type, #type \ + } + +static const QMI_NAME_T qmux_ctl_QMICTLType[] = { + // QMICTL Type + qmi_name_item(QMICTL_SET_INSTANCE_ID_REQ), // 0x0020 + qmi_name_item(QMICTL_SET_INSTANCE_ID_RESP), // 0x0020 + qmi_name_item(QMICTL_GET_VERSION_REQ), // 0x0021 + qmi_name_item(QMICTL_GET_VERSION_RESP), // 0x0021 + qmi_name_item(QMICTL_GET_CLIENT_ID_REQ), // 0x0022 + qmi_name_item(QMICTL_GET_CLIENT_ID_RESP), // 0x0022 + qmi_name_item(QMICTL_RELEASE_CLIENT_ID_REQ), // 0x0023 + qmi_name_item(QMICTL_RELEASE_CLIENT_ID_RESP), // 0x0023 + qmi_name_item(QMICTL_REVOKE_CLIENT_ID_IND), // 0x0024 + qmi_name_item(QMICTL_INVALID_CLIENT_ID_IND), // 0x0025 + qmi_name_item(QMICTL_SET_DATA_FORMAT_REQ), // 0x0026 + qmi_name_item(QMICTL_SET_DATA_FORMAT_RESP), // 0x0026 + qmi_name_item(QMICTL_SYNC_REQ), // 0x0027 + qmi_name_item(QMICTL_SYNC_RESP), // 0x0027 + qmi_name_item(QMICTL_SYNC_IND), // 0x0027 +}; + +static const QMI_NAME_T qmux_CtlFlags[] = { + qmi_name_item(QMUX_CTL_FLAG_TYPE_CMD), + qmi_name_item(QMUX_CTL_FLAG_TYPE_RSP), + qmi_name_item(QMUX_CTL_FLAG_TYPE_IND), +}; + +static const QMI_NAME_T qmux_wds_Type[] = { + qmi_name_item(QMIWDS_SET_EVENT_REPORT_REQ), // 0x0001 + qmi_name_item(QMIWDS_SET_EVENT_REPORT_RESP), // 0x0001 + qmi_name_item(QMIWDS_EVENT_REPORT_IND), // 0x0001 + qmi_name_item(QMIWDS_START_NETWORK_INTERFACE_REQ), // 0x0020 + qmi_name_item(QMIWDS_START_NETWORK_INTERFACE_RESP), // 0x0020 + qmi_name_item(QMIWDS_STOP_NETWORK_INTERFACE_REQ), // 0x0021 + qmi_name_item(QMIWDS_STOP_NETWORK_INTERFACE_RESP), // 0x0021 + qmi_name_item(QMIWDS_GET_PKT_SRVC_STATUS_REQ), // 0x0022 + qmi_name_item(QMIWDS_GET_PKT_SRVC_STATUS_RESP), // 0x0022 + qmi_name_item(QMIWDS_GET_PKT_SRVC_STATUS_IND), // 0x0022 + qmi_name_item(QMIWDS_GET_CURRENT_CHANNEL_RATE_REQ), // 0x0023 + qmi_name_item(QMIWDS_GET_CURRENT_CHANNEL_RATE_RESP), // 0x0023 + qmi_name_item(QMIWDS_GET_PKT_STATISTICS_REQ), // 0x0024 + qmi_name_item(QMIWDS_GET_PKT_STATISTICS_RESP), // 0x0024 +//begin modified by zhangkaibo add create profile qmi. mantis 0049137,0048741 20200610 + qmi_name_item(QMIWDS_CREATE_PROFILE_SETTINGS_REQ), // 0x0027 + qmi_name_item(QMIWDS_CREATE_PROFILE_SETTINGS_RESP), // 0x0027 +//end modified by zhangkaibo add create profile qmi. mantis 0049137,0048741 20200610 + qmi_name_item(QMIWDS_MODIFY_PROFILE_SETTINGS_REQ), // 0x0028 + qmi_name_item(QMIWDS_MODIFY_PROFILE_SETTINGS_RESP), // 0x0028 + qmi_name_item(QMIWDS_GET_PROFILE_SETTINGS_REQ), // 0x002B + qmi_name_item(QMIWDS_GET_PROFILE_SETTINGS_RESP), // 0x002BD + qmi_name_item(QMIWDS_GET_DEFAULT_SETTINGS_REQ), // 0x002C + qmi_name_item(QMIWDS_GET_DEFAULT_SETTINGS_RESP), // 0x002C + qmi_name_item(QMIWDS_GET_RUNTIME_SETTINGS_REQ), // 0x002D + qmi_name_item(QMIWDS_GET_RUNTIME_SETTINGS_RESP), // 0x002D + qmi_name_item(QMIWDS_GET_MIP_MODE_REQ), // 0x002F + qmi_name_item(QMIWDS_GET_MIP_MODE_RESP), // 0x002F + qmi_name_item(QMIWDS_GET_DATA_BEARER_REQ), // 0x0037 + qmi_name_item(QMIWDS_GET_DATA_BEARER_RESP), // 0x0037 + qmi_name_item(QMIWDS_DUN_CALL_INFO_REQ), // 0x0038 + qmi_name_item(QMIWDS_DUN_CALL_INFO_RESP), // 0x0038 + qmi_name_item(QMIWDS_DUN_CALL_INFO_IND), // 0x0038 + qmi_name_item(QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ), // 0x004D + qmi_name_item(QMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP), // 0x004D + qmi_name_item(QMIWDS_SET_AUTO_CONNECT_REQ), // 0x0051 + qmi_name_item(QMIWDS_SET_AUTO_CONNECT_RESP), // 0x0051 + qmi_name_item(QMIWDS_BIND_MUX_DATA_PORT_REQ), // 0x00A2 + qmi_name_item(QMIWDS_BIND_MUX_DATA_PORT_RESP), // 0x00A2 +}; + +static const QMI_NAME_T qmux_dms_Type[] = { + // ======================= DMS ============================== + qmi_name_item(QMIDMS_SET_EVENT_REPORT_REQ), // 0x0001 + qmi_name_item(QMIDMS_SET_EVENT_REPORT_RESP), // 0x0001 + qmi_name_item(QMIDMS_EVENT_REPORT_IND), // 0x0001 + qmi_name_item(QMIDMS_GET_DEVICE_CAP_REQ), // 0x0020 + qmi_name_item(QMIDMS_GET_DEVICE_CAP_RESP), // 0x0020 + qmi_name_item(QMIDMS_GET_DEVICE_MFR_REQ), // 0x0021 + qmi_name_item(QMIDMS_GET_DEVICE_MFR_RESP), // 0x0021 + qmi_name_item(QMIDMS_GET_DEVICE_MODEL_ID_REQ), // 0x0022 + qmi_name_item(QMIDMS_GET_DEVICE_MODEL_ID_RESP), // 0x0022 + qmi_name_item(QMIDMS_GET_DEVICE_REV_ID_REQ), // 0x0023 + qmi_name_item(QMIDMS_GET_DEVICE_REV_ID_RESP), // 0x0023 + qmi_name_item(QMIDMS_GET_MSISDN_REQ), // 0x0024 + qmi_name_item(QMIDMS_GET_MSISDN_RESP), // 0x0024 + qmi_name_item(QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ), // 0x0025 + qmi_name_item(QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP), // 0x0025 + qmi_name_item(QMIDMS_UIM_SET_PIN_PROTECTION_REQ), // 0x0027 + qmi_name_item(QMIDMS_UIM_SET_PIN_PROTECTION_RESP), // 0x0027 + qmi_name_item(QMIDMS_UIM_VERIFY_PIN_REQ), // 0x0028 + qmi_name_item(QMIDMS_UIM_VERIFY_PIN_RESP), // 0x0028 + qmi_name_item(QMIDMS_UIM_UNBLOCK_PIN_REQ), // 0x0029 + qmi_name_item(QMIDMS_UIM_UNBLOCK_PIN_RESP), // 0x0029 + qmi_name_item(QMIDMS_UIM_CHANGE_PIN_REQ), // 0x002A + qmi_name_item(QMIDMS_UIM_CHANGE_PIN_RESP), // 0x002A + qmi_name_item(QMIDMS_UIM_GET_PIN_STATUS_REQ), // 0x002B + qmi_name_item(QMIDMS_UIM_GET_PIN_STATUS_RESP), // 0x002B + qmi_name_item(QMIDMS_GET_DEVICE_HARDWARE_REV_REQ), // 0x002C + qmi_name_item(QMIDMS_GET_DEVICE_HARDWARE_REV_RESP), // 0x002C + qmi_name_item(QMIDMS_GET_OPERATING_MODE_REQ), // 0x002D + qmi_name_item(QMIDMS_GET_OPERATING_MODE_RESP), // 0x002D + qmi_name_item(QMIDMS_SET_OPERATING_MODE_REQ), // 0x002E + qmi_name_item(QMIDMS_SET_OPERATING_MODE_RESP), // 0x002E + qmi_name_item(QMIDMS_GET_ACTIVATED_STATUS_REQ), // 0x0031 + qmi_name_item(QMIDMS_GET_ACTIVATED_STATUS_RESP), // 0x0031 + qmi_name_item(QMIDMS_ACTIVATE_AUTOMATIC_REQ), // 0x0032 + qmi_name_item(QMIDMS_ACTIVATE_AUTOMATIC_RESP), // 0x0032 + qmi_name_item(QMIDMS_ACTIVATE_MANUAL_REQ), // 0x0033 + qmi_name_item(QMIDMS_ACTIVATE_MANUAL_RESP), // 0x0033 + qmi_name_item(QMIDMS_UIM_GET_ICCID_REQ), // 0x003C + qmi_name_item(QMIDMS_UIM_GET_ICCID_RESP), // 0x003C + qmi_name_item(QMIDMS_UIM_GET_CK_STATUS_REQ), // 0x0040 + qmi_name_item(QMIDMS_UIM_GET_CK_STATUS_RESP), // 0x0040 + qmi_name_item(QMIDMS_UIM_SET_CK_PROTECTION_REQ), // 0x0041 + qmi_name_item(QMIDMS_UIM_SET_CK_PROTECTION_RESP), // 0x0041 + qmi_name_item(QMIDMS_UIM_UNBLOCK_CK_REQ), // 0x0042 + qmi_name_item(QMIDMS_UIM_UNBLOCK_CK_RESP), // 0x0042 + qmi_name_item(QMIDMS_UIM_GET_IMSI_REQ), // 0x0043 + qmi_name_item(QMIDMS_UIM_GET_IMSI_RESP), // 0x0043 + qmi_name_item(QMIDMS_UIM_GET_STATE_REQ), // 0x0044 + qmi_name_item(QMIDMS_UIM_GET_STATE_RESP), // 0x0044 + qmi_name_item(QMIDMS_GET_BAND_CAP_REQ), // 0x0045 + qmi_name_item(QMIDMS_GET_BAND_CAP_RESP), // 0x0045 +}; + +static const QMI_NAME_T qmux_nas_Type[] = { + // ======================= NAS ============================== + qmi_name_item(QMINAS_SET_EVENT_REPORT_REQ), // 0x0002 + qmi_name_item(QMINAS_SET_EVENT_REPORT_RESP), // 0x0002 + qmi_name_item(QMINAS_EVENT_REPORT_IND), // 0x0002 + qmi_name_item(QMINAS_GET_SIGNAL_STRENGTH_REQ), // 0x0020 + qmi_name_item(QMINAS_GET_SIGNAL_STRENGTH_RESP), // 0x0020 + qmi_name_item(QMINAS_PERFORM_NETWORK_SCAN_REQ), // 0x0021 + qmi_name_item(QMINAS_PERFORM_NETWORK_SCAN_RESP), // 0x0021 + qmi_name_item(QMINAS_INITIATE_NW_REGISTER_REQ), // 0x0022 + qmi_name_item(QMINAS_INITIATE_NW_REGISTER_RESP), // 0x0022 + qmi_name_item(QMINAS_INITIATE_ATTACH_REQ), // 0x0023 + qmi_name_item(QMINAS_INITIATE_ATTACH_RESP), // 0x0023 + qmi_name_item(QMINAS_GET_SERVING_SYSTEM_REQ), // 0x0024 + qmi_name_item(QMINAS_GET_SERVING_SYSTEM_RESP), // 0x0024 + qmi_name_item(QMINAS_SERVING_SYSTEM_IND), // 0x0024 + qmi_name_item(QMINAS_GET_HOME_NETWORK_REQ), // 0x0025 + qmi_name_item(QMINAS_GET_HOME_NETWORK_RESP), // 0x0025 + qmi_name_item(QMINAS_GET_PREFERRED_NETWORK_REQ), // 0x0026 + qmi_name_item(QMINAS_GET_PREFERRED_NETWORK_RESP), // 0x0026 + qmi_name_item(QMINAS_SET_PREFERRED_NETWORK_REQ), // 0x0027 + qmi_name_item(QMINAS_SET_PREFERRED_NETWORK_RESP), // 0x0027 + qmi_name_item(QMINAS_GET_FORBIDDEN_NETWORK_REQ), // 0x0028 + qmi_name_item(QMINAS_GET_FORBIDDEN_NETWORK_RESP), // 0x0028 + qmi_name_item(QMINAS_SET_FORBIDDEN_NETWORK_REQ), // 0x0029 + qmi_name_item(QMINAS_SET_FORBIDDEN_NETWORK_RESP), // 0x0029 + qmi_name_item(QMINAS_SET_TECHNOLOGY_PREF_REQ), // 0x002A + qmi_name_item(QMINAS_SET_TECHNOLOGY_PREF_RESP), // 0x002A + qmi_name_item(QMINAS_GET_RF_BAND_INFO_REQ), // 0x0031 + qmi_name_item(QMINAS_GET_RF_BAND_INFO_RESP), // 0x0031 + qmi_name_item(QMINAS_GET_PLMN_NAME_REQ), // 0x0044 + qmi_name_item(QMINAS_GET_PLMN_NAME_RESP), // 0x0044 + qmi_name_item(FIBO_PACKET_TRANSFER_START_IND), // 0X100 + qmi_name_item(FIBO_PACKET_TRANSFER_END_IND), // 0X101 + qmi_name_item(QMINAS_GET_SYS_INFO_REQ), // 0x004D + qmi_name_item(QMINAS_GET_SYS_INFO_RESP), // 0x004D + qmi_name_item(QMINAS_SYS_INFO_IND), // 0x004D +}; + +static const QMI_NAME_T qmux_wms_Type[] = { + // ======================= WMS ============================== + qmi_name_item(QMIWMS_SET_EVENT_REPORT_REQ), // 0x0001 + qmi_name_item(QMIWMS_SET_EVENT_REPORT_RESP), // 0x0001 + qmi_name_item(QMIWMS_EVENT_REPORT_IND), // 0x0001 + qmi_name_item(QMIWMS_RAW_SEND_REQ), // 0x0020 + qmi_name_item(QMIWMS_RAW_SEND_RESP), // 0x0020 + qmi_name_item(QMIWMS_RAW_WRITE_REQ), // 0x0021 + qmi_name_item(QMIWMS_RAW_WRITE_RESP), // 0x0021 + qmi_name_item(QMIWMS_RAW_READ_REQ), // 0x0022 + qmi_name_item(QMIWMS_RAW_READ_RESP), // 0x0022 + qmi_name_item(QMIWMS_MODIFY_TAG_REQ), // 0x0023 + qmi_name_item(QMIWMS_MODIFY_TAG_RESP), // 0x0023 + qmi_name_item(QMIWMS_DELETE_REQ), // 0x0024 + qmi_name_item(QMIWMS_DELETE_RESP), // 0x0024 + qmi_name_item(QMIWMS_GET_MESSAGE_PROTOCOL_REQ), // 0x0030 + qmi_name_item(QMIWMS_GET_MESSAGE_PROTOCOL_RESP), // 0x0030 + qmi_name_item(QMIWMS_LIST_MESSAGES_REQ), // 0x0031 + qmi_name_item(QMIWMS_LIST_MESSAGES_RESP), // 0x0031 + qmi_name_item(QMIWMS_GET_SMSC_ADDRESS_REQ), // 0x0034 + qmi_name_item(QMIWMS_GET_SMSC_ADDRESS_RESP), // 0x0034 + qmi_name_item(QMIWMS_SET_SMSC_ADDRESS_REQ), // 0x0035 + qmi_name_item(QMIWMS_SET_SMSC_ADDRESS_RESP), // 0x0035 + qmi_name_item(QMIWMS_GET_STORE_MAX_SIZE_REQ), // 0x0036 + qmi_name_item(QMIWMS_GET_STORE_MAX_SIZE_RESP), // 0x0036 +}; + +static const QMI_NAME_T qmux_wds_admin_Type[] = { + qmi_name_item(QMIWDS_ADMIN_SET_DATA_FORMAT_REQ), // 0x0020 + qmi_name_item(QMIWDS_ADMIN_SET_DATA_FORMAT_RESP), // 0x0020 + qmi_name_item(QMIWDS_ADMIN_GET_DATA_FORMAT_REQ), // 0x0021 + qmi_name_item(QMIWDS_ADMIN_GET_DATA_FORMAT_RESP), // 0x0021 + qmi_name_item(QMIWDS_ADMIN_SET_QMAP_SETTINGS_REQ), // 0x002B + qmi_name_item(QMIWDS_ADMIN_SET_QMAP_SETTINGS_RESP), // 0x002B + qmi_name_item(QMIWDS_ADMIN_GET_QMAP_SETTINGS_REQ), // 0x002C + qmi_name_item(QMIWDS_ADMIN_GET_QMAP_SETTINGS_RESP), // 0x002C +}; + +static const QMI_NAME_T qmux_uim_Type[] = { + qmi_name_item(QMIUIM_READ_TRANSPARENT_REQ), // 0x0020 + qmi_name_item(QMIUIM_READ_TRANSPARENT_RESP), // 0x0020 + qmi_name_item(QMIUIM_READ_TRANSPARENT_IND), // 0x0020 + qmi_name_item(QMIUIM_READ_RECORD_REQ), // 0x0021 + qmi_name_item(QMIUIM_READ_RECORD_RESP), // 0x0021 + qmi_name_item(QMIUIM_READ_RECORD_IND), // 0x0021 + qmi_name_item(QMIUIM_WRITE_TRANSPARENT_REQ), // 0x0022 + qmi_name_item(QMIUIM_WRITE_TRANSPARENT_RESP), // 0x0022 + qmi_name_item(QMIUIM_WRITE_TRANSPARENT_IND), // 0x0022 + qmi_name_item(QMIUIM_WRITE_RECORD_REQ), // 0x0023 + qmi_name_item(QMIUIM_WRITE_RECORD_RESP), // 0x0023 + qmi_name_item(QMIUIM_WRITE_RECORD_IND), // 0x0023 + qmi_name_item(QMIUIM_SET_PIN_PROTECTION_REQ), // 0x0025 + qmi_name_item(QMIUIM_SET_PIN_PROTECTION_RESP), // 0x0025 + qmi_name_item(QMIUIM_SET_PIN_PROTECTION_IND), // 0x0025 + qmi_name_item(QMIUIM_VERIFY_PIN_REQ), // 0x0026 + qmi_name_item(QMIUIM_VERIFY_PIN_RESP), // 0x0026 + qmi_name_item(QMIUIM_VERIFY_PIN_IND), // 0x0026 + qmi_name_item(QMIUIM_UNBLOCK_PIN_REQ), // 0x0027 + qmi_name_item(QMIUIM_UNBLOCK_PIN_RESP), // 0x0027 + qmi_name_item(QMIUIM_UNBLOCK_PIN_IND), // 0x0027 + qmi_name_item(QMIUIM_CHANGE_PIN_REQ), // 0x0028 + qmi_name_item(QMIUIM_CHANGE_PIN_RESP), // 0x0028 + qmi_name_item(QMIUIM_CHANGE_PIN_IND), // 0x0028 + qmi_name_item(QMIUIM_DEPERSONALIZATION_REQ), // 0x0029 + qmi_name_item(QMIUIM_DEPERSONALIZATION_RESP), // 0x0029 + qmi_name_item(QMIUIM_EVENT_REG_REQ), // 0x002E + qmi_name_item(QMIUIM_EVENT_REG_RESP), // 0x002E + qmi_name_item(QMIUIM_GET_CARD_STATUS_REQ), // 0x002F + qmi_name_item(QMIUIM_GET_CARD_STATUS_RESP), // 0x002F + qmi_name_item(QMIUIM_STATUS_CHANGE_IND), // 0x0032 +}; + +static const char *qmi_name_get(const QMI_NAME_T *table, size_t size, int type, + const char *tag) +{ + static char unknow[40]; + size_t i; + + if (qmux_CtlFlags == table) { + if (!strcmp(tag, "_REQ")) + tag = "_CMD"; + else if (!strcmp(tag, "_RESP")) + tag = "_RSP"; + } + + for (i = 0; i < size; i++) { + if (table[i].type == (UINT)type) { + if (!tag || (strstr(table[i].name, tag))) + return table[i].name; + } + } + sprintf(unknow, "unknow_%x", type); + return unknow; +} + +#define QMI_NAME(table, type) \ + qmi_name_get(table, sizeof(table) / sizeof(table[0]), type, 0) +#define QMUX_NAME(table, type, tag) \ + qmi_name_get(table, sizeof(table) / sizeof(table[0]), type, tag) + +void dump_tlv(PQCQMUX_MSG_HDR pQMUXMsgHdr) +{ + int TLVFind = 0; + int i; + // dbg("QCQMUX_TLV-----------------------------------\n"); + // dbg("{Type,\tLength,\tValue}\n"); + + while (1) { + PQMI_TLV_HDR TLVHdr = GetTLV(pQMUXMsgHdr, 0x1000 + (++TLVFind)); + if (TLVHdr == NULL) + break; + + // if ((TLVHdr->TLVType == 0x02) && ((USHORT *)(TLVHdr+1))[0]) + { + dbg("{%02x,\t%04x,\t", TLVHdr->TLVType, + le16_to_cpu(TLVHdr->TLVLength)); + for (i = 0; i < le16_to_cpu(TLVHdr->TLVLength); i++) { + dbg("%02x ", ((UCHAR *)(TLVHdr + 1))[i]); + } + dbg("}\n"); + } + } // while +} + +void dump_ctl(PQCQMICTL_MSG_HDR CTLHdr) +{ + const char *tag; + + // dbg("QCQMICTL_MSG--------------------------------------------\n"); + // dbg("CtlFlags: %02x\t\t%s\n", CTLHdr->CtlFlags, + // QMI_NAME(qmi_ctl_CtlFlags, CTLHdr->CtlFlags)); + dbg("TransactionId: %02x\n", CTLHdr->TransactionId); + switch (CTLHdr->CtlFlags) { + case QMICTL_FLAG_REQUEST: + tag = "_REQ"; + break; + case QMICTL_FLAG_RESPONSE: + tag = "_RESP"; + break; + case QMICTL_FLAG_INDICATION: + tag = "_IND"; + break; + default: + tag = 0; + break; + } + dbg("QMICTLType: %04x\t%s\n", le16_to_cpu(CTLHdr->QMICTLType), + QMUX_NAME(qmux_ctl_QMICTLType, le16_to_cpu(CTLHdr->QMICTLType), tag)); + dbg("Length: %04x\n", le16_to_cpu(CTLHdr->Length)); + + dump_tlv((PQCQMUX_MSG_HDR)(&CTLHdr->QMICTLType)); +} + +int dump_qmux(QMI_SERVICE_TYPE serviceType, PQCQMUX_HDR QMUXHdr) +{ + PQCQMUX_MSG_HDR QMUXMsgHdr = (PQCQMUX_MSG_HDR)(QMUXHdr + 1); + CHAR *tag; + + // dbg("QCQMUX--------------------------------------------\n"); + switch (QMUXHdr->CtlFlags & QMUX_CTL_FLAG_MASK_TYPE) { + case QMUX_CTL_FLAG_TYPE_CMD: + tag = "_REQ"; + break; + case QMUX_CTL_FLAG_TYPE_RSP: + tag = "_RESP"; + break; + case QMUX_CTL_FLAG_TYPE_IND: + tag = "_IND"; + break; + default: + tag = 0; + break; + } + // dbg("CtlFlags: %02x\t\t%s\n", QMUXHdr->CtlFlags, + // QMUX_NAME(qmux_CtlFlags, QMUXHdr->CtlFlags, tag)); + dbg("TransactionId: %04x\n", le16_to_cpu(QMUXHdr->TransactionId)); + + // dbg("QCQMUX_MSG_HDR-----------------------------------\n"); + switch (serviceType) { + case QMUX_TYPE_DMS: + dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), + QMUX_NAME(qmux_dms_Type, le16_to_cpu(QMUXMsgHdr->Type), tag)); + break; + case QMUX_TYPE_NAS: + dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), + QMUX_NAME(qmux_nas_Type, le16_to_cpu(QMUXMsgHdr->Type), tag)); + break; + case QMUX_TYPE_WDS: + dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), + QMUX_NAME(qmux_wds_Type, le16_to_cpu(QMUXMsgHdr->Type), tag)); + break; + case QMUX_TYPE_WMS: + dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), + QMUX_NAME(qmux_wms_Type, le16_to_cpu(QMUXMsgHdr->Type), tag)); + break; + case QMUX_TYPE_WDS_ADMIN: + dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), + QMUX_NAME(qmux_wds_admin_Type, le16_to_cpu(QMUXMsgHdr->Type), tag)); + break; + case QMUX_TYPE_UIM: + dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), + QMUX_NAME(qmux_uim_Type, le16_to_cpu(QMUXMsgHdr->Type), tag)); + break; + case QMUX_TYPE_PDS: + case QMUX_TYPE_QOS: + case QMUX_TYPE_CTL: + default: + dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), + "PDS/QOS/CTL/unknown!"); + break; + } + dbg("Length: %04x\n", le16_to_cpu(QMUXMsgHdr->Length)); + + dump_tlv(QMUXMsgHdr); + + return 0; +} + +void dump_qmi(void *dataBuffer, int dataLen) +{ + PQCQMI_HDR QMIHdr = (PQCQMI_HDR)dataBuffer; + PQCQMUX_HDR QMUXHdr = (PQCQMUX_HDR)(QMIHdr + 1); + PQCQMICTL_MSG_HDR CTLHdr = (PQCQMICTL_MSG_HDR)(QMIHdr + 1); + + int i; + + if (!debug_qmi) + return; + + pthread_mutex_lock(&dumpQMIMutex); + line[0] = 0; + for (i = 0; i < dataLen; i++) { + dbg("%02x ", ((unsigned char *)dataBuffer)[i]); + } + dbg_time("%s", line); + line[0] = 0; + + // dbg("QCQMI_HDR-----------------------------------------"); + // dbg("IFType: %02x\t\t%s", QMIHdr->IFType, + // QMI_NAME(qmi_IFType, QMIHdr->IFType)); dbg("Length: %04x", + // le16_to_cpu(QMIHdr->Length)); dbg("CtlFlags: %02x\t\t%s", + // QMIHdr->CtlFlags, QMI_NAME(qmi_CtlFlags, QMIHdr->CtlFlags)); + // dbg("QMIType: %02x\t\t%s", QMIHdr->QMIType, QMI_NAME(qmi_QMIType, + // QMIHdr->QMIType)); dbg("ClientId: %02x", QMIHdr->ClientId); + + if (QMIHdr->QMIType == QMUX_TYPE_CTL) { + dump_ctl(CTLHdr); + } else { + dump_qmux(QMIHdr->QMIType, QMUXHdr); + } + dbg_time("%s", line); + pthread_mutex_unlock(&dumpQMIMutex); +} diff --git a/fibocom-dial/src/MPQMUX.h b/fibocom-dial/src/MPQMUX.h new file mode 100644 index 000000000..bae0c4ef3 --- /dev/null +++ b/fibocom-dial/src/MPQMUX.h @@ -0,0 +1,3671 @@ +/*=========================================================================== + + M P Q M U X. H +DESCRIPTION: + + This file provides support for QMUX. + +INITIALIZATION AND SEQUENCING REQUIREMENTS: + +Copyright (C) 2011 by Qualcomm Technologies, Incorporated. All Rights Reserved. +===========================================================================*/ + +#ifndef MPQMUX_H +#define MPQMUX_H + +#include "MPQMI.h" + +#pragma pack(push, 1) + +#define QMIWDS_SET_EVENT_REPORT_REQ 0x0001 +#define QMIWDS_SET_EVENT_REPORT_RESP 0x0001 +#define QMIWDS_EVENT_REPORT_IND 0x0001 +#define QMIWDS_START_NETWORK_INTERFACE_REQ 0x0020 +#define QMIWDS_START_NETWORK_INTERFACE_RESP 0x0020 +#define QMIWDS_STOP_NETWORK_INTERFACE_REQ 0x0021 +#define QMIWDS_STOP_NETWORK_INTERFACE_RESP 0x0021 +#define QMIWDS_GET_PKT_SRVC_STATUS_REQ 0x0022 +#define QMIWDS_GET_PKT_SRVC_STATUS_RESP 0x0022 +#define QMIWDS_GET_PKT_SRVC_STATUS_IND 0x0022 +#define QMIWDS_GET_CURRENT_CHANNEL_RATE_REQ 0x0023 +#define QMIWDS_GET_CURRENT_CHANNEL_RATE_RESP 0x0023 +#define QMIWDS_GET_PKT_STATISTICS_REQ 0x0024 +#define QMIWDS_GET_PKT_STATISTICS_RESP 0x0024 +//begin modified by zhangkaibo add create profile qmi. mantis 0049137,0048741 20200610 +#define QMIWDS_CREATE_PROFILE_SETTINGS_REQ 0x0027 +#define QMIWDS_CREATE_PROFILE_SETTINGS_RESP 0x0027 +//end modified by zhangkaibo add create profile qmi. mantis 0049137,0048741 20200610 +#define QMIWDS_MODIFY_PROFILE_SETTINGS_REQ 0x0028 +#define QMIWDS_MODIFY_PROFILE_SETTINGS_RESP 0x0028 +#define QMIWDS_GET_PROFILE_SETTINGS_REQ 0x002B +#define QMIWDS_GET_PROFILE_SETTINGS_RESP 0x002B +#define QMIWDS_GET_DEFAULT_SETTINGS_REQ 0x002C +#define QMIWDS_GET_DEFAULT_SETTINGS_RESP 0x002C +#define QMIWDS_GET_RUNTIME_SETTINGS_REQ 0x002D +#define QMIWDS_GET_RUNTIME_SETTINGS_RESP 0x002D +#define QMIWDS_GET_MIP_MODE_REQ 0x002F +#define QMIWDS_GET_MIP_MODE_RESP 0x002F +#define QMIWDS_GET_DATA_BEARER_REQ 0x0037 +#define QMIWDS_GET_DATA_BEARER_RESP 0x0037 +#define QMIWDS_DUN_CALL_INFO_REQ 0x0038 +#define QMIWDS_DUN_CALL_INFO_RESP 0x0038 +#define QMIWDS_DUN_CALL_INFO_IND 0x0038 +#define QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ 0x004D +#define QMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP 0x004D +#define QMIWDS_SET_AUTO_CONNECT_REQ 0x0051 +#define QMIWDS_SET_AUTO_CONNECT_RESP 0x0051 +#define QMIWDS_BIND_MUX_DATA_PORT_REQ 0x00A2 +#define QMIWDS_BIND_MUX_DATA_PORT_RESP 0x00A2 + +// Stats masks +#define QWDS_STAT_MASK_TX_PKT_OK 0x00000001 +#define QWDS_STAT_MASK_RX_PKT_OK 0x00000002 +#define QWDS_STAT_MASK_TX_PKT_ER 0x00000004 +#define QWDS_STAT_MASK_RX_PKT_ER 0x00000008 +#define QWDS_STAT_MASK_TX_PKT_OF 0x00000010 +#define QWDS_STAT_MASK_RX_PKT_OF 0x00000020 + +// TLV Types for xfer statistics +#define TLV_WDS_TX_GOOD_PKTS 0x10 +#define TLV_WDS_RX_GOOD_PKTS 0x11 +#define TLV_WDS_TX_ERROR 0x12 +#define TLV_WDS_RX_ERROR 0x13 +#define TLV_WDS_TX_OVERFLOW 0x14 +#define TLV_WDS_RX_OVERFLOW 0x15 +#define TLV_WDS_CHANNEL_RATE 0x16 +#define TLV_WDS_DATA_BEARER 0x17 +#define TLV_WDS_DORMANCY_STATUS 0x18 + +#define QWDS_PKT_DATA_DISCONNECTED 0x01 +#define QWDS_PKT_DATA_CONNECTED 0x02 +#define QWDS_PKT_DATA_SUSPENDED 0x03 +#define QWDS_PKT_DATA_AUTHENTICATING 0x04 + +#define QMIWDS_ADMIN_SET_DATA_FORMAT_REQ 0x0020 +#define QMIWDS_ADMIN_SET_DATA_FORMAT_RESP 0x0020 +#define QMIWDS_ADMIN_GET_DATA_FORMAT_REQ 0x0021 +#define QMIWDS_ADMIN_GET_DATA_FORMAT_RESP 0x0021 +#define QMIWDS_ADMIN_SET_QMAP_SETTINGS_REQ 0x002B +#define QMIWDS_ADMIN_SET_QMAP_SETTINGS_RESP 0x002B +#define QMIWDS_ADMIN_GET_QMAP_SETTINGS_REQ 0x002C +#define QMIWDS_ADMIN_GET_QMAP_SETTINGS_RESP 0x002C + +#define NETWORK_DESC_ENCODING_OCTET 0x00 +#define NETWORK_DESC_ENCODING_EXTPROTOCOL 0x01 +#define NETWORK_DESC_ENCODING_7BITASCII 0x02 +#define NETWORK_DESC_ENCODING_IA5 0x03 +#define NETWORK_DESC_ENCODING_UNICODE 0x04 +#define NETWORK_DESC_ENCODING_SHIFTJIS 0x05 +#define NETWORK_DESC_ENCODING_KOREAN 0x06 +#define NETWORK_DESC_ENCODING_LATINH 0x07 +#define NETWORK_DESC_ENCODING_LATIN 0x08 +#define NETWORK_DESC_ENCODING_GSM7BIT 0x09 +#define NETWORK_DESC_ENCODING_GSMDATA 0x0A +#define NETWORK_DESC_ENCODING_UNKNOWN 0xFF + +typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT { + USHORT Type; // QMUX type 0x0000 + USHORT Length; +} __attribute__((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT, + *PQMIWDS_ADMIN_SET_DATA_FORMAT; + +typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS { + UCHAR TLVType; + USHORT TLVLength; + UCHAR QOSSetting; +} __attribute__((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS, + *PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS; + +typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_TLV { + UCHAR TLVType; + USHORT TLVLength; + ULONG Value; +} __attribute__((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_TLV, + *PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV; + +typedef struct _QMIWDS_ENDPOINT_TLV { + UCHAR TLVType; + USHORT TLVLength; + ULONG ep_type; + ULONG iface_id; +} __attribute__((packed)) QMIWDS_ENDPOINT_TLV, *PQMIWDS_ENDPOINT_TLV; + +typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG { + USHORT Type; + USHORT Length; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS QosDataFormatTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UnderlyingLinkLayerProtocolTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationProtocolTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationProtocolTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationMaxDatagramsTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationMaxSizeTlv; +#if 0 + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationMaxDatagramsTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationMaxSizeTlv; +#else + QMIWDS_ENDPOINT_TLV epTlv; +#endif +} __attribute__((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG, + *PQMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG; + +#if 0 +typedef enum _QMI_RETURN_CODES { + QMI_SUCCESS = 0, + QMI_SUCCESS_NOT_COMPLETE, + QMI_FAILURE +}QMI_RETURN_CODES; + +typedef struct _QMIWDS_GET_PKT_SRVC_STATUS_REQ_MSG +{ + USHORT Type; // 0x0022 + USHORT Length; // 0x0000 +} QMIWDS_GET_PKT_SRVC_STATUS_REQ_MSG, *PQMIWDS_GET_PKT_SRVC_STATUS_REQ_MSG; + +typedef struct _QMIWDS_GET_PKT_SRVC_STATUS_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLVType2; + USHORT TLVLength2; + UCHAR ConnectionStatus; // 0x01: QWDS_PKT_DATAC_DISCONNECTED + // 0x02: QWDS_PKT_DATA_CONNECTED + // 0x03: QWDS_PKT_DATA_SUSPENDED + // 0x04: QWDS_PKT_DATA_AUTHENTICATING +} QMIWDS_GET_PKT_SRVC_STATUS_RESP_MSG, *PQMIWDS_GET_PKT_SRVC_STATUS_RESP_MSG; + +typedef struct _QMIWDS_GET_PKT_SRVC_STATUS_IND_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR ConnectionStatus; // 0x01: QWDS_PKT_DATAC_DISCONNECTED + // 0x02: QWDS_PKT_DATA_CONNECTED + // 0x03: QWDS_PKT_DATA_SUSPENDED + UCHAR ReconfigRequired; // 0x00: No need to reconfigure + // 0x01: Reconfiguration required +} QMIWDS_GET_PKT_SRVC_STATUS_IND_MSG, *PQMIWDS_GET_PKT_SRVC_STATUS_IND_MSG; + +typedef struct _WDS_PKT_SRVC_IP_FAMILY_TLV +{ + UCHAR TLVType; // 0x12 + USHORT TLVLength; // 1 + UCHAR IpFamily; // IPV4-0x04, IPV6-0x06 +} WDS_PKT_SRVC_IP_FAMILY_TLV, *PWDS_PKT_SRVC_IP_FAMILY_TLV; + +typedef struct _QMIWDS_DUN_CALL_INFO_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + ULONG Mask; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR ReportConnectionStatus; +} QMIWDS_DUN_CALL_INFO_REQ_MSG, *PQMIWDS_DUN_CALL_INFO_REQ_MSG; + +typedef struct _QMIWDS_DUN_CALL_INFO_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} QMIWDS_DUN_CALL_INFO_RESP_MSG, *PQMIWDS_DUN_CALL_INFO_RESP_MSG; + +typedef struct _QMIWDS_DUN_CALL_INFO_IND_MSG +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR ConnectionStatus; +} QMIWDS_DUN_CALL_INFO_IND_MSG, *PQMIWDS_DUN_CALL_INFO_IND_MSG; + +typedef struct _QMIWDS_GET_CURRENT_CHANNEL_RATE_REQ_MSG +{ + USHORT Type; // QMUX type 0x0040 + USHORT Length; +} QMIWDS_GET_CURRENT_CHANNEL_RATE_REQ_MSG, *PQMIWDS_GET_CURRENT_CHANNEL_RATE_REQ_MSG; + +typedef struct _QMIWDS_GET_CURRENT_CHANNEL_RATE_RESP_MSG +{ + USHORT Type; // QMUX type 0x0040 + USHORT Length; + UCHAR TLVType; // 0x02 + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + + UCHAR TLV2Type; // 0x01 + USHORT TLV2Length; // 16 + //ULONG CallHandle; // Context corresponding to reported channel + ULONG CurrentTxRate; // bps + ULONG CurrentRxRate; // bps + ULONG ServingSystemTxRate; // bps + ULONG ServingSystemRxRate; // bps + +} QMIWDS_GET_CURRENT_CHANNEL_RATE_RESP_MSG, *PQMIWDS_GET_CURRENT_CHANNEL_RATE_RESP; + +#define QWDS_EVENT_REPORT_MASK_RATES 0x01 +#define QWDS_EVENT_REPORT_MASK_STATS 0x02 + +#ifdef QCUSB_MUX_PROTOCOL +#error code not present +#endif // QCUSB_MUX_PROTOCOL + +typedef struct _QMIWDS_SET_EVENT_REPORT_REQ_MSG +{ + USHORT Type; // QMUX type 0x0042 + USHORT Length; + + UCHAR TLVType; // 0x10 -- current channel rate indicator + USHORT TLVLength; // 1 + UCHAR Mode; // 0-do not report; 1-report when rate changes + + UCHAR TLV2Type; // 0x11 + USHORT TLV2Length; // 5 + UCHAR StatsPeriod; // seconds between reports; 0-do not report + ULONG StatsMask; // + + UCHAR TLV3Type; // 0x12 -- current data bearer indicator + USHORT TLV3Length; // 1 + UCHAR Mode3; // 0-do not report; 1-report when changes + + UCHAR TLV4Type; // 0x13 -- dormancy status indicator + USHORT TLV4Length; // 1 + UCHAR DormancyStatus; // 0-do not report; 1-report when changes +} QMIWDS_SET_EVENT_REPORT_REQ_MSG, *PQMIWDS_SET_EVENT_REPORT_REQ_MSG; + +typedef struct _QMIWDS_SET_EVENT_REPORT_RESP_MSG +{ + USHORT Type; // QMUX type 0x0042 + USHORT Length; + + UCHAR TLVType; // 0x02 result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_NO_BATTERY + // QMI_ERR_FAULT +} QMIWDS_SET_EVENT_REPORT_RESP_MSG, *PQMIWDS_SET_EVENT_REPORT_RESP_MSG; + +typedef struct _QMIWDS_EVENT_REPORT_IND_MSG +{ + USHORT Type; // QMUX type 0x0001 + USHORT Length; +} QMIWDS_EVENT_REPORT_IND_MSG, *PQMIWDS_EVENT_REPORT_IND_MSG; + +// PQCTLV_PKT_STATISTICS + +typedef struct _QMIWDS_EVENT_REPORT_IND_CHAN_RATE_TLV +{ + UCHAR Type; + USHORT Length; // 8 + ULONG TxRate; + ULONG RxRate; +} QMIWDS_EVENT_REPORT_IND_CHAN_RATE_TLV, *PQMIWDS_EVENT_REPORT_IND_CHAN_RATE_TLV; + +#ifdef QCUSB_MUX_PROTOCOL +#error code not present +#endif // QCUSB_MUX_PROTOCOL + +typedef struct _QMIWDS_GET_PKT_STATISTICS_REQ_MSG +{ + USHORT Type; // QMUX type 0x0041 + USHORT Length; + UCHAR TLVType; // 0x01 + USHORT TLVLength; // 4 + ULONG StateMask; // 0x00000001 tx success packets + // 0x00000002 rx success packets + // 0x00000004 rx packet errors (checksum) + // 0x00000008 rx packets dropped (memory) + +} QMIWDS_GET_PKT_STATISTICS_REQ_MSG, *PQMIWDS_GET_PKT_STATISTICS_REQ_MSG; + +typedef struct _QMIWDS_GET_PKT_STATISTICS_RESP_MSG +{ + USHORT Type; // QMUX type 0x0041 + USHORT Length; + UCHAR TLVType; // 0x02 + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMIWDS_GET_PKT_STATISTICS_RESP_MSG, *PQMIWDS_GET_PKT_STATISTICS_RESP_MSG; + +// optional TLV for stats +typedef struct _QCTLV_PKT_STATISTICS +{ + UCHAR TLVType; // see above definitions for TLV types + USHORT TLVLength; // 4 + ULONG Count; +} QCTLV_PKT_STATISTICS, *PQCTLV_PKT_STATISTICS; +#endif + +//#ifdef QC_IP_MODE + +#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4DNS_ADDR 0x0010 +#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4_ADDR 0x0100 +#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4GATEWAY_ADDR 0x0200 +#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_MTU 0x2000 + +typedef struct _QMIWDS_GET_RUNTIME_SETTINGS_REQ_MSG { + USHORT Type; // QMIWDS_GET_RUNTIME_SETTINGS_REQ + USHORT Length; + UCHAR TLVType; // 0x10 + USHORT TLVLength; // 0x0004 + ULONG Mask; // mask, bit 8: IP addr -- 0x0100 +} __attribute__((packed)) QMIWDS_GET_RUNTIME_SETTINGS_REQ_MSG, + *PQMIWDS_GET_RUNTIME_SETTINGS_REQ_MSG; + +typedef struct _QMIWDS_BIND_MUX_DATA_PORT_REQ_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + ULONG ep_type; + ULONG iface_id; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR MuxId; + UCHAR TLV3Type; + USHORT TLV3Length; + ULONG client_type; +} __attribute__((packed)) QMIWDS_BIND_MUX_DATA_PORT_REQ_MSG, + *PQMIWDS_BIND_MUX_DATA_PORT_REQ_MSG; + +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4PRIMARYDNS 0x15 +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4SECONDARYDNS 0x16 +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4 0x1E +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4GATEWAY 0x20 +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4SUBNET 0x21 + +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6 0x25 +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6GATEWAY 0x26 +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6PRIMARYDNS 0x27 +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6SECONDARYDNS 0x28 +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_MTU 0x29 + +typedef struct _QMIWDS_GET_RUNTIME_SETTINGS_TLV_MTU { + UCHAR TLVType; // QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_MTU + USHORT TLVLength; // 4 + ULONG Mtu; // MTU +} __attribute__((packed)) QMIWDS_GET_RUNTIME_SETTINGS_TLV_MTU, + *PQMIWDS_GET_RUNTIME_SETTINGS_TLV_MTU; + +typedef struct _QMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR { + UCHAR TLVType; // QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4 + USHORT TLVLength; // 4 + ULONG IPV4Address; // address +} __attribute__((packed)) QMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR, + *PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR; + +typedef struct _QMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR { + UCHAR TLVType; // QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6 + USHORT TLVLength; // 16 + UCHAR IPV6Address[16]; // address + UCHAR PrefixLength; // prefix length +} __attribute__((packed)) QMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR, + *PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR; + + typedef struct _QMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_DNS_ADDR { + UCHAR TLVType; // QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6 + USHORT TLVLength; // 16 + UCHAR IPV6Address[16]; // address + } __attribute__((packed)) QMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_DNS_ADDR, + *PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_DNS_ADDR; + +typedef struct _QMIWDS_GET_RUNTIME_SETTINGS_RESP_MSG { + USHORT Type; // QMIWDS_GET_RUNTIME_SETTINGS_RESP + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE + USHORT TLVLength; // 0x0004 + USHORT QMUXResult; // result code + USHORT QMUXError; // error code +} __attribute__((packed)) QMIWDS_GET_RUNTIME_SETTINGS_RESP_MSG, + *PQMIWDS_GET_RUNTIME_SETTINGS_RESP_MSG; + +//#endif // QC_IP_MODE + +typedef struct _QMIWDS_IP_FAMILY_TLV { + UCHAR TLVType; // 0x12 + USHORT TLVLength; // 1 + UCHAR IpFamily; // IPV4-0x04, IPV6-0x06 +} __attribute__((packed)) QMIWDS_IP_FAMILY_TLV, *PQMIWDS_IP_FAMILY_TLV; + +typedef struct _QMIWDS_PKT_SRVC_TLV { + UCHAR TLVType; + USHORT TLVLength; + UCHAR ConnectionStatus; + UCHAR ReconfigReqd; +} __attribute__((packed)) QMIWDS_PKT_SRVC_TLV, *PQMIWDS_PKT_SRVC_TLV; + +typedef struct _QMIWDS_CALL_END_REASON_TLV { + UCHAR TLVType; + USHORT TLVLength; + USHORT CallEndReason; +} __attribute__((packed)) QMIWDS_CALL_END_REASON_TLV, + *PQMIWDS_CALL_END_REASON_TLV; + +typedef struct _QMIWDS_CALL_END_REASON_V_TLV { + UCHAR TLVType; + USHORT TLVLength; + USHORT CallEndReasonType; + USHORT CallEndReason; +} __attribute__((packed)) QMIWDS_CALL_END_REASON_V_TLV, + *PQMIWDS_CALL_END_REASON_V_TLV; + +typedef struct _QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ_MSG { + USHORT Type; // QMUX type 0x004D + USHORT Length; + UCHAR TLVType; // 0x01 + USHORT TLVLength; // 1 + UCHAR IpPreference; // IPV4-0x04, IPV6-0x06 +} __attribute__((packed)) QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ_MSG, + *PQMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ_MSG; + +typedef struct _QMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP_MSG { + USHORT Type; // QMUX type 0x0037 + USHORT Length; + UCHAR TLVType; // 0x02 + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS, QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INTERNAL, QMI_ERR_MALFORMED_MSG, + // QMI_ERR_INVALID_ARG +} __attribute__((packed)) QMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP_MSG, + *PQMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP_MSG; + +typedef struct _QMIWDS_SET_AUTO_CONNECT_REQ_MSG { + USHORT Type; // QMUX type 0x0051 + USHORT Length; + UCHAR TLVType; // 0x01 + USHORT TLVLength; // 1 + UCHAR autoconnect_setting; // 0x00 ?C Disabled, 0x01 ?C Enabled, 0x02 ?C + // Paused (resume on power cycle) +} __attribute__((packed)) QMIWDS_SET_AUTO_CONNECT_REQ_MSG, + *PQMIWDS_SET_AUTO_CONNECT_REQ_MSG; + +#if 0 +typedef struct _QMIWDS_GET_MIP_MODE_REQ_MSG +{ + USHORT Type; // QMUX type 0x0040 + USHORT Length; +} QMIWDS_GET_MIP_MODE_REQ_MSG, *PQMIWDS_GET_MIP_MODE_REQ_MSG; + +typedef struct _QMIWDS_GET_MIP_MODE_RESP_MSG +{ + USHORT Type; // QMUX type 0x0040 + USHORT Length; + UCHAR TLVType; // 0x02 + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + + UCHAR TLV2Type; // 0x01 + USHORT TLV2Length; // 20 + UCHAR MipMode; // +} QMIWDS_GET_MIP_MODE_RESP_MSG, *PQMIWDS_GET_MIP_MODE_RESP_MSG; +#endif + +typedef struct _QMIWDS_TECHNOLOGY_PREFERECE { + UCHAR TLVType; + USHORT TLVLength; + UCHAR TechPreference; +} __attribute__((packed)) QMIWDS_TECHNOLOGY_PREFERECE, + *PQMIWDS_TECHNOLOGY_PREFERECE; + +typedef struct _QMIWDS_PROFILE_IDENTIFIER { + UCHAR TLVType; + USHORT TLVLength; + UCHAR ProfileIndex; +} __attribute__((packed)) QMIWDS_PROFILE_IDENTIFIER, + *PQMIWDS_PROFILE_IDENTIFIER; + +#if 0 +typedef struct _QMIWDS_IPADDRESS +{ + UCHAR TLVType; + USHORT TLVLength; + ULONG IPv4Address; +}QMIWDS_IPADDRESS, *PQMIWDS_IPADDRESS; + +/* +typedef struct _QMIWDS_UMTS_QOS +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR TrafficClass; + ULONG MaxUplinkBitRate; + ULONG MaxDownlinkBitRate; + ULONG GuarUplinkBitRate; + ULONG GuarDownlinkBitRate; + UCHAR QOSDevOrder; + ULONG MAXSDUSize; + UCHAR SDUErrorRatio; + UCHAR ResidualBerRatio; + UCHAR DeliveryErrorSDUs; + ULONG TransferDelay; + ULONG TrafficHndPri; +}QMIWDS_UMTS_QOS, *PQMIWDS_UMTS_QOS; + +typedef struct _QMIWDS_GPRS_QOS +{ + UCHAR TLVType; + USHORT TLVLength; + ULONG PrecedenceClass; + ULONG DelayClass; + ULONG ReliabilityClass; + ULONG PeekThroClass; + ULONG MeanThroClass; +}QMIWDS_GPRS_QOS, *PQMIWDS_GPRS_QOS; +*/ +#endif + +typedef struct _QMIWDS_PROFILENAME { + UCHAR TLVType; + USHORT TLVLength; + UCHAR ProfileName; +} __attribute__((packed)) QMIWDS_PROFILENAME, *PQMIWDS_PROFILENAME; + +typedef struct _QMIWDS_PDPTYPE { + UCHAR TLVType; + USHORT TLVLength; + // 0 ?C PDP-IP (IPv4) + // 1 ?C PDP-PPP + // 2 ?C PDP-IPv6 + // 3 ?C PDP-IPv4v6 + UCHAR PdpType; +} __attribute__((packed)) QMIWDS_PDPTYPE, *PQMIWDS_PDPTYPE; + +typedef struct _QMIWDS_USERNAME { + UCHAR TLVType; + USHORT TLVLength; + UCHAR UserName; +} __attribute__((packed)) QMIWDS_USERNAME, *PQMIWDS_USERNAME; + +typedef struct _QMIWDS_PASSWD { + UCHAR TLVType; + USHORT TLVLength; + UCHAR Passwd; +} __attribute__((packed)) QMIWDS_PASSWD, *PQMIWDS_PASSWD; + +typedef struct _QMIWDS_AUTH_PREFERENCE { + UCHAR TLVType; + USHORT TLVLength; + UCHAR AuthPreference; +} __attribute__((packed)) QMIWDS_AUTH_PREFERENCE, *PQMIWDS_AUTH_PREFERENCE; + +typedef struct _QMIWDS_APNNAME { + UCHAR TLVType; + USHORT TLVLength; + UCHAR ApnName; +} __attribute__((packed)) QMIWDS_APNNAME, *PQMIWDS_APNNAME; + +typedef struct _QMIWDS_AUTOCONNECT { + UCHAR TLVType; + USHORT TLVLength; + UCHAR AutoConnect; +} __attribute__((packed)) QMIWDS_AUTOCONNECT, *PQMIWDS_AUTOCONNECT; + +typedef struct _QMIWDS_START_NETWORK_INTERFACE_REQ_MSG { + USHORT Type; + USHORT Length; +} __attribute__((packed)) QMIWDS_START_NETWORK_INTERFACE_REQ_MSG, + *PQMIWDS_START_NETWORK_INTERFACE_REQ_MSG; + +typedef struct _QMIWDS_CALLENDREASON { + UCHAR TLVType; + USHORT TLVLength; + USHORT Reason; +} __attribute__((packed)) QMIWDS_CALLENDREASON, *PQMIWDS_CALLENDREASON; + +typedef struct _QMIWDS_START_NETWORK_INTERFACE_RESP_MSG { + USHORT Type; // QMUX type 0x0040 + USHORT Length; + UCHAR TLVType; // 0x02 + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + + UCHAR TLV2Type; // 0x01 + USHORT TLV2Length; // 20 + ULONG Handle; // +} __attribute__((packed)) QMIWDS_START_NETWORK_INTERFACE_RESP_MSG, + *PQMIWDS_START_NETWORK_INTERFACE_RESP_MSG; + +typedef struct _QMIWDS_STOP_NETWORK_INTERFACE_REQ_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + ULONG Handle; +} __attribute__((packed)) QMIWDS_STOP_NETWORK_INTERFACE_REQ_MSG, + *PQMIWDS_STOP_NETWORK_INTERFACE_REQ_MSG; + +typedef struct _QMIWDS_STOP_NETWORK_INTERFACE_RESP_MSG { + USHORT Type; // QMUX type 0x0040 + USHORT Length; + UCHAR TLVType; // 0x02 + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + +} __attribute__((packed)) QMIWDS_STOP_NETWORK_INTERFACE_RESP_MSG, + *PQMIWDS_STOP_NETWORK_INTERFACE_RESP_MSG; + +typedef struct _QMIWDS_GET_DEFAULT_SETTINGS_REQ_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR ProfileType; +} __attribute__((packed)) QMIWDS_GET_DEFAULT_SETTINGS_REQ_MSG, + *PQMIWDS_GET_DEFAULT_SETTINGS_REQ_MSG; + +typedef struct _QMIWDS_GET_DEFAULT_SETTINGS_RESP_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} __attribute__((packed)) QMIWDS_GET_DEFAULT_SETTINGS_RESP_MSG, + *PQMIWDS_GET_DEFAULT_SETTINGS_RESP_MSG; +//begin modified by zhangkaibo add create profile qmi. mantis 0049137,0048741 20200610 +typedef struct _QMIWDS_CREATE_PROFILE_SETTINGS_REQ_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR ProfileType; +} __attribute__((packed)) QMIWDS_CREATE_PROFILE_SETTINGS_REQ_MSG, + *PQMIWDS_CREATE_PROFILE_SETTINGS_REQ_MSG; + + +typedef struct _QMIWDS_CREATE_PROFILE_SETTINGS_RESP_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} __attribute__((packed)) QMIWDS_CREATE_PROFILE_SETTINGS_RESP_MSG, + *PQMIWDS_CREATE_PROFILE_SETTINGS_RESP_MSG; +//end modified by zhangkaibo add create profile qmi. mantis 0049137,0048741 20200610 +typedef struct _QMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR ProfileType; + UCHAR ProfileIndex; +} __attribute__((packed)) QMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG, + *PQMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG; + +typedef struct _QMIWDS_MODIFY_PROFILE_SETTINGS_RESP_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} __attribute__((packed)) QMIWDS_MODIFY_PROFILE_SETTINGS_RESP_MSG, + *PQMIWDS_MODIFY_PROFILE_SETTINGS_RESP_MSG; + +typedef struct _QMIWDS_GET_PROFILE_SETTINGS_REQ_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR ProfileType; + UCHAR ProfileIndex; +} __attribute__((packed)) QMIWDS_GET_PROFILE_SETTINGS_REQ_MSG, + *PQMIWDS_GET_PROFILE_SETTINGS_REQ_MSG; + +#if 0 +typedef struct _QMIWDS_EVENT_REPORT_IND_DATA_BEARER_TLV +{ + UCHAR Type; + USHORT Length; + UCHAR DataBearer; +} QMIWDS_EVENT_REPORT_IND_DATA_BEARER_TLV, *PQMIWDS_EVENT_REPORT_IND_DATA_BEARER_TLV; + +typedef struct _QMIWDS_EVENT_REPORT_IND_DORMANCY_STATUS_TLV +{ + UCHAR Type; + USHORT Length; + UCHAR DormancyStatus; +} QMIWDS_EVENT_REPORT_IND_DORMANCY_STATUS_TLV, *PQMIWDS_EVENT_REPORT_IND_DORMANCY_STATUS_TLV; + + +typedef struct _QMIWDS_GET_DATA_BEARER_REQ_MSG +{ + USHORT Type; // QMUX type 0x0037 + USHORT Length; +} QMIWDS_GET_DATA_BEARER_REQ_MSG, *PQMIWDS_GET_DATA_BEARER_REQ_MSG; + +typedef struct _QMIWDS_GET_DATA_BEARER_RESP_MSG +{ + USHORT Type; // QMUX type 0x0037 + USHORT Length; + UCHAR TLVType; // 0x02 + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INTERNAL + // QMI_ERR_MALFORMED_MSG + // QMI_ERR_NO_MEMORY + // QMI_ERR_OUT_OF_CALL + // QMI_ERR_INFO_UNAVAILABLE + UCHAR TLV2Type; // 0x01 + USHORT TLV2Length; // + UCHAR Technology; // +} QMIWDS_GET_DATA_BEARER_RESP_MSG, *PQMIWDS_GET_DATA_BEARER_RESP_MSG; +#endif + +// ======================= DMS ============================== +#define QMIDMS_SET_EVENT_REPORT_REQ 0x0001 +#define QMIDMS_SET_EVENT_REPORT_RESP 0x0001 +#define QMIDMS_EVENT_REPORT_IND 0x0001 +#define QMIDMS_GET_DEVICE_CAP_REQ 0x0020 +#define QMIDMS_GET_DEVICE_CAP_RESP 0x0020 +#define QMIDMS_GET_DEVICE_MFR_REQ 0x0021 +#define QMIDMS_GET_DEVICE_MFR_RESP 0x0021 +#define QMIDMS_GET_DEVICE_MODEL_ID_REQ 0x0022 +#define QMIDMS_GET_DEVICE_MODEL_ID_RESP 0x0022 +#define QMIDMS_GET_DEVICE_REV_ID_REQ 0x0023 +#define QMIDMS_GET_DEVICE_REV_ID_RESP 0x0023 +#define QMIDMS_GET_MSISDN_REQ 0x0024 +#define QMIDMS_GET_MSISDN_RESP 0x0024 +#define QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ 0x0025 +#define QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP 0x0025 +#define QMIDMS_UIM_SET_PIN_PROTECTION_REQ 0x0027 +#define QMIDMS_UIM_SET_PIN_PROTECTION_RESP 0x0027 +#define QMIDMS_UIM_VERIFY_PIN_REQ 0x0028 +#define QMIDMS_UIM_VERIFY_PIN_RESP 0x0028 +#define QMIDMS_UIM_UNBLOCK_PIN_REQ 0x0029 +#define QMIDMS_UIM_UNBLOCK_PIN_RESP 0x0029 +#define QMIDMS_UIM_CHANGE_PIN_REQ 0x002A +#define QMIDMS_UIM_CHANGE_PIN_RESP 0x002A +#define QMIDMS_UIM_GET_PIN_STATUS_REQ 0x002B +#define QMIDMS_UIM_GET_PIN_STATUS_RESP 0x002B +#define QMIDMS_GET_DEVICE_HARDWARE_REV_REQ 0x002C +#define QMIDMS_GET_DEVICE_HARDWARE_REV_RESP 0x002C +#define QMIDMS_GET_OPERATING_MODE_REQ 0x002D +#define QMIDMS_GET_OPERATING_MODE_RESP 0x002D +#define QMIDMS_SET_OPERATING_MODE_REQ 0x002E +#define QMIDMS_SET_OPERATING_MODE_RESP 0x002E +#define QMIDMS_GET_ACTIVATED_STATUS_REQ 0x0031 +#define QMIDMS_GET_ACTIVATED_STATUS_RESP 0x0031 +#define QMIDMS_ACTIVATE_AUTOMATIC_REQ 0x0032 +#define QMIDMS_ACTIVATE_AUTOMATIC_RESP 0x0032 +#define QMIDMS_ACTIVATE_MANUAL_REQ 0x0033 +#define QMIDMS_ACTIVATE_MANUAL_RESP 0x0033 +#define QMIDMS_UIM_GET_ICCID_REQ 0x003C +#define QMIDMS_UIM_GET_ICCID_RESP 0x003C +#define QMIDMS_UIM_GET_CK_STATUS_REQ 0x0040 +#define QMIDMS_UIM_GET_CK_STATUS_RESP 0x0040 +#define QMIDMS_UIM_SET_CK_PROTECTION_REQ 0x0041 +#define QMIDMS_UIM_SET_CK_PROTECTION_RESP 0x0041 +#define QMIDMS_UIM_UNBLOCK_CK_REQ 0x0042 +#define QMIDMS_UIM_UNBLOCK_CK_RESP 0x0042 +#define QMIDMS_UIM_GET_IMSI_REQ 0x0043 +#define QMIDMS_UIM_GET_IMSI_RESP 0x0043 +#define QMIDMS_UIM_GET_STATE_REQ 0x0044 +#define QMIDMS_UIM_GET_STATE_RESP 0x0044 +#define QMIDMS_GET_BAND_CAP_REQ 0x0045 +#define QMIDMS_GET_BAND_CAP_RESP 0x0045 + +#if 0 +typedef struct _QMIDMS_GET_DEVICE_MFR_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; +} QMIDMS_GET_DEVICE_MFR_REQ_MSG, *PQMIDMS_GET_DEVICE_MFR_REQ_MSG; + +typedef struct _QMIDMS_GET_DEVICE_MFR_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; // 0x01 - required parameter + USHORT TLV2Length; // length of the mfr string + UCHAR DeviceManufacturer; // first byte of string +} QMIDMS_GET_DEVICE_MFR_RESP_MSG, *PQMIDMS_GET_DEVICE_MFR_RESP_MSG; + +typedef struct _QMIDMS_GET_DEVICE_MODEL_ID_REQ_MSG +{ + USHORT Type; // QMUX type 0x0004 + USHORT Length; +} QMIDMS_GET_DEVICE_MODEL_ID_REQ_MSG, *PQMIDMS_GET_DEVICE_MODEL_ID_REQ_MSG; + +typedef struct _QMIDMS_GET_DEVICE_MODEL_ID_RESP_MSG +{ + USHORT Type; // QMUX type 0x0004 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; // 0x01 - required parameter + USHORT TLV2Length; // length of the modem id string + UCHAR DeviceModelID; // device model id +} QMIDMS_GET_DEVICE_MODEL_ID_RESP_MSG, *PQMIDMS_GET_DEVICE_MODEL_ID_RESP_MSG; +#endif + +typedef struct _QMIDMS_GET_DEVICE_REV_ID_REQ_MSG { + USHORT Type; // QMUX type 0x0005 + USHORT Length; +} __attribute__((packed)) QMIDMS_GET_DEVICE_REV_ID_REQ_MSG, + *PQMIDMS_GET_DEVICE_REV_ID_REQ_MSG; + +typedef struct _DEVICE_REV_ID { + UCHAR TLVType; + USHORT TLVLength; + UCHAR RevisionID; +} __attribute__((packed)) DEVICE_REV_ID, *PDEVICE_REV_ID; + +#if 0 +typedef struct _QMIDMS_GET_DEVICE_REV_ID_RESP_MSG +{ + USHORT Type; // QMUX type 0x0023 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMIDMS_GET_DEVICE_REV_ID_RESP_MSG, *PQMIDMS_GET_DEVICE_REV_ID_RESP_MSG; + +typedef struct _QMIDMS_GET_MSISDN_REQ_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; +} QMIDMS_GET_MSISDN_REQ_MSG, *PQMIDMS_GET_MSISDN_REQ_MSG; + +typedef struct _QCTLV_DEVICE_VOICE_NUMBERS +{ + UCHAR TLVType; // as defined above + USHORT TLVLength; // 4/7/7 + UCHAR VoideNumberString; // ESN, IMEI, or MEID + +} QCTLV_DEVICE_VOICE_NUMBERS, *PQCTLV_DEVICE_VOICE_NUMBERS; + + +typedef struct _QMIDMS_GET_MSISDN_RESP_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG +} QMIDMS_GET_MSISDN_RESP_MSG, *PQMIDMS_GET_MSISDN_RESP_MSG; +#endif + +typedef struct _QMIDMS_UIM_GET_IMSI_REQ_MSG { + USHORT Type; + USHORT Length; +} __attribute__((packed)) QMIDMS_UIM_GET_IMSI_REQ_MSG, + *PQMIDMS_UIM_GET_IMSI_REQ_MSG; + +typedef struct _QMIDMS_UIM_GET_IMSI_RESP_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR IMSI; +} __attribute__((packed)) QMIDMS_UIM_GET_IMSI_RESP_MSG, + *PQMIDMS_UIM_GET_IMSI_RESP_MSG; + +#if 0 +typedef struct _QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ_MSG +{ + USHORT Type; // QMUX type 0x0007 + USHORT Length; +} QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ_MSG, *PQMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ_MSG; + +#define QCTLV_TYPE_SER_NUM_ESN 0x10 +#define QCTLV_TYPE_SER_NUM_IMEI 0x11 +#define QCTLV_TYPE_SER_NUM_MEID 0x12 + +typedef struct _QCTLV_DEVICE_SERIAL_NUMBER +{ + UCHAR TLVType; // as defined above + USHORT TLVLength; // 4/7/7 + UCHAR SerialNumberString; // ESN, IMEI, or MEID + +} QCTLV_DEVICE_SERIAL_NUMBER, *PQCTLV_DEVICE_SERIAL_NUMBER; + +typedef struct _QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP_MSG +{ + USHORT Type; // QMUX type 0x0007 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + // followed by optional TLV +} QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP_MSG, *PQMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP; + +typedef struct _QMIDMS_GET_DMS_BAND_CAP +{ + USHORT Type; + USHORT Length; +} QMIDMS_GET_BAND_CAP_REQ_MSG, *PQMIDMS_GET_BAND_CAP_REQ_MSG; + +typedef struct _QMIDMS_GET_BAND_CAP_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_NONE + // QMI_ERR_INTERNAL + // QMI_ERR_MALFORMED_MSG + // QMI_ERR_NO_MEMORY + + UCHAR TLV2Type; // 0x01 + USHORT TLV2Length; // 2 + ULONG64 BandCap; +} QMIDMS_GET_BAND_CAP_RESP_MSG, *PQMIDMS_GET_BAND_CAP_RESP; + +typedef struct _QMIDMS_GET_DEVICE_CAP_REQ_MSG +{ + USHORT Type; // QMUX type 0x0002 + USHORT Length; +} QMIDMS_GET_DEVICE_CAP_REQ_MSG, *PQMIDMS_GET_DEVICE_CAP_REQ_MSG; + +typedef struct _QMIDMS_GET_DEVICE_CAP_RESP_MSG +{ + USHORT Type; // QMUX type 0x0002 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMUX_RESULT_SUCCESS + // QMUX_RESULT_FAILURE + USHORT QMUXError; // QMUX_ERR_INVALID_ARG + // QMUX_ERR_NO_MEMORY + // QMUX_ERR_INTERNAL + // QMUX_ERR_FAULT + UCHAR TLV2Type; // 0x01 + USHORT TLV2Length; // 2 + + ULONG MaxTxChannelRate; + ULONG MaxRxChannelRate; + UCHAR VoiceCap; + UCHAR SimCap; + + UCHAR RadioIfListCnt; // #elements in radio interface list + UCHAR RadioIfList; // N 1-byte elements +} QMIDMS_GET_DEVICE_CAP_RESP_MSG, *PQMIDMS_GET_DEVICE_CAP_RESP_MSG; + +typedef struct _QMIDMS_GET_ACTIVATED_STATUS_REQ_MSG +{ + USHORT Type; // QMUX type 0x0002 + USHORT Length; +} QMIDMS_GET_ACTIVATED_STATUS_REQ_MSG, *PQMIDMS_GET_ACTIVATES_STATUD_REQ_MSG; + +typedef struct _QMIDMS_GET_ACTIVATED_STATUS_RESP_MSG +{ + USHORT Type; // QMUX type 0x0002 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMUX_RESULT_SUCCESS + // QMUX_RESULT_FAILURE + USHORT QMUXError; // QMUX_ERR_INVALID_ARG + // QMUX_ERR_NO_MEMORY + // QMUX_ERR_INTERNAL + // QMUX_ERR_FAULT + UCHAR TLV2Type; // 0x01 + USHORT TLV2Length; // 2 + + USHORT ActivatedStatus; +} QMIDMS_GET_ACTIVATED_STATUS_RESP_MSG, *PQMIDMS_GET_ACTIVATED_STATUS_RESP_MSG; + +typedef struct _QMIDMS_GET_OPERATING_MODE_REQ_MSG +{ + USHORT Type; // QMUX type 0x0002 + USHORT Length; +} QMIDMS_GET_OPERATING_MODE_REQ_MSG, *PQMIDMS_GET_OPERATING_MODE_REQ_MSG; + +typedef struct _OFFLINE_REASON +{ + UCHAR TLVType; + USHORT TLVLength; + USHORT OfflineReason; +} OFFLINE_REASON, *POFFLINE_REASON; + +typedef struct _HARDWARE_RESTRICTED_MODE +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR HardwareControlledMode; +} HARDWARE_RESTRICTED_MODE, *PHARDWARE_RESTRICTED_MODE; + +typedef struct _QMIDMS_GET_OPERATING_MODE_RESP_MSG +{ + USHORT Type; // QMUX type 0x0002 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMUX_RESULT_SUCCESS + // QMUX_RESULT_FAILURE + USHORT QMUXError; // QMUX_ERR_INVALID_ARG + // QMUX_ERR_NO_MEMORY + // QMUX_ERR_INTERNAL + // QMUX_ERR_FAULT + UCHAR TLV2Type; // 0x01 + USHORT TLV2Length; // 2 + + UCHAR OperatingMode; +} QMIDMS_GET_OPERATING_MODE_RESP_MSG, *PQMIDMS_GET_OPERATING_MODE_RESP_MSG; + +typedef struct _QMIDMS_UIM_GET_ICCID_REQ_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; +} QMIDMS_UIM_GET_ICCID_REQ_MSG, *PQMIDMS_UIM_GET_ICCID_REQ_MSG; + +typedef struct _QMIDMS_UIM_GET_ICCID_RESP_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; // 0x01 - required parameter + USHORT TLV2Length; // var + UCHAR ICCID; // String of voice number +} QMIDMS_UIM_GET_ICCID_RESP_MSG, *PQMIDMS_UIM_GET_ICCID_RESP_MSG; +#endif + +typedef struct _QMIDMS_SET_OPERATING_MODE_REQ_MSG { + USHORT Type; // QMUX type 0x0002 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + UCHAR OperatingMode; +} __attribute__((packed)) QMIDMS_SET_OPERATING_MODE_REQ_MSG, + *PQMIDMS_SET_OPERATING_MODE_REQ_MSG; + +typedef struct _QMIDMS_SET_OPERATING_MODE_RESP_MSG { + USHORT Type; // QMUX type 0x0002 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMUX_RESULT_SUCCESS + // QMUX_RESULT_FAILURE + USHORT QMUXError; // QMUX_ERR_INVALID_ARG + // QMUX_ERR_NO_MEMORY + // QMUX_ERR_INTERNAL + // QMUX_ERR_FAULT +} __attribute__((packed)) QMIDMS_SET_OPERATING_MODE_RESP_MSG, + *PQMIDMS_SET_OPERATING_MODE_RESP_MSG; + +#if 0 +typedef struct _QMIDMS_ACTIVATE_AUTOMATIC_REQ_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // + UCHAR ActivateCodelen; + UCHAR ActivateCode; +} QMIDMS_ACTIVATE_AUTOMATIC_REQ_MSG, *PQMIDMS_ACTIVATE_AUTOMATIC_REQ_MSG; + +typedef struct _QMIDMS_ACTIVATE_AUTOMATIC_RESP_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMIDMS_ACTIVATE_AUTOMATIC_RESP_MSG, *PQMIDMS_ACTIVATE_AUTOMATIC_RESP_MSG; + + +typedef struct _SPC_MSG +{ + UCHAR SPC[6]; + USHORT SID; +} SPC_MSG, *PSPC_MSG; + +typedef struct _MDN_MSG +{ + UCHAR MDNLEN; + UCHAR MDN; +} MDN_MSG, *PMDN_MSG; + +typedef struct _MIN_MSG +{ + UCHAR MINLEN; + UCHAR MIN; +} MIN_MSG, *PMIN_MSG; + +typedef struct _PRL_MSG +{ + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // + USHORT PRLLEN; + UCHAR PRL; +} PRL_MSG, *PPRL_MSG; + +typedef struct _MN_HA_KEY_MSG +{ + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // + UCHAR MN_HA_KEY_LEN; + UCHAR MN_HA_KEY; +} MN_HA_KEY_MSG, *PMN_HA_KEY_MSG; + +typedef struct _MN_AAA_KEY_MSG +{ + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // + UCHAR MN_AAA_KEY_LEN; + UCHAR MN_AAA_KEY; +} MN_AAA_KEY_MSG, *PMN_AAA_KEY_MSG; + +typedef struct _QMIDMS_ACTIVATE_MANUAL_REQ_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // + UCHAR Value; +} QMIDMS_ACTIVATE_MANUAL_REQ_MSG, *PQMIDMS_ACTIVATE_MANUAL_REQ_MSG; + +typedef struct _QMIDMS_ACTIVATE_MANUAL_RESP_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMIDMS_ACTIVATE_MANUAL_RESP_MSG, *PQMIDMS_ACTIVATE_MANUAL_RESP_MSG; +#endif + +typedef struct _QMIDMS_UIM_GET_STATE_REQ_MSG { + USHORT Type; + USHORT Length; +} __attribute__((packed)) QMIDMS_UIM_GET_STATE_REQ_MSG, + *PQMIDMS_UIM_GET_STATE_REQ_MSG; + +typedef struct _QMIDMS_UIM_GET_STATE_RESP_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR UIMState; +} __attribute__((packed)) QMIDMS_UIM_GET_STATE_RESP_MSG, + *PQMIDMS_UIM_GET_STATE_RESP_MSG; + +typedef struct _QMIDMS_UIM_GET_PIN_STATUS_REQ_MSG { + USHORT Type; // QMUX type 0x0024 + USHORT Length; +} __attribute__((packed)) QMIDMS_UIM_GET_PIN_STATUS_REQ_MSG, + *PQMIDMS_UIM_GET_PIN_STATUS_REQ_MSG; + +typedef struct _QMIDMS_UIM_PIN_STATUS { + UCHAR TLVType; + USHORT TLVLength; + UCHAR PINStatus; + UCHAR PINVerifyRetriesLeft; + UCHAR PINUnblockRetriesLeft; +} __attribute__((packed)) QMIDMS_UIM_PIN_STATUS, *PQMIDMS_UIM_PIN_STATUS; + +#define QMI_PIN_STATUS_NOT_INIT 0 +#define QMI_PIN_STATUS_NOT_VERIF 1 +#define QMI_PIN_STATUS_VERIFIED 2 +#define QMI_PIN_STATUS_DISABLED 3 +#define QMI_PIN_STATUS_BLOCKED 4 +#define QMI_PIN_STATUS_PERM_BLOCKED 5 +#define QMI_PIN_STATUS_UNBLOCKED 6 +#define QMI_PIN_STATUS_CHANGED 7 + +typedef struct _QMIDMS_UIM_GET_PIN_STATUS_RESP_MSG { + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR PinStatus; +} __attribute__((packed)) QMIDMS_UIM_GET_PIN_STATUS_RESP_MSG, + *PQMIDMS_UIM_GET_PIN_STATUS_RESP_MSG; + +#if 0 +typedef struct _QMIDMS_UIM_GET_CK_STATUS_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR Facility; +} QMIDMS_UIM_GET_CK_STATUS_REQ_MSG, *PQMIDMS_UIM_GET_CK_STATUS_REQ_MSG; + + +typedef struct _QMIDMS_UIM_CK_STATUS +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR FacilityStatus; + UCHAR FacilityVerifyRetriesLeft; + UCHAR FacilityUnblockRetriesLeft; +} QMIDMS_UIM_CK_STATUS, *PQMIDMS_UIM_CK_STATUS; + +typedef struct _QMIDMS_UIM_CK_OPERATION_STATUS +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR OperationBlocking; +} QMIDMS_UIM_CK_OPERATION_STATUS, *PQMIDMS_UIM_CK_OPERATION_STATUS; + +typedef struct _QMIDMS_UIM_GET_CK_STATUS_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR CkStatus; +} QMIDMS_UIM_GET_CK_STATUS_RESP_MSG, *PQMIDMS_UIM_GET_CK_STATUS_RESP_MSG; +#endif + +typedef struct _QMIDMS_UIM_VERIFY_PIN_REQ_MSG { + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + UCHAR PINID; + UCHAR PINLen; + UCHAR PINValue; +} __attribute__((packed)) QMIDMS_UIM_VERIFY_PIN_REQ_MSG, + *PQMIDMS_UIM_VERIFY_PIN_REQ_MSG; + +typedef struct _QMIDMS_UIM_VERIFY_PIN_RESP_MSG { + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR PINVerifyRetriesLeft; + UCHAR PINUnblockRetriesLeft; +} __attribute__((packed)) QMIDMS_UIM_VERIFY_PIN_RESP_MSG, + *PQMIDMS_UIM_VERIFY_PIN_RESP_MSG; + +#if 0 +typedef struct _QMIDMS_UIM_SET_PIN_PROTECTION_REQ_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + UCHAR PINID; + UCHAR ProtectionSetting; + UCHAR PINLen; + UCHAR PINValue; +} QMIDMS_UIM_SET_PIN_PROTECTION_REQ_MSG, *PQMIDMS_UIM_SET_PIN_PROTECTION_REQ_MSG; + +typedef struct _QMIDMS_UIM_SET_PIN_PROTECTION_RESP_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR PINVerifyRetriesLeft; + UCHAR PINUnblockRetriesLeft; +} QMIDMS_UIM_SET_PIN_PROTECTION_RESP_MSG, *PQMIDMS_UIM_SET_PIN_PROTECTION_RESP_MSG; + +typedef struct _QMIDMS_UIM_SET_CK_PROTECTION_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR Facility; + UCHAR FacilityState; + UCHAR FacliltyLen; + UCHAR FacliltyValue; +} QMIDMS_UIM_SET_CK_PROTECTION_REQ_MSG, *PQMIDMS_UIM_SET_CK_PROTECTION_REQ_MSG; + +typedef struct _QMIDMS_UIM_SET_CK_PROTECTION_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR FacilityRetriesLeft; +} QMIDMS_UIM_SET_CK_PROTECTION_RESP_MSG, *PQMIDMS_UIM_SET_CK_PROTECTION_RESP_MSG; + + +typedef struct _UIM_PIN +{ + UCHAR PinLength; + UCHAR PinValue; +} UIM_PIN, *PUIM_PIN; + +typedef struct _QMIDMS_UIM_CHANGE_PIN_REQ_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + UCHAR PINID; + UCHAR PinDetails; +} QMIDMS_UIM_CHANGE_PIN_REQ_MSG, *PQMIDMS_UIM_CHANGE_PIN_REQ_MSG; + +typedef struct QMIDMS_UIM_CHANGE_PIN_RESP_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR PINVerifyRetriesLeft; + UCHAR PINUnblockRetriesLeft; +} QMIDMS_UIM_CHANGE_PIN_RESP_MSG, *PQMIDMS_UIM_CHANGE_PIN_RESP_MSG; + +typedef struct _UIM_PUK +{ + UCHAR PukLength; + UCHAR PukValue; +} UIM_PUK, *PUIM_PUK; + +typedef struct _QMIDMS_UIM_UNBLOCK_PIN_REQ_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + UCHAR PINID; + UCHAR PinDetails; +} QMIDMS_UIM_UNBLOCK_PIN_REQ_MSG, *PQMIDMS_UIM_BLOCK_PIN_REQ_MSG; + +typedef struct QMIDMS_UIM_UNBLOCK_PIN_RESP_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR PINVerifyRetriesLeft; + UCHAR PINUnblockRetriesLeft; +} QMIDMS_UIM_UNBLOCK_PIN_RESP_MSG, *PQMIDMS_UIM_UNBLOCK_PIN_RESP_MSG; + +typedef struct _QMIDMS_UIM_UNBLOCK_CK_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR Facility; + UCHAR FacliltyUnblockLen; + UCHAR FacliltyUnblockValue; +} QMIDMS_UIM_UNBLOCK_CK_REQ_MSG, *PQMIDMS_UIM_BLOCK_CK_REQ_MSG; + +typedef struct QMIDMS_UIM_UNBLOCK_CK_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR FacilityUnblockRetriesLeft; +} QMIDMS_UIM_UNBLOCK_CK_RESP_MSG, *PQMIDMS_UIM_UNBLOCK_CK_RESP_MSG; + +typedef struct _QMIDMS_SET_EVENT_REPORT_REQ_MSG +{ + USHORT Type; + USHORT Length; +} QMIDMS_SET_EVENT_REPORT_REQ_MSG, *PQMIDMS_SET_EVENT_REPORT_REQ_MSG; + +typedef struct _QMIDMS_SET_EVENT_REPORT_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG +} QMIDMS_SET_EVENT_REPORT_RESP_MSG, *PQMIDMS_SET_EVENT_REPORT_RESP_MSG; + +typedef struct _PIN_STATUS +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR ReportPinState; +} PIN_STATUS, *PPIN_STATUS; + +typedef struct _POWER_STATUS +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR PowerStatus; + UCHAR BatteryLvl; +} POWER_STATUS, *PPOWER_STATUS; + +typedef struct _ACTIVATION_STATE +{ + UCHAR TLVType; + USHORT TLVLength; + USHORT ActivationState; +} ACTIVATION_STATE, *PACTIVATION_STATE; + +typedef struct _ACTIVATION_STATE_REQ +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR ActivationState; +} ACTIVATION_STATE_REQ, *PACTIVATION_STATE_REQ; + +typedef struct _OPERATING_MODE +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR OperatingMode; +} OPERATING_MODE, *POPERATING_MODE; + +typedef struct _UIM_STATE +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR UIMState; +} UIM_STATE, *PUIM_STATE; + +typedef struct _WIRELESS_DISABLE_STATE +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR WirelessDisableState; +} WIRELESS_DISABLE_STATE, *PWIRELESS_DISABLE_STATE; + +typedef struct _QMIDMS_EVENT_REPORT_IND_MSG +{ + USHORT Type; + USHORT Length; +} QMIDMS_EVENT_REPORT_IND_MSG, *PQMIDMS_EVENT_REPORT_IND_MSG; +#endif + +// ============================ END OF DMS =============================== + +// ======================= QOS ============================== +typedef struct _MPIOC_DEV_INFO MPIOC_DEV_INFO, *PMPIOC_DEV_INFO; + +#define QMI_QOS_SET_EVENT_REPORT_REQ 0x0001 +#define QMI_QOS_SET_EVENT_REPORT_RESP 0x0001 +#define QMI_QOS_EVENT_REPORT_IND 0x0001 + +#if 0 +typedef struct _QMI_QOS_SET_EVENT_REPORT_REQ_MSG +{ + USHORT Type; // QMUX type 0x0001 + USHORT Length; + // UCHAR TLVType; // 0x01 - physical link state + // USHORT TLVLength; // 1 + // UCHAR PhyLinkStatusRpt; // 0-enable; 1-disable + UCHAR TLVType2; // 0x02 = global flow reporting + USHORT TLVLength2; // 1 + UCHAR GlobalFlowRpt; // 1-enable; 0-disable +} QMI_QOS_SET_EVENT_REPORT_REQ_MSG, *PQMI_QOS_SET_EVENT_REPORT_REQ_MSG; + +typedef struct _QMI_QOS_SET_EVENT_REPORT_RESP_MSG +{ + USHORT Type; // QMUX type 0x0010 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMUX_RESULT_SUCCESS + // QMUX_RESULT_FAILURE + USHORT QMUXError; // QMUX_ERR_INVALID_ARG + // QMUX_ERR_NO_MEMORY + // QMUX_ERR_INTERNAL + // QMUX_ERR_FAULT +} QMI_QOS_SET_EVENT_REPORT_RESP_MSG, *PQMI_QOS_SET_EVENT_REPORT_RESP_MSG; + +typedef struct _QMI_QOS_EVENT_REPORT_IND_MSG +{ + USHORT Type; // QMUX type 0x0001 + USHORT Length; + UCHAR TLVs; +} QMI_QOS_EVENT_REPORT_IND_MSG, *PQMI_QOS_EVENT_REPORT_IND_MSG; + +#define QOS_EVENT_RPT_IND_FLOW_ACTIVATED 0x01 +#define QOS_EVENT_RPT_IND_FLOW_MODIFIED 0x02 +#define QOS_EVENT_RPT_IND_FLOW_DELETED 0x03 +#define QOS_EVENT_RPT_IND_FLOW_SUSPENDED 0x04 +#define QOS_EVENT_RPT_IND_FLOW_ENABLED 0x05 +#define QOS_EVENT_RPT_IND_FLOW_DISABLED 0x06 + +#define QOS_EVENT_RPT_IND_TLV_PHY_LINK_STATE_TYPE 0x01 +#define QOS_EVENT_RPT_IND_TLV_GLOBAL_FL_RPT_STATE 0x10 +#define QOS_EVENT_RPT_IND_TLV_GLOBAL_FL_RPT_TYPE 0x10 +#define QOS_EVENT_RPT_IND_TLV_TX_FLOW_TYPE 0x11 +#define QOS_EVENT_RPT_IND_TLV_RX_FLOW_TYPE 0x12 +#define QOS_EVENT_RPT_IND_TLV_TX_FILTER_TYPE 0x13 +#define QOS_EVENT_RPT_IND_TLV_RX_FILTER_TYPE 0x14 +#define QOS_EVENT_RPT_IND_TLV_FLOW_SPEC 0x10 +#define QOS_EVENT_RPT_IND_TLV_FILTER_SPEC 0x10 + +typedef struct _QOS_EVENT_RPT_IND_TLV_PHY_LINK_STATE +{ + UCHAR TLVType; // 0x01 + USHORT TLVLength; // 1 + UCHAR PhyLinkState; // 0-dormant, 1-active +} QOS_EVENT_RPT_IND_TLV_PHY_LINK_STATE, *PQOS_EVENT_RPT_IND_TLV_PHY_LINK_STATE; + +typedef struct _QOS_EVENT_RPT_IND_TLV_GLOBAL_FL_RPT +{ + UCHAR TLVType; // 0x10 + USHORT TLVLength; // 6 + ULONG QosId; + UCHAR NewFlow; // 1: newly added flow; 0: existing flow + UCHAR StateChange; // 1: activated; 2: modified; 3: deleted; + // 4: suspended(delete); 5: enabled; 6: disabled +} QOS_EVENT_RPT_IND_TLV_GLOBAL_FL_RPT, *PQOS_EVENT_RPT_IND_TLV_GLOBAL_FL_RPT; + +// QOS Flow + +typedef struct _QOS_EVENT_RPT_IND_TLV_FLOW +{ + UCHAR TLVType; // 0x10-TX flow; 0x11-RX flow + USHORT TLVLength; // var + // embedded TLV's +} QOS_EVENT_RPT_IND_TLV_TX_FLOW, *PQOS_EVENT_RPT_IND_TLV_TX_FLOW; + +#define QOS_FLOW_TLV_IP_FLOW_IDX_TYPE 0x10 +#define QOS_FLOW_TLV_IP_FLOW_TRAFFIC_CLASS_TYPE 0x11 +#define QOS_FLOW_TLV_IP_FLOW_DATA_RATE_MIN_MAX_TYPE 0x12 +#define QOS_FLOW_TLV_IP_FLOW_DATA_RATE_TOKEN_BUCKET_TYPE 0x13 +#define QOS_FLOW_TLV_IP_FLOW_LATENCY_TYPE 0x14 +#define QOS_FLOW_TLV_IP_FLOW_JITTER_TYPE 0x15 +#define QOS_FLOW_TLV_IP_FLOW_PKT_ERR_RATE_TYPE 0x16 +#define QOS_FLOW_TLV_IP_FLOW_MIN_PKT_SIZE_TYPE 0x17 +#define QOS_FLOW_TLV_IP_FLOW_MAX_PKT_SIZE_TYPE 0x18 +#define QOS_FLOW_TLV_IP_FLOW_3GPP_BIT_ERR_RATE_TYPE 0x19 +#define QOS_FLOW_TLV_IP_FLOW_3GPP_TRAF_PRIORITY_TYPE 0x1A +#define QOS_FLOW_TLV_IP_FLOW_3GPP2_PROFILE_ID_TYPE 0x1B + +typedef struct _QOS_FLOW_TLV_IP_FLOW_IDX +{ + UCHAR TLVType; // 0x10 + USHORT TLVLength; // 1 + UCHAR IpFlowIndex; +} QOS_FLOW_TLV_IP_FLOW_IDX, *PQOS_FLOW_TLV_IP_FLOW_IDX; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_TRAFFIC_CLASS +{ + UCHAR TLVType; // 0x11 + USHORT TLVLength; // 1 + UCHAR TrafficClass; +} QOS_FLOW_TLV_IP_FLOW_TRAFFIC_CLASS, *PQOS_FLOW_TLV_IP_FLOW_TRAFFIC_CLASS; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_DATA_RATE_MIN_MAX +{ + UCHAR TLVType; // 0x12 + USHORT TLVLength; // 8 + ULONG DataRateMax; + ULONG GuaranteedRate; +} QOS_FLOW_TLV_IP_FLOW_DATA_RATE_MIN_MAX, *PQOS_FLOW_TLV_IP_FLOW_DATA_RATE_MIN_MAX; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_DATA_RATE_TOKEN_BUCKET +{ + UCHAR TLVType; // 0x13 + USHORT TLVLength; // 12 + ULONG PeakRate; + ULONG TokenRate; + ULONG BucketSize; +} QOS_FLOW_TLV_IP_FLOW_DATA_RATE_TOKEN_BUCKET, *PQOS_FLOW_TLV_IP_FLOW_DATA_RATE_TOKEN_BUCKET; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_LATENCY +{ + UCHAR TLVType; // 0x14 + USHORT TLVLength; // 4 + ULONG IpFlowLatency; +} QOS_FLOW_TLV_IP_FLOW_LATENCY, *PQOS_FLOW_TLV_IP_FLOW_LATENCY; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_JITTER +{ + UCHAR TLVType; // 0x15 + USHORT TLVLength; // 4 + ULONG IpFlowJitter; +} QOS_FLOW_TLV_IP_FLOW_JITTER, *PQOS_FLOW_TLV_IP_FLOW_JITTER; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_PKT_ERR_RATE +{ + UCHAR TLVType; // 0x16 + USHORT TLVLength; // 4 + USHORT ErrRateMultiplier; + USHORT ErrRateExponent; +} QOS_FLOW_TLV_IP_FLOW_PKT_ERR_RATE, *PQOS_FLOW_TLV_IP_FLOW_PKT_ERR_RATE; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_MIN_PKT_SIZE +{ + UCHAR TLVType; // 0x17 + USHORT TLVLength; // 4 + ULONG MinPolicedPktSize; +} QOS_FLOW_TLV_IP_FLOW_MIN_PKT_SIZE, *PQOS_FLOW_TLV_IP_FLOW_MIN_PKT_SIZE; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_MAX_PKT_SIZE +{ + UCHAR TLVType; // 0x18 + USHORT TLVLength; // 4 + ULONG MaxAllowedPktSize; +} QOS_FLOW_TLV_IP_FLOW_MAX_PKT_SIZE, *PQOS_FLOW_TLV_IP_FLOW_MAX_PKT_SIZE; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_3GPP_BIT_ERR_RATE +{ + UCHAR TLVType; // 0x19 + USHORT TLVLength; // 1 + UCHAR ResidualBitErrorRate; +} QOS_FLOW_TLV_IP_FLOW_3GPP_BIT_ERR_RATE, *PQOS_FLOW_TLV_IP_FLOW_3GPP_BIT_ERR_RATE; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_3GPP_TRAF_PRIORITY +{ + UCHAR TLVType; // 0x1A + USHORT TLVLength; // 1 + UCHAR TrafficHandlingPriority; +} QOS_FLOW_TLV_IP_FLOW_3GPP_TRAF_PRIORITY, *PQOS_FLOW_TLV_IP_FLOW_3GPP_TRAF_PRIORITY; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_3GPP2_PROFILE_ID +{ + UCHAR TLVType; // 0x1B + USHORT TLVLength; // 2 + USHORT ProfileId; +} QOS_FLOW_TLV_IP_FLOW_3GPP2_PROFILE_ID, *PQOS_FLOW_TLV_IP_FLOW_3GPP2_PROFILE_ID; + +// QOS Filter + +#define QOS_FILTER_TLV_IP_FILTER_IDX_TYPE 0x10 +#define QOS_FILTER_TLV_IP_VERSION_TYPE 0x11 +#define QOS_FILTER_TLV_IPV4_SRC_ADDR_TYPE 0x12 +#define QOS_FILTER_TLV_IPV4_DEST_ADDR_TYPE 0x13 +#define QOS_FILTER_TLV_NEXT_HDR_PROTOCOL_TYPE 0x14 +#define QOS_FILTER_TLV_IPV4_TYPE_OF_SERVICE_TYPE 0x15 +#define QOS_FILTER_TLV_TCP_UDP_PORT_SRC_TCP_TYPE 0x1B +#define QOS_FILTER_TLV_TCP_UDP_PORT_DEST_TCP_TYPE 0x1C +#define QOS_FILTER_TLV_TCP_UDP_PORT_SRC_UDP_TYPE 0x1D +#define QOS_FILTER_TLV_TCP_UDP_PORT_DEST_UDP_TYPE 0x1E +#define QOS_FILTER_TLV_ICMP_FILTER_MSG_TYPE_TYPE 0x1F +#define QOS_FILTER_TLV_ICMP_FILTER_MSG_CODE_TYPE 0x20 +#define QOS_FILTER_TLV_TCP_UDP_PORT_SRC_TYPE 0x24 +#define QOS_FILTER_TLV_TCP_UDP_PORT_DEST_TYPE 0x25 + +typedef struct _QOS_EVENT_RPT_IND_TLV_FILTER +{ + UCHAR TLVType; // 0x12-TX filter; 0x13-RX filter + USHORT TLVLength; // var + // embedded TLV's +} QOS_EVENT_RPT_IND_TLV_RX_FILTER, *PQOS_EVENT_RPT_IND_TLV_RX_FILTER; + +typedef struct _QOS_FILTER_TLV_IP_FILTER_IDX +{ + UCHAR TLVType; // 0x10 + USHORT TLVLength; // 1 + UCHAR IpFilterIndex; +} QOS_FILTER_TLV_IP_FILTER_IDX, *PQOS_FILTER_TLV_IP_FILTER_IDX; + +typedef struct _QOS_FILTER_TLV_IP_VERSION +{ + UCHAR TLVType; // 0x11 + USHORT TLVLength; // 1 + UCHAR IpVersion; +} QOS_FILTER_TLV_IP_VERSION, *PQOS_FILTER_TLV_IP_VERSION; + +typedef struct _QOS_FILTER_TLV_IPV4_SRC_ADDR +{ + UCHAR TLVType; // 0x12 + USHORT TLVLength; // 8 + ULONG IpSrcAddr; + ULONG IpSrcSubnetMask; +} QOS_FILTER_TLV_IPV4_SRC_ADDR, *PQOS_FILTER_TLV_IPV4_SRC_ADDR; + +typedef struct _QOS_FILTER_TLV_IPV4_DEST_ADDR +{ + UCHAR TLVType; // 0x13 + USHORT TLVLength; // 8 + ULONG IpDestAddr; + ULONG IpDestSubnetMask; +} QOS_FILTER_TLV_IPV4_DEST_ADDR, *PQOS_FILTER_TLV_IPV4_DEST_ADDR; + +typedef struct _QOS_FILTER_TLV_NEXT_HDR_PROTOCOL +{ + UCHAR TLVType; // 0x14 + USHORT TLVLength; // 1 + UCHAR NextHdrProtocol; +} QOS_FILTER_TLV_NEXT_HDR_PROTOCOL, *PQOS_FILTER_TLV_NEXT_HDR_PROTOCOL; + +typedef struct _QOS_FILTER_TLV_IPV4_TYPE_OF_SERVICE +{ + UCHAR TLVType; // 0x15 + USHORT TLVLength; // 2 + UCHAR Ipv4TypeOfService; + UCHAR Ipv4TypeOfServiceMask; +} QOS_FILTER_TLV_IPV4_TYPE_OF_SERVICE, *PQOS_FILTER_TLV_IPV4_TYPE_OF_SERVICE; + +typedef struct _QOS_FILTER_TLV_TCP_UDP_PORT +{ + UCHAR TLVType; // source port: 0x1B-TCP; 0x1D-UDP + // dest port: 0x1C-TCP; 0x1E-UDP + USHORT TLVLength; // 4 + USHORT FilterPort; + USHORT FilterPortRange; +} QOS_FILTER_TLV_TCP_UDP_PORT, *PQOS_FILTER_TLV_TCP_UDP_PORT; + +typedef struct _QOS_FILTER_TLV_ICMP_FILTER_MSG_TYPE +{ + UCHAR TLVType; // 0x1F + USHORT TLVLength; // 1 + UCHAR IcmpFilterMsgType; +} QOS_FILTER_TLV_ICMP_FILTER_MSG_TYPE, *PQOS_FILTER_TLV_ICMP_FILTER_MSG_TYPE; + +typedef struct _QOS_FILTER_TLV_ICMP_FILTER_MSG_CODE +{ + UCHAR TLVType; // 0x20 + USHORT TLVLength; // 1 + UCHAR IcmpFilterMsgCode; +} QOS_FILTER_TLV_ICMP_FILTER_MSG_CODE, *PQOS_FILTER_TLV_ICMP_FILTER_MSG_CODE; + +#define QOS_FILTER_PRECEDENCE_INVALID 256 +#define QOS_FILTER_TLV_PRECEDENCE_TYPE 0x22 +#define QOS_FILTER_TLV_ID_TYPE 0x23 + +typedef struct _QOS_FILTER_TLV_PRECEDENCE +{ + UCHAR TLVType; // 0x22 + USHORT TLVLength; // 2 + USHORT Precedence; // precedence of the filter +} QOS_FILTER_TLV_PRECEDENCE, *PQOS_FILTER_TLV_PRECEDENCE; + +typedef struct _QOS_FILTER_TLV_ID +{ + UCHAR TLVType; // 0x23 + USHORT TLVLength; // 2 + USHORT FilterId; // filter ID +} QOS_FILTER_TLV_ID, *PQOS_FILTER_TLV_ID; + +#ifdef QCQOS_IPV6 + +#define QOS_FILTER_TLV_IPV6_SRC_ADDR_TYPE 0x16 +#define QOS_FILTER_TLV_IPV6_DEST_ADDR_TYPE 0x17 +#define QOS_FILTER_TLV_IPV6_NEXT_HDR_PROTOCOL_TYPE 0x14 // same as IPV4 +#define QOS_FILTER_TLV_IPV6_TRAFFIC_CLASS_TYPE 0x19 +#define QOS_FILTER_TLV_IPV6_FLOW_LABEL_TYPE 0x1A + +typedef struct _QOS_FILTER_TLV_IPV6_SRC_ADDR +{ + UCHAR TLVType; // 0x16 + USHORT TLVLength; // 17 + UCHAR IpSrcAddr[16]; + UCHAR IpSrcAddrPrefixLen; // [0..128] +} QOS_FILTER_TLV_IPV6_SRC_ADDR, *PQOS_FILTER_TLV_IPV6_SRC_ADDR; + +typedef struct _QOS_FILTER_TLV_IPV6_DEST_ADDR +{ + UCHAR TLVType; // 0x17 + USHORT TLVLength; // 17 + UCHAR IpDestAddr[16]; + UCHAR IpDestAddrPrefixLen; // [0..128] +} QOS_FILTER_TLV_IPV6_DEST_ADDR, *PQOS_FILTER_TLV_IPV6_DEST_ADDR; + +#define QOS_FILTER_IPV6_NEXT_HDR_PROTOCOL_TCP 0x06 +#define QOS_FILTER_IPV6_NEXT_HDR_PROTOCOL_UDP 0x11 + +typedef struct _QOS_FILTER_TLV_IPV6_TRAFFIC_CLASS +{ + UCHAR TLVType; // 0x19 + USHORT TLVLength; // 2 + UCHAR TrafficClass; + UCHAR TrafficClassMask; // compare the first 6 bits only +} QOS_FILTER_TLV_IPV6_TRAFFIC_CLASS, *PQOS_FILTER_TLV_IPV6_TRAFFIC_CLASS; + +typedef struct _QOS_FILTER_TLV_IPV6_FLOW_LABEL +{ + UCHAR TLVType; // 0x1A + USHORT TLVLength; // 4 + ULONG FlowLabel; +} QOS_FILTER_TLV_IPV6_FLOW_LABEL, *PQOS_FILTER_TLV_IPV6_FLOW_LABEL; + +#endif // QCQOS_IPV6 +#endif + +// ======================= WMS ============================== +#define QMIWMS_SET_EVENT_REPORT_REQ 0x0001 +#define QMIWMS_SET_EVENT_REPORT_RESP 0x0001 +#define QMIWMS_EVENT_REPORT_IND 0x0001 +#define QMIWMS_RAW_SEND_REQ 0x0020 +#define QMIWMS_RAW_SEND_RESP 0x0020 +#define QMIWMS_RAW_WRITE_REQ 0x0021 +#define QMIWMS_RAW_WRITE_RESP 0x0021 +#define QMIWMS_RAW_READ_REQ 0x0022 +#define QMIWMS_RAW_READ_RESP 0x0022 +#define QMIWMS_MODIFY_TAG_REQ 0x0023 +#define QMIWMS_MODIFY_TAG_RESP 0x0023 +#define QMIWMS_DELETE_REQ 0x0024 +#define QMIWMS_DELETE_RESP 0x0024 +#define QMIWMS_GET_MESSAGE_PROTOCOL_REQ 0x0030 +#define QMIWMS_GET_MESSAGE_PROTOCOL_RESP 0x0030 +#define QMIWMS_LIST_MESSAGES_REQ 0x0031 +#define QMIWMS_LIST_MESSAGES_RESP 0x0031 +#define QMIWMS_GET_SMSC_ADDRESS_REQ 0x0034 +#define QMIWMS_GET_SMSC_ADDRESS_RESP 0x0034 +#define QMIWMS_SET_SMSC_ADDRESS_REQ 0x0035 +#define QMIWMS_SET_SMSC_ADDRESS_RESP 0x0035 +#define QMIWMS_GET_STORE_MAX_SIZE_REQ 0x0036 +#define QMIWMS_GET_STORE_MAX_SIZE_RESP 0x0036 + +#define WMS_MESSAGE_PROTOCOL_CDMA 0x00 +#define WMS_MESSAGE_PROTOCOL_WCDMA 0x01 + +#if 0 +typedef struct _QMIWMS_GET_MESSAGE_PROTOCOL_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; +} QMIWMS_GET_MESSAGE_PROTOCOL_REQ_MSG, *PQMIWMS_GET_MESSAGE_PROTOCOL_REQ_MSG; + +typedef struct _QMIWMS_GET_MESSAGE_PROTOCOL_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR MessageProtocol; +} QMIWMS_GET_MESSAGE_PROTOCOL_RESP_MSG, *PQMIWMS_GET_MESSAGE_PROTOCOL_RESP_MSG; + +typedef struct _QMIWMS_GET_STORE_MAX_SIZE_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR StorageType; +} QMIWMS_GET_STORE_MAX_SIZE_REQ_MSG, *PQMIWMS_GET_STORE_MAX_SIZE_REQ_MSG; + +typedef struct _QMIWMS_GET_STORE_MAX_SIZE_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + ULONG MemStoreMaxSize; +} QMIWMS_GET_STORE_MAX_SIZE_RESP_MSG, *PQMIWMS_GET_STORE_MAX_SIZE_RESP_MSG; + +typedef struct _REQUEST_TAG +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR TagType; +} REQUEST_TAG, *PREQUEST_TAG; + +typedef struct _QMIWMS_LIST_MESSAGES_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR StorageType; +} QMIWMS_LIST_MESSAGES_REQ_MSG, *PQMIWMS_LIST_MESSAGES_REQ_MSG; + +typedef struct _QMIWMS_MESSAGE +{ + ULONG MessageIndex; + UCHAR TagType; +} QMIWMS_MESSAGE, *PQMIWMS_MESSAGE; + +typedef struct _QMIWMS_LIST_MESSAGES_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + ULONG NumMessages; +} QMIWMS_LIST_MESSAGES_RESP_MSG, *PQMIWMS_LIST_MESSAGES_RESP_MSG; + +typedef struct _QMIWMS_RAW_READ_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR StorageType; + ULONG MemoryIndex; +} QMIWMS_RAW_READ_REQ_MSG, *PQMIWMS_RAW_READ_REQ_MSG; + +typedef struct _QMIWMS_RAW_READ_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR TagType; + UCHAR Format; + USHORT MessageLength; + UCHAR Message; +} QMIWMS_RAW_READ_RESP_MSG, *PQMIWMS_RAW_READ_RESP_MSG; + +typedef struct _QMIWMS_MODIFY_TAG_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR StorageType; + ULONG MemoryIndex; + UCHAR TagType; +} QMIWMS_MODIFY_TAG_REQ_MSG, *PQMIWMS_MODIFY_TAG_REQ_MSG; + +typedef struct _QMIWMS_MODIFY_TAG_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} QMIWMS_MODIFY_TAG_RESP_MSG, *PQMIWMS_MODIFY_TAG_RESP_MSG; + +typedef struct _QMIWMS_RAW_SEND_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR SmsFormat; + USHORT SmsLength; + UCHAR SmsMessage; +} QMIWMS_RAW_SEND_REQ_MSG, *PQMIWMS_RAW_SEND_REQ_MSG; + +typedef struct _RAW_SEND_CAUSE_CODE +{ + UCHAR TLVType; + USHORT TLVLength; + USHORT CauseCode; +} RAW_SEND_CAUSE_CODE, *PRAW_SEND_CAUSE_CODE; + + +typedef struct _QMIWMS_RAW_SEND_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} QMIWMS_RAW_SEND_RESP_MSG, *PQMIWMS_RAW_SEND_RESP_MSG; + + +typedef struct _WMS_DELETE_MESSAGE_INDEX +{ + UCHAR TLVType; + USHORT TLVLength; + ULONG MemoryIndex; +} WMS_DELETE_MESSAGE_INDEX, *PWMS_DELETE_MESSAGE_INDEX; + +typedef struct _WMS_DELETE_MESSAGE_TAG +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR MessageTag; +} WMS_DELETE_MESSAGE_TAG, *PWMS_DELETE_MESSAGE_TAG; + +typedef struct _QMIWMS_DELETE_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR StorageType; +} QMIWMS_DELETE_REQ_MSG, *PQMIWMS_DELETE_REQ_MSG; + +typedef struct _QMIWMS_DELETE_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} QMIWMS_DELETE_RESP_MSG, *PQMIWMS_DELETE_RESP_MSG; + + +typedef struct _QMIWMS_GET_SMSC_ADDRESS_REQ_MSG +{ + USHORT Type; + USHORT Length; +} QMIWMS_GET_SMSC_ADDRESS_REQ_MSG, *PQMIWMS_GET_SMSC_ADDRESS_REQ_MSG; + +typedef struct _QMIWMS_SMSC_ADDRESS +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR SMSCAddressType[3]; + UCHAR SMSCAddressLength; + UCHAR SMSCAddressDigits; +} QMIWMS_SMSC_ADDRESS, *PQMIWMS_SMSC_ADDRESS; + + +typedef struct _QMIWMS_GET_SMSC_ADDRESS_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR SMSCAddress; +} QMIWMS_GET_SMSC_ADDRESS_RESP_MSG, *PQMIWMS_GET_SMSC_ADDRESS_RESP_MSG; + +typedef struct _QMIWMS_SET_SMSC_ADDRESS_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR SMSCAddress; +} QMIWMS_SET_SMSC_ADDRESS_REQ_MSG, *PQMIWMS_SET_SMSC_ADDRESS_REQ_MSG; + +typedef struct _QMIWMS_SET_SMSC_ADDRESS_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} QMIWMS_SET_SMSC_ADDRESS_RESP_MSG, *PQMIWMS_SET_SMSC_ADDRESS_RESP_MSG; + +typedef struct _QMIWMS_SET_EVENT_REPORT_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR ReportNewMessage; +} QMIWMS_SET_EVENT_REPORT_REQ_MSG, *PQMIWMS_SET_EVENT_REPORT_REQ_MSG; + +typedef struct _QMIWMS_SET_EVENT_REPORT_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} QMIWMS_SET_EVENT_REPORT_RESP_MSG, *PQMIWMS_SET_EVENT_REPORT_RESP_MSG; + +typedef struct _QMIWMS_EVENT_REPORT_IND_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR StorageType; + ULONG StorageIndex; +} QMIWMS_EVENT_REPORT_IND_MSG, *PQMIWMS_EVENT_REPORT_IND_MSG; +#endif + +// ======================= End of WMS ============================== + +// ======================= NAS ============================== +#define QMINAS_SET_EVENT_REPORT_REQ 0x0002 +#define QMINAS_SET_EVENT_REPORT_RESP 0x0002 +#define QMINAS_EVENT_REPORT_IND 0x0002 +#define QMINAS_GET_SIGNAL_STRENGTH_REQ 0x0020 +#define QMINAS_GET_SIGNAL_STRENGTH_RESP 0x0020 +#define QMINAS_PERFORM_NETWORK_SCAN_REQ 0x0021 +#define QMINAS_PERFORM_NETWORK_SCAN_RESP 0x0021 +#define QMINAS_INITIATE_NW_REGISTER_REQ 0x0022 +#define QMINAS_INITIATE_NW_REGISTER_RESP 0x0022 +#define QMINAS_INITIATE_ATTACH_REQ 0x0023 +#define QMINAS_INITIATE_ATTACH_RESP 0x0023 +#define QMINAS_GET_SERVING_SYSTEM_REQ 0x0024 +#define QMINAS_GET_SERVING_SYSTEM_RESP 0x0024 +#define QMINAS_SERVING_SYSTEM_IND 0x0024 +#define QMINAS_GET_HOME_NETWORK_REQ 0x0025 +#define QMINAS_GET_HOME_NETWORK_RESP 0x0025 +#define QMINAS_GET_PREFERRED_NETWORK_REQ 0x0026 +#define QMINAS_GET_PREFERRED_NETWORK_RESP 0x0026 +#define QMINAS_SET_PREFERRED_NETWORK_REQ 0x0027 +#define QMINAS_SET_PREFERRED_NETWORK_RESP 0x0027 +#define QMINAS_GET_FORBIDDEN_NETWORK_REQ 0x0028 +#define QMINAS_GET_FORBIDDEN_NETWORK_RESP 0x0028 +#define QMINAS_SET_FORBIDDEN_NETWORK_REQ 0x0029 +#define QMINAS_SET_FORBIDDEN_NETWORK_RESP 0x0029 +#define QMINAS_SET_TECHNOLOGY_PREF_REQ 0x002A +#define QMINAS_SET_TECHNOLOGY_PREF_RESP 0x002A +#define QMINAS_GET_RF_BAND_INFO_REQ 0x0031 +#define QMINAS_GET_RF_BAND_INFO_RESP 0x0031 +#define QMINAS_GET_PLMN_NAME_REQ 0x0044 +#define QMINAS_GET_PLMN_NAME_RESP 0x0044 +#define FIBO_PACKET_TRANSFER_START_IND 0X100 +#define FIBO_PACKET_TRANSFER_END_IND 0X101 +#define QMINAS_GET_SYS_INFO_REQ 0x004D +#define QMINAS_GET_SYS_INFO_RESP 0x004D +#define QMINAS_SYS_INFO_IND 0x004D + +typedef struct _QMINAS_GET_HOME_NETWORK_REQ_MSG { + USHORT Type; // QMUX type 0x0003 + USHORT Length; +} __attribute__((packed)) QMINAS_GET_HOME_NETWORK_REQ_MSG, + *PQMINAS_GET_HOME_NETWORK_REQ_MSG; + +typedef struct _HOME_NETWORK_SYSTEMID { + UCHAR TLVType; + USHORT TLVLength; + USHORT SystemID; + USHORT NetworkID; +} __attribute__((packed)) HOME_NETWORK_SYSTEMID, *PHOME_NETWORK_SYSTEMID; + +typedef struct _HOME_NETWORK { + UCHAR TLVType; + USHORT TLVLength; + USHORT MobileCountryCode; + USHORT MobileNetworkCode; + UCHAR NetworkDesclen; + UCHAR NetworkDesc; +} __attribute__((packed)) HOME_NETWORK, *PHOME_NETWORK; + +#if 0 +typedef struct _HOME_NETWORK_EXT +{ + UCHAR TLVType; + USHORT TLVLength; + USHORT MobileCountryCode; + USHORT MobileNetworkCode; + UCHAR NetworkDescDisp; + UCHAR NetworkDescEncoding; + UCHAR NetworkDesclen; + UCHAR NetworkDesc; +} HOME_NETWORK_EXT, *PHOME_NETWORK_EXT; + +typedef struct _QMINAS_GET_HOME_NETWORK_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} QMINAS_GET_HOME_NETWORK_RESP_MSG, *PQMINAS_GET_HOME_NETWORK_RESP_MSG; + +typedef struct _QMINAS_GET_PREFERRED_NETWORK_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; +} QMINAS_GET_PREFERRED_NETWORK_REQ_MSG, *PQMINAS_GET_PREFERRED_NETWORK_REQ_MSG; + + +typedef struct _PREFERRED_NETWORK +{ + USHORT MobileCountryCode; + USHORT MobileNetworkCode; + USHORT RadioAccess; +} PREFERRED_NETWORK, *PPREFERRED_NETWORK; + +typedef struct _QMINAS_GET_PREFERRED_NETWORK_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; // 0x01 - required parameter + USHORT TLV2Length; // length of the mfr string + USHORT NumPreferredNetwork; +} QMINAS_GET_PREFERRED_NETWORK_RESP_MSG, *PQMINAS_GET_PREFERRED_NETWORK_RESP_MSG; + +typedef struct _QMINAS_GET_FORBIDDEN_NETWORK_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; +} QMINAS_GET_FORBIDDEN_NETWORK_REQ_MSG, *PQMINAS_GET_FORBIDDEN_NETWORK_REQ_MSG; + +typedef struct _FORBIDDEN_NETWORK +{ + USHORT MobileCountryCode; + USHORT MobileNetworkCode; +} FORBIDDEN_NETWORK, *PFORBIDDEN_NETWORK; + +typedef struct _QMINAS_GET_FORBIDDEN_NETWORK_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; // 0x01 - required parameter + USHORT TLV2Length; // length of the mfr string + USHORT NumForbiddenNetwork; +} QMINAS_GET_FORBIDDEN_NETWORK_RESP_MSG, *PQMINAS_GET_FORBIDDEN_NETWORK_RESP_MSG; + +typedef struct _QMINAS_GET_SERVING_SYSTEM_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; +} QMINAS_GET_SERVING_SYSTEM_REQ_MSG, *PQMINAS_GET_SERVING_SYSTEM_REQ_MSG; + +typedef struct _QMINAS_ROAMING_INDICATOR_MSG +{ + UCHAR TLVType; // 0x01 - required parameter + USHORT TLVLength; // length of the mfr string + UCHAR RoamingIndicator; +} QMINAS_ROAMING_INDICATOR_MSG, *PQMINAS_ROAMING_INDICATOR_MSG; +#endif + +typedef struct _QMINAS_DATA_CAP { + UCHAR TLVType; // 0x01 - required parameter + USHORT TLVLength; // length of the mfr string + UCHAR DataCapListLen; + UCHAR DataCap; +} __attribute__((packed)) QMINAS_DATA_CAP, *PQMINAS_DATA_CAP; + +typedef struct _QMINAS_CURRENT_PLMN_MSG { + UCHAR TLVType; // 0x01 - required parameter + USHORT TLVLength; // length of the mfr string + USHORT MobileCountryCode; + USHORT MobileNetworkCode; + UCHAR NetworkDesclen; + UCHAR NetworkDesc; +} __attribute__((packed)) QMINAS_CURRENT_PLMN_MSG, *PQMINAS_CURRENT_PLMN_MSG; + +typedef struct _QMINAS_GET_SERVING_SYSTEM_RESP_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} __attribute__((packed)) QMINAS_GET_SERVING_SYSTEM_RESP_MSG, + *PQMINAS_GET_SERVING_SYSTEM_RESP_MSG; + +typedef struct _SERVING_SYSTEM { + UCHAR TLVType; + USHORT TLVLength; + UCHAR RegistrationState; + UCHAR CSAttachedState; + UCHAR PSAttachedState; + UCHAR RegistredNetwork; + UCHAR InUseRadioIF; + UCHAR RadioIF; +} __attribute__((packed)) SERVING_SYSTEM, *PSERVING_SYSTEM; + +typedef struct _QMINAS_GET_SYS_INFO_RESP_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} __attribute__((packed)) QMINAS_GET_SYS_INFO_RESP_MSG, + *PQMINAS_GET_SYS_INFO_RESP_MSG; + +typedef struct _QMINAS_SYS_INFO_IND_MSG { + USHORT Type; + USHORT Length; +} __attribute__((packed)) QMINAS_SYS_INFO_IND_MSG, *PQMINAS_SYS_INFO_IND_MSG; + +typedef struct _SERVICE_STATUS_INFO { + UCHAR TLVType; + USHORT TLVLength; + UCHAR SrvStatus; + UCHAR true_srv_status; + UCHAR IsPrefDataPath; +} __attribute__((packed)) SERVICE_STATUS_INFO, *PSERVICE_STATUS_INFO; + +typedef struct _CDMA_SYSTEM_INFO { + UCHAR TLVType; + USHORT TLVLength; + UCHAR SrvDomainValid; + UCHAR SrvDomain; + UCHAR SrvCapabilityValid; + UCHAR SrvCapability; + UCHAR RoamStatusValid; + UCHAR RoamStatus; + UCHAR IsSysForbiddenValid; + UCHAR IsSysForbidden; + UCHAR IsSysPrlMatchValid; + UCHAR IsSysPrlMatch; + UCHAR PRevInUseValid; + UCHAR PRevInUse; + UCHAR BSPRevValid; + UCHAR BSPRev; + UCHAR CCSSupportedValid; + UCHAR CCSSupported; + UCHAR CDMASysIdValid; + USHORT SID; + USHORT NID; + UCHAR BSInfoValid; + USHORT BaseID; + ULONG BaseLAT; + ULONG BaseLONG; + UCHAR PacketZoneValid; + USHORT PacketZone; + UCHAR NetworkIdValid; + UCHAR MCC[3]; + UCHAR MNC[3]; +} __attribute__((packed)) CDMA_SYSTEM_INFO, *PCDMA_SYSTEM_INFO; + +typedef struct _HDR_SYSTEM_INFO { + UCHAR TLVType; + USHORT TLVLength; + UCHAR SrvDomainValid; + UCHAR SrvDomain; + UCHAR SrvCapabilityValid; + UCHAR SrvCapability; + UCHAR RoamStatusValid; + UCHAR RoamStatus; + UCHAR IsSysForbiddenValid; + UCHAR IsSysForbidden; + UCHAR IsSysPrlMatchValid; + UCHAR IsSysPrlMatch; + UCHAR HdrPersonalityValid; + UCHAR HdrPersonality; + UCHAR HdrActiveProtValid; + UCHAR HdrActiveProt; + UCHAR is856SysIdValid; + UCHAR is856SysId[16]; +} __attribute__((packed)) HDR_SYSTEM_INFO, *PHDR_SYSTEM_INFO; + +typedef struct _GSM_SYSTEM_INFO { + UCHAR TLVType; + USHORT TLVLength; + UCHAR SrvDomainValid; + UCHAR SrvDomain; + UCHAR SrvCapabilityValid; + UCHAR SrvCapability; + UCHAR RoamStatusValid; + UCHAR RoamStatus; + UCHAR IsSysForbiddenValid; + UCHAR IsSysForbidden; + UCHAR LacValid; + USHORT Lac; + UCHAR CellIdValid; + ULONG CellId; + UCHAR RegRejectInfoValid; + UCHAR RejectSrvDomain; + UCHAR RejCause; + UCHAR NetworkIdValid; + UCHAR MCC[3]; + UCHAR MNC[3]; + UCHAR EgprsSuppValid; + UCHAR EgprsSupp; + UCHAR DtmSuppValid; + UCHAR DtmSupp; +} __attribute__((packed)) GSM_SYSTEM_INFO, *PGSM_SYSTEM_INFO; + +typedef struct _WCDMA_SYSTEM_INFO { + UCHAR TLVType; + USHORT TLVLength; + UCHAR SrvDomainValid; + UCHAR SrvDomain; + UCHAR SrvCapabilityValid; + UCHAR SrvCapability; + UCHAR RoamStatusValid; + UCHAR RoamStatus; + UCHAR IsSysForbiddenValid; + UCHAR IsSysForbidden; + UCHAR LacValid; + USHORT Lac; + UCHAR CellIdValid; + ULONG CellId; + UCHAR RegRejectInfoValid; + UCHAR RejectSrvDomain; + UCHAR RejCause; + UCHAR NetworkIdValid; + UCHAR MCC[3]; + UCHAR MNC[3]; + UCHAR HsCallStatusValid; + UCHAR HsCallStatus; + UCHAR HsIndValid; + UCHAR HsInd; + UCHAR PscValid; + UCHAR Psc; +} __attribute__((packed)) WCDMA_SYSTEM_INFO, *PWCDMA_SYSTEM_INFO; + +typedef struct _LTE_SYSTEM_INFO { + UCHAR TLVType; + USHORT TLVLength; + UCHAR SrvDomainValid; + UCHAR SrvDomain; + UCHAR SrvCapabilityValid; + UCHAR SrvCapability; + UCHAR RoamStatusValid; + UCHAR RoamStatus; + UCHAR IsSysForbiddenValid; + UCHAR IsSysForbidden; + UCHAR LacValid; + USHORT Lac; + UCHAR CellIdValid; + ULONG CellId; + UCHAR RegRejectInfoValid; + UCHAR RejectSrvDomain; + UCHAR RejCause; + UCHAR NetworkIdValid; + UCHAR MCC[3]; + UCHAR MNC[3]; + UCHAR TacValid; + USHORT Tac; +} __attribute__((packed)) LTE_SYSTEM_INFO, *PLTE_SYSTEM_INFO; +//begin modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605 +typedef struct _NR5G_SYSTEM_INFO { + UCHAR TLVType; + USHORT TLVLength; + UCHAR SrvDomainValid; + UCHAR SrvDomain; + UCHAR SrvCapabilityValid; + UCHAR SrvCapability; + UCHAR RoamStatusValid; + UCHAR RoamStatus; + UCHAR IsSysForbiddenValid; + UCHAR IsSysForbidden; + UCHAR LacValid; + USHORT Lac; + UCHAR CellIdValid; + ULONG CellId; + UCHAR RegRejectInfoValid; + UCHAR RejectSrvDomain; + UCHAR RejCause; + UCHAR NetworkIdValid; + UCHAR MCC[3]; + UCHAR MNC[3]; + UCHAR TacValid; + USHORT Tac; +} __attribute__((packed)) NR5G_SYSTEM_INFO, *PNR5G_SYSTEM_INFO; + +//end modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605 +typedef struct _TDSCDMA_SYSTEM_INFO { + UCHAR TLVType; + USHORT TLVLength; + UCHAR SrvDomainValid; + UCHAR SrvDomain; + UCHAR SrvCapabilityValid; + UCHAR SrvCapability; + UCHAR RoamStatusValid; + UCHAR RoamStatus; + UCHAR IsSysForbiddenValid; + UCHAR IsSysForbidden; + UCHAR LacValid; + USHORT Lac; + UCHAR CellIdValid; + ULONG CellId; + UCHAR RegRejectInfoValid; + UCHAR RejectSrvDomain; + UCHAR RejCause; + UCHAR NetworkIdValid; + UCHAR MCC[3]; + UCHAR MNC[3]; + UCHAR HsCallStatusValid; + UCHAR HsCallStatus; + UCHAR HsIndValid; + UCHAR HsInd; + UCHAR CellParameterIdValid; + USHORT CellParameterId; + UCHAR CellBroadcastCapValid; + ULONG CellBroadcastCap; + UCHAR CsBarStatusValid; + ULONG CsBarStatus; + UCHAR PsBarStatusValid; + ULONG PsBarStatus; + UCHAR CipherDomainValid; + UCHAR CipherDomain; +} __attribute__((packed)) TDSCDMA_SYSTEM_INFO, *PTDSCDMA_SYSTEM_INFO; + +#if 0 +typedef struct _QMINAS_SERVING_SYSTEM_IND_MSG +{ + USHORT Type; + USHORT Length; +} QMINAS_SERVING_SYSTEM_IND_MSG, *PQMINAS_SERVING_SYSTEM_IND_MSG; + +typedef struct _QMINAS_SET_PREFERRED_NETWORK_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT NumPreferredNetwork; + USHORT MobileCountryCode; + USHORT MobileNetworkCode; + USHORT RadioAccess; +} QMINAS_SET_PREFERRED_NETWORK_REQ_MSG, *PQMINAS_SET_PREFERRED_NETWORK_REQ_MSG; + +typedef struct _QMINAS_SET_PREFERRED_NETWORK_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMINAS_SET_PREFERRED_NETWORK_RESP_MSG, *PQMINAS_SET_PREFERRED_NETWORK_RESP_MSG; + +typedef struct _QMINAS_SET_FORBIDDEN_NETWORK_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT NumForbiddenNetwork; + USHORT MobileCountryCode; + USHORT MobileNetworkCode; +} QMINAS_SET_FORBIDDEN_NETWORK_REQ_MSG, *PQMINAS_SET_FORBIDDEN_NETWORK_REQ_MSG; + +typedef struct _QMINAS_SET_FORBIDDEN_NETWORK_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMINAS_SET_FORBIDDEN_NETWORK_RESP_MSG, *PQMINAS_SET_FORBIDDEN_NETWORK_RESP_MSG; + +typedef struct _QMINAS_PERFORM_NETWORK_SCAN_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; +} QMINAS_PERFORM_NETWORK_SCAN_REQ_MSG, *PQMINAS_PERFORM_NETWORK_SCAN_REQ_MSG; + +typedef struct _VISIBLE_NETWORK +{ + USHORT MobileCountryCode; + USHORT MobileNetworkCode; + UCHAR NetworkStatus; + UCHAR NetworkDesclen; +} VISIBLE_NETWORK, *PVISIBLE_NETWORK; + +typedef struct _QMINAS_PERFORM_NETWORK_SCAN_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMINAS_PERFORM_NETWORK_SCAN_RESP_MSG, *PQMINAS_PERFORM_NETWORK_SCAN_RESP_MSG; + +typedef struct _QMINAS_PERFORM_NETWORK_SCAN_NETWORK_INFO +{ + UCHAR TLVType; // 0x010 - required parameter + USHORT TLVLength; // length + USHORT NumNetworkInstances; +} QMINAS_PERFORM_NETWORK_SCAN_NETWORK_INFO, *PQMINAS_PERFORM_NETWORK_SCAN_NETWORK_INFO; + +typedef struct _QMINAS_PERFORM_NETWORK_SCAN_RAT_INFO +{ + UCHAR TLVType; // 0x011 - required parameter + USHORT TLVLength; // length + USHORT NumInst; +} QMINAS_PERFORM_NETWORK_SCAN_RAT_INFO, *PQMINAS_PERFORM_NETWORK_SCAN_RAT_INFO; + +typedef struct _QMINAS_PERFORM_NETWORK_SCAN_RAT +{ + USHORT MCC; + USHORT MNC; + UCHAR RAT; +} QMINAS_PERFORM_NETWORK_SCAN_RAT, *PQMINAS_PERFORM_NETWORK_SCAN_RAT; + + +typedef struct _QMINAS_MANUAL_NW_REGISTER +{ + UCHAR TLV2Type; // 0x02 - result code + USHORT TLV2Length; // 4 + USHORT MobileCountryCode; + USHORT MobileNetworkCode; + UCHAR RadioAccess; +} QMINAS_MANUAL_NW_REGISTER, *PQMINAS_MANUAL_NW_REGISTER; + +typedef struct _QMINAS_INITIATE_NW_REGISTER_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + UCHAR RegisterAction; +} QMINAS_INITIATE_NW_REGISTER_REQ_MSG, *PQMINAS_INITIATE_NW_REGISTER_REQ_MSG; + +typedef struct _QMINAS_INITIATE_NW_REGISTER_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMINAS_INITIATE_NW_REGISTER_RESP_MSG, *PQMINAS_INITIATE_NW_REGISTER_RESP_MSG; + +typedef struct _QMINAS_SET_TECHNOLOGY_PREF_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT TechPref; + UCHAR Duration; +} QMINAS_SET_TECHNOLOGY_PREF_REQ_MSG, *PQMINAS_SET_TECHNOLOGY_PREF_REQ_MSG; + +typedef struct _QMINAS_SET_TECHNOLOGY_PREF_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMINAS_SET_TECHNOLOGY_PREF_RESP_MSG, *PQMINAS_SET_TECHNOLOGY_PREF_RESP_MSG; + +typedef struct _QMINAS_GET_SIGNAL_STRENGTH_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; +} QMINAS_GET_SIGNAL_STRENGTH_REQ_MSG, *PQMINAS_GET_SIGNAL_STRENGTH_REQ_MSG; + +typedef struct _QMINAS_SIGNAL_STRENGTH +{ + CHAR SigStrength; + UCHAR RadioIf; +} QMINAS_SIGNAL_STRENGTH, *PQMINAS_SIGNAL_STRENGTH; + +typedef struct _QMINAS_SIGNAL_STRENGTH_LIST +{ + UCHAR TLV3Type; + USHORT TLV3Length; + USHORT NumInstance; +} QMINAS_SIGNAL_STRENGTH_LIST, *PQMINAS_SIGNAL_STRENGTH_LIST; + + +typedef struct _QMINAS_GET_SIGNAL_STRENGTH_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; + USHORT TLV2Length; + CHAR SignalStrength; + UCHAR RadioIf; +} QMINAS_GET_SIGNAL_STRENGTH_RESP_MSG, *PQMINAS_GET_SIGNAL_STRENGTH_RESP_MSG; + + +typedef struct _QMINAS_SET_EVENT_REPORT_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR ReportSigStrength; + UCHAR NumTresholds; + CHAR TresholdList[2]; +} QMINAS_SET_EVENT_REPORT_REQ_MSG, *PQMINAS_SET_EVENT_REPORT_REQ_MSG; + +typedef struct _QMINAS_SET_EVENT_REPORT_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMINAS_SET_EVENT_REPORT_RESP_MSG, *PQMINAS_SET_EVENT_REPORT_RESP_MSG; + +typedef struct _QMINAS_SIGNAL_STRENGTH_TLV +{ + UCHAR TLVType; + USHORT TLVLength; + CHAR SigStrength; + UCHAR RadioIf; +} QMINAS_SIGNAL_STRENGTH_TLV, *PQMINAS_SIGNAL_STRENGTH_TLV; + +typedef struct _QMINAS_REJECT_CAUSE_TLV +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR ServiceDomain; + USHORT RejectCause; +} QMINAS_REJECT_CAUSE_TLV, *PQMINAS_REJECT_CAUSE_TLV; + +typedef struct _QMINAS_EVENT_REPORT_IND_MSG +{ + USHORT Type; + USHORT Length; +} QMINAS_EVENT_REPORT_IND_MSG, *PQMINAS_EVENT_REPORT_IND_MSG; + +typedef struct _QMINAS_GET_RF_BAND_INFO_REQ_MSG +{ + USHORT Type; + USHORT Length; +} QMINAS_GET_RF_BAND_INFO_REQ_MSG, *PQMINAS_GET_RF_BAND_INFO_REQ_MSG; + +typedef struct _QMINASRF_BAND_INFO +{ + UCHAR RadioIf; + USHORT ActiveBand; + USHORT ActiveChannel; +} QMINASRF_BAND_INFO, *PQMINASRF_BAND_INFO; + +typedef struct _QMINAS_GET_RF_BAND_INFO_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR NumInstances; +} QMINAS_GET_RF_BAND_INFO_RESP_MSG, *PQMINAS_GET_RF_BAND_INFO_RESP_MSG; + + +typedef struct _QMINAS_GET_PLMN_NAME_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT MCC; + USHORT MNC; +} QMINAS_GET_PLMN_NAME_REQ_MSG, *PQMINAS_GET_PLMN_NAME_REQ_MSG; + +typedef struct _QMINAS_GET_PLMN_NAME_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMINAS_GET_PLMN_NAME_RESP_MSG, *PQMINAS_GET_PLMN_NAME_RESP_MSG; + +typedef struct _QMINAS_GET_PLMN_NAME_SPN +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR SPN_Enc; + UCHAR SPN_Len; +} QMINAS_GET_PLMN_NAME_SPN, *PQMINAS_GET_PLMN_NAME_SPN; + +typedef struct _QMINAS_GET_PLMN_NAME_PLMN +{ + UCHAR PLMN_Enc; + UCHAR PLMN_Ci; + UCHAR PLMN_SpareBits; + UCHAR PLMN_Len; +} QMINAS_GET_PLMN_NAME_PLMN, *PQMINAS_GET_PLMN_NAME_PLMN; + +typedef struct _QMINAS_INITIATE_ATTACH_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR PsAttachAction; +} QMINAS_INITIATE_ATTACH_REQ_MSG, *PQMINAS_INITIATE_ATTACH_REQ_MSG; + +typedef struct _QMINAS_INITIATE_ATTACH_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMINAS_INITIATE_ATTACH_RESP_MSG, *PQMINAS_INITIATE_ATTACH_RESP_MSG; +#endif +// ======================= End of NAS ============================== + +// ======================= UIM ============================== +#define QMIUIM_READ_TRANSPARENT_REQ 0x0020 +#define QMIUIM_READ_TRANSPARENT_RESP 0x0020 +#define QMIUIM_READ_TRANSPARENT_IND 0x0020 +#define QMIUIM_READ_RECORD_REQ 0x0021 +#define QMIUIM_READ_RECORD_RESP 0x0021 +#define QMIUIM_READ_RECORD_IND 0x0021 +#define QMIUIM_WRITE_TRANSPARENT_REQ 0x0022 +#define QMIUIM_WRITE_TRANSPARENT_RESP 0x0022 +#define QMIUIM_WRITE_TRANSPARENT_IND 0x0022 +#define QMIUIM_WRITE_RECORD_REQ 0x0023 +#define QMIUIM_WRITE_RECORD_RESP 0x0023 +#define QMIUIM_WRITE_RECORD_IND 0x0023 +#define QMIUIM_SET_PIN_PROTECTION_REQ 0x0025 +#define QMIUIM_SET_PIN_PROTECTION_RESP 0x0025 +#define QMIUIM_SET_PIN_PROTECTION_IND 0x0025 +#define QMIUIM_VERIFY_PIN_REQ 0x0026 +#define QMIUIM_VERIFY_PIN_RESP 0x0026 +#define QMIUIM_VERIFY_PIN_IND 0x0026 +#define QMIUIM_UNBLOCK_PIN_REQ 0x0027 +#define QMIUIM_UNBLOCK_PIN_RESP 0x0027 +#define QMIUIM_UNBLOCK_PIN_IND 0x0027 +#define QMIUIM_CHANGE_PIN_REQ 0x0028 +#define QMIUIM_CHANGE_PIN_RESP 0x0028 +#define QMIUIM_CHANGE_PIN_IND 0x0028 +#define QMIUIM_DEPERSONALIZATION_REQ 0x0029 +#define QMIUIM_DEPERSONALIZATION_RESP 0x0029 +#define QMIUIM_EVENT_REG_REQ 0x002E +#define QMIUIM_EVENT_REG_RESP 0x002E +#define QMIUIM_GET_CARD_STATUS_REQ 0x002F +#define QMIUIM_GET_CARD_STATUS_RESP 0x002F +#define QMIUIM_STATUS_CHANGE_IND 0x0032 + +typedef struct _QMIUIM_GET_CARD_STATUS_RESP_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} __attribute__((packed)) QMIUIM_GET_CARD_STATUS_RESP_MSG, + *PQMIUIM_GET_CARD_STATUS_RESP_MSG; + +typedef struct _QMIUIM_PIN_STATE { + UCHAR UnivPIN; + UCHAR PIN1State; + UCHAR PIN1Retries; + UCHAR PUK1Retries; + UCHAR PIN2State; + UCHAR PIN2Retries; + UCHAR PUK2Retries; +} __attribute__((packed)) QMIUIM_PIN_STATE, *PQMIUIM_PIN_STATE; + +typedef struct _QMIUIM_APP_STATUS { + UCHAR AppType; + UCHAR AppState; + UCHAR PersoState; + UCHAR PersoFeature; + UCHAR PersoRetries; + UCHAR PersoUnblockRetries; + UCHAR AIDLength; + UCHAR AIDValue[16]; + QMIUIM_PIN_STATE PinState; +} __attribute__((packed)) QMIUIM_APP_STATUS, *PQMIUIM_APP_STATUS; + +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 +typedef struct _instance{ + UCHAR CardState; + UCHAR UPINState; + UCHAR UPINRetries; + UCHAR UPUKRetries; + UCHAR ErrorCode; + UCHAR NumApp; +}Instance; + +typedef struct _QMIUIM_CARD_STATUS { + UCHAR TLVType; + USHORT TLVLength; + USHORT IndexGWPri; + USHORT Index1XPri; + USHORT IndexGWSec; + USHORT Index1XSec; + UCHAR NumSlot; +/* + UCHAR CardState; + UCHAR UPINState; + UCHAR UPINRetries; + UCHAR UPUKRetries; + UCHAR ErrorCode; + UCHAR NumApp; + QMIUIM_APP_STATUS AppStatus[8]; +*/ +}__attribute__((packed)) QMIUIM_CARD_STATUS, *PQMIUIM_CARD_STATUS; +//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817 + +typedef struct _QMIUIM_VERIFY_PIN_REQ_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR Session_Type; + UCHAR Aid_Len; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR PINID; + UCHAR PINLen; + UCHAR PINValue; +} __attribute__((packed)) QMIUIM_VERIFY_PIN_REQ_MSG, + *PQMIUIM_VERIFY_PIN_REQ_MSG; + +typedef struct _QMIUIM_VERIFY_PIN_RESP_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR PINVerifyRetriesLeft; + UCHAR PINUnblockRetriesLeft; +} __attribute__((packed)) QMIUIM_VERIFY_PIN_RESP_MSG, + *PQMIUIM_VERIFY_PIN_RESP_MSG; + +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 +typedef struct _QMIUIM_BIND_PIN_REQ_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + uint8_t Value; +} __attribute__((packed)) QMIUIM_BIND_PIN_REQ_MSG, *PQMIUIM_BIND_PIN_REQ_MSG; + +typedef struct _QMIUIM_BIND_PIN_REQ_MSG_4 { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UINT Value; +} __attribute__((packed)) QMIUIM_BIND_PIN_REQ_MSG_4; +//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817 + +typedef struct _QMIUIM_READ_TRANSPARENT_REQ_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR Session_Type; + UCHAR Aid_Len; + UCHAR TLV2Type; + USHORT TLV2Length; + USHORT file_id; + UCHAR path_len; + UCHAR path[]; +} __attribute__((packed)) QMIUIM_READ_TRANSPARENT_REQ_MSG, + *PQMIUIM_READ_TRANSPARENT_REQ_MSG; + +typedef struct _READ_TRANSPARENT_TLV { + UCHAR TLVType; + USHORT TLVLength; + USHORT Offset; + USHORT Length; +} __attribute__((packed)) READ_TRANSPARENT_TLV, *PREAD_TRANSPARENT_TLV; + +typedef struct _QMIUIM_CONTENT { + UCHAR TLVType; + USHORT TLVLength; + USHORT content_len; + UCHAR content[]; +} __attribute__((packed)) QMIUIM_CONTENT, *PQMIUIM_CONTENT; + +typedef struct _QMIUIM_READ_TRANSPARENT_RESP_MSG { + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} __attribute__((packed)) QMIUIM_READ_TRANSPARENT_RESP_MSG, + *PQMIUIM_READ_TRANSPARENT_RESP_MSG; + +typedef struct _QMUX_MSG { + QCQMUX_HDR QMUXHdr; + union { + // Message Header + QCQMUX_MSG_HDR QMUXMsgHdr; + QCQMUX_MSG_HDR_RESP QMUXMsgHdrResp; + + // QMIWDS Message +#if 0 + QMIWDS_GET_PKT_SRVC_STATUS_REQ_MSG PacketServiceStatusReq; + QMIWDS_GET_PKT_SRVC_STATUS_RESP_MSG PacketServiceStatusRsp; + QMIWDS_GET_PKT_SRVC_STATUS_IND_MSG PacketServiceStatusInd; + QMIWDS_EVENT_REPORT_IND_MSG EventReportInd; + QMIWDS_GET_CURRENT_CHANNEL_RATE_REQ_MSG GetCurrChannelRateReq; + QMIWDS_GET_CURRENT_CHANNEL_RATE_RESP_MSG GetCurrChannelRateRsp; + QMIWDS_GET_PKT_STATISTICS_REQ_MSG GetPktStatsReq; + QMIWDS_GET_PKT_STATISTICS_RESP_MSG GetPktStatsRsp; + QMIWDS_SET_EVENT_REPORT_REQ_MSG EventReportReq; + QMIWDS_SET_EVENT_REPORT_RESP_MSG EventReportRsp; +#endif + //#ifdef QC_IP_MODE + QMIWDS_GET_RUNTIME_SETTINGS_REQ_MSG GetRuntimeSettingsReq; + QMIWDS_GET_RUNTIME_SETTINGS_RESP_MSG GetRuntimeSettingsRsp; + //#endif // QC_IP_MODE + QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ_MSG SetClientIpFamilyPrefReq; + QMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP_MSG SetClientIpFamilyPrefResp; + QMIWDS_SET_AUTO_CONNECT_REQ_MSG SetAutoConnectReq; +#if 0 + QMIWDS_GET_MIP_MODE_REQ_MSG GetMipModeReq; + QMIWDS_GET_MIP_MODE_RESP_MSG GetMipModeResp; +#endif + QMIWDS_START_NETWORK_INTERFACE_REQ_MSG StartNwInterfaceReq; + QMIWDS_START_NETWORK_INTERFACE_RESP_MSG StartNwInterfaceResp; + QMIWDS_STOP_NETWORK_INTERFACE_REQ_MSG StopNwInterfaceReq; + QMIWDS_STOP_NETWORK_INTERFACE_RESP_MSG StopNwInterfaceResp; + QMIWDS_GET_DEFAULT_SETTINGS_REQ_MSG GetDefaultSettingsReq; + QMIWDS_GET_DEFAULT_SETTINGS_RESP_MSG GetDefaultSettingsResp; +//begin modified by zhangkaibo add create profile qmi. mantis 0049137,0048741 20200610 + QMIWDS_CREATE_PROFILE_SETTINGS_REQ_MSG CreateProfileSettingsReq; + QMIWDS_CREATE_PROFILE_SETTINGS_RESP_MSG CreateProfileSettingsResp; +//end modified by zhangkaibo add create profile qmi. mantis 0049137,0048741 20200610 + QMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG ModifyProfileSettingsReq; + QMIWDS_MODIFY_PROFILE_SETTINGS_RESP_MSG ModifyProfileSettingsResp; + QMIWDS_GET_PROFILE_SETTINGS_REQ_MSG GetProfileSettingsReq; +#if 0 + QMIWDS_GET_DATA_BEARER_REQ_MSG GetDataBearerReq; + QMIWDS_GET_DATA_BEARER_RESP_MSG GetDataBearerResp; + QMIWDS_DUN_CALL_INFO_REQ_MSG DunCallInfoReq; + QMIWDS_DUN_CALL_INFO_RESP_MSG DunCallInfoResp; +#endif + QMIWDS_BIND_MUX_DATA_PORT_REQ_MSG BindMuxDataPortReq; + + // QMIDMS Messages +#if 0 + QMIDMS_GET_DEVICE_MFR_REQ_MSG GetDeviceMfrReq; + QMIDMS_GET_DEVICE_MFR_RESP_MSG GetDeviceMfrRsp; + QMIDMS_GET_DEVICE_MODEL_ID_REQ_MSG GetDeviceModeIdReq; + QMIDMS_GET_DEVICE_MODEL_ID_RESP_MSG GetDeviceModeIdRsp; + QMIDMS_GET_DEVICE_REV_ID_REQ_MSG GetDeviceRevIdReq; + QMIDMS_GET_DEVICE_REV_ID_RESP_MSG GetDeviceRevIdRsp; + QMIDMS_GET_MSISDN_REQ_MSG GetMsisdnReq; + QMIDMS_GET_MSISDN_RESP_MSG GetMsisdnRsp; + QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ_MSG GetDeviceSerialNumReq; + QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP_MSG GetDeviceSerialNumRsp; + QMIDMS_GET_DEVICE_CAP_REQ_MSG GetDeviceCapReq; + QMIDMS_GET_DEVICE_CAP_RESP_MSG GetDeviceCapResp; + QMIDMS_GET_BAND_CAP_REQ_MSG GetBandCapReq; + QMIDMS_GET_BAND_CAP_RESP_MSG GetBandCapRsp; + QMIDMS_GET_ACTIVATED_STATUS_REQ_MSG GetActivatedStatusReq; + QMIDMS_GET_ACTIVATED_STATUS_RESP_MSG GetActivatedStatusResp; + QMIDMS_GET_OPERATING_MODE_REQ_MSG GetOperatingModeReq; + QMIDMS_GET_OPERATING_MODE_RESP_MSG GetOperatingModeResp; +#endif + QMIDMS_SET_OPERATING_MODE_REQ_MSG SetOperatingModeReq; + QMIDMS_SET_OPERATING_MODE_RESP_MSG SetOperatingModeResp; +#if 0 + QMIDMS_UIM_GET_ICCID_REQ_MSG GetICCIDReq; + QMIDMS_UIM_GET_ICCID_RESP_MSG GetICCIDResp; + QMIDMS_ACTIVATE_AUTOMATIC_REQ_MSG ActivateAutomaticReq; + QMIDMS_ACTIVATE_AUTOMATIC_RESP_MSG ActivateAutomaticResp; + QMIDMS_ACTIVATE_MANUAL_REQ_MSG ActivateManualReq; + QMIDMS_ACTIVATE_MANUAL_RESP_MSG ActivateManualResp; +#endif + QMIDMS_UIM_GET_PIN_STATUS_REQ_MSG UIMGetPinStatusReq; + QMIDMS_UIM_GET_PIN_STATUS_RESP_MSG UIMGetPinStatusResp; + QMIDMS_UIM_VERIFY_PIN_REQ_MSG UIMVerifyPinReq; + QMIDMS_UIM_VERIFY_PIN_RESP_MSG UIMVerifyPinResp; +#if 0 + QMIDMS_UIM_SET_PIN_PROTECTION_REQ_MSG UIMSetPinProtectionReq; + QMIDMS_UIM_SET_PIN_PROTECTION_RESP_MSG UIMSetPinProtectionResp; + QMIDMS_UIM_CHANGE_PIN_REQ_MSG UIMChangePinReq; + QMIDMS_UIM_CHANGE_PIN_RESP_MSG UIMChangePinResp; + QMIDMS_UIM_UNBLOCK_PIN_REQ_MSG UIMUnblockPinReq; + QMIDMS_UIM_UNBLOCK_PIN_RESP_MSG UIMUnblockPinResp; + QMIDMS_SET_EVENT_REPORT_REQ_MSG DmsSetEventReportReq; + QMIDMS_SET_EVENT_REPORT_RESP_MSG DmsSetEventReportResp; + QMIDMS_EVENT_REPORT_IND_MSG DmsEventReportInd; +#endif + QMIDMS_UIM_GET_STATE_REQ_MSG UIMGetStateReq; + QMIDMS_UIM_GET_STATE_RESP_MSG UIMGetStateResp; + QMIDMS_UIM_GET_IMSI_REQ_MSG UIMGetIMSIReq; + QMIDMS_UIM_GET_IMSI_RESP_MSG UIMGetIMSIResp; +#if 0 + QMIDMS_UIM_GET_CK_STATUS_REQ_MSG UIMGetCkStatusReq; + QMIDMS_UIM_GET_CK_STATUS_RESP_MSG UIMGetCkStatusResp; + QMIDMS_UIM_SET_CK_PROTECTION_REQ_MSG UIMSetCkProtectionReq; + QMIDMS_UIM_SET_CK_PROTECTION_RESP_MSG UIMSetCkProtectionResp; + QMIDMS_UIM_UNBLOCK_CK_REQ_MSG UIMUnblockCkReq; + QMIDMS_UIM_UNBLOCK_CK_RESP_MSG UIMUnblockCkResp; +#endif + + // QMIQOS Messages +#if 0 + QMI_QOS_SET_EVENT_REPORT_REQ_MSG QosSetEventReportReq; + QMI_QOS_SET_EVENT_REPORT_RESP_MSG QosSetEventReportRsp; + QMI_QOS_EVENT_REPORT_IND_MSG QosEventReportInd; +#endif + + // QMIWMS Messages +#if 0 + QMIWMS_GET_MESSAGE_PROTOCOL_REQ_MSG GetMessageProtocolReq; + QMIWMS_GET_MESSAGE_PROTOCOL_RESP_MSG GetMessageProtocolResp; + QMIWMS_GET_SMSC_ADDRESS_REQ_MSG GetSMSCAddressReq; + QMIWMS_GET_SMSC_ADDRESS_RESP_MSG GetSMSCAddressResp; + QMIWMS_SET_SMSC_ADDRESS_REQ_MSG SetSMSCAddressReq; + QMIWMS_SET_SMSC_ADDRESS_RESP_MSG SetSMSCAddressResp; + QMIWMS_GET_STORE_MAX_SIZE_REQ_MSG GetStoreMaxSizeReq; + QMIWMS_GET_STORE_MAX_SIZE_RESP_MSG GetStoreMaxSizeResp; + QMIWMS_LIST_MESSAGES_REQ_MSG ListMessagesReq; + QMIWMS_LIST_MESSAGES_RESP_MSG ListMessagesResp; + QMIWMS_RAW_READ_REQ_MSG RawReadMessagesReq; + QMIWMS_RAW_READ_RESP_MSG RawReadMessagesResp; + QMIWMS_SET_EVENT_REPORT_REQ_MSG WmsSetEventReportReq; + QMIWMS_SET_EVENT_REPORT_RESP_MSG WmsSetEventReportResp; + QMIWMS_EVENT_REPORT_IND_MSG WmsEventReportInd; + QMIWMS_DELETE_REQ_MSG WmsDeleteReq; + QMIWMS_DELETE_RESP_MSG WmsDeleteResp; + QMIWMS_RAW_SEND_REQ_MSG RawSendMessagesReq; + QMIWMS_RAW_SEND_RESP_MSG RawSendMessagesResp; + QMIWMS_MODIFY_TAG_REQ_MSG WmsModifyTagReq; + QMIWMS_MODIFY_TAG_RESP_MSG WmsModifyTagResp; +#endif + + // QMINAS Messages +#if 0 + QMINAS_GET_HOME_NETWORK_REQ_MSG GetHomeNetworkReq; + QMINAS_GET_HOME_NETWORK_RESP_MSG GetHomeNetworkResp; + QMINAS_GET_PREFERRED_NETWORK_REQ_MSG GetPreferredNetworkReq; + QMINAS_GET_PREFERRED_NETWORK_RESP_MSG GetPreferredNetworkResp; + QMINAS_GET_FORBIDDEN_NETWORK_REQ_MSG GetForbiddenNetworkReq; + QMINAS_GET_FORBIDDEN_NETWORK_RESP_MSG GetForbiddenNetworkResp; + QMINAS_GET_SERVING_SYSTEM_REQ_MSG GetServingSystemReq; +#endif + QMINAS_GET_SERVING_SYSTEM_RESP_MSG GetServingSystemResp; + QMINAS_GET_SYS_INFO_RESP_MSG GetSysInfoResp; + QMINAS_SYS_INFO_IND_MSG NasSysInfoInd; +#if 0 + QMINAS_SERVING_SYSTEM_IND_MSG NasServingSystemInd; + QMINAS_SET_PREFERRED_NETWORK_REQ_MSG SetPreferredNetworkReq; + QMINAS_SET_PREFERRED_NETWORK_RESP_MSG SetPreferredNetworkResp; + QMINAS_SET_FORBIDDEN_NETWORK_REQ_MSG SetForbiddenNetworkReq; + QMINAS_SET_FORBIDDEN_NETWORK_RESP_MSG SetForbiddenNetworkResp; + QMINAS_PERFORM_NETWORK_SCAN_REQ_MSG PerformNetworkScanReq; + QMINAS_PERFORM_NETWORK_SCAN_RESP_MSG PerformNetworkScanResp; + QMINAS_INITIATE_NW_REGISTER_REQ_MSG InitiateNwRegisterReq; + QMINAS_INITIATE_NW_REGISTER_RESP_MSG InitiateNwRegisterResp; + QMINAS_SET_TECHNOLOGY_PREF_REQ_MSG SetTechnologyPrefReq; + QMINAS_SET_TECHNOLOGY_PREF_RESP_MSG SetTechnologyPrefResp; + QMINAS_GET_SIGNAL_STRENGTH_REQ_MSG GetSignalStrengthReq; + QMINAS_GET_SIGNAL_STRENGTH_RESP_MSG GetSignalStrengthResp; + QMINAS_SET_EVENT_REPORT_REQ_MSG SetEventReportReq; + QMINAS_SET_EVENT_REPORT_RESP_MSG SetEventReportResp; + QMINAS_EVENT_REPORT_IND_MSG NasEventReportInd; + QMINAS_GET_RF_BAND_INFO_REQ_MSG GetRFBandInfoReq; + QMINAS_GET_RF_BAND_INFO_RESP_MSG GetRFBandInfoResp; + QMINAS_INITIATE_ATTACH_REQ_MSG InitiateAttachReq; + QMINAS_INITIATE_ATTACH_RESP_MSG InitiateAttachResp; + QMINAS_GET_PLMN_NAME_REQ_MSG GetPLMNNameReq; + QMINAS_GET_PLMN_NAME_RESP_MSG GetPLMNNameResp; +#endif + + // QMIUIM Messages + QMIUIM_GET_CARD_STATUS_RESP_MSG UIMGetCardStatus; + QMIUIM_VERIFY_PIN_REQ_MSG UIMUIMVerifyPinReq; + QMIUIM_VERIFY_PIN_RESP_MSG UIMUIMVerifyPinResp; +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 + QMIUIM_BIND_PIN_REQ_MSG UIMUIMBindPinReq; + QMIUIM_BIND_PIN_REQ_MSG_4 UIMUIMBindPinReq_4; +//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817 +#if 0 + QMIUIM_SET_PIN_PROTECTION_REQ_MSG UIMUIMSetPinProtectionReq; + QMIUIM_SET_PIN_PROTECTION_RESP_MSG UIMUIMSetPinProtectionResp; + QMIUIM_CHANGE_PIN_REQ_MSG UIMUIMChangePinReq; + QMIUIM_CHANGE_PIN_RESP_MSG UIMUIMChangePinResp; + QMIUIM_UNBLOCK_PIN_REQ_MSG UIMUIMUnblockPinReq; + QMIUIM_UNBLOCK_PIN_RESP_MSG UIMUIMUnblockPinResp; +#endif + QMIUIM_READ_TRANSPARENT_REQ_MSG UIMUIMReadTransparentReq; + QMIUIM_READ_TRANSPARENT_RESP_MSG UIMUIMReadTransparentResp; + + QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG SetDataFormatReq; + }; +} __attribute__((packed)) QMUX_MSG, *PQMUX_MSG; + +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 +#define QMI_WDS_BIND_SUBSCRIPTION_REQ_V01 0x00AF +#define QMI_WDS_BIND_SUBSCRIPTION_RESP_V01 0x00AF +#define QMI_DMS_BIND_SUBSCRIPTION_REQ_V01 0x0054 +#define QMI_DMS_BIND_SUBSCRIPTION_RESP_V01 0x0054 +#define QMI_NAS_BIND_SUBSCRIPTION_REQ_V01 0x0045 +#define QMI_NAS_BIND_SUBSCRIPTION_RESP_V01 0x0045 +#define QMI_QOS_BIND_SUBSCRIPTION_REQ_V01 0x002D +#define QMI_QOS_BIND_SUBSCRIPTION_RESP_V01 0x002D +#define QMI_WMS_BIND_SUBSCRIPTION_REQ_V01 0x004C +#define QMI_WMS_BIND_SUBSCRIPTION_RESP_V01 0x004C + +typedef enum { + QMI_RESULT_TYPE_MIN_ENUM_VAL_V01 = -2147483647, /**< To force a 32 bit signed enum. Do not change or use*/ + QMI_RESULT_SUCCESS_V01 = 0, + QMI_RESULT_FAILURE_V01 = 1, + QMI_RESULT_TYPE_MAX_ENUM_VAL_V01 = 2147483647 /**< To force a 32 bit signed enum. Do not change or use*/ +}qmi_result_type_v01; + +typedef enum { + QMI_ERROR_TYPE_MIN_ENUM_VAL_V01 = -2147483647, /**< To force a 32 bit signed enum. Do not change or use*/ + QMI_ERR_NONE_V01 = 0x0000, + QMI_ERR_MALFORMED_MSG_V01 = 0x0001, + QMI_ERR_NO_MEMORY_V01 = 0x0002, + QMI_ERR_INTERNAL_V01 = 0x0003, + QMI_ERR_ABORTED_V01 = 0x0004, + QMI_ERR_CLIENT_IDS_EXHAUSTED_V01 = 0x0005, + QMI_ERR_UNABORTABLE_TRANSACTION_V01 = 0x0006, + QMI_ERR_INVALID_CLIENT_ID_V01 = 0x0007, + QMI_ERR_NO_THRESHOLDS_V01 = 0x0008, + QMI_ERR_INVALID_HANDLE_V01 = 0x0009, + QMI_ERR_INVALID_PROFILE_V01 = 0x000A, + QMI_ERR_INVALID_PINID_V01 = 0x000B, + QMI_ERR_INCORRECT_PIN_V01 = 0x000C, + QMI_ERR_NO_NETWORK_FOUND_V01 = 0x000D, + QMI_ERR_CALL_FAILED_V01 = 0x000E, + QMI_ERR_OUT_OF_CALL_V01 = 0x000F, + QMI_ERR_NOT_PROVISIONED_V01 = 0x0010, + QMI_ERR_MISSING_ARG_V01 = 0x0011, + QMI_ERR_ARG_TOO_LONG_V01 = 0x0013, + QMI_ERR_INVALID_TX_ID_V01 = 0x0016, + QMI_ERR_DEVICE_IN_USE_V01 = 0x0017, + QMI_ERR_OP_NETWORK_UNSUPPORTED_V01 = 0x0018, + QMI_ERR_OP_DEVICE_UNSUPPORTED_V01 = 0x0019, + QMI_ERR_NO_EFFECT_V01 = 0x001A, + QMI_ERR_NO_FREE_PROFILE_V01 = 0x001B, + QMI_ERR_INVALID_PDP_TYPE_V01 = 0x001C, + QMI_ERR_INVALID_TECH_PREF_V01 = 0x001D, + QMI_ERR_INVALID_PROFILE_TYPE_V01 = 0x001E, + QMI_ERR_INVALID_SERVICE_TYPE_V01 = 0x001F, + QMI_ERR_INVALID_REGISTER_ACTION_V01 = 0x0020, + QMI_ERR_INVALID_PS_ATTACH_ACTION_V01 = 0x0021, + QMI_ERR_AUTHENTICATION_FAILED_V01 = 0x0022, + QMI_ERR_PIN_BLOCKED_V01 = 0x0023, + QMI_ERR_PIN_PERM_BLOCKED_V01 = 0x0024, + QMI_ERR_SIM_NOT_INITIALIZED_V01 = 0x0025, + QMI_ERR_MAX_QOS_REQUESTS_IN_USE_V01 = 0x0026, + QMI_ERR_INCORRECT_FLOW_FILTER_V01 = 0x0027, + QMI_ERR_NETWORK_QOS_UNAWARE_V01 = 0x0028, + QMI_ERR_INVALID_ID_V01 = 0x0029, + QMI_ERR_INVALID_QOS_ID_V01 = 0x0029, + QMI_ERR_REQUESTED_NUM_UNSUPPORTED_V01 = 0x002A, + QMI_ERR_INTERFACE_NOT_FOUND_V01 = 0x002B, + QMI_ERR_FLOW_SUSPENDED_V01 = 0x002C, + QMI_ERR_INVALID_DATA_FORMAT_V01 = 0x002D, + QMI_ERR_GENERAL_V01 = 0x002E, + QMI_ERR_UNKNOWN_V01 = 0x002F, + QMI_ERR_INVALID_ARG_V01 = 0x0030, + QMI_ERR_INVALID_INDEX_V01 = 0x0031, + QMI_ERR_NO_ENTRY_V01 = 0x0032, + QMI_ERR_DEVICE_STORAGE_FULL_V01 = 0x0033, + QMI_ERR_DEVICE_NOT_READY_V01 = 0x0034, + QMI_ERR_NETWORK_NOT_READY_V01 = 0x0035, + QMI_ERR_CAUSE_CODE_V01 = 0x0036, + QMI_ERR_MESSAGE_NOT_SENT_V01 = 0x0037, + QMI_ERR_MESSAGE_DELIVERY_FAILURE_V01 = 0x0038, + QMI_ERR_INVALID_MESSAGE_ID_V01 = 0x0039, + QMI_ERR_ENCODING_V01 = 0x003A, + QMI_ERR_AUTHENTICATION_LOCK_V01 = 0x003B, + QMI_ERR_INVALID_TRANSITION_V01 = 0x003C, + QMI_ERR_NOT_A_MCAST_IFACE_V01 = 0x003D, + QMI_ERR_MAX_MCAST_REQUESTS_IN_USE_V01 = 0x003E, + QMI_ERR_INVALID_MCAST_HANDLE_V01 = 0x003F, + QMI_ERR_INVALID_IP_FAMILY_PREF_V01 = 0x0040, + QMI_ERR_SESSION_INACTIVE_V01 = 0x0041, + QMI_ERR_SESSION_INVALID_V01 = 0x0042, + QMI_ERR_SESSION_OWNERSHIP_V01 = 0x0043, + QMI_ERR_INSUFFICIENT_RESOURCES_V01 = 0x0044, + QMI_ERR_DISABLED_V01 = 0x0045, + QMI_ERR_INVALID_OPERATION_V01 = 0x0046, + QMI_ERR_INVALID_QMI_CMD_V01 = 0x0047, + QMI_ERR_TPDU_TYPE_V01 = 0x0048, + QMI_ERR_SMSC_ADDR_V01 = 0x0049, + QMI_ERR_INFO_UNAVAILABLE_V01 = 0x004A, + QMI_ERR_SEGMENT_TOO_LONG_V01 = 0x004B, + QMI_ERR_SEGMENT_ORDER_V01 = 0x004C, + QMI_ERR_BUNDLING_NOT_SUPPORTED_V01 = 0x004D, + QMI_ERR_OP_PARTIAL_FAILURE_V01 = 0x004E, + QMI_ERR_POLICY_MISMATCH_V01 = 0x004F, + QMI_ERR_SIM_FILE_NOT_FOUND_V01 = 0x0050, + QMI_ERR_EXTENDED_INTERNAL_V01 = 0x0051, + QMI_ERR_ACCESS_DENIED_V01 = 0x0052, + QMI_ERR_HARDWARE_RESTRICTED_V01 = 0x0053, + QMI_ERR_ACK_NOT_SENT_V01 = 0x0054, + QMI_ERR_INJECT_TIMEOUT_V01 = 0x0055, + QMI_ERR_INCOMPATIBLE_STATE_V01 = 0x005A, + QMI_ERR_FDN_RESTRICT_V01 = 0x005B, + QMI_ERR_SUPS_FAILURE_CAUSE_V01 = 0x005C, + QMI_ERR_NO_RADIO_V01 = 0x005D, + QMI_ERR_NOT_SUPPORTED_V01 = 0x005E, + QMI_ERR_NO_SUBSCRIPTION_V01 = 0x005F, + QMI_ERR_CARD_CALL_CONTROL_FAILED_V01 = 0x0060, + QMI_ERR_NETWORK_ABORTED_V01 = 0x0061, + QMI_ERR_MSG_BLOCKED_V01 = 0x0062, + QMI_ERR_INVALID_SESSION_TYPE_V01 = 0x0064, + QMI_ERR_INVALID_PB_TYPE_V01 = 0x0065, + QMI_ERR_NO_SIM_V01 = 0x0066, + QMI_ERR_PB_NOT_READY_V01 = 0x0067, + QMI_ERR_PIN_RESTRICTION_V01 = 0x0068, + QMI_ERR_PIN2_RESTRICTION_V01 = 0x0069, + QMI_ERR_PUK_RESTRICTION_V01 = 0x006A, + QMI_ERR_PUK2_RESTRICTION_V01 = 0x006B, + QMI_ERR_PB_ACCESS_RESTRICTED_V01 = 0x006C, + QMI_ERR_PB_DELETE_IN_PROG_V01 = 0x006D, + QMI_ERR_PB_TEXT_TOO_LONG_V01 = 0x006E, + QMI_ERR_PB_NUMBER_TOO_LONG_V01 = 0x006F, + QMI_ERR_PB_HIDDEN_KEY_RESTRICTION_V01 = 0x0070, + QMI_ERR_PB_NOT_AVAILABLE_V01 = 0x0071, + QMI_ERR_DEVICE_MEMORY_ERROR_V01 = 0x0072, + QMI_ERR_NO_PERMISSION_V01 = 0x0073, + QMI_ERR_TOO_SOON_V01 = 0x0074, + QMI_ERR_TIME_NOT_ACQUIRED_V01 = 0x0075, + QMI_ERR_OP_IN_PROGRESS_V01 = 0x0076, + QMI_ERR_EPERM_V01 = 0x101, /**< Operation not permitted */ + QMI_ERR_ENOENT_V01 = 0x102, /**< No such file or directory */ + QMI_ERR_ESRCH_V01 = 0x103, /**< No such process */ + QMI_ERR_EINTR_V01 = 0x104, /**< Interrupted system call */ + QMI_ERR_EIO_V01 = 0x105, /**< I/O error */ + QMI_ERR_ENXIO_V01 = 0x106, /**< No such device or address */ + QMI_ERR_E2BIG_V01 = 0x107, /**< Argument list too long */ + QMI_ERR_ENOEXEC_V01 = 0x108, /**< Exec format error */ + QMI_ERR_EBADF_V01 = 0x109, /**< Bad file number */ + QMI_ERR_ECHILD_V01 = 0x10A, /**< No child processes */ + QMI_ERR_EAGAIN_V01 = 0x10B, /**< Try again */ + QMI_ERR_ENOMEM_V01 = 0x10C, /**< Out of memory */ + QMI_ERR_EACCES_V01 = 0x10D, /**< Permission denied */ + QMI_ERR_EFAULT_V01 = 0x10E, /**< Bad address */ + QMI_ERR_ENOTBLK_V01 = 0x10F, /**< Block device required */ + QMI_ERR_EBUSY_V01 = 0x110, /**< Device or resource busy */ + QMI_ERR_EEXIST_V01 = 0x111, /**< File exists */ + QMI_ERR_EXDEV_V01 = 0x112, /**< Cross-device link */ + QMI_ERR_ENODEV_V01 = 0x113, /**< No such device */ + QMI_ERR_ENOTDIR_V01 = 0x114, /**< Not a directory */ + QMI_ERR_EISDIR_V01 = 0x115, /**< Is a directory */ + QMI_ERR_EINVAL_V01 = 0x116, /**< Invalid argument */ + QMI_ERR_ENFILE_V01 = 0x117, /**< File table overflow */ + QMI_ERR_EMFILE_V01 = 0x118, /**< Too many open files */ + QMI_ERR_ENOTTY_V01 = 0x119, /**< Not a typewriter */ + QMI_ERR_ETXTBSY_V01 = 0x11A, /**< Text file busy */ + QMI_ERR_EFBIG_V01 = 0x11B, /**< File too large */ + QMI_ERR_ENOSPC_V01 = 0x11C, /**< No space left on device */ + QMI_ERR_ESPIPE_V01 = 0x11D, /**< Illegal seek */ + QMI_ERR_EROFS_V01 = 0x11E, /**< Read-only file system */ + QMI_ERR_EMLINK_V01 = 0x11F, /**< Too many links */ + QMI_ERR_EPIPE_V01 = 0x120, /**< Broken pipe */ + QMI_ERR_EDOM_V01 = 0x121, /**< Math argument out of domain of func */ + QMI_ERR_ERANGE_V01 = 0x122, /**< Math result not representable */ + QMI_ERR_EDEADLK_V01 = 0x123, /**< Resource deadlock would occur */ + QMI_ERR_ENAMETOOLONG_V01 = 0x124, /**< File name too long */ + QMI_ERR_ENOLCK_V01 = 0x125, /**< No record locks available */ + QMI_ERR_ENOSYS_V01 = 0x126, /**< Function not implemented */ + QMI_ERR_ENOTEMPTY_V01 = 0x127, /**< Directory not empty */ + QMI_ERR_ELOOP_V01 = 0x128, /**< Too many symbolic links encountered */ + QMI_ERR_EWOULDBLOCK_V01 = 0x10B, /**< Operation would block */ + QMI_ERR_ENOMSG_V01 = 0x12A, /**< No message of desired type */ + QMI_ERR_EIDRM_V01 = 0x12B, /**< Identifier removed */ + QMI_ERR_ECHRNG_V01 = 0x12C, /**< Channel number out of range */ + QMI_ERR_EL2NSYNC_V01 = 0x12D, /**< Level 2 not synchronized */ + QMI_ERR_EL3HLT_V01 = 0x12E, /**< Level 3 halted */ + QMI_ERR_EL3RST_V01 = 0x12F, /**< Level 3 reset */ + QMI_ERR_ELNRNG_V01 = 0x130, /**< Link number out of range */ + QMI_ERR_EUNATCH_V01 = 0x131, /**< Protocol driver not attached */ + QMI_ERR_ENOCSI_V01 = 0x132, /**< No CSI structure available */ + QMI_ERR_EL2HLT_V01 = 0x133, /**< Level 2 halted */ + QMI_ERR_EBADE_V01 = 0x134, /**< Invalid exchange */ + QMI_ERR_EBADR_V01 = 0x135, /**< Invalid request descriptor */ + QMI_ERR_EXFULL_V01 = 0x136, /**< Exchange full */ + QMI_ERR_ENOANO_V01 = 0x137, /**< No anode */ + QMI_ERR_EBADRQC_V01 = 0x138, /**< Invalid request code */ + QMI_ERR_EBADSLT_V01 = 0x139, /**< Invalid slot */ + QMI_ERR_EDEADLOCK_V01 = 0x123, /**< Resource deadlock would occur */ + QMI_ERR_EBFONT_V01 = 0x13B, /**< Bad font file format */ + QMI_ERR_ENOSTR_V01 = 0x13C, /**< Device not a stream */ + QMI_ERR_ENODATA_V01 = 0x13D, /**< No data available */ + QMI_ERR_ETIME_V01 = 0x13E, /**< Timer expired */ + QMI_ERR_ENOSR_V01 = 0x13F, /**< Out of streams resources */ + QMI_ERR_ENONET_V01 = 0x140, /**< Machine is not on the network */ + QMI_ERR_ENOPKG_V01 = 0x141, /**< Package not installed */ + QMI_ERR_EREMOTE_V01 = 0x142, /**< Object is remote */ + QMI_ERR_ENOLINK_V01 = 0x143, /**< Link has been severed */ + QMI_ERR_EADV_V01 = 0x144, /**< Advertise error */ + QMI_ERR_ESRMNT_V01 = 0x145, /**< Srmount error */ + QMI_ERR_ECOMM_V01 = 0x146, /**< Communication error on send */ + QMI_ERR_EPROTO_V01 = 0x147, /**< Protocol error */ + QMI_ERR_EMULTIHOP_V01 = 0x148, /**< Multihop attempted */ + QMI_ERR_EDOTDOT_V01 = 0x149, /**< RFS specific error */ + QMI_ERR_EBADMSG_V01 = 0x14A, /**< Not a data message */ + QMI_ERR_EOVERFLOW_V01 = 0x14B, /**< Value too large for defined data type */ + QMI_ERR_ENOTUNIQ_V01 = 0x14C, /**< Name not unique on network */ + QMI_ERR_EBADFD_V01 = 0x14D, /**< File descriptor in bad state */ + QMI_ERR_EREMCHG_V01 = 0x14E, /**< Remote address changed */ + QMI_ERR_ELIBACC_V01 = 0x14F, /**< Can not access a needed shared library */ + QMI_ERR_ELIBBAD_V01 = 0x150, /**< Accessing a corrupted shared library */ + QMI_ERR_ELIBSCN_V01 = 0x151, /**< .lib section in a.out corrupted */ + QMI_ERR_ELIBMAX_V01 = 0x152, /**< Attempting to link in too many shared libraries */ + QMI_ERR_ELIBEXEC_V01 = 0x153, /**< Cannot exec a shared library directly */ + QMI_ERR_EILSEQ_V01 = 0x154, /**< Illegal byte sequence */ + QMI_ERR_ERESTART_V01 = 0x155, /**< Interrupted system call should be restarted */ + QMI_ERR_ESTRPIPE_V01 = 0x156, /**< Streams pipe error */ + QMI_ERR_EUSERS_V01 = 0x157, /**< Too many users */ + QMI_ERR_ENOTSOCK_V01 = 0x158, /**< Socket operation on non-socket */ + QMI_ERR_EDESTADDRREQ_V01 = 0x159, /**< Destination address required */ + QMI_ERR_EMSGSIZE_V01 = 0x15A, /**< Message too long */ + QMI_ERR_EPROTOTYPE_V01 = 0x15B, /**< Protocol wrong type for socket */ + QMI_ERR_ENOPROTOOPT_V01 = 0x15C, /**< Protocol not available */ + QMI_ERR_EPROTONOSUPPORT_V01 = 0x15D, /**< Protocol not supported */ + QMI_ERR_ESOCKTNOSUPPORT_V01 = 0x15E, /**< Socket type not supported */ + QMI_ERR_EOPNOTSUPP_V01 = 0x15F, /**< Operation not supported on transport endpoint */ + QMI_ERR_EPFNOSUPPORT_V01 = 0x160, /**< Protocol family not supported */ + QMI_ERR_EAFNOSUPPORT_V01 = 0x161, /**< Address family not supported by protocol */ + QMI_ERR_EADDRINUSE_V01 = 0x162, /**< Address already in use */ + QMI_ERR_EADDRNOTAVAIL_V01 = 0x163, /**< Cannot assign requested address */ + QMI_ERR_ENETDOWN_V01 = 0x164, /**< Network is down */ + QMI_ERR_ENETUNREACH_V01 = 0x165, /**< Network is unreachable */ + QMI_ERR_ENETRESET_V01 = 0x166, /**< Network dropped connection because of reset */ + QMI_ERR_ECONNABORTED_V01 = 0x167, /**< Software caused connection abort */ + QMI_ERR_ECONNRESET_V01 = 0x168, /**< Connection reset by peer */ + QMI_ERR_ENOBUFS_V01 = 0x169, /**< No buffer space available */ + QMI_ERR_EISCONN_V01 = 0x16A, /**< Transport endpoint is already connected */ + QMI_ERR_ENOTCONN_V01 = 0x16B, /**< Transport endpoint is not connected */ + QMI_ERR_ESHUTDOWN_V01 = 0x16C, /**< Cannot send after transport endpoint shutdown */ + QMI_ERR_ETOOMANYREFS_V01 = 0x16D, /**< Too many references: cannot splice */ + QMI_ERR_ETIMEDOUT_V01 = 0x16E, /**< Connection timed out */ + QMI_ERR_ECONNREFUSED_V01 = 0x16F, /**< Connection refused */ + QMI_ERR_EHOSTDOWN_V01 = 0x170, /**< Host is down */ + QMI_ERR_EHOSTUNREACH_V01 = 0x171, /**< No route to host */ + QMI_ERR_EALREADY_V01 = 0x172, /**< Operation already in progress */ + QMI_ERR_EINPROGRESS_V01 = 0x173, /**< Operation now in progress */ + QMI_ERR_ESTALE_V01 = 0x174, /**< Stale NFS file handle */ + QMI_ERR_EUCLEAN_V01 = 0x175, /**< Structure needs cleaning */ + QMI_ERR_ENOTNAM_V01 = 0x176, /**< Not a XENIX named type file */ + QMI_ERR_ENAVAIL_V01 = 0x177, /**< No XENIX semaphores available */ + QMI_ERR_EISNAM_V01 = 0x178, /**< Is a named type file */ + QMI_ERR_EREMOTEIO_V01 = 0x179, /**< Remote I/O error */ + QMI_ERR_EDQUOT_V01 = 0x17A, /**< Quota exceeded */ + QMI_ERR_ENOMEDIUM_V01 = 0x17B, /**< No medium found */ + QMI_ERR_EMEDIUMTYPE_V01 = 0x17C, /**< Wrong medium type */ + QMI_ERR_ECANCELED_V01 = 0x17D, /**< Operation Canceled */ + QMI_ERR_ENOKEY_V01 = 0x17E, /**< Required key not available */ + QMI_ERR_EKEYEXPIRED_V01 = 0x17F, /**< Key has expired */ + QMI_ERR_EKEYREVOKED_V01 = 0x180, /**< Key has been revoked */ + QMI_ERR_EKEYREJECTED_V01 = 0x181, /**< Key was rejected by service */ + QMI_ERR_EOWNERDEAD_V01 = 0x182, /**< Owner died */ + QMI_ERR_ENOTRECOVERABLE_V01 = 0x183, /**< State not recoverable */ + QMI_ERROR_TYPE_MAX_ENUM_VAL_V01 = 2147483647 /**< To force a 32 bit signed enum. Do not change or use*/ +}qmi_error_type_v01; + +typedef struct { + qmi_result_type_v01 result; + qmi_error_type_v01 error; +}qmi_response_type_v01; /* Type */ + +typedef enum { + NAS_SUBS_TYPE_ENUM_MIN_ENUM_VAL_V01 = -2147483647, /**< To force a 32 bit signed enum. Do not change or use*/ + NAS_PRIMARY_SUBSCRIPTION_V01 = 0x00, /**< Primary subscription \n */ + NAS_SECONDARY_SUBSCRIPTION_V01 = 0x01, /**< Secondary subscription \n */ + NAS_TERTIARY_SUBSCRIPTION_V01 = 0x02, /**< Tertiary subscription */ + NAS_SUBS_TYPE_ENUM_MAX_ENUM_VAL_V01 = 2147483647 /**< To force a 32 bit signed enum. Do not change or use*/ +}nas_subs_type_enum_v01; + +typedef struct { + nas_subs_type_enum_v01 subs_type; +}nas_bind_subscription_req_msg_v01; /* Message */ +//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817 +#pragma pack(pop) + +#endif// MPQMUX_H diff --git a/fibocom-dial/src/Makefile b/fibocom-dial/src/Makefile new file mode 100644 index 000000000..992b951b4 --- /dev/null +++ b/fibocom-dial/src/Makefile @@ -0,0 +1,42 @@ +ifneq ($(CROSS_COMPILE),) +CROSS-COMPILE:=$(CROSS_COMPILE) +endif + +CFLAGS += -DGHT_FEATURE_PCIE_AUTO + +ifeq ($(CC),cc) +CC:=$(CROSS-COMPILE)gcc +endif +LD:=$(CROSS-COMPILE)ld + +SRC=QmiWwanCM.c GobiNetCM.c main.c MPQMUX.c QMIThread.c util.c qmap_bridge_mode.c query_pcie_mode.c + +FB_DHCP=udhcpc.c + +FIBO_PROXY_SRC=fibo_qmimsg_server.c + +LIBMNL=libmnl/ifutils.c libmnl/attr.c libmnl/callback.c libmnl/nlmsg.c libmnl/socket.c +FB_NDHCP=udhcpc_netlink.c +FB_NDHCP+=${LIBMNL} + +release: clean + $(CC) $(CFLAGS) -Wall -s ${SRC} ${FB_NDHCP} -o fibocom-dial -lpthread -ldl + $(CC) -Wall -s multi-pdn-manager.c query_pcie_mode.c util.c -o multi-pdn-manager -lpthread -ldl + $(CC) -Wall -s ${FIBO_PROXY_SRC} -o fibo_qmimsg_server -lpthread -ldl + +dhcp: clean + $(CC) $(CFLAGS) -Wall -s ${SRC} ${FB_DHCP} -o fibocom-dial -lpthread -ldl + $(CC) -Wall -s multi-pdn-manager.c query_pcie_mode.c util.c -o multi-pdn-manager -lpthread -ldl + $(CC) -Wall -s ${FIBO_PROXY_SRC} -o fibo_qmimsg_server -lpthread -ldl + +ndhcp: clean + $(CC) $(CFLAGS) -Wall -s ${SRC} ${FB_NDHCP} -o fibocom-dial -lpthread -ldl + $(CC) -Wall -s multi-pdn-manager.c query_pcie_mode.cutil.c -o multi-pdn-manager -lpthread -ldl + $(CC) -Wall -s ${FIBO_PROXY_SRC} -o fibo_qmimsg_server -lpthread -ldl + +qmi-proxy: + $(CC) -Wall -s fibo-qmi-proxy.c -o fibo-qmi-proxy -lpthread -ldl + +clean: + rm -rf fibocom-dial *~ multi-pdn-manager fibo_qmimsg_server + diff --git a/fibocom-dial/src/QMIThread.c b/fibocom-dial/src/QMIThread.c new file mode 100644 index 000000000..ecce6b9cf --- /dev/null +++ b/fibocom-dial/src/QMIThread.c @@ -0,0 +1,2551 @@ +#include "QMIThread.h" +#include "query_pcie_mode.h" +extern char *strndup(const char *__string, size_t __n); + +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 +//extern int *speed_arr; +//extern int *name_arr; + +PQCQMIMSG pResponse; +PQMUX_MSG pMUXMsg; +//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817 + +#define qmi_rsp_check_and_return()\ + do { \ + if (err < 0 || pResponse == NULL) { \ + dbg_time("%s err = %d", __func__, err); \ + return err; \ + } \ + pMUXMsg = &pResponse->MUXMsg; \ + if (le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXResult) || \ + le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError)) { \ + USHORT QMUXError = le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError); \ + dbg_time("%s QMUXResult = 0x%x, QMUXError = 0x%x", __func__, \ + le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXResult), \ + QMUXError); \ + free(pResponse); \ + return QMUXError; \ + } \ + } while (0) + +#define qmi_rsp_check() \ + do { \ + if (err < 0 || pResponse == NULL) { \ + dbg_time("%s err = %d", __func__, err); \ + return err; \ + } \ + pMUXMsg = &pResponse->MUXMsg; \ + if (le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXResult) || \ + le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError)) { \ + USHORT QMUXError = le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError); \ + dbg_time("%s QMUXResult = 0x%x, QMUXError = 0x%x", __func__, \ + le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXResult), \ + QMUXError); \ + } \ + } while (0) + +int qmiclientId[QMUX_TYPE_WDS_ADMIN + + 1]; // GobiNet use fd to indicate client ID, so type of + // qmiclientId must be int +static uint32_t WdsConnectionIPv4Handle = 0; +static uint32_t WdsConnectionIPv6Handle = 0; +static int s_is_cdma = 0; +static int s_hdr_personality = 0; // 0x01-HRPD, 0x02-eHRPD +static char *qstrcpy(char *to, const char *from) +{ // no __strcpy_chk + char *save = to; + for (; (*to = *from) != '\0'; ++from, ++to) + ; + return (save); +} + + + +typedef USHORT (*CUSTOMQMUX)(PQMUX_MSG pMUXMsg, void *arg); + +// To retrieve the ith (Index) TLV +PQMI_TLV_HDR GetTLV(PQCQMUX_MSG_HDR pQMUXMsgHdr, int TLVType) +{ + int TLVFind = 0; + USHORT Length = le16_to_cpu(pQMUXMsgHdr->Length); + PQMI_TLV_HDR pTLVHdr = (PQMI_TLV_HDR)(pQMUXMsgHdr + 1); + + while (Length >= sizeof(QMI_TLV_HDR)) { + TLVFind++; + if (TLVType > 0x1000) { + if ((TLVFind + 0x1000) == TLVType) + return pTLVHdr; + } else if (pTLVHdr->TLVType == TLVType) { + return pTLVHdr; + } + + Length -= (le16_to_cpu((pTLVHdr->TLVLength)) + sizeof(QMI_TLV_HDR)); + pTLVHdr = (PQMI_TLV_HDR)(((UCHAR *)pTLVHdr) + + le16_to_cpu(pTLVHdr->TLVLength) + + sizeof(QMI_TLV_HDR)); + } + + return NULL; +} + +static USHORT GetQMUXTransactionId(void) +{ + static int TransactionId = 0; + if (++TransactionId > 0xFFFF) + TransactionId = 1; + return TransactionId; +} + +static PQCQMIMSG ComposeQMUXMsg(UCHAR QMIType, USHORT Type, + CUSTOMQMUX customQmuxMsgFunction, void *arg) +{ + UCHAR QMIBuf[WDM_DEFAULT_BUFSIZE]; + PQCQMIMSG pRequest = (PQCQMIMSG)QMIBuf; + int Length; + + memset(QMIBuf, 0x00, sizeof(QMIBuf)); + pRequest->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI; + pRequest->QMIHdr.CtlFlags = 0x00; + pRequest->QMIHdr.QMIType = QMIType; + pRequest->QMIHdr.ClientId = qmiclientId[QMIType] & 0xFF; + + if (qmiclientId[QMIType] == 0) { + dbg_time("QMIType %d has no clientID", QMIType); + return NULL; + } + + pRequest->MUXMsg.QMUXHdr.CtlFlags = + QMUX_CTL_FLAG_SINGLE_MSG | QMUX_CTL_FLAG_TYPE_CMD; + pRequest->MUXMsg.QMUXHdr.TransactionId = + cpu_to_le16(GetQMUXTransactionId()); + pRequest->MUXMsg.QMUXMsgHdr.Type = cpu_to_le16(Type); + if (customQmuxMsgFunction) + pRequest->MUXMsg.QMUXMsgHdr.Length = + cpu_to_le16(customQmuxMsgFunction(&pRequest->MUXMsg, arg) - + sizeof(QCQMUX_MSG_HDR)); + else + pRequest->MUXMsg.QMUXMsgHdr.Length = cpu_to_le16(0x0000); + + pRequest->QMIHdr.Length = cpu_to_le16( + le16_to_cpu(pRequest->MUXMsg.QMUXMsgHdr.Length) + + sizeof(QCQMUX_MSG_HDR) + sizeof(QCQMUX_HDR) + sizeof(QCQMI_HDR) - 1); + Length = le16_to_cpu(pRequest->QMIHdr.Length) + 1; + + pRequest = (PQCQMIMSG)malloc(Length); + if (pRequest == NULL) { + dbg_time("%s fail to malloc", __func__); + } else { + memcpy(pRequest, QMIBuf, Length); + } + + return pRequest; +} + +static USHORT WdsStartNwInterfaceReq(PQMUX_MSG pMUXMsg, void *arg) +{ + PQMIWDS_TECHNOLOGY_PREFERECE pTechPref; + PQMIWDS_AUTH_PREFERENCE pAuthPref; + PQMIWDS_USERNAME pUserName; + PQMIWDS_PASSWD pPasswd; + PQMIWDS_APNNAME pApnName; + PQMIWDS_IP_FAMILY_TLV pIpFamily; + USHORT TLVLength = 0; + UCHAR *pTLV; + PROFILE_T *profile = (PROFILE_T *)arg; + const char *profile_user = profile->user; + const char *profile_password = profile->password; + int profile_auth = profile->auth; + + if (s_is_cdma && (profile_user == NULL || profile_user[0] == '\0') && + (profile_password == NULL || profile_password[0] == '\0')) { + profile_user = "ctnet@mycdma.cn"; + profile_password = "vnet.mobi"; + profile_auth = 2; // chap + } + + pTLV = (UCHAR *)(&pMUXMsg->StartNwInterfaceReq + 1); + pMUXMsg->StartNwInterfaceReq.Length = 0; + + // Set technology Preferece + pTechPref = (PQMIWDS_TECHNOLOGY_PREFERECE)(pTLV + TLVLength); + pTechPref->TLVType = 0x30; + pTechPref->TLVLength = cpu_to_le16(0x01); + if (s_is_cdma == 0) + pTechPref->TechPreference = 0x01; + else + pTechPref->TechPreference = 0x02; + TLVLength += (le16_to_cpu(pTechPref->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + + // Set APN Name + if (profile->apn && !s_is_cdma) { // cdma no apn + pApnName = (PQMIWDS_APNNAME)(pTLV + TLVLength); + pApnName->TLVType = 0x14; + pApnName->TLVLength = cpu_to_le16(strlen(profile->apn)); + qstrcpy((char *)&pApnName->ApnName, profile->apn); + TLVLength += + (le16_to_cpu(pApnName->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + // Set User Name + if (profile_user) { + pUserName = (PQMIWDS_USERNAME)(pTLV + TLVLength); + pUserName->TLVType = 0x17; + pUserName->TLVLength = cpu_to_le16(strlen(profile_user)); + qstrcpy((char *)&pUserName->UserName, profile_user); + TLVLength += + (le16_to_cpu(pUserName->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + // Set Password + if (profile_password) { + pPasswd = (PQMIWDS_PASSWD)(pTLV + TLVLength); + pPasswd->TLVType = 0x18; + pPasswd->TLVLength = cpu_to_le16(strlen(profile_password)); + qstrcpy((char *)&pPasswd->Passwd, profile_password); + TLVLength += + (le16_to_cpu(pPasswd->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + // Set Auth Protocol + if (profile_user && profile_password) { + pAuthPref = (PQMIWDS_AUTH_PREFERENCE)(pTLV + TLVLength); + pAuthPref->TLVType = 0x16; + pAuthPref->TLVLength = cpu_to_le16(0x01); + pAuthPref->AuthPreference = + profile_auth; // 0 ~ None, 1 ~ Pap, 2 ~ Chap, 3 ~ MsChapV2 + TLVLength += + (le16_to_cpu(pAuthPref->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + // Add IP Family Preference + pIpFamily = (PQMIWDS_IP_FAMILY_TLV)(pTLV + TLVLength); + pIpFamily->TLVType = 0x19; + pIpFamily->TLVLength = cpu_to_le16(0x01); + pIpFamily->IpFamily = profile->curIpFamily; + TLVLength += (le16_to_cpu(pIpFamily->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + + // Set Profile Index + if (profile->pdp && !s_is_cdma) { // cdma only support one pdp, so no need + // to set profile index + PQMIWDS_PROFILE_IDENTIFIER pProfileIndex = + (PQMIWDS_PROFILE_IDENTIFIER)(pTLV + TLVLength); + pProfileIndex->TLVLength = cpu_to_le16(0x01); + pProfileIndex->TLVType = 0x31; + pProfileIndex->ProfileIndex = profile->pdpindex; + if (s_is_cdma && s_hdr_personality == 0x02) { + pProfileIndex->TLVType = 0x32; // profile_index_3gpp2 + pProfileIndex->ProfileIndex = 101; + } + TLVLength += + (le16_to_cpu(pProfileIndex->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + return sizeof(QMIWDS_START_NETWORK_INTERFACE_REQ_MSG) + TLVLength; +} + +static USHORT WdsStopNwInterfaceReq(PQMUX_MSG pMUXMsg, void *arg) +{ + pMUXMsg->StopNwInterfaceReq.TLVType = 0x01; + pMUXMsg->StopNwInterfaceReq.TLVLength = cpu_to_le16(0x04); + if (*((int *)arg) == IpFamilyV4) + pMUXMsg->StopNwInterfaceReq.Handle = + cpu_to_le32(WdsConnectionIPv4Handle); + else + pMUXMsg->StopNwInterfaceReq.Handle = + cpu_to_le32(WdsConnectionIPv6Handle); + return sizeof(QMIWDS_STOP_NETWORK_INTERFACE_REQ_MSG); +} + +static USHORT WdsSetClientIPFamilyPref(PQMUX_MSG pMUXMsg, void *arg) +{ + pMUXMsg->SetClientIpFamilyPrefReq.TLVType = 0x01; + pMUXMsg->SetClientIpFamilyPrefReq.TLVLength = cpu_to_le16(0x01); + pMUXMsg->SetClientIpFamilyPrefReq.IpPreference = *((UCHAR *)arg); + return sizeof(QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ_MSG); +} + +static USHORT WdsSetAutoConnect(PQMUX_MSG pMUXMsg, void *arg) +{ + pMUXMsg->SetAutoConnectReq.TLVType = 0x01; + pMUXMsg->SetAutoConnectReq.TLVLength = cpu_to_le16(0x01); + pMUXMsg->SetAutoConnectReq.autoconnect_setting = *((UCHAR *)arg); + return sizeof(QMIWDS_SET_AUTO_CONNECT_REQ_MSG); +} + +enum peripheral_ep_type { + DATA_EP_TYPE_RESERVED = 0x0, + DATA_EP_TYPE_HSIC = 0x1, + DATA_EP_TYPE_HSUSB = 0x2, + DATA_EP_TYPE_PCIE = 0x3, + DATA_EP_TYPE_EMBEDDED = 0x4, + DATA_EP_TYPE_BAM_DMUX = 0x5, +}; + +typedef struct { + UINT rx_urb_size; + enum peripheral_ep_type ep_type; + UINT iface_id; + UCHAR MuxId; +} QMAP_SETTING; +static USHORT WdsSetQMUXBindMuxDataPort(PQMUX_MSG pMUXMsg, void *arg) +{ + QMAP_SETTING *qmap_settings = (QMAP_SETTING *)arg; + + pMUXMsg->BindMuxDataPortReq.TLVType = 0x10; + pMUXMsg->BindMuxDataPortReq.TLVLength = cpu_to_le16(0x08); + pMUXMsg->BindMuxDataPortReq.ep_type = cpu_to_le32(qmap_settings->ep_type); + pMUXMsg->BindMuxDataPortReq.iface_id = cpu_to_le32(qmap_settings->iface_id); + pMUXMsg->BindMuxDataPortReq.TLV2Type = 0x11; + pMUXMsg->BindMuxDataPortReq.TLV2Length = cpu_to_le16(0x01); + pMUXMsg->BindMuxDataPortReq.MuxId = qmap_settings->MuxId; + pMUXMsg->BindMuxDataPortReq.TLV3Type = 0x13; + pMUXMsg->BindMuxDataPortReq.TLV3Length = cpu_to_le16(0x04); + pMUXMsg->BindMuxDataPortReq.client_type = + cpu_to_le32(1); // WDS_CLIENT_TYPE_TETHERED + + return sizeof(QMIWDS_BIND_MUX_DATA_PORT_REQ_MSG); +} + +static USHORT WdaSetDataFormat(PQMUX_MSG pMUXMsg, void *arg) +{ + QMAP_SETTING *qmap_settings = (QMAP_SETTING *)arg; + + if (qmap_settings->rx_urb_size == 0) { + PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS pWdsAdminQosTlv; + PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV linkProto; + PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV dlTlp; + + pWdsAdminQosTlv = + (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS)(&pMUXMsg->QMUXMsgHdr + 1); + pWdsAdminQosTlv->TLVType = 0x10; + pWdsAdminQosTlv->TLVLength = cpu_to_le16(0x0001); + pWdsAdminQosTlv->QOSSetting = 0; /* no-QOS header */ + + linkProto = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)(pWdsAdminQosTlv + 1); + linkProto->TLVType = 0x11; + linkProto->TLVLength = cpu_to_le16(4); + linkProto->Value = cpu_to_le32(0x01); /* Set Ethernet mode */ + + dlTlp = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)(linkProto + 1); + ; + dlTlp->TLVType = 0x13; + dlTlp->TLVLength = cpu_to_le16(4); + dlTlp->Value = cpu_to_le32(0x00); + + if (sizeof(*linkProto) != 7) + dbg_time("%s sizeof(*linkProto) = %d, is not 7!", __func__, + sizeof(*linkProto)); + + return sizeof(QCQMUX_MSG_HDR) + sizeof(*pWdsAdminQosTlv) + + sizeof(*linkProto) + sizeof(*dlTlp); + } else { + // Indicates whether the Quality of Service(QOS) data format is used by + // the client. + pMUXMsg->SetDataFormatReq.QosDataFormatTlv.TLVType = 0x10; + pMUXMsg->SetDataFormatReq.QosDataFormatTlv.TLVLength = + cpu_to_le16(0x0001); + pMUXMsg->SetDataFormatReq.QosDataFormatTlv.QOSSetting = + 0; /* no-QOS header */ + // Underlying Link Layer Protocol + pMUXMsg->SetDataFormatReq.UnderlyingLinkLayerProtocolTlv.TLVType = 0x11; + pMUXMsg->SetDataFormatReq.UnderlyingLinkLayerProtocolTlv.TLVLength = + cpu_to_le16(4); + pMUXMsg->SetDataFormatReq.UnderlyingLinkLayerProtocolTlv.Value = + cpu_to_le32(0x02); /* Set IP mode */ + // Uplink (UL) data aggregation protocol to be used for uplink data + // transfer. + pMUXMsg->SetDataFormatReq.UplinkDataAggregationProtocolTlv.TLVType = + 0x12; + pMUXMsg->SetDataFormatReq.UplinkDataAggregationProtocolTlv.TLVLength = + cpu_to_le16(4); + pMUXMsg->SetDataFormatReq.UplinkDataAggregationProtocolTlv.Value = + cpu_to_le32(0x05); // UL QMAP is enabled + // Downlink (DL) data aggregation protocol to be used for downlink data + // transfer + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationProtocolTlv.TLVType = + 0x13; + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationProtocolTlv.TLVLength = + cpu_to_le16(4); + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationProtocolTlv.Value = + cpu_to_le32(0x05); // UL QMAP is enabled + // Maximum number of datagrams in a single aggregated packet on downlink + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv + .TLVType = 0x15; + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv + .TLVLength = cpu_to_le16(4); + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.Value = + cpu_to_le32(qmap_settings->rx_urb_size / 512); + // Maximum size in bytes of a single aggregated packet allowed on + // downlink + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.TLVType = + 0x16; + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.TLVLength = + cpu_to_le16(4); + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.Value = + cpu_to_le32(qmap_settings->rx_urb_size); + // Peripheral End Point ID + pMUXMsg->SetDataFormatReq.epTlv.TLVType = 0x17; + pMUXMsg->SetDataFormatReq.epTlv.TLVLength = cpu_to_le16(8); + pMUXMsg->SetDataFormatReq.epTlv.ep_type = + cpu_to_le32(qmap_settings->ep_type); + pMUXMsg->SetDataFormatReq.epTlv.iface_id = + cpu_to_le32(qmap_settings->iface_id); + + return sizeof(QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG); + } +} + +#ifdef CONFIG_SIM + +static USHORT UimVerifyPinReqSend(PQMUX_MSG pMUXMsg, void *arg) +{ + pMUXMsg->UIMUIMVerifyPinReq.TLVType = 0x01; + pMUXMsg->UIMUIMVerifyPinReq.TLVLength = cpu_to_le16(0x02); + pMUXMsg->UIMUIMVerifyPinReq.Session_Type = 0x00; + pMUXMsg->UIMUIMVerifyPinReq.Aid_Len = 0x00; + pMUXMsg->UIMUIMVerifyPinReq.TLV2Type = 0x02; + pMUXMsg->UIMUIMVerifyPinReq.TLV2Length = + cpu_to_le16(2 + strlen((const char *)arg)); + pMUXMsg->UIMUIMVerifyPinReq.PINID = 0x01; // Pin1, not Puk + pMUXMsg->UIMUIMVerifyPinReq.PINLen = strlen((const char *)arg); + qstrcpy((PCHAR)&pMUXMsg->UIMUIMVerifyPinReq.PINValue, ((const char *)arg)); + return sizeof(QMIUIM_VERIFY_PIN_REQ_MSG) + (strlen((const char *)arg) - 1); +} +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 +static USHORT UimBindPinReqSend_NAS_WMS(PQMUX_MSG pMUXMsg, void *arg) +{ + pMUXMsg->UIMUIMBindPinReq.TLVType = 0x01; + pMUXMsg->UIMUIMBindPinReq.TLVLength = cpu_to_le16(0x01); + pMUXMsg->UIMUIMBindPinReq.Value = 0x01; + return sizeof(QMIUIM_BIND_PIN_REQ_MSG); +} + +static USHORT UimBindPinReqSend_WDS_DMS_QOS(PQMUX_MSG pMUXMsg, void *arg) +{ + pMUXMsg->UIMUIMBindPinReq_4.TLVType = 0x01; + pMUXMsg->UIMUIMBindPinReq_4.TLVLength = cpu_to_le16(0x04); + pMUXMsg->UIMUIMBindPinReq_4.Value = 0x0002; + return sizeof(QMIUIM_BIND_PIN_REQ_MSG_4); +} +//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817 +#ifdef CONFIG_IMSI_ICCID +static USHORT UimReadTransparentIMSIReqSend(PQMUX_MSG pMUXMsg, void *arg) +{ + PREAD_TRANSPARENT_TLV pReadTransparent; + + pMUXMsg->UIMUIMReadTransparentReq.TLVType = 0x01; + pMUXMsg->UIMUIMReadTransparentReq.TLVLength = cpu_to_le16(0x02); + if (!strcmp((char *)arg, "EF_ICCID")) { + pMUXMsg->UIMUIMReadTransparentReq.Session_Type = 0x06; + pMUXMsg->UIMUIMReadTransparentReq.Aid_Len = 0x00; + + pMUXMsg->UIMUIMReadTransparentReq.TLV2Type = 0x02; + pMUXMsg->UIMUIMReadTransparentReq.file_id = cpu_to_le16(0x2FE2); + pMUXMsg->UIMUIMReadTransparentReq.path_len = 0x02; + pMUXMsg->UIMUIMReadTransparentReq.path[0] = 0x00; + pMUXMsg->UIMUIMReadTransparentReq.path[1] = 0x3F; + } else if (!strcmp((char *)arg, "EF_IMSI")) { + pMUXMsg->UIMUIMReadTransparentReq.Session_Type = 0x00; + pMUXMsg->UIMUIMReadTransparentReq.Aid_Len = 0x00; + + pMUXMsg->UIMUIMReadTransparentReq.TLV2Type = 0x02; + pMUXMsg->UIMUIMReadTransparentReq.file_id = cpu_to_le16(0x6F07); + pMUXMsg->UIMUIMReadTransparentReq.path_len = 0x04; + pMUXMsg->UIMUIMReadTransparentReq.path[0] = 0x00; + pMUXMsg->UIMUIMReadTransparentReq.path[1] = 0x3F; + pMUXMsg->UIMUIMReadTransparentReq.path[2] = 0xFF; + pMUXMsg->UIMUIMReadTransparentReq.path[3] = 0x7F; + } + + pMUXMsg->UIMUIMReadTransparentReq.TLV2Length = + cpu_to_le16(3 + pMUXMsg->UIMUIMReadTransparentReq.path_len); + + pReadTransparent = (PREAD_TRANSPARENT_TLV)( + &pMUXMsg->UIMUIMReadTransparentReq + .path[pMUXMsg->UIMUIMReadTransparentReq.path_len]); + pReadTransparent->TLVType = 0x03; + pReadTransparent->TLVLength = cpu_to_le16(0x04); + pReadTransparent->Offset = cpu_to_le16(0x00); + pReadTransparent->Length = cpu_to_le16(0x00); + + return (sizeof(QMIUIM_READ_TRANSPARENT_REQ_MSG) + + pMUXMsg->UIMUIMReadTransparentReq.path_len + + sizeof(READ_TRANSPARENT_TLV)); +} +#endif +#endif + +#ifdef CONFIG_APN +static USHORT WdsGetProfileSettingsReqSend(PQMUX_MSG pMUXMsg, void *arg) +{ + PROFILE_T *profile = (PROFILE_T *)arg; + pMUXMsg->GetProfileSettingsReq.Length = + cpu_to_le16(sizeof(QMIWDS_GET_PROFILE_SETTINGS_REQ_MSG) - 4); + pMUXMsg->GetProfileSettingsReq.TLVType = 0x01; + pMUXMsg->GetProfileSettingsReq.TLVLength = cpu_to_le16(0x02); + pMUXMsg->GetProfileSettingsReq.ProfileType = 0x00; // 0 ~ 3GPP, 1 ~ 3GPP2 + pMUXMsg->GetProfileSettingsReq.ProfileIndex = profile->pdpindex; + return sizeof(QMIWDS_GET_PROFILE_SETTINGS_REQ_MSG); +} +//begin modified by zhangkaibo add create profile qmi. mantis 0049137,0048741 20200610 +static USHORT WdsCreateProfileSettingsReq(PQMUX_MSG pMUXMsg, void *arg) +{ + USHORT TLVLength = 0; + UCHAR *pTLV; + PROFILE_T *profile = (PROFILE_T *)arg; + PQMIWDS_PDPTYPE pPdpType; + + pMUXMsg->CreateProfileSettingsReq.Length = + cpu_to_le16(sizeof(QMIWDS_CREATE_PROFILE_SETTINGS_REQ_MSG) - 4); + pMUXMsg->CreateProfileSettingsReq.TLVType = 0x01; + pMUXMsg->CreateProfileSettingsReq.TLVLength = cpu_to_le16(0x01); + pMUXMsg->CreateProfileSettingsReq.ProfileType = + 0x00; // 0 ~ 3GPP, 1 ~ 3GPP2 + + pTLV = (UCHAR *)(&pMUXMsg->CreateProfileSettingsReq + 1); + + pPdpType = (PQMIWDS_PDPTYPE)(pTLV + TLVLength); + pPdpType->TLVType = 0x11; + pPdpType->TLVLength = cpu_to_le16(0x01); + // 0 ?C PDP-IP (IPv4) + // 1 ?C PDP-PPP + // 2 ?C PDP-IPv6 + // 3 ?C PDP-IPv4v6 + if (profile->ipv4_flag && profile->ipv6_flag) + pPdpType->PdpType = 3; + else if (profile->ipv6_flag) + pPdpType->PdpType = 2; + else + pPdpType->PdpType = 0; + TLVLength += (le16_to_cpu(pPdpType->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + + // Set APN Name + if (profile->apn) { + PQMIWDS_APNNAME pApnName = (PQMIWDS_APNNAME)(pTLV + TLVLength); + pApnName->TLVType = 0x14; + pApnName->TLVLength = cpu_to_le16(strlen(profile->apn)); + qstrcpy((char *)&pApnName->ApnName, profile->apn); + TLVLength += + (le16_to_cpu(pApnName->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + // Set User Name + if (profile->user) { + PQMIWDS_USERNAME pUserName = (PQMIWDS_USERNAME)(pTLV + TLVLength); + pUserName->TLVType = 0x1B; + + pUserName->TLVLength = cpu_to_le16(strlen(profile->user)); + qstrcpy((char *)&pUserName->UserName, profile->user); + TLVLength += + (le16_to_cpu(pUserName->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + // Set Password + if (profile->password) { + PQMIWDS_PASSWD pPasswd = (PQMIWDS_PASSWD)(pTLV + TLVLength); + pPasswd->TLVType = 0x1C; + pPasswd->TLVLength = cpu_to_le16(strlen(profile->password)); + qstrcpy((char *)&pPasswd->Passwd, profile->password); + TLVLength += + (le16_to_cpu(pPasswd->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + // Set Auth Protocol + if (profile->user && profile->password) { + PQMIWDS_AUTH_PREFERENCE pAuthPref = + (PQMIWDS_AUTH_PREFERENCE)(pTLV + TLVLength); + pAuthPref->TLVType = 0x1D; + pAuthPref->TLVLength = cpu_to_le16(0x01); + pAuthPref->AuthPreference = + profile->auth; // 0 ~ None, 1 ~ Pap, 2 ~ Chap, 3 ~ MsChapV2 + TLVLength += + (le16_to_cpu(pAuthPref->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + return sizeof(QMIWDS_CREATE_PROFILE_SETTINGS_REQ_MSG) + TLVLength; +} +//end modified by zhangkaibo add create profile qmi. mantis 0049137,0048741 20200610 +static USHORT WdsModifyProfileSettingsReq(PQMUX_MSG pMUXMsg, void *arg) +{ + USHORT TLVLength = 0; + UCHAR *pTLV; + PROFILE_T *profile = (PROFILE_T *)arg; + PQMIWDS_PDPTYPE pPdpType; + + pMUXMsg->ModifyProfileSettingsReq.Length = + cpu_to_le16(sizeof(QMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG) - 4); + pMUXMsg->ModifyProfileSettingsReq.TLVType = 0x01; + pMUXMsg->ModifyProfileSettingsReq.TLVLength = cpu_to_le16(0x02); + pMUXMsg->ModifyProfileSettingsReq.ProfileType = + 0x00; // 0 ~ 3GPP, 1 ~ 3GPP2 + pMUXMsg->ModifyProfileSettingsReq.ProfileIndex = profile->pdpindex; + + pTLV = (UCHAR *)(&pMUXMsg->ModifyProfileSettingsReq + 1); + + pPdpType = (PQMIWDS_PDPTYPE)(pTLV + TLVLength); + pPdpType->TLVType = 0x11; + pPdpType->TLVLength = cpu_to_le16(0x01); + // 0 ?C PDP-IP (IPv4) + // 1 ?C PDP-PPP + // 2 ?C PDP-IPv6 + // 3 ?C PDP-IPv4v6 + if (profile->ipv6_flag && profile->ipv4_flag) + pPdpType->PdpType = 3; + else if (profile->ipv6_flag) + pPdpType->PdpType = 2; + else + pPdpType->PdpType = 0; + TLVLength += (le16_to_cpu(pPdpType->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + + // Set APN Name + if (profile->apn) { + PQMIWDS_APNNAME pApnName = (PQMIWDS_APNNAME)(pTLV + TLVLength); + pApnName->TLVType = 0x14; + pApnName->TLVLength = cpu_to_le16(strlen(profile->apn)); + qstrcpy((char *)&pApnName->ApnName, profile->apn); + TLVLength += + (le16_to_cpu(pApnName->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + // Set User Name + if (profile->user) { + PQMIWDS_USERNAME pUserName = (PQMIWDS_USERNAME)(pTLV + TLVLength); + pUserName->TLVType = 0x1B; + pUserName->TLVLength = cpu_to_le16(strlen(profile->user)); + qstrcpy((char *)&pUserName->UserName, profile->user); + TLVLength += + (le16_to_cpu(pUserName->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + // Set Password + if (profile->password) { + PQMIWDS_PASSWD pPasswd = (PQMIWDS_PASSWD)(pTLV + TLVLength); + pPasswd->TLVType = 0x1C; + pPasswd->TLVLength = cpu_to_le16(strlen(profile->password)); + qstrcpy((char *)&pPasswd->Passwd, profile->password); + TLVLength += + (le16_to_cpu(pPasswd->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + // Set Auth Protocol + if (profile->user && profile->password) { + PQMIWDS_AUTH_PREFERENCE pAuthPref = + (PQMIWDS_AUTH_PREFERENCE)(pTLV + TLVLength); + pAuthPref->TLVType = 0x1D; + pAuthPref->TLVLength = cpu_to_le16(0x01); + pAuthPref->AuthPreference = + profile->auth; // 0 ~ None, 1 ~ Pap, 2 ~ Chap, 3 ~ MsChapV2 + TLVLength += + (le16_to_cpu(pAuthPref->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + return sizeof(QMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG) + TLVLength; +} +#endif + +static USHORT WdsGetRuntimeSettingReq(PQMUX_MSG pMUXMsg, void *arg) +{ + pMUXMsg->GetRuntimeSettingsReq.TLVType = 0x10; + pMUXMsg->GetRuntimeSettingsReq.TLVLength = cpu_to_le16(0x04); + // the following mask also applies to IPV6 + pMUXMsg->GetRuntimeSettingsReq.Mask = + cpu_to_le32(QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4DNS_ADDR | + QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4_ADDR | + QMIWDS_GET_RUNTIME_SETTINGS_MASK_MTU | + QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4GATEWAY_ADDR); // | + // QMIWDS_GET_RUNTIME_SETTINGS_MASK_PCSCF_SV_ADDR | + // QMIWDS_GET_RUNTIME_SETTINGS_MASK_PCSCF_DOM_NAME; + + return sizeof(QMIWDS_GET_RUNTIME_SETTINGS_REQ_MSG); +} + +static PQCQMIMSG s_pRequest; +static PQCQMIMSG s_pResponse; +static pthread_mutex_t s_commandmutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t s_commandcond = PTHREAD_COND_INITIALIZER; + +static int is_response(const PQCQMIMSG pRequest, const PQCQMIMSG pResponse) +{ + if ((pRequest->QMIHdr.QMIType == pResponse->QMIHdr.QMIType) && + (pRequest->QMIHdr.ClientId == pResponse->QMIHdr.ClientId)) { + USHORT requestTID, responseTID; + if (pRequest->QMIHdr.QMIType == QMUX_TYPE_CTL) { + requestTID = pRequest->CTLMsg.QMICTLMsgHdr.TransactionId; + responseTID = pResponse->CTLMsg.QMICTLMsgHdr.TransactionId; + } else { + requestTID = le16_to_cpu(pRequest->MUXMsg.QMUXHdr.TransactionId); + responseTID = le16_to_cpu(pResponse->MUXMsg.QMUXHdr.TransactionId); + } + return (requestTID == responseTID); + } + return 0; +} + +int (*qmidev_send)(PQCQMIMSG pRequest); + +int QmiThreadSendQMITimeout(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse, + unsigned msecs) +{ + int ret; + + if (!pRequest) { + return -EINVAL; + } + + pthread_mutex_lock(&s_commandmutex); + + if (ppResponse) + *ppResponse = NULL; + + dump_qmi(pRequest, le16_to_cpu(pRequest->QMIHdr.Length) + 1); + + s_pRequest = pRequest; + s_pResponse = NULL; + + ret = qmidev_send(pRequest); + + if (ret == 0) { + ret = pthread_cond_timeout_np(&s_commandcond, &s_commandmutex, msecs); + if (!ret) { + if (s_pResponse && ppResponse) { + *ppResponse = s_pResponse; + } else { + if (s_pResponse) { + free(s_pResponse); + s_pResponse = NULL; + } + } + } else { + dbg_time("%s pthread_cond_timeout_np=%d, errno: %d (%s)", __func__, + ret, errno, strerror(errno)); + } + } + + pthread_mutex_unlock(&s_commandmutex); + + return ret; +} + +int QmiThreadSendQMI(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse) +{ + return QmiThreadSendQMITimeout(pRequest, ppResponse, 30 * 1000); +} + +void QmiThreadRecvQMI(PQCQMIMSG pResponse) +{ + pthread_mutex_lock(&s_commandmutex); + if (pResponse == NULL) { + if (s_pRequest) { + free(s_pRequest); + s_pRequest = NULL; + s_pResponse = NULL; + pthread_cond_signal(&s_commandcond); + } + pthread_mutex_unlock(&s_commandmutex); + return; + } + dump_qmi(pResponse, le16_to_cpu(pResponse->QMIHdr.Length) + 1); + if (s_pRequest && is_response(s_pRequest, pResponse)) { + free(s_pRequest); + s_pRequest = NULL; + s_pResponse = malloc(le16_to_cpu(pResponse->QMIHdr.Length) + 1); + if (s_pResponse != NULL) { + memcpy(s_pResponse, pResponse, + le16_to_cpu(pResponse->QMIHdr.Length) + 1); + } + pthread_cond_signal(&s_commandcond); + } else if ((pResponse->QMIHdr.QMIType == QMUX_TYPE_NAS) && + (le16_to_cpu(pResponse->MUXMsg.QMUXMsgHdrResp.Type) == + QMINAS_SERVING_SYSTEM_IND)) { + qmidevice_send_event_to_main( + RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED); + } else if ((pResponse->QMIHdr.QMIType == QMUX_TYPE_WDS) && + (le16_to_cpu(pResponse->MUXMsg.QMUXMsgHdrResp.Type) == + QMIWDS_GET_PKT_SRVC_STATUS_IND)) { + qmidevice_send_event_to_main(RIL_UNSOL_DATA_CALL_LIST_CHANGED); + } else if ((pResponse->QMIHdr.QMIType == QMUX_TYPE_WDS_IPV6) && + (le16_to_cpu(pResponse->MUXMsg.QMUXMsgHdrResp.Type) == + QMIWDS_GET_PKT_SRVC_STATUS_IND)) { + qmidevice_send_event_to_main(RIL_UNSOL_DATA_CALL_LIST_CHANGED); + } else if ((pResponse->QMIHdr.QMIType == QMUX_TYPE_NAS) && + (le16_to_cpu(pResponse->MUXMsg.QMUXMsgHdrResp.Type) == + QMINAS_SYS_INFO_IND)) { + qmidevice_send_event_to_main( + RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED); + } else { + if (debug_qmi) + dbg_time("nobody care this qmi msg!!"); + } + pthread_mutex_unlock(&s_commandmutex); +} + +int requestSetEthMode(PROFILE_T *profile) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse = NULL; + PQMUX_MSG pMUXMsg; + int err; + PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV linkProto; + UCHAR IpPreference; + UCHAR autoconnect_setting = 0; + QMAP_SETTING qmap_settings = {0, 0, 0, 0}; + + if (profile->qmap_mode) { + profile->rawIP = 1; + + + qmap_settings.MuxId = profile->muxid; + + if (qmidev_is_pciemhi(profile->qmichannel)) { // SDX20_PCIE + qmap_settings.rx_urb_size = 32*1024; //SDX24&SDX55 support 32KB + qmap_settings.ep_type = DATA_EP_TYPE_PCIE; + qmap_settings.iface_id = 0x04; + } else { // for MDM9x07&MDM9x40&SDX20 USB + qmap_settings.rx_urb_size = profile->qmap_size; + //profile->qmap_size; // SDX24&SDX55 support 32KB + qmap_settings.ep_type = DATA_EP_TYPE_HSUSB; +//2021-02-08 zhangkaibo@fibocom.com changed begin for mantis 0070613 + qmap_settings.iface_id = profile->interfacenum; +//2021-02-08 zhangkaibo@fibocom.com changed end for mantis 0070613 + } + + if (qmidev_is_gobinet( + profile->qmichannel)) { // GobiNet set data format + // in GobiNet driver + goto skip_WdaSetDataFormat; + } else if (profile->qmap_mode > + 1) { // QMAP MUX enabled, set data format in fibo-qmi-proxy + +//2021-06-03 willa.liu@fibocom.com changed begin for support mantis 0079541 +//2021-01-27 willa.liu@fibocom.com changed begin for support mantis 0068849 + //goto skip_WdaSetDataFormat; + goto skip_WdaSetDataFormat; +//2021-01-27 willa.liu@fibocom.com changed end for support mantis 0068849 +//2021-06-03 willa.liu@fibocom.com changed end for support mantis 0079541 + + } + } + + pRequest = + ComposeQMUXMsg(QMUX_TYPE_WDS_ADMIN, QMIWDS_ADMIN_SET_DATA_FORMAT_REQ, + WdaSetDataFormat, (void *)&qmap_settings); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + linkProto = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)GetTLV( + &pResponse->MUXMsg.QMUXMsgHdr, 0x11); + if (linkProto != NULL) { + profile->rawIP = (le32_to_cpu(linkProto->Value) == 2); + } + + linkProto = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)GetTLV( + &pResponse->MUXMsg.QMUXMsgHdr, 0x16); + if (linkProto != NULL && profile->qmap_mode) { + qmap_settings.rx_urb_size = le32_to_cpu(linkProto->Value); + dbg_time( + "qmap_settings.rx_urb_size = %d", + qmap_settings.rx_urb_size); // must same as rx_urb_size defined + // in GobiNet&qmi_wwan driver + } + + free(pResponse); + +skip_WdaSetDataFormat: +//bind v4 client + // set ipv4 + if(profile->ipv4_flag) + { +/* IpPreference = IpFamilyV4; + pRequest = + ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ, + WdsSetClientIPFamilyPref, (void *)&IpPreference); + err = QmiThreadSendQMI(pRequest, &pResponse); + if (pResponse) + free(pResponse); +*/ + if (profile->qmapnet_adapter) { + // bind wds mux data port + pRequest = ComposeQMUXMsg( + QMUX_TYPE_WDS, QMIWDS_BIND_MUX_DATA_PORT_REQ, + WdsSetQMUXBindMuxDataPort, (void *)&qmap_settings); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + if (pResponse) + free(pResponse); + } +// set auto connect + pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_SET_AUTO_CONNECT_REQ, + WdsSetAutoConnect, (void *)&autoconnect_setting); + QmiThreadSendQMI(pRequest, &pResponse); + if (pResponse) + free(pResponse); + } + +//bind v6 client + if(profile->ipv6_flag) + { + IpPreference = IpFamilyV6; + pRequest = ComposeQMUXMsg( + QMUX_TYPE_WDS_IPV6, QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ, + WdsSetClientIPFamilyPref, (void *)&IpPreference); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + if (pResponse) + free(pResponse); + + if (profile->qmapnet_adapter) { + // bind wds mux data port + pRequest = ComposeQMUXMsg( + QMUX_TYPE_WDS_IPV6, QMIWDS_BIND_MUX_DATA_PORT_REQ, + WdsSetQMUXBindMuxDataPort, (void *)&qmap_settings); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + if (pResponse) + free(pResponse); + } + IpPreference = IpFamilyV6; + pRequest = ComposeQMUXMsg( + QMUX_TYPE_WDS_IPV6, QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ, + WdsSetClientIPFamilyPref, (void *)&IpPreference); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + if (pResponse) + free(pResponse); + +// set auto connect + pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS_IPV6, QMIWDS_SET_AUTO_CONNECT_REQ, + WdsSetAutoConnect, (void *)&autoconnect_setting); + QmiThreadSendQMI(pRequest, &pResponse); + if (pResponse) + free(pResponse); + } + return 0; +} + +#ifdef CONFIG_SIM +int requestGetPINStatus(SIM_Status *pSIMStatus) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + PQMIDMS_UIM_PIN_STATUS pPin1Status = NULL; + // PQMIDMS_UIM_PIN_STATUS pPin2Status = NULL; + + + pRequest = ComposeQMUXMsg(QMUX_TYPE_UIM, QMIUIM_GET_CARD_STATUS_REQ, + NULL, NULL); + + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + pPin1Status = + (PQMIDMS_UIM_PIN_STATUS)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11); + // pPin2Status = + // (PQMIDMS_UIM_PIN_STATUS)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x12); + + if (pPin1Status != NULL) { + if (pPin1Status->PINStatus == QMI_PIN_STATUS_NOT_VERIF) { + *pSIMStatus = SIM_PIN; + } else if (pPin1Status->PINStatus == QMI_PIN_STATUS_BLOCKED) { + *pSIMStatus = SIM_PUK; + } else if (pPin1Status->PINStatus == QMI_PIN_STATUS_PERM_BLOCKED) { + *pSIMStatus = SIM_BAD; + } + } + + free(pResponse); + return 0; +} + +static int requestGetSIMMode(PROFILE_T *profile) +{ + int i; + int fd; + int ret; + int len; + char buffer[1024] = {0}; + char *sendbuffer = "AT+GTDUALSIMEN?"; + int totallen = 0; + fd_set readfds; + struct timeval timeout; + struct termios tiosfd, tio; + int timeoutVal = 5; + char *dev = NULL; + if(qmidev_is_gobinet(profile->qmichannel)) + { + dev = "/dev/ttyUSB1"; //The port under Linux is operated by opening the device file + } + else + { + dev = "/dev/mhi_DUN"; //The port under Linux is operated by opening the device file + } + + fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK); + if(fd < 0) + goto ERR; + fcntl(fd, F_SETFL, O_RDWR); + xget1(fd, &tio, &tiosfd); + + if(xset1(fd, &tio, dev)) + goto ERR; + + tcflush(fd, TCIOFLUSH); + + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + sprintf(buffer, "%s\r", sendbuffer); + ret = write (fd, buffer, strlen(buffer)); + if(ret < 0) + { + dbg_time("write failed\n"); + goto ERR; + } + dbg_time("dev: %s\nsendbuffer:%s\n", dev, sendbuffer); + sleep(3); + + while(1) + { + timeout.tv_sec = timeoutVal; + timeout.tv_usec = 0; + + ret = select(fd+1, &readfds, (fd_set *)0, (fd_set *)0, &timeout); + if(ret > 0) + { + ret = read(fd, buffer+totallen, sizeof(buffer)-totallen-1); + if(ret < 0) + { + dbg_time("read failed\n"); + goto ERR; + } + + if(ret == 0) + { + goto ERR; + } + + totallen += ret; + buffer[totallen] = '\0'; + dbg_time("read %d %s", ret, &buffer[totallen-ret]); + +//current buffer is return value + if(strstr(buffer, "+GTDUALSIMEN: 1")) + { + close(fd); + dbg_time("read 1\n"); + return 1; + } + else + { + close(fd); + dbg_time("read 0/2/3\n"); + return 0; + } + } + else + { + dbg_time("select timeout\n"); + goto ERR; + } + + } + + close(fd); + +ERR: + if(fd > 0) + close(fd); + return -1; +} + + +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 +int requestGetSIMCardNumber(PROFILE_T *profile) +{ + int i; + int fd; + int ret; + int len; + char buffer[1024] = {0}; + int rate; + char *sendbuffer; + int totallen = 0; + fd_set readfds; + struct timeval timeout; + struct termios tiosfd, tio; + int timeoutVal = 5; + char *dev = NULL; + if(qmidev_is_gobinet(profile->qmichannel)) + { + dev = "/dev/ttyUSB1"; //The port under Linux is operated by opening the device file + } + else + { + dev = "/dev/mhi_DUN"; //The port under Linux is operated by opening the device file + } + rate = 115200; + sendbuffer = "AT+GTDUALSIM?"; + if(requestGetSIMMode(profile) != 1) + { + return 0; + } + fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK); + if(fd < 0) + goto ERR; + fcntl(fd, F_SETFL, O_RDWR); + xget1(fd, &tio, &tiosfd); + + if(xset1(fd, &tio, dev)) + goto ERR; + + tcflush(fd, TCIOFLUSH); + + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + sprintf(buffer, "%s\r", sendbuffer); + ret = write (fd, buffer, strlen(buffer)); + if(ret < 0) + { + dbg_time("write failed\n"); + goto ERR; + } + dbg_time("dev: %s\nrate:%d\nsendbuffer:%s\n", dev, rate, sendbuffer); + sleep(3); + goto read_result; + while(1) + { + timeout.tv_sec = timeoutVal; + timeout.tv_usec = 0; + + //ret = select(fd+1, &readfds, (fd_set *)0, (fd_set *)0, &timeout); + //if(ret > 0) + { + read_result: + ret = read(fd, buffer+totallen, sizeof(buffer)-totallen-1); + if(ret < 0) + { + dbg_time("read failed\n"); + goto ERR; + } + + if(ret == 0) + { + goto ERR; + } + + totallen += ret; + buffer[totallen] = '\0'; + dbg_time("read %d %s", ret, &buffer[totallen-ret]); + +//current buffer is return value + if(strstr(buffer, "+GTDUALSIM: 0")) + { + close(fd); + dbg_time("read 0\n"); + return 0; + } + else if(strstr(buffer, "+GTDUALSIM: 1")) + { + close(fd); + dbg_time("read 1\n"); + return 1; + } + else + continue; + } + //else + { + //dbg_time("select timeout\n"); + //goto ERR; + } + + } + + close(fd); + +ERR: + if(fd > 0) + close(fd); + return -1; +} + +int requestSimBindSubscription_NAS_WMS() +{ + PQCQMIMSG pRequest_nas; + PQCQMIMSG pResponse_nas; + int err; + + pRequest_nas = ComposeQMUXMsg(QMUX_TYPE_NAS, QMI_NAS_BIND_SUBSCRIPTION_REQ_V01, + UimBindPinReqSend_NAS_WMS, NULL); + err = QmiThreadSendQMI(pRequest_nas, &pResponse_nas); + free(pResponse_nas); + + return 0; +} + +int requestSimBindSubscription_WDS_DMS_QOS() +{ + PQCQMIMSG pRequest_wds; + PQCQMIMSG pRequest_dms; + + PQCQMIMSG pResponse_wds; + PQCQMIMSG pResponse_dms; + int err; + + pRequest_wds = ComposeQMUXMsg(QMUX_TYPE_WDS, QMI_WDS_BIND_SUBSCRIPTION_REQ_V01, + UimBindPinReqSend_WDS_DMS_QOS, NULL); + err = QmiThreadSendQMI(pRequest_wds, &pResponse_wds); + + pRequest_dms = ComposeQMUXMsg(QMUX_TYPE_DMS, QMI_DMS_BIND_SUBSCRIPTION_REQ_V01, + UimBindPinReqSend_WDS_DMS_QOS, NULL); + err = QmiThreadSendQMI(pRequest_dms, &pResponse_dms); + + free(pResponse_wds); + free(pResponse_dms); + + return 0; +} + +//int requestGetSIMStatus(SIM_Status *pSIMStatus) +int requestGetSIMStatus(SIM_Status *pSIMStatus, const int sim_select) +{ // RIL_REQUEST_GET_SIM_STATUS + int i; + dbg_time("sim_select = %d\n", sim_select); + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + const char *SIM_Status_String[] = { + "SIM_ABSENT", + "SIM_NOT_READY", + "SIM_READY", /* SIM_READY means the radio state is RADIO_STATE_SIM_READY + */ + "SIM_PIN", + "SIM_PUK", + "SIM_NETWORK_PERSONALIZATION"}; + + pRequest = ComposeQMUXMsg(QMUX_TYPE_UIM, QMIUIM_GET_CARD_STATUS_REQ, + NULL, NULL); + + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + *pSIMStatus = SIM_ABSENT; + PQMIUIM_CARD_STATUS pCardStatus = NULL; + PQMIUIM_PIN_STATE pPINState = NULL; + UCHAR CardState = 0x01; + UCHAR PIN1State = QMI_PIN_STATUS_NOT_VERIF; + // UCHAR PIN1Retries; + // UCHAR PUK1Retries; + // UCHAR PIN2State; + // UCHAR PIN2Retries; + // UCHAR PUK2Retries; + + void *temp = NULL; + QMIUIM_APP_STATUS * curr_app = NULL; + Instance * curr_ints = NULL; + + pCardStatus = + (PQMIUIM_CARD_STATUS)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x10); + if(sim_select == 0) + { + temp =(void*)pCardStatus + sizeof(QMIUIM_CARD_STATUS); + curr_ints = temp; + dbg_time("curr_ints_sim1 %s", curr_ints); + temp = temp + sizeof(Instance); + curr_app = temp; + } + else + { + temp = (void*)pCardStatus + sizeof(QMIUIM_CARD_STATUS); + temp = temp + sizeof(Instance) + (sizeof(QMIUIM_APP_STATUS)*(((Instance *)temp)->NumApp)); + curr_ints = temp; + dbg_time("curr_ints_sim2 %s", curr_ints); + curr_app = temp + sizeof(Instance); + } +/* + if (pCardStatus != NULL) { + + for (i = 0; i < pCardStatus->NumApp; i++) { + if (pCardStatus->AppStatus[i].AppType == 2) { + pPINState = &(pCardStatus->AppStatus[i].PinState); + break; + } + } + if (i == pCardStatus->NumApp) { + dbg_time("no USIM Card info"); + return -1; + } + + CardState = pCardStatus->CardState; + if (pPINState->UnivPIN == 1) { + PIN1State = pCardStatus->UPINState; +*/ + if (curr_ints != NULL) { + dbg_time("curr_ints->CardState is %d", curr_ints->CardState); + dbg_time("curr_ints->NumApp is %d", curr_ints->NumApp); + for (i = 0; i < curr_ints->NumApp; i++) { + dbg_time("AppType = %d\n", curr_app[i].AppType); + if (curr_app[i].AppType == 2) { + pPINState = &(curr_app[i].PinState); + break; + } + } + if (i == curr_ints->NumApp) { + dbg_time("no USIM Card info"); + return -1; + } + + //CardState = pCardStatus->CardState; + CardState = curr_ints->CardState; + if (pPINState->UnivPIN == 1) { + PIN1State = curr_ints->UPINState; + // PIN1Retries = pCardStatus->UPINRetries; + // PUK1Retries = pCardStatus->UPUKRetries; + } else { + PIN1State = pPINState->PIN1State; + // PIN1Retries = pPINState->PIN1Retries; + // PUK1Retries = pPINState->PUK1Retries; + } + // PIN2State = pPINState->PIN2State; + // PIN2Retries = pPINState->PIN2Retries; + // PUK2Retries = pPINState->PUK2Retries; + } +//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817 + *pSIMStatus = SIM_ABSENT; + if ((CardState == 0x01) && ((PIN1State == QMI_PIN_STATUS_VERIFIED) || + (PIN1State == QMI_PIN_STATUS_DISABLED))) { + *pSIMStatus = SIM_READY; + } else if (CardState == 0x01) { + if (PIN1State == QMI_PIN_STATUS_NOT_VERIF) { + *pSIMStatus = SIM_PIN; + } + if (PIN1State == QMI_PIN_STATUS_BLOCKED) { + *pSIMStatus = SIM_PUK; + } else if (PIN1State == QMI_PIN_STATUS_PERM_BLOCKED) { + *pSIMStatus = SIM_BAD; + } else if (PIN1State == QMI_PIN_STATUS_NOT_INIT || + PIN1State == QMI_PIN_STATUS_VERIFIED || + PIN1State == QMI_PIN_STATUS_DISABLED) { + *pSIMStatus = SIM_READY; + } + } else if (CardState == 0x00 || CardState == 0x02) { + } else { + } + + dbg_time("%s SIMStatus: %s", __func__, SIM_Status_String[*pSIMStatus]); + + free(pResponse); + + return 0; +} + +int requestEnterSimPin(const CHAR *pPinCode) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + + pRequest = ComposeQMUXMsg(QMUX_TYPE_UIM, QMIUIM_VERIFY_PIN_REQ, + UimVerifyPinReqSend, (void *)pPinCode); + + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + free(pResponse); + return 0; +} + +#ifdef CONFIG_IMSI_ICCID +int requestGetICCID(void) +{ // RIL_REQUEST_GET_IMSI + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + PQMIUIM_CONTENT pUimContent; + int err; + + pRequest = + ComposeQMUXMsg(QMUX_TYPE_UIM, QMIUIM_READ_TRANSPARENT_REQ, + UimReadTransparentIMSIReqSend, (void *)"EF_ICCID"); + err = QmiThreadSendQMI(pRequest, &pResponse); + + + qmi_rsp_check_and_return(); + + pUimContent = (PQMIUIM_CONTENT)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11); + if (pUimContent != NULL) { + static char DeviceICCID[32] = {'\0'}; + int i = 0, j = 0; + + for (i = 0, j = 0; i < le16_to_cpu(pUimContent->content_len); ++i) { + char charmaps[] = "0123456789ABCDEF"; + + DeviceICCID[j++] = charmaps[(pUimContent->content[i] & 0x0F)]; + DeviceICCID[j++] = + charmaps[((pUimContent->content[i] & 0xF0) >> 0x04)]; + } + DeviceICCID[j] = '\0'; + + dbg_time("%s DeviceICCID: %s", __func__, DeviceICCID); + } + + free(pResponse); + return 0; +} + +int requestGetIMSI(void) +{ // RIL_REQUEST_GET_IMSI + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + PQMIUIM_CONTENT pUimContent; + int err; + + pRequest = + ComposeQMUXMsg(QMUX_TYPE_UIM, QMIUIM_READ_TRANSPARENT_REQ, + UimReadTransparentIMSIReqSend, (void *)"EF_IMSI"); + err = QmiThreadSendQMI(pRequest, &pResponse); + + + qmi_rsp_check_and_return(); + + pUimContent = (PQMIUIM_CONTENT)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11); + if (pUimContent != NULL) { + static char DeviceIMSI[32] = {'\0'}; + int i = 0, j = 0; + + for (i = 0, j = 0; i < le16_to_cpu(pUimContent->content[0]); ++i) { + if (i != 0) + DeviceIMSI[j++] = (pUimContent->content[i + 1] & 0x0F) + '0'; + DeviceIMSI[j++] = + ((pUimContent->content[i + 1] & 0xF0) >> 0x04) + '0'; + } + DeviceIMSI[j] = '\0'; + + dbg_time("%s DeviceIMSI: %s", __func__, DeviceIMSI); + } + + free(pResponse); + return 0; +} +#endif +#endif + +static void fibo_convert_cdma_mcc_2_ascii_mcc(USHORT *p_mcc, USHORT mcc) +{ + unsigned int d1, d2, d3, buf = mcc + 111; + + if (mcc == 0x3FF) // wildcard + { + *p_mcc = 3; + } else { + d3 = buf % 10; + buf = (d3 == 0) ? (buf - 10) / 10 : buf / 10; + + d2 = buf % 10; + buf = (d2 == 0) ? (buf - 10) / 10 : buf / 10; + + d1 = (buf == 10) ? 0 : buf; + + // dbg_time("d1:%d, d2:%d,d3:%d",d1,d2,d3); + if (d1 < 10 && d2 < 10 && d3 < 10) { + *p_mcc = d1 * 100 + d2 * 10 + d3; + } else { + // dbg_time( "invalid digits %d %d %d", d1, d2, d3 ); + *p_mcc = 0; + } + } +} + +static void fibo_convert_cdma_mnc_2_ascii_mnc(USHORT *p_mnc, USHORT imsi_11_12) +{ + unsigned int d1, d2, buf = imsi_11_12 + 11; + + if (imsi_11_12 == 0x7F) // wildcard + { + *p_mnc = 7; + } else { + d2 = buf % 10; + buf = (d2 == 0) ? (buf - 10) / 10 : buf / 10; + + d1 = (buf == 10) ? 0 : buf; + + if (d1 < 10 && d2 < 10) { + *p_mnc = d1 * 10 + d2; + } else { + // dbg_time( "invalid digits %d %d", d1, d2, 0 ); + *p_mnc = 0; + } + } +} + +int requestGetHomeNetwork(USHORT *p_mcc, USHORT *p_mnc, USHORT *p_sid, + USHORT *p_nid) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + PHOME_NETWORK pHomeNetwork; + PHOME_NETWORK_SYSTEMID pHomeNetworkSystemID; + + pRequest = + ComposeQMUXMsg(QMUX_TYPE_NAS, QMINAS_GET_HOME_NETWORK_REQ, NULL, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + pHomeNetwork = (PHOME_NETWORK)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x01); + if (pHomeNetwork && p_mcc && p_mnc) { + *p_mcc = le16_to_cpu(pHomeNetwork->MobileCountryCode); + *p_mnc = le16_to_cpu(pHomeNetwork->MobileNetworkCode); + // dbg_time("%s MobileCountryCode: %d, MobileNetworkCode: %d", __func__, + // *pMobileCountryCode, *pMobileNetworkCode); + } + + pHomeNetworkSystemID = + (PHOME_NETWORK_SYSTEMID)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x10); + if (pHomeNetworkSystemID && p_sid && p_nid) { + *p_sid = le16_to_cpu( + pHomeNetworkSystemID->SystemID); // china-hefei: sid 14451 + *p_nid = le16_to_cpu(pHomeNetworkSystemID->NetworkID); + // dbg_time("%s SystemID: %d, NetworkID: %d", __func__, *pSystemID, + // *pNetworkID); + } + + free(pResponse); + + return 0; +} + +struct wwan_data_class_str class2str[] = { + {WWAN_DATA_CLASS_NONE, "UNKNOWN"}, + {WWAN_DATA_CLASS_GPRS, "GPRS"}, + {WWAN_DATA_CLASS_EDGE, "EDGE"}, + {WWAN_DATA_CLASS_UMTS, "UMTS"}, + {WWAN_DATA_CLASS_HSDPA, "HSDPA"}, + {WWAN_DATA_CLASS_HSUPA, "HSUPA"}, + {WWAN_DATA_CLASS_LTE, "LTE"}, + {WWAN_DATA_CLASS_1XRTT, "1XRTT"}, + {WWAN_DATA_CLASS_1XEVDO, "1XEVDO"}, + {WWAN_DATA_CLASS_1XEVDO_REVA, "1XEVDO_REVA"}, + {WWAN_DATA_CLASS_1XEVDV, "1XEVDV"}, + {WWAN_DATA_CLASS_3XRTT, "3XRTT"}, + {WWAN_DATA_CLASS_1XEVDO_REVB, "1XEVDO_REVB"}, + {WWAN_DATA_CLASS_UMB, "UMB"}, + {WWAN_DATA_CLASS_CUSTOM, "CUSTOM"}, +//begin modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605 + {WWAN_DATA_CLASS_5G, "NR5G"}, +//end modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605 +}; + +CHAR *wwan_data_class2str(ULONG class) +{ + unsigned int i = 0; + for (i = 0; i < sizeof(class2str) / sizeof(class2str[0]); i++) { + if (class2str[i].class == class) { + return class2str[i].str; + } + } + return "UNKNOWN"; +} + +int requestRegistrationState2(UCHAR *pPSAttachedState) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + USHORT MobileCountryCode = 0; + USHORT MobileNetworkCode = 0; + const char *pDataCapStr = "UNKNOW"; + LONG remainingLen; + PSERVICE_STATUS_INFO pServiceStatusInfo; + int is_lte = 0; + PCDMA_SYSTEM_INFO pCdmaSystemInfo; + PHDR_SYSTEM_INFO pHdrSystemInfo; + PGSM_SYSTEM_INFO pGsmSystemInfo; + PWCDMA_SYSTEM_INFO pWcdmaSystemInfo; + PLTE_SYSTEM_INFO pLteSystemInfo; +//begin modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605 + PNR5G_SYSTEM_INFO pNr5gSystemInfo; +//end modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605 + PTDSCDMA_SYSTEM_INFO pTdscdmaSystemInfo; + UCHAR DeviceClass = 0; + ULONG DataCapList = 0; + + *pPSAttachedState = 0; + pRequest = + ComposeQMUXMsg(QMUX_TYPE_NAS, QMINAS_GET_SYS_INFO_REQ, NULL, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + pServiceStatusInfo = (PSERVICE_STATUS_INFO)( + ((PCHAR)&pMUXMsg->GetSysInfoResp) + QCQMUX_MSG_HDR_SIZE); + remainingLen = le16_to_cpu(pMUXMsg->GetSysInfoResp.Length); + + s_is_cdma = 0; + s_hdr_personality = 0; + while (remainingLen > 0) { + switch (pServiceStatusInfo->TLVType) { + case 0x10: // CDMA + if (pServiceStatusInfo->SrvStatus == 0x02) { + DataCapList = WWAN_DATA_CLASS_1XRTT | WWAN_DATA_CLASS_1XEVDO | + WWAN_DATA_CLASS_1XEVDO_REVA | + WWAN_DATA_CLASS_1XEVDV | + WWAN_DATA_CLASS_1XEVDO_REVB; + DeviceClass = DEVICE_CLASS_CDMA; + s_is_cdma = (0 == is_lte); + } + break; + case 0x11: // HDR + if (pServiceStatusInfo->SrvStatus == 0x02) { + DataCapList = WWAN_DATA_CLASS_3XRTT | WWAN_DATA_CLASS_UMB; + DeviceClass = DEVICE_CLASS_CDMA; + s_is_cdma = (0 == is_lte); + } + break; + case 0x12: // GSM + if (pServiceStatusInfo->SrvStatus == 0x02) { + DataCapList = WWAN_DATA_CLASS_GPRS | WWAN_DATA_CLASS_EDGE; + DeviceClass = DEVICE_CLASS_GSM; + } + break; + case 0x13: // WCDMA + if (pServiceStatusInfo->SrvStatus == 0x02) { + DataCapList = WWAN_DATA_CLASS_UMTS; + DeviceClass = DEVICE_CLASS_GSM; + } + break; +//begin modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605 + case 0x4A: // NR5G + if (pServiceStatusInfo->SrvStatus == 0x02) { + DataCapList = WWAN_DATA_CLASS_5G; + DeviceClass = DEVICE_CLASS_GSM; + is_lte = 1; + s_is_cdma = 0; + } + break; +//end modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605 + case 0x14: // LTE + if (pServiceStatusInfo->SrvStatus == 0x02) { + DataCapList = WWAN_DATA_CLASS_LTE; + DeviceClass = DEVICE_CLASS_GSM; + is_lte = 1; + s_is_cdma = 0; + } + break; + case 0x24: // TDSCDMA + if (pServiceStatusInfo->SrvStatus == 0x02) { + pDataCapStr = "TD-SCDMA"; + } + break; + case 0x15: // CDMA + // CDMA_SYSTEM_INFO + pCdmaSystemInfo = (PCDMA_SYSTEM_INFO)pServiceStatusInfo; + if (pCdmaSystemInfo->SrvDomainValid == 0x01) { + *pPSAttachedState = 0; + if (pCdmaSystemInfo->SrvDomain & 0x02) { + *pPSAttachedState = 1; + s_is_cdma = (0 == is_lte); + } + } +#if 0 + if (pCdmaSystemInfo->SrvCapabilityValid == 0x01) { + *pPSAttachedState = 0; + if (pCdmaSystemInfo->SrvCapability & 0x02) { + *pPSAttachedState = 1; + s_is_cdma = (0 == is_lte); + } + } +#endif + if (pCdmaSystemInfo->NetworkIdValid == 0x01) { + int i; + CHAR temp[10]; + strncpy(temp, (CHAR *)pCdmaSystemInfo->MCC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileCountryCode = (USHORT)atoi(temp); + + strncpy(temp, (CHAR *)pCdmaSystemInfo->MNC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileNetworkCode = (USHORT)atoi(temp); + } + break; + case 0x16: // HDR + // HDR_SYSTEM_INFO + pHdrSystemInfo = (PHDR_SYSTEM_INFO)pServiceStatusInfo; + if (pHdrSystemInfo->SrvDomainValid == 0x01) { + *pPSAttachedState = 0; + if (pHdrSystemInfo->SrvDomain & 0x02) { + *pPSAttachedState = 1; + s_is_cdma = (0 == is_lte); + } + } +#if 0 + if (pHdrSystemInfo->SrvCapabilityValid == 0x01) { + *pPSAttachedState = 0; + if (pHdrSystemInfo->SrvCapability & 0x02) { + *pPSAttachedState = 1; + s_is_cdma = (0 == is_lte); + } + } +#endif + if (*pPSAttachedState && + pHdrSystemInfo->HdrPersonalityValid == 0x01) { + if (pHdrSystemInfo->HdrPersonality == 0x03) + s_hdr_personality = 0x02; + // else if (pHdrSystemInfo->HdrPersonality == 0x02) + // s_hdr_personality = 0x01; + } + USHORT cmda_mcc = 0, cdma_mnc = 0; + if (!requestGetHomeNetwork(&cmda_mcc, &cdma_mnc, NULL, NULL) && + cmda_mcc) { + fibo_convert_cdma_mcc_2_ascii_mcc(&MobileCountryCode, cmda_mcc); + fibo_convert_cdma_mnc_2_ascii_mnc(&MobileNetworkCode, cdma_mnc); + } + break; + case 0x17: // GSM + // GSM_SYSTEM_INFO + pGsmSystemInfo = (PGSM_SYSTEM_INFO)pServiceStatusInfo; + if (pGsmSystemInfo->SrvDomainValid == 0x01) { + *pPSAttachedState = 0; + if (pGsmSystemInfo->SrvDomain & 0x02) { + *pPSAttachedState = 1; + } + } +#if 0 + if (pGsmSystemInfo->SrvCapabilityValid == 0x01) { + *pPSAttachedState = 0; + if (pGsmSystemInfo->SrvCapability & 0x02) { + *pPSAttachedState = 1; + } + } +#endif + if (pGsmSystemInfo->NetworkIdValid == 0x01) { + int i; + CHAR temp[10]; + strncpy(temp, (CHAR *)pGsmSystemInfo->MCC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileCountryCode = (USHORT)atoi(temp); + + strncpy(temp, (CHAR *)pGsmSystemInfo->MNC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileNetworkCode = (USHORT)atoi(temp); + } + break; + case 0x18: // WCDMA + // WCDMA_SYSTEM_INFO + pWcdmaSystemInfo = (PWCDMA_SYSTEM_INFO)pServiceStatusInfo; + if (pWcdmaSystemInfo->SrvDomainValid == 0x01) { + *pPSAttachedState = 0; + if (pWcdmaSystemInfo->SrvDomain & 0x02) { + *pPSAttachedState = 1; + } + } +#if 0 + if (pWcdmaSystemInfo->SrvCapabilityValid == 0x01) { + *pPSAttachedState = 0; + if (pWcdmaSystemInfo->SrvCapability & 0x02) { + *pPSAttachedState = 1; + } + } +#endif + if (pWcdmaSystemInfo->NetworkIdValid == 0x01) { + int i; + CHAR temp[10]; + strncpy(temp, (CHAR *)pWcdmaSystemInfo->MCC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileCountryCode = (USHORT)atoi(temp); + + strncpy(temp, (CHAR *)pWcdmaSystemInfo->MNC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileNetworkCode = (USHORT)atoi(temp); + } + break; + case 0x19: // LTE_SYSTEM_INFO + // LTE_SYSTEM_INFO + pLteSystemInfo = (PLTE_SYSTEM_INFO)pServiceStatusInfo; + if (pLteSystemInfo->SrvDomainValid == 0x01) { + *pPSAttachedState = 0; + if (pLteSystemInfo->SrvDomain & 0x02) { + *pPSAttachedState = 1; + is_lte = 1; + s_is_cdma = 0; + } + } +#if 0 + if (pLteSystemInfo->SrvCapabilityValid == 0x01) { + *pPSAttachedState = 0; + if (pLteSystemInfo->SrvCapability & 0x02) { + *pPSAttachedState = 1; + is_lte = 1; + s_is_cdma = 0; + } + } +#endif + if (pLteSystemInfo->NetworkIdValid == 0x01) { + int i; + CHAR temp[10]; + strncpy(temp, (CHAR *)pLteSystemInfo->MCC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileCountryCode = (USHORT)atoi(temp); + + strncpy(temp, (CHAR *)pLteSystemInfo->MNC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileNetworkCode = (USHORT)atoi(temp); + } + break; +//begin modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605 + case 0x4b: // NR5G_SYSTEM_INFO + // NR5G_SYSTEM_INFO + pNr5gSystemInfo = (PNR5G_SYSTEM_INFO)pServiceStatusInfo; + if (pNr5gSystemInfo->SrvDomainValid == 0x01) { + *pPSAttachedState = 0; + if (pNr5gSystemInfo->SrvDomain & 0x02) { + *pPSAttachedState = 1; + is_lte = 1; + s_is_cdma = 0; + } + } + + if (pNr5gSystemInfo->NetworkIdValid == 0x01) { + int i; + CHAR temp[10]; + strncpy(temp, (CHAR *)pNr5gSystemInfo->MCC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileCountryCode = (USHORT)atoi(temp); + + strncpy(temp, (CHAR *)pNr5gSystemInfo->MNC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileNetworkCode = (USHORT)atoi(temp); + } + break; +//end modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605 + case 0x25: // TDSCDMA + // TDSCDMA_SYSTEM_INFO + pTdscdmaSystemInfo = (PTDSCDMA_SYSTEM_INFO)pServiceStatusInfo; + if (pTdscdmaSystemInfo->SrvDomainValid == 0x01) { + *pPSAttachedState = 0; + if (pTdscdmaSystemInfo->SrvDomain & 0x02) { + *pPSAttachedState = 1; + } + } +#if 0 + if (pTdscdmaSystemInfo->SrvCapabilityValid == 0x01) { + *pPSAttachedState = 0; + if (pTdscdmaSystemInfo->SrvCapability & 0x02) { + *pPSAttachedState = 1; + } + } +#endif + if (pTdscdmaSystemInfo->NetworkIdValid == 0x01) { + int i; + CHAR temp[10]; + strncpy(temp, (CHAR *)pTdscdmaSystemInfo->MCC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileCountryCode = (USHORT)atoi(temp); + + strncpy(temp, (CHAR *)pTdscdmaSystemInfo->MNC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileNetworkCode = (USHORT)atoi(temp); + } + break; + default: + break; + } /* switch (pServiceStatusInfo->TLYType) */ + remainingLen -= (le16_to_cpu(pServiceStatusInfo->TLVLength) + 3); + pServiceStatusInfo = (PSERVICE_STATUS_INFO)( + (PCHAR)&pServiceStatusInfo->TLVLength + + le16_to_cpu(pServiceStatusInfo->TLVLength) + sizeof(USHORT)); + } /* while (remainingLen > 0) */ + + if (DeviceClass == DEVICE_CLASS_CDMA) { + if (s_hdr_personality == 2) { + pDataCapStr = s_hdr_personality == 2 ? "eHRPD" : "HRPD"; + } else if (DataCapList & WWAN_DATA_CLASS_1XEVDO_REVB) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_1XEVDO_REVB); + } else if (DataCapList & WWAN_DATA_CLASS_1XEVDO_REVA) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_1XEVDO_REVA); + } else if (DataCapList & WWAN_DATA_CLASS_1XEVDO) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_1XEVDO); + } else if (DataCapList & WWAN_DATA_CLASS_1XRTT) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_1XRTT); + } else if (DataCapList & WWAN_DATA_CLASS_3XRTT) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_3XRTT); + } else if (DataCapList & WWAN_DATA_CLASS_UMB) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_UMB); + } + } else { + if (DataCapList & WWAN_DATA_CLASS_LTE) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_LTE); + } else if ((DataCapList & WWAN_DATA_CLASS_HSDPA) && + (DataCapList & WWAN_DATA_CLASS_HSUPA)) { + pDataCapStr = "HSDPA_HSUPA"; + } else if (DataCapList & WWAN_DATA_CLASS_HSDPA) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_HSDPA); + } else if (DataCapList & WWAN_DATA_CLASS_HSUPA) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_HSUPA); + } else if (DataCapList & WWAN_DATA_CLASS_UMTS) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_UMTS); + } else if (DataCapList & WWAN_DATA_CLASS_EDGE) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_EDGE); + } else if (DataCapList & WWAN_DATA_CLASS_GPRS) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_GPRS); +//begin modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605 + } else if (DataCapList & WWAN_DATA_CLASS_5G) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_5G); +//end modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605 + } + } + g_MobileCountryCode = MobileCountryCode; + g_MobileNetworkCode = MobileNetworkCode; + dbg_time("%s MCC: %d, MNC: %d, PS: %s, DataCap: %s", __func__, + MobileCountryCode, MobileNetworkCode, + (*pPSAttachedState == 1) ? "Attached" : "Detached", pDataCapStr); + + free(pResponse); + + return 0; +} + +int requestRegistrationState(UCHAR *pPSAttachedState) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + PQMINAS_CURRENT_PLMN_MSG pCurrentPlmn; + PSERVING_SYSTEM pServingSystem; + PQMINAS_DATA_CAP pDataCap; + USHORT MobileCountryCode = 0; + USHORT MobileNetworkCode = 0; + const char *pDataCapStr = "UNKNOW"; +//begin modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605 + /* + for 9x07 later, QMINAS_GET_SERVING_SYSTEM_REQ has been declara as Deprecated + so sdx55 NR5G register info can not get by QMINAS_GET_SERVING_SYSTEM_REQ. + coninue use QMINAS_GET_SERVING_SYSTEM_REQ, fibocom-dial will show network as UNKNOWN + */ + //begin modified by zhaofei delete incorrect registration interface on x55/x24 platform. 20200603 +// #if 0 +// if (s_9x07) { +//eturn requestRegistrationState2(pPSAttachedState); +// } +// #endif + //end modified by zhaofei delete incorrect registration interface on x55/x24 platform.20200603 +//begin modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605 + pRequest = ComposeQMUXMsg(QMUX_TYPE_NAS, QMINAS_GET_SERVING_SYSTEM_REQ, + NULL, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + pCurrentPlmn = + (PQMINAS_CURRENT_PLMN_MSG)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x12); + if (pCurrentPlmn) { + MobileCountryCode = le16_to_cpu(pCurrentPlmn->MobileCountryCode); + MobileNetworkCode = le16_to_cpu(pCurrentPlmn->MobileNetworkCode); + } + + *pPSAttachedState = 0; + pServingSystem = + (PSERVING_SYSTEM)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x01); + if (pServingSystem) { + // Packet-switched domain attach state of the mobile. + // 0x00 PS_UNKNOWN ?Unknown or not applicable + // 0x01 PS_ATTACHED ?Attached + // 0x02 PS_DETACHED ?Detached + *pPSAttachedState = pServingSystem->RegistrationState; + if (pServingSystem->RegistrationState == + 0x01) // 0x01 ?C REGISTERED ?C Registered with a network + *pPSAttachedState = pServingSystem->PSAttachedState; + else { + // MobileCountryCode = MobileNetworkCode = 0; + *pPSAttachedState = 0x02; + } + } + + pDataCap = (PQMINAS_DATA_CAP)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11); + if (pDataCap && pDataCap->DataCapListLen) { + UCHAR *DataCap = &pDataCap->DataCap; + if (pDataCap->DataCapListLen == 2) { + if ((DataCap[0] == 0x06) && + ((DataCap[1] == 0x08) || (DataCap[1] == 0x0A))) + DataCap[0] = DataCap[1]; + } + switch (DataCap[0]) { + case 0x01: + pDataCapStr = "GPRS"; + break; + case 0x02: + pDataCapStr = "EDGE"; + break; + case 0x03: + pDataCapStr = "HSDPA"; + break; + case 0x04: + pDataCapStr = "HSUPA"; + break; + case 0x05: + pDataCapStr = "UMTS"; + break; + case 0x06: + pDataCapStr = "1XRTT"; + break; + case 0x07: + pDataCapStr = "1XEVDO"; + break; + case 0x08: + pDataCapStr = "1XEVDO_REVA"; + break; + case 0x09: + pDataCapStr = "GPRS"; + break; + case 0x0A: + pDataCapStr = "1XEVDO_REVB"; + break; + case 0x0B: + pDataCapStr = "LTE"; + break; + case 0x0C: + pDataCapStr = "HSDPA"; + break; + case 0x0D: + pDataCapStr = "HSDPA"; + break; + default: + pDataCapStr = "UNKNOW"; + break; + } + } + + if (pServingSystem && pServingSystem->RegistrationState == 0x01 && + pServingSystem->InUseRadioIF && pServingSystem->RadioIF == 0x09) { + pDataCapStr = "TD-SCDMA"; + } + + s_is_cdma = 0; + if (pServingSystem && pServingSystem->RegistrationState == 0x01 && + pServingSystem->InUseRadioIF && + (pServingSystem->RadioIF == 0x01 || pServingSystem->RadioIF == 0x02)) { + USHORT cmda_mcc = 0, cdma_mnc = 0; + s_is_cdma = 1; + if (!requestGetHomeNetwork(&cmda_mcc, &cdma_mnc, NULL, NULL) && + cmda_mcc) { + fibo_convert_cdma_mcc_2_ascii_mcc(&MobileCountryCode, cmda_mcc); + fibo_convert_cdma_mnc_2_ascii_mnc(&MobileNetworkCode, cdma_mnc); + } + if (1) { + PQCQMUX_TLV pTLV = + (PQCQMUX_TLV)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x23); + if (pTLV) + s_hdr_personality = pTLV->Value; + else + s_hdr_personality = 0; + if (s_hdr_personality == 2) + pDataCapStr = "eHRPD"; + } + } + + dbg_time("%s MCC: %d, MNC: %d, PS: %s, DataCap: %s", __func__, + MobileCountryCode, MobileNetworkCode, + (*pPSAttachedState == 1) ? "Attached" : "Detached", pDataCapStr); + + free(pResponse); + + return 0; +} + +int requestQueryDataCall(UCHAR *pConnectionStatus, int curIpFamily) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + PQMIWDS_PKT_SRVC_TLV pPktSrvc; + UCHAR oldConnectionStatus = *pConnectionStatus; + UCHAR QMIType = + (curIpFamily == IpFamilyV4) ? QMUX_TYPE_WDS : QMUX_TYPE_WDS_IPV6; + + pRequest = + ComposeQMUXMsg(QMIType, QMIWDS_GET_PKT_SRVC_STATUS_REQ, NULL, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + *pConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; + pPktSrvc = + (PQMIWDS_PKT_SRVC_TLV)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x01); + if (pPktSrvc) { + *pConnectionStatus = pPktSrvc->ConnectionStatus; + if ((le16_to_cpu(pPktSrvc->TLVLength) == 2) && + (pPktSrvc->ReconfigReqd == 0x01)) + *pConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; + } + + if (*pConnectionStatus == QWDS_PKT_DATA_DISCONNECTED) { + if (curIpFamily == IpFamilyV4) + WdsConnectionIPv4Handle = 0; + else + WdsConnectionIPv6Handle = 0; + } + + if (oldConnectionStatus != *pConnectionStatus || debug_qmi) { + dbg_time("%s %sConnectionStatus: %s", __func__, + (curIpFamily == IpFamilyV4) ? "IPv4" : "IPv6", + (*pConnectionStatus == QWDS_PKT_DATA_CONNECTED) + ? "CONNECTED" + : "DISCONNECTED"); + } + + free(pResponse); + return 0; +} + +int requestSetupDataCall(PROFILE_T *profile, int curIpFamily) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err = 0; + UCHAR QMIType = + (curIpFamily == IpFamilyV4) ? QMUX_TYPE_WDS : QMUX_TYPE_WDS_IPV6; + + // DualIPSupported means can get ipv4 & ipv6 address at the same time, one + // wds for ipv4, the other wds for ipv6 + profile->curIpFamily = curIpFamily; + pRequest = ComposeQMUXMsg(QMIType, QMIWDS_START_NETWORK_INTERFACE_REQ, + WdsStartNwInterfaceReq, profile); + err = QmiThreadSendQMITimeout(pRequest, &pResponse, 120 * 1000); + qmi_rsp_check(); + + if (le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXResult) || + le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError)) { + PQMI_TLV_HDR pTLVHdr; + + pTLVHdr = GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x10); + if (pTLVHdr) { + uint16_t *data16 = (uint16_t *)(pTLVHdr + 1); + uint16_t call_end_reason = le16_to_cpu(data16[0]); + dbg_time("call_end_reason is %d", call_end_reason); + } + + pTLVHdr = GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11); + if (pTLVHdr) { + uint16_t *data16 = (uint16_t *)(pTLVHdr + 1); + uint16_t call_end_reason_type = le16_to_cpu(data16[0]); + uint16_t verbose_call_end_reason = le16_to_cpu(data16[1]); + + dbg_time("call_end_reason_type is %d", call_end_reason_type); + dbg_time("call_end_reason_verbose is %d", verbose_call_end_reason); + } + + free(pResponse); + return le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError); + } + + if (curIpFamily == IpFamilyV4) { + WdsConnectionIPv4Handle = + le32_to_cpu(pResponse->MUXMsg.StartNwInterfaceResp.Handle); + dbg_time("%s WdsConnectionIPv4Handle: 0x%08x", __func__, + WdsConnectionIPv4Handle); + } else { + WdsConnectionIPv6Handle = + le32_to_cpu(pResponse->MUXMsg.StartNwInterfaceResp.Handle); + dbg_time("%s WdsConnectionIPv6Handle: 0x%08x", __func__, + WdsConnectionIPv6Handle); + } + + free(pResponse); + + return 0; +} + +int requestDeactivateDefaultPDP(PROFILE_T *profile, int curIpFamily) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + UCHAR QMIType = (curIpFamily == 0x04) ? QMUX_TYPE_WDS : QMUX_TYPE_WDS_IPV6; + + if (curIpFamily == IpFamilyV4 && WdsConnectionIPv4Handle == 0) + return 0; + if (curIpFamily == IpFamilyV6 && WdsConnectionIPv6Handle == 0) + return 0; + + pRequest = ComposeQMUXMsg(QMIType, QMIWDS_STOP_NETWORK_INTERFACE_REQ, + WdsStopNwInterfaceReq, &curIpFamily); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + if (curIpFamily == IpFamilyV4) + WdsConnectionIPv4Handle = 0; + else + WdsConnectionIPv6Handle = 0; + free(pResponse); + return 0; +} + +int requestGetIPAddress(PROFILE_T *profile, int curIpFamily) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR pIpv4Addr; + PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR pIpv6Addr = NULL; + PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_DNS_ADDR pIpv6DNSAddr = NULL; + PQMIWDS_GET_RUNTIME_SETTINGS_TLV_MTU pMtu; + IPV4_T *pIpv4 = &profile->ipv4; + IPV6_T *pIpv6 = &profile->ipv6; + UCHAR QMIType = (curIpFamily == 0x04) ? QMUX_TYPE_WDS : QMUX_TYPE_WDS_IPV6; + + if (curIpFamily == IpFamilyV4) { + memset(pIpv4, 0x00, sizeof(IPV4_T)); + if (WdsConnectionIPv4Handle == 0) + return 0; + } else if (curIpFamily == IpFamilyV6) { + memset(pIpv6, 0x00, sizeof(IPV6_T)); + if (WdsConnectionIPv6Handle == 0) + return 0; + } + + pRequest = ComposeQMUXMsg(QMIType, QMIWDS_GET_RUNTIME_SETTINGS_REQ, + WdsGetRuntimeSettingReq, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + pIpv4Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR)GetTLV( + &pResponse->MUXMsg.QMUXMsgHdr, + QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4PRIMARYDNS); + if (pIpv4Addr) { + pIpv4->DnsPrimary = pIpv4Addr->IPV4Address; + } + + pIpv4Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR)GetTLV( + &pResponse->MUXMsg.QMUXMsgHdr, + QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4SECONDARYDNS); + if (pIpv4Addr) { + pIpv4->DnsSecondary = pIpv4Addr->IPV4Address; + } + + pIpv4Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR)GetTLV( + &pResponse->MUXMsg.QMUXMsgHdr, + QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4GATEWAY); + if (pIpv4Addr) { + pIpv4->Gateway = pIpv4Addr->IPV4Address; + } + + pIpv4Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR)GetTLV( + &pResponse->MUXMsg.QMUXMsgHdr, + QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4SUBNET); + if (pIpv4Addr) { + pIpv4->SubnetMask = pIpv4Addr->IPV4Address; + } + + pIpv4Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR)GetTLV( + &pResponse->MUXMsg.QMUXMsgHdr, + QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4); + if (pIpv4Addr) { + pIpv4->Address = pIpv4Addr->IPV4Address; + } + + pIpv6DNSAddr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_DNS_ADDR)GetTLV( + &pResponse->MUXMsg.QMUXMsgHdr, + QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6PRIMARYDNS); + if (pIpv6DNSAddr) { + memcpy(pIpv6->DnsPrimary, pIpv6DNSAddr->IPV6Address, 16); + } + + pIpv6DNSAddr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_DNS_ADDR)GetTLV( + &pResponse->MUXMsg.QMUXMsgHdr, + QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6SECONDARYDNS); + if (pIpv6DNSAddr) { + memcpy(pIpv6->DnsSecondary, pIpv6DNSAddr->IPV6Address, 16); + } + +//2021-02-25 willa.liu@fibocom.com changed begin for support eipd SN-20210129001 + +/* + pIpv6Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR)GetTLV( + &pResponse->MUXMsg.QMUXMsgHdr, + QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6GATEWAY); + if(pIpv6Addr) { + memcpy(pIpv6->Gateway, pIpv6Addr->IPV6Address, 16); + pIpv6->PrefixLengthGateway = pIpv6Addr->PrefixLength; + } +*/ + + if(profile->ipv6_prigateway_flag == 1) + { + char localip6gateway[1024] = {0}; + get_private_gateway(localip6gateway); + int length = sizeof(localip6gateway)/sizeof(localip6gateway[0]); + if (pIpv6Addr) + { + int i = 0; + char *token = strtok(localip6gateway, "."); + while(token != NULL) + { + pIpv6->Gateway[i++] = atoi(token); + //printf("token:%s, pIpv6->Gateway[%d]:%d\n", token, i-1, pIpv6->Gateway[i-1]); + token = strtok(NULL, "."); + } + pIpv6->PrefixLengthGateway = 16; + } + } + else + { + pIpv6Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR)GetTLV( + &pResponse->MUXMsg.QMUXMsgHdr, + QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6GATEWAY); + if(pIpv6Addr) + { + memcpy(pIpv6->Gateway, pIpv6Addr->IPV6Address, 16); + pIpv6->PrefixLengthGateway = pIpv6Addr->PrefixLength; + } + } +//2021-02-25 willa.liu@fibocom.com changed end for support eipd SN-20210129001 + + + pIpv6Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR)GetTLV( + &pResponse->MUXMsg.QMUXMsgHdr, + QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6); + if (pIpv6Addr) { + memcpy(pIpv6->Address, pIpv6Addr->IPV6Address, 16); + pIpv6->PrefixLengthIPAddr = pIpv6Addr->PrefixLength; + } + + pMtu = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_MTU)GetTLV( + &pResponse->MUXMsg.QMUXMsgHdr, + QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_MTU); + if (pMtu) { + pIpv4->Mtu = pIpv6->Mtu = le32_to_cpu(pMtu->Mtu); + } + + free(pResponse); + return 0; +} + +#ifdef CONFIG_APN +int requestSetProfile(PROFILE_T *profile) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + + if (!profile->pdp) + return 0; + + dbg_time("%s[%d] %s/%s/%s/%d", __func__, profile->pdpindex, profile->apn, + profile->user, profile->password, profile->auth); + + UCHAR QMIType = (profile->ipv4_flag) ? QMUX_TYPE_WDS : QMUX_TYPE_WDS_IPV6; + pRequest = ComposeQMUXMsg(QMIType, QMIWDS_MODIFY_PROFILE_SETTINGS_REQ, + WdsModifyProfileSettingsReq, profile); + err = QmiThreadSendQMI(pRequest, &pResponse); +//begin modified by zhangkaibo add create profile qmi. mantis 0049137,0048741 20200610 + //modify profile no check return value, try create + qmi_rsp_check(); + if(le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError)) + { + pRequest = ComposeQMUXMsg(QMIType, QMIWDS_CREATE_PROFILE_SETTINGS_REQ, + WdsCreateProfileSettingsReq, profile); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check(); + if(le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError)){ + free(pResponse); + return -1; + } + dbg_time("WdsCreateProfileSettingsReq[%d] %s/%s/%s/%d", profile->pdpindex, profile->apn, + profile->user, profile->password, profile->auth); + } +//end modified by zhangkaibo add create profile qmi. mantis 0049137,0048741 20200610 + free(pResponse); + return 0; +} + +int requestGetProfile(PROFILE_T *profile) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + char *apn = NULL; + char *user = NULL; + char *password = NULL; + int auth = 0; + PQMIWDS_APNNAME pApnName; + PQMIWDS_USERNAME pUserName; + PQMIWDS_PASSWD pPassWd; + PQMIWDS_AUTH_PREFERENCE pAuthPref; + + if (!profile->pdp) + return 0; + + UCHAR QMIType = (profile->ipv4_flag) ? QMUX_TYPE_WDS : QMUX_TYPE_WDS_IPV6; + pRequest = ComposeQMUXMsg(QMIType, QMIWDS_GET_PROFILE_SETTINGS_REQ, + WdsGetProfileSettingsReqSend, profile); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + pApnName = (PQMIWDS_APNNAME)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x14); + pUserName = (PQMIWDS_USERNAME)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x1B); + pPassWd = (PQMIWDS_PASSWD)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x1C); + pAuthPref = + (PQMIWDS_AUTH_PREFERENCE)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x1D); + + if (pApnName /* && le16_to_cpu(pApnName->TLVLength)*/) + apn = strndup((const char *)(&pApnName->ApnName), + le16_to_cpu(pApnName->TLVLength)); + if (pUserName /* && pUserName->UserName*/) + user = strndup((const char *)(&pUserName->UserName), + le16_to_cpu(pUserName->TLVLength)); + if (pPassWd /* && le16_to_cpu(pPassWd->TLVLength)*/) + password = strndup((const char *)(&pPassWd->Passwd), + le16_to_cpu(pPassWd->TLVLength)); + if (pAuthPref /* && le16_to_cpu(pAuthPref->TLVLength)*/) { + auth = pAuthPref->AuthPreference; + } + +#if 0 + if (profile) { + profile->apn = apn; + profile->user = user; + profile->password = password; + profile->auth = auth; + } +#endif + + dbg_time("%s[%d] %s/%s/%s/%d", __func__, profile->pdpindex, apn, user, password, + auth); + + free(pResponse); + return 0; +} +#endif + +#ifdef CONFIG_VERSION +int requestBaseBandVersion(const char **pp_reversion) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + PDEVICE_REV_ID revId; + int err; + + if (pp_reversion) + *pp_reversion = NULL; + + pRequest = + ComposeQMUXMsg(QMUX_TYPE_DMS, QMIDMS_GET_DEVICE_REV_ID_REQ, NULL, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + revId = (PDEVICE_REV_ID)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x01); + + if (revId && le16_to_cpu(revId->TLVLength)) { + char *DeviceRevisionID = strndup((const char *)(&revId->RevisionID), + le16_to_cpu(revId->TLVLength)); + dbg_time("%s %s", __func__, DeviceRevisionID); + + if (pp_reversion) + *pp_reversion = DeviceRevisionID; + } + + free(pResponse); + return 0; +} +#endif + +#ifdef CONFIG_RESET_RADIO +static USHORT DmsSetOperatingModeReq(PQMUX_MSG pMUXMsg, void *arg) +{ + pMUXMsg->SetOperatingModeReq.TLVType = 0x01; + pMUXMsg->SetOperatingModeReq.TLVLength = cpu_to_le16(1); + pMUXMsg->SetOperatingModeReq.OperatingMode = *((UCHAR *)arg); + + return sizeof(QMIDMS_SET_OPERATING_MODE_REQ_MSG); +} + +int requestSetOperatingMode(UCHAR OperatingMode) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + + dbg_time("%s(%d)", __func__, OperatingMode); + + pRequest = ComposeQMUXMsg(QMUX_TYPE_DMS, QMIDMS_SET_OPERATING_MODE_REQ, + DmsSetOperatingModeReq, &OperatingMode); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + free(pResponse); + return 0; +} +#endif diff --git a/fibocom-dial/src/QMIThread.h b/fibocom-dial/src/QMIThread.h new file mode 100644 index 000000000..d3c80086b --- /dev/null +++ b/fibocom-dial/src/QMIThread.h @@ -0,0 +1,260 @@ +#ifndef __QMI_THREAD_H__ +#define __QMI_THREAD_H__ + +#define CONFIG_GOBINET +#define CONFIG_QMIWWAN +#define CONFIG_SIM +#define CONFIG_APN +#define CONFIG_VERSION +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 +#define CONFIG_IMSI_ICCID +//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817 +#define CONFIG_DEFAULT_PDP 1 +#define CONFIG_DEFAULT_PDPINDEX 1 +//#define CONFIG_IMSI_ICCID + +#define CONFIG_RESET_RADIO \ + (45) // Reset Radiao(AT+CFUN=4,AT+CFUN=1) when cann not register network or + // setup data call in 45 seconds + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MPQCTL.h" +#include "MPQMI.h" +#include "MPQMUX.h" + +#define DEVICE_CLASS_UNKNOWN 0 +#define DEVICE_CLASS_CDMA 1 +#define DEVICE_CLASS_GSM 2 + +#define WWAN_DATA_CLASS_NONE 0x00000000 +#define WWAN_DATA_CLASS_GPRS 0x00000001 +#define WWAN_DATA_CLASS_EDGE 0x00000002 /* EGPRS */ +#define WWAN_DATA_CLASS_UMTS 0x00000004 +#define WWAN_DATA_CLASS_HSDPA 0x00000008 +#define WWAN_DATA_CLASS_HSUPA 0x00000010 +#define WWAN_DATA_CLASS_LTE 0x00000020 +//begin modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605 +#define WWAN_DATA_CLASS_5G 0x00000040 +//end modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605 +#define WWAN_DATA_CLASS_1XRTT 0x00010000 +#define WWAN_DATA_CLASS_1XEVDO 0x00020000 +#define WWAN_DATA_CLASS_1XEVDO_REVA 0x00040000 +#define WWAN_DATA_CLASS_1XEVDV 0x00080000 +#define WWAN_DATA_CLASS_3XRTT 0x00100000 +#define WWAN_DATA_CLASS_1XEVDO_REVB 0x00200000 /* for future use */ +#define WWAN_DATA_CLASS_UMB 0x00400000 +#define WWAN_DATA_CLASS_CUSTOM 0x80000000 + +struct wwan_data_class_str { + ULONG class; + CHAR *str; +}; + +#pragma pack(push, 1) + +typedef struct _QCQMIMSG { + QCQMI_HDR QMIHdr; + union { + QMICTL_MSG CTLMsg; + QMUX_MSG MUXMsg; +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 + QMUX_MSG QMUXMsgHdrResp; +//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817 + }; +} __attribute__((packed)) QCQMIMSG, *PQCQMIMSG; + +#pragma pack(pop) + +typedef struct __IPV4 { + uint32_t Address; + uint32_t Gateway; + uint32_t SubnetMask; + uint32_t DnsPrimary; + uint32_t DnsSecondary; + uint32_t Mtu; +} IPV4_T; + +typedef struct __IPV6 { + UCHAR Address[16]; + UCHAR Gateway[16]; + UCHAR SubnetMask[16]; + UCHAR DnsPrimary[16]; + UCHAR DnsSecondary[16]; + UCHAR PrefixLengthIPAddr; + UCHAR PrefixLengthGateway; + ULONG Mtu; +} IPV6_T; + +#define IpFamilyV4 (0x04) +#define IpFamilyV6 (0x06) + +struct __PROFILE; +struct qmi_device_ops { + int (*init)(struct __PROFILE *profile); + int (*deinit)(void); + int (*send)(PQCQMIMSG pRequest); + void *(*read)(void *pData); +}; +extern int (*qmidev_send)(PQCQMIMSG pRequest); + +#ifndef bool +#define bool uint8_t +#endif +//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037 +typedef struct { + unsigned int size; + unsigned int rx_urb_size; + unsigned int ep_type; + unsigned int iface_id; + unsigned int qmap_mode; + unsigned int qmap_version; + unsigned int dl_minimum_padding; + char ifname[8][16]; + unsigned char mux_id[8]; +} RMNET_INFO; +//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037 + +typedef struct __PROFILE { + char *qmichannel; + char *usbnet_adapter; + char *qmapnet_adapter; + const char *driver_name; + int qmap_mode; + int qmap_size; + int qmap_version; + const char *apn; + const char *user; + const char *password; + const char *pincode; + int auth; + int pdp; + int pdpindex; + int pdpnum; + int curIpFamily; + int rawIP; + int muxid; + IPV4_T ipv4; + IPV6_T ipv6; + int ipv4_flag; + int ipv6_flag; + +//2021-02-25 willa.liu@fibocom.com changed begin for support eipd SN-20210129001 + int ipv6_prigateway_flag; +//2021-02-25 willa.liu@fibocom.com changed begin for support eipd SN-20210129001 + + int dual_flag; + int apntype; + const struct qmi_device_ops *qmi_ops; + bool loopback_state; + int replication_factor; +//2021-02-08 zhangkaibo@fibocom.com changed begin for mantis 0070613 + int interfacenum; +//2021-02-08 zhangkaibo@fibocom.com changed end for mantis 0070613 +//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037 + RMNET_INFO rmnet_info; +//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037 +} PROFILE_T; + +typedef enum { + SIM_ABSENT = 0, + SIM_NOT_READY = 1, + SIM_READY = + 2, /* SIM_READY means the radio state is RADIO_STATE_SIM_READY */ + SIM_PIN = 3, + SIM_PUK = 4, + SIM_NETWORK_PERSONALIZATION = 5, + SIM_BAD = 6, +} SIM_Status; +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 +typedef enum { + SIM_Card0 = 0, + SIM_Card1 = 1 +} SIM_Select; +//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817 +#define WDM_DEFAULT_BUFSIZE 256 +#define RIL_REQUEST_QUIT 0x1000 +#define RIL_INDICATE_DEVICE_CONNECTED 0x1002 +#define RIL_INDICATE_DEVICE_DISCONNECTED 0x1003 +#define RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED 0x1004 +#define RIL_UNSOL_DATA_CALL_LIST_CHANGED 0x1005 + +extern int pthread_cond_timeout_np(pthread_cond_t *cond, pthread_mutex_t *mutex, + unsigned msecs); +extern int QmiThreadSendQMI(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse); +extern int QmiThreadSendQMITimeout(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse, + unsigned msecs); +extern void QmiThreadRecvQMI(PQCQMIMSG pResponse); +extern int fibo_raw_ip_mode_check(const char *ifname); +extern void udhcpc_start(PROFILE_T *profile); +extern void udhcpc_stop(PROFILE_T *profile); +extern void udhcpc_start_pcie(PROFILE_T *profile); +extern void udhcpc_stop_pcie(PROFILE_T *profile); + +extern void dump_qmi(void *dataBuffer, int dataLen); +extern void qmidevice_send_event_to_main(int triger_event); +extern int requestSetEthMode(PROFILE_T *profile); +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 +//extern int requestGetSIMStatus(SIM_Status *pSIMStatus); +extern int requestGetSIMStatus(SIM_Status *pSIMStatus , const int sim_select); +//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817 +extern int requestEnterSimPin(const CHAR *pPinCode); +extern int requestGetICCID(void); +extern int requestGetIMSI(void); +extern int requestRegistrationState(UCHAR *pPSAttachedState); +extern int requestQueryDataCall(UCHAR *pConnectionStatus, int curIpFamily); +extern int requestSetupDataCall(PROFILE_T *profile, int curIpFamily); +extern int requestDeactivateDefaultPDP(PROFILE_T *profile, int curIpFamily); +extern int requestSetProfile(PROFILE_T *profile); +extern int requestGetProfile(PROFILE_T *profile); +extern int requestBaseBandVersion(const char **pp_reversion); +extern int requestGetIPAddress(PROFILE_T *profile, int curIpFamily); +extern int requestSetOperatingMode(UCHAR OperatingMode); +int requestRegistrationState2(UCHAR *pPSAttachedState); + +extern int fibo_qmap_mode_set(PROFILE_T *profile); +extern int fibo_bridge_mode_detect(PROFILE_T *profile); +extern int fibo_qmap_mode_detect(PROFILE_T *profile); +extern const struct qmi_device_ops qmiwwan_qmidev_ops; + +#define qmidev_is_gobinet(_qmichannel) \ + (strncmp(_qmichannel, "/dev/qcqmi", strlen("/dev/qcqmi")) == 0) +#define qmidev_is_qmiwwan(_qmichannel) \ + (strncmp(_qmichannel, "/dev/cdc-wdm", strlen("/dev/cdc-wdm")) == 0) +#define qmidev_is_pciemhi(_qmichannel) \ + (strncmp(_qmichannel, "/dev/mhi_", strlen("/dev/mhi_")) == 0) + +#define driver_is_qmi(_drv_name) \ + (strncasecmp(_drv_name, "qmi_wwan", strlen("qmi_wwan")) == 0) +#define driver_is_mbim(_drv_name) \ + (strncasecmp(_drv_name, "cdc_mbim", strlen("cdc_mbim")) == 0) + +extern FILE *logfilefp; +extern int debug_qmi; +extern USHORT g_MobileCountryCode; +extern USHORT g_MobileNetworkCode; +extern int qmidevice_control_fd[2]; +extern int qmiclientId[QMUX_TYPE_WDS_ADMIN + 1]; +extern void dbg_time(const char *fmt, ...); +extern USHORT le16_to_cpu(USHORT v16); +extern UINT le32_to_cpu(UINT v32); +extern USHORT cpu_to_le16(USHORT v16); +extern UINT cpu_to_le32(UINT v32); +#endif diff --git a/fibocom-dial/src/QmiWwanCM.c b/fibocom-dial/src/QmiWwanCM.c new file mode 100644 index 000000000..ed1cccd2f --- /dev/null +++ b/fibocom-dial/src/QmiWwanCM.c @@ -0,0 +1,426 @@ +#include +#include +#include +#include +typedef unsigned short sa_family_t; +#include + +#include "QMIThread.h" + +#ifdef CONFIG_QMIWWAN +#define FIBO_QMI_PROXY "fibo_qmimsg_server" +static int cdc_wdm_fd = -1; + +static UCHAR GetQCTLTransactionId(void) +{ + static int TransactionId = 0; + if (++TransactionId > 0xFF) + TransactionId = 1; + return TransactionId; +} + +typedef USHORT (*CUSTOMQCTL)(PQMICTL_MSG pCTLMsg, void *arg); + +static PQCQMIMSG ComposeQCTLMsg(USHORT QMICTLType, + CUSTOMQCTL customQctlMsgFunction, void *arg) +{ + UCHAR QMIBuf[WDM_DEFAULT_BUFSIZE]; + PQCQMIMSG pRequest = (PQCQMIMSG)QMIBuf; + int Length; + + pRequest->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI; + pRequest->QMIHdr.CtlFlags = 0x00; + pRequest->QMIHdr.QMIType = QMUX_TYPE_CTL; + pRequest->QMIHdr.ClientId = 0x00; + + pRequest->CTLMsg.QMICTLMsgHdr.CtlFlags = QMICTL_FLAG_REQUEST; + pRequest->CTLMsg.QMICTLMsgHdr.TransactionId = GetQCTLTransactionId(); + pRequest->CTLMsg.QMICTLMsgHdr.QMICTLType = cpu_to_le16(QMICTLType); + if (customQctlMsgFunction) + pRequest->CTLMsg.QMICTLMsgHdr.Length = + cpu_to_le16(customQctlMsgFunction(&pRequest->CTLMsg, arg) - + sizeof(QCQMICTL_MSG_HDR)); + else + pRequest->CTLMsg.QMICTLMsgHdr.Length = cpu_to_le16(0x0000); + + pRequest->QMIHdr.Length = + cpu_to_le16(le16_to_cpu(pRequest->CTLMsg.QMICTLMsgHdr.Length) + + sizeof(QCQMICTL_MSG_HDR) + sizeof(QCQMI_HDR) - 1); + Length = le16_to_cpu(pRequest->QMIHdr.Length) + 1; + + pRequest = (PQCQMIMSG)malloc(Length); + if (pRequest == NULL) { + dbg_time("%s fail to malloc", __func__); + } else { + memcpy(pRequest, QMIBuf, Length); + } + + return pRequest; +} + +static USHORT CtlGetVersionReq(PQMICTL_MSG QCTLMsg, void *arg) +{ + QCTLMsg->GetVersionReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER; + QCTLMsg->GetVersionReq.TLVLength = cpu_to_le16(0x0001); + QCTLMsg->GetVersionReq.QMUXTypes = QMUX_TYPE_ALL; + return sizeof(QMICTL_GET_VERSION_REQ_MSG); +} + +static USHORT CtlGetClientIdReq(PQMICTL_MSG QCTLMsg, void *arg) +{ + QCTLMsg->GetClientIdReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER; + QCTLMsg->GetClientIdReq.TLVLength = cpu_to_le16(0x0001); + QCTLMsg->GetClientIdReq.QMIType = ((UCHAR *)arg)[0]; + return sizeof(QMICTL_GET_CLIENT_ID_REQ_MSG); +} + +static USHORT CtlReleaseClientIdReq(PQMICTL_MSG QCTLMsg, void *arg) +{ + QCTLMsg->ReleaseClientIdReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER; + QCTLMsg->ReleaseClientIdReq.TLVLength = cpu_to_le16(0x0002); + QCTLMsg->ReleaseClientIdReq.QMIType = ((UCHAR *)arg)[0]; + QCTLMsg->ReleaseClientIdReq.ClientId = ((UCHAR *)arg)[1]; + return sizeof(QMICTL_RELEASE_CLIENT_ID_REQ_MSG); +} + +static int QmiWwanSendQMI(PQCQMIMSG pRequest) +{ + struct pollfd pollfds[] = {{cdc_wdm_fd, POLLOUT, 0}}; + int ret; + + if (cdc_wdm_fd == -1) { + dbg_time("%s cdc_wdm_fd = -1", __func__); + return -ENODEV; + } + + if (pRequest->QMIHdr.QMIType == QMUX_TYPE_WDS_IPV6) + pRequest->QMIHdr.QMIType = QMUX_TYPE_WDS; + + do { + ret = poll(pollfds, sizeof(pollfds) / sizeof(pollfds[0]), 5000); + } while ((ret < 0) && (errno == EINTR)); + + if (pollfds[0].revents & POLLOUT) { + ssize_t nwrites = le16_to_cpu(pRequest->QMIHdr.Length) + 1; + ret = write(cdc_wdm_fd, pRequest, nwrites); + if (ret == nwrites) { + ret = 0; + } else { + dbg_time("%s write=%d, errno: %d (%s)", __func__, ret, errno, + strerror(errno)); + } + } else { + dbg_time("%s poll=%d, revents = 0x%x, errno: %d (%s)", __func__, ret, + pollfds[0].revents, errno, strerror(errno)); + } + + return ret; +} + +static int QmiWwanGetClientID(UCHAR QMIType) +{ + PQCQMIMSG pResponse; + + QmiThreadSendQMI( + ComposeQCTLMsg(QMICTL_GET_CLIENT_ID_REQ, CtlGetClientIdReq, &QMIType), + &pResponse); + + if (pResponse) { + USHORT QMUXResult = cpu_to_le16(pResponse->CTLMsg.QMICTLMsgHdrRsp + .QMUXResult); // QMI_RESULT_SUCCESS + USHORT QMUXError = cpu_to_le16(pResponse->CTLMsg.QMICTLMsgHdrRsp + .QMUXError); // QMI_ERR_INVALID_ARG + // UCHAR QMIType = pResponse->CTLMsg.GetClientIdRsp.QMIType; + UCHAR ClientId = pResponse->CTLMsg.GetClientIdRsp.ClientId; + + dbg_time("%s: QMIType = %d clientid %d", __func__, QMIType, ClientId); + + if (!QMUXResult && !QMUXError && + (QMIType == pResponse->CTLMsg.GetClientIdRsp.QMIType)) { + 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; + } + } + return 0; +} + +static int QmiWwanReleaseClientID(QMI_SERVICE_TYPE QMIType, UCHAR ClientId) +{ + UCHAR argv[] = {QMIType, ClientId}; + QmiThreadSendQMI(ComposeQCTLMsg(QMICTL_RELEASE_CLIENT_ID_REQ, + CtlReleaseClientIdReq, argv), + NULL); + return 0; +} + +static int QmiWwanInit(PROFILE_T *profile) +{ + unsigned i; + int ret; + PQCQMIMSG pResponse; + + if (profile->qmap_mode == 0 || profile->qmap_mode == 1) { + for (i = 0; i < 10; i++) { + ret = QmiThreadSendQMITimeout( + ComposeQCTLMsg(QMICTL_SYNC_REQ, NULL, NULL), NULL, 1 * 1000); + if (!ret) + break; + sleep(1); + } + if (ret) + return ret; + } + + QmiThreadSendQMI( + ComposeQCTLMsg(QMICTL_GET_VERSION_REQ, CtlGetVersionReq, NULL), + &pResponse); + if (profile->qmap_mode) { + if (pResponse) { + if (pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXResult == 0 && + pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXError == 0) { + uint8_t NumElements = 0; + + for (NumElements = 0; + NumElements < pResponse->CTLMsg.GetVersionRsp.NumElements; + NumElements++) { + if (pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements] + .QMUXType == QMUX_TYPE_WDS_ADMIN) + profile->qmap_version = (pResponse->CTLMsg.GetVersionRsp + .TypeVersion[NumElements] + .MinorVersion > 16); + } + } + } + } + if (pResponse) + free(pResponse); + if (profile->ipv4_flag) + qmiclientId[QMUX_TYPE_WDS] = QmiWwanGetClientID(QMUX_TYPE_WDS); + if (profile->ipv6_flag) + qmiclientId[QMUX_TYPE_WDS_IPV6] = QmiWwanGetClientID(QMUX_TYPE_WDS); + qmiclientId[QMUX_TYPE_DMS] = QmiWwanGetClientID(QMUX_TYPE_DMS); + qmiclientId[QMUX_TYPE_NAS] = QmiWwanGetClientID(QMUX_TYPE_NAS); + qmiclientId[QMUX_TYPE_UIM] = QmiWwanGetClientID(QMUX_TYPE_UIM); + if (profile->qmap_mode == 0 || profile->qmap_mode == 1) + qmiclientId[QMUX_TYPE_WDS_ADMIN] = + QmiWwanGetClientID(QMUX_TYPE_WDS_ADMIN); + + return 0; +} + +static int QmiWwanDeInit(void) +{ + unsigned int i; + for (i = 0; i < sizeof(qmiclientId) / sizeof(qmiclientId[0]); i++) { + if (qmiclientId[i] != 0) { + QmiWwanReleaseClientID(i, qmiclientId[i]); + qmiclientId[i] = 0; + } + } + + return 0; +} + +static int qmi_proxy_open(const char *name) +{ + int sockfd = -1; + int reuse_addr = 1; + struct sockaddr_un sockaddr; + socklen_t alen; + + /*Create server socket*/ + (sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)); + if (sockfd < 0) + return sockfd; + + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sun_family = AF_LOCAL; + sockaddr.sun_path[0] = 0; + memcpy(sockaddr.sun_path + 1, name, strlen(name)); + + alen = strlen(name) + offsetof(struct sockaddr_un, sun_path) + 1; + if (connect(sockfd, (struct sockaddr *)&sockaddr, alen) < 0) { + close(sockfd); + dbg_time("%s connect %s errno: %d (%s)\n", __func__, name, errno, + strerror(errno)); + return -1; + } + (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, + sizeof(reuse_addr))); + + dbg_time("connect to %s sockfd = %d\n", name, sockfd); + + return sockfd; +} + +static ssize_t qmi_proxy_read(int fd, void *buf, size_t size) +{ + ssize_t nreads; + PQCQMI_HDR pHdr = (PQCQMI_HDR)buf; + + nreads = read(fd, pHdr, sizeof(QCQMI_HDR)); + if (nreads == sizeof(QCQMI_HDR)) { + nreads += read(fd, pHdr + 1, + le16_to_cpu(pHdr->Length) + 1 - sizeof(QCQMI_HDR)); + } + + return nreads; +} + +static void *QmiWwanThread(void *pData) +{ + PROFILE_T *profile = (PROFILE_T *)pData; + const char *cdc_wdm = (const char *)profile->qmichannel; + + if (profile->qmap_mode == 0 || profile->qmap_mode == 1) + cdc_wdm_fd = open(cdc_wdm, O_RDWR | O_NONBLOCK | O_NOCTTY); + else + cdc_wdm_fd = qmi_proxy_open(FIBO_QMI_PROXY); + + if (cdc_wdm_fd == -1) { + dbg_time("%s Failed to open %s, errno: %d (%s)", __func__, cdc_wdm, + errno, strerror(errno)); + qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED); + pthread_exit(NULL); + return NULL; + } + + fcntl(cdc_wdm_fd, F_SETFL, fcntl(cdc_wdm_fd, F_GETFL) | O_NONBLOCK); + fcntl(cdc_wdm_fd, F_SETFD, FD_CLOEXEC); + + dbg_time("cdc_wdm_fd = %d", cdc_wdm_fd); + + qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED); + + while (1) { + struct pollfd pollfds[] = {{qmidevice_control_fd[1], POLLIN, 0}, + {cdc_wdm_fd, POLLIN, 0}}; + int ne, ret, nevents = sizeof(pollfds) / sizeof(pollfds[0]); + + do { + ret = poll(pollfds, nevents, -1); + } while ((ret < 0) && (errno == EINTR)); + + 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; + + // dbg_time("{%d, %x, %x}", pollfds[ne].fd, pollfds[ne].events, + // pollfds[ne].revents); + + if (revents & (POLLERR | POLLHUP | POLLNVAL)) { + dbg_time("%s poll err/hup/inval", __func__); + dbg_time("poll fd = %d, events = 0x%04x", fd, revents); + if (fd == cdc_wdm_fd) { + } else { + } + if (revents & + (POLLHUP | POLLNVAL)) // EC20 bug, Can get POLLERR + goto __QmiWwanThread_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 __QmiWwanThread_quit; + break; + case SIGTERM: + case SIGHUP: + case SIGINT: + QmiThreadRecvQMI(NULL); + break; + default: + break; + } + } + } + + if (fd == cdc_wdm_fd) { + ssize_t nreads; + UCHAR QMIBuf[4096]; + PQCQMIMSG pResponse = (PQCQMIMSG)QMIBuf; + + if (profile->qmap_mode == 0 || profile->qmap_mode == 1) + nreads = read(fd, QMIBuf, sizeof(QMIBuf)); + else + nreads = qmi_proxy_read(fd, QMIBuf, sizeof(QMIBuf)); + // dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads, + // errno, strerror(errno)); + if (nreads <= 0) { + dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads, + errno, strerror(errno)); + break; + } + + if (nreads != (le16_to_cpu(pResponse->QMIHdr.Length) + 1)) { + dbg_time("%s nreads=%d, pQCQMI->QMIHdr.Length = %d", + __func__, (int)nreads, + le16_to_cpu(pResponse->QMIHdr.Length)); + continue; + } + + QmiThreadRecvQMI(pResponse); + } + } + } + +__QmiWwanThread_quit: + if (cdc_wdm_fd != -1) { + close(cdc_wdm_fd); + cdc_wdm_fd = -1; + } + 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; +} + +#endif + +const struct qmi_device_ops qmiwwan_qmidev_ops = { + .init = QmiWwanInit, + .deinit = QmiWwanDeInit, + .send = QmiWwanSendQMI, + .read = QmiWwanThread, +}; diff --git a/fibocom-dial/src/default.script b/fibocom-dial/src/default.script new file mode 100644 index 000000000..26b95c1b9 --- /dev/null +++ b/fibocom-dial/src/default.script @@ -0,0 +1,63 @@ +#!/bin/sh +# Busybox udhcpc dispatcher script. Copyright (C) 2009 by Axel Beckert. +# +# Based on the busybox example scripts and the old udhcp source +# package default.* scripts. + +RESOLV_CONF="/etc/resolv.conf" + +case $1 in + bound|renew) + [ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" + [ -n "$subnet" ] && NETMASK="netmask $subnet" + + /sbin/ifconfig $interface $ip $BROADCAST $NETMASK + + if [ -n "$router" ]; then + echo "$0: Resetting default routes" + while /sbin/route del default gw 0.0.0.0 dev $interface; do :; done + + metric=0 + for i in $router; do + /sbin/route add default gw $i dev $interface metric $metric + metric=$(($metric + 1)) + done + fi + + # Update resolver configuration file + R="" + [ -n "$domain" ] && R="domain $domain +" + for i in $dns; do + echo "$0: Adding DNS $i" + R="${R}nameserver $i +" + done + + if [ -x /sbin/resolvconf ]; then + echo -n "$R" | resolvconf -a "${interface}.udhcpc" + else + echo -n "$R" > "$RESOLV_CONF" + fi + ;; + + deconfig) + if [ -x /sbin/resolvconf ]; then + resolvconf -d "${interface}.udhcpc" + fi + /sbin/ifconfig $interface 0.0.0.0 + ;; + + leasefail) + echo "$0: Lease failed: $message" + ;; + + nak) + echo "$0: Received a NAK: $message" + ;; + + *) + echo "$0: Unknown udhcpc command: $1"; + exit 1; + ;; +esac diff --git a/fibocom-dial/src/fibo_qmimsg_server.c b/fibocom-dial/src/fibo_qmimsg_server.c new file mode 100644 index 000000000..a7a29efd9 --- /dev/null +++ b/fibocom-dial/src/fibo_qmimsg_server.c @@ -0,0 +1,1331 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; + +#define cpu_to_le16(x) (x) +#define cpu_to_le32(x) (x) +#define le16_to_cpu(x) (x) +#define le32_to_cpu(x) (x) + +#define dprintf(fmt, arg...) do { printf(fmt, ##arg); } while(0) +#define SYSCHECK(c) do{if((c)<0) {dprintf("%s %d error: '%s' (code: %d)\n", __func__, __LINE__, strerror(errno), errno); return -1;}}while(0) +#define cfmakenoblock(fd) do{fcntl(fd, F_SETFL, fcntl(fd,F_GETFL) | O_NONBLOCK);}while(0) + +#define qmidev_is_pciemhi(_qmichannel) (strncmp(_qmichannel, "/dev/mhi_", strlen("/dev/mhi_")) == 0) + +typedef struct _QCQMI_HDR +{ + uint8_t IFType; + uint16_t Length; + uint8_t CtlFlags; // reserved + uint8_t QMIType; + uint8_t ClientId; +} __attribute__ ((packed)) QCQMI_HDR, *PQCQMI_HDR; + +typedef struct _QMICTL_SYNC_REQ_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_REQUEST + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_CTL_SYNC_REQ + uint16_t Length; // 0 +} __attribute__ ((packed)) QMICTL_SYNC_REQ_MSG, *PQMICTL_SYNC_REQ_MSG; + +typedef struct _QMICTL_SYNC_RESP_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_RESPONSE + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_CTL_SYNC_RESP + uint16_t Length; + uint8_t TLVType; // QCTLV_TYPE_RESULT_CODE + uint16_t TLVLength; // 0x0004 + uint16_t QMIResult; + uint16_t QMIError; +} __attribute__ ((packed)) QMICTL_SYNC_RESP_MSG, *PQMICTL_SYNC_RESP_MSG; + +typedef struct _QMICTL_SYNC_IND_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_INDICATION + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND + uint16_t Length; +} __attribute__ ((packed)) QMICTL_SYNC_IND_MSG, *PQMICTL_SYNC_IND_MSG; + +typedef struct _QMICTL_GET_CLIENT_ID_REQ_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_REQUEST + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_GET_CLIENT_ID_REQ + uint16_t Length; + uint8_t TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + uint16_t TLVLength; // 1 + uint8_t QMIType; // QMUX type +} __attribute__ ((packed)) QMICTL_GET_CLIENT_ID_REQ_MSG, *PQMICTL_GET_CLIENT_ID_REQ_MSG; + +typedef struct _QMICTL_GET_CLIENT_ID_RESP_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_RESPONSE + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_GET_CLIENT_ID_RESP + uint16_t Length; + uint8_t TLVType; // QCTLV_TYPE_RESULT_CODE + uint16_t TLVLength; // 0x0004 + uint16_t QMIResult; // result code + uint16_t QMIError; // error code + uint8_t TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER + uint16_t TLV2Length; // 2 + uint8_t QMIType; + uint8_t ClientId; +} __attribute__ ((packed)) QMICTL_GET_CLIENT_ID_RESP_MSG, *PQMICTL_GET_CLIENT_ID_RESP_MSG; + +typedef struct _QMICTL_RELEASE_CLIENT_ID_REQ_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_REQUEST + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_RELEASE_CLIENT_ID_REQ + uint16_t Length; + uint8_t TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + uint16_t TLVLength; // 0x0002 + uint8_t QMIType; + uint8_t ClientId; +} __attribute__ ((packed)) QMICTL_RELEASE_CLIENT_ID_REQ_MSG, *PQMICTL_RELEASE_CLIENT_ID_REQ_MSG; + +typedef struct _QMICTL_RELEASE_CLIENT_ID_RESP_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_RESPONSE + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_RELEASE_CLIENT_ID_RESP + uint16_t Length; + uint8_t TLVType; // QCTLV_TYPE_RESULT_CODE + uint16_t TLVLength; // 0x0004 + uint16_t QMIResult; // result code + uint16_t QMIError; // error code + uint8_t TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER + uint16_t TLV2Length; // 2 + uint8_t QMIType; + uint8_t ClientId; +} __attribute__ ((packed)) QMICTL_RELEASE_CLIENT_ID_RESP_MSG, *PQMICTL_RELEASE_CLIENT_ID_RESP_MSG; + +// QMICTL Control Flags +#define QMICTL_CTL_FLAG_CMD 0x00 +#define QMICTL_CTL_FLAG_RSP 0x01 +#define QMICTL_CTL_FLAG_IND 0x02 + +typedef struct _QCQMICTL_MSG_HDR +{ + uint8_t CtlFlags; // 00-cmd, 01-rsp, 10-ind + uint8_t TransactionId; + uint16_t QMICTLType; + uint16_t Length; +} __attribute__ ((packed)) QCQMICTL_MSG_HDR, *PQCQMICTL_MSG_HDR; + +#define QCQMICTL_MSG_HDR_SIZE sizeof(QCQMICTL_MSG_HDR) + +typedef struct _QCQMICTL_MSG_HDR_RESP +{ + uint8_t CtlFlags; // 00-cmd, 01-rsp, 10-ind + uint8_t TransactionId; + uint16_t QMICTLType; + uint16_t Length; + uint8_t TLVType; // 0x02 - result code + uint16_t TLVLength; // 4 + uint16_t QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + uint16_t QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} __attribute__ ((packed)) QCQMICTL_MSG_HDR_RESP, *PQCQMICTL_MSG_HDR_RESP; + +typedef struct _QMICTL_GET_VERSION_REQ_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_REQUEST + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_GET_VERSION_REQ + uint16_t Length; // 0 + uint8_t TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + uint16_t TLVLength; // var + uint8_t QMUXTypes; // List of one byte QMUX_TYPE values + // 0xFF returns a list of versions for all + // QMUX_TYPEs implemented on the device +} __attribute__ ((packed)) QMICTL_GET_VERSION_REQ_MSG, *PQMICTL_GET_VERSION_REQ_MSG; + +typedef struct _QMUX_TYPE_VERSION_STRUCT +{ + uint8_t QMUXType; + uint16_t MajorVersion; + uint16_t MinorVersion; +} __attribute__ ((packed)) QMUX_TYPE_VERSION_STRUCT, *PQMUX_TYPE_VERSION_STRUCT; + +typedef struct _QMICTL_GET_VERSION_RESP_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_RESPONSE + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_GET_VERSION_RESP + uint16_t Length; + uint8_t TLVType; // QCTLV_TYPE_RESULT_CODE + uint16_t TLVLength; // 0x0004 + uint16_t QMIResult; + uint16_t QMIError; + uint8_t TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER + uint16_t TLV2Length; // var + uint8_t NumElements; // Num of QMUX_TYPE_VERSION_STRUCT + QMUX_TYPE_VERSION_STRUCT TypeVersion[0]; +} __attribute__ ((packed)) QMICTL_GET_VERSION_RESP_MSG, *PQMICTL_GET_VERSION_RESP_MSG; + + +typedef struct _QMICTL_MSG +{ + union + { + // Message Header + QCQMICTL_MSG_HDR QMICTLMsgHdr; + QCQMICTL_MSG_HDR_RESP QMICTLMsgHdrRsp; + + // QMICTL Message + //QMICTL_SET_INSTANCE_ID_REQ_MSG SetInstanceIdReq; + //QMICTL_SET_INSTANCE_ID_RESP_MSG SetInstanceIdRsp; + QMICTL_GET_VERSION_REQ_MSG GetVersionReq; + QMICTL_GET_VERSION_RESP_MSG GetVersionRsp; + QMICTL_GET_CLIENT_ID_REQ_MSG GetClientIdReq; + QMICTL_GET_CLIENT_ID_RESP_MSG GetClientIdRsp; + //QMICTL_RELEASE_CLIENT_ID_REQ_MSG ReleaseClientIdReq; + QMICTL_RELEASE_CLIENT_ID_RESP_MSG ReleaseClientIdRsp; + //QMICTL_REVOKE_CLIENT_ID_IND_MSG RevokeClientIdInd; + //QMICTL_INVALID_CLIENT_ID_IND_MSG InvalidClientIdInd; + //QMICTL_SET_DATA_FORMAT_REQ_MSG SetDataFormatReq; + //QMICTL_SET_DATA_FORMAT_RESP_MSG SetDataFormatRsp; + QMICTL_SYNC_REQ_MSG SyncReq; + QMICTL_SYNC_RESP_MSG SyncRsp; + QMICTL_SYNC_IND_MSG SyncInd; + }; +} __attribute__ ((packed)) QMICTL_MSG, *PQMICTL_MSG; + +typedef struct _QCQMUX_MSG_HDR +{ + uint8_t CtlFlags; // 0: single QMUX Msg; 1: + uint16_t TransactionId; + uint16_t Type; + uint16_t Length; + uint8_t payload[0]; +} __attribute__ ((packed)) QCQMUX_MSG_HDR, *PQCQMUX_MSG_HDR; + +typedef struct _QCQMUX_MSG_HDR_RESP +{ + uint8_t CtlFlags; // 0: single QMUX Msg; 1: + uint16_t TransactionId; + uint16_t Type; + uint16_t Length; + uint8_t TLVType; // 0x02 - result code + uint16_t TLVLength; // 4 + uint16_t QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + uint16_t QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} __attribute__ ((packed)) QCQMUX_MSG_HDR_RESP, *PQCQMUX_MSG_HDR_RESP; + +typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT +{ + uint16_t Type; // QMUX type 0x0000 + uint16_t Length; +} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT, *PQMIWDS_ADMIN_SET_DATA_FORMAT; + +typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS +{ + uint8_t TLVType; + uint16_t TLVLength; + uint8_t QOSSetting; +} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS, *PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS; + +typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_TLV +{ + uint8_t TLVType; + uint16_t TLVLength; + uint32_t Value; +} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_TLV, *PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV; + +typedef struct _QMIWDS_ENDPOINT_TLV +{ + uint8_t TLVType; + uint16_t TLVLength; + uint32_t ep_type; + uint32_t iface_id; +} __attribute__ ((packed)) QMIWDS_ENDPOINT_TLV, *PQMIWDS_ENDPOINT_TLV; + +typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG +{ + uint8_t CtlFlags; // 0: single QMUX Msg; 1: + uint16_t TransactionId; + uint16_t Type; + uint16_t Length; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS QosDataFormatTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UnderlyingLinkLayerProtocolTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationProtocolTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationProtocolTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationMaxDatagramsTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationMaxSizeTlv; + QMIWDS_ENDPOINT_TLV epTlv; +} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG, *PQMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG; + +typedef struct _QCQMUX_TLV +{ + uint8_t Type; + uint16_t Length; + uint8_t Value[0]; +} __attribute__ ((packed)) QCQMUX_TLV, *PQCQMUX_TLV; + +typedef struct _QMUX_MSG +{ + union + { + // Message Header + QCQMUX_MSG_HDR QMUXMsgHdr; + QCQMUX_MSG_HDR_RESP QMUXMsgHdrResp; + QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG SetDataFormatReq; + }; +} __attribute__ ((packed)) QMUX_MSG, *PQMUX_MSG; + +typedef struct _QCQMIMSG { + QCQMI_HDR QMIHdr; +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 + //QMUX_MSG QMUXMsgHdrResp; +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 + union { + QMICTL_MSG CTLMsg; + QMUX_MSG MUXMsg; + }; +} __attribute__ ((packed)) QCQMIMSG, *PQCQMIMSG; + +// QMUX Message Definitions -- QMI SDU +#define QMUX_CTL_FLAG_SINGLE_MSG 0x00 +#define QMUX_CTL_FLAG_COMPOUND_MSG 0x01 +#define QMUX_CTL_FLAG_TYPE_CMD 0x00 +#define QMUX_CTL_FLAG_TYPE_RSP 0x02 +#define QMUX_CTL_FLAG_TYPE_IND 0x04 +#define QMUX_CTL_FLAG_MASK_COMPOUND 0x01 +#define QMUX_CTL_FLAG_MASK_TYPE 0x06 // 00-cmd, 01-rsp, 10-ind + +#define USB_CTL_MSG_TYPE_QMI 0x01 + +#define QMICTL_FLAG_REQUEST 0x00 +#define QMICTL_FLAG_RESPONSE 0x01 +#define QMICTL_FLAG_INDICATION 0x02 + +// QMICTL Type +#define QMICTL_SET_INSTANCE_ID_REQ 0x0020 +#define QMICTL_SET_INSTANCE_ID_RESP 0x0020 +#define QMICTL_GET_VERSION_REQ 0x0021 +#define QMICTL_GET_VERSION_RESP 0x0021 +#define QMICTL_GET_CLIENT_ID_REQ 0x0022 +#define QMICTL_GET_CLIENT_ID_RESP 0x0022 +#define QMICTL_RELEASE_CLIENT_ID_REQ 0x0023 +#define QMICTL_RELEASE_CLIENT_ID_RESP 0x0023 +#define QMICTL_REVOKE_CLIENT_ID_IND 0x0024 +#define QMICTL_INVALID_CLIENT_ID_IND 0x0025 +#define QMICTL_SET_DATA_FORMAT_REQ 0x0026 +#define QMICTL_SET_DATA_FORMAT_RESP 0x0026 +#define QMICTL_SYNC_REQ 0x0027 +#define QMICTL_SYNC_RESP 0x0027 +#define QMICTL_SYNC_IND 0x0027 + +#define QCTLV_TYPE_REQUIRED_PARAMETER 0x01 + +// Define QMI Type +typedef enum _QMI_SERVICE_TYPE +{ + QMUX_TYPE_CTL = 0x00, + QMUX_TYPE_WDS = 0x01, + QMUX_TYPE_DMS = 0x02, + QMUX_TYPE_NAS = 0x03, + QMUX_TYPE_QOS = 0x04, + QMUX_TYPE_WMS = 0x05, + QMUX_TYPE_PDS = 0x06, + QMUX_TYPE_UIM = 0x0B, + QMUX_TYPE_WDS_IPV6 = 0x11, + QMUX_TYPE_WDS_ADMIN = 0x1A, + QMUX_TYPE_MAX = 0xFF, + QMUX_TYPE_ALL = 0xFF +} QMI_SERVICE_TYPE; + +#define QMIWDS_ADMIN_SET_DATA_FORMAT_REQ 0x0020 +#define QMIWDS_ADMIN_SET_DATA_FORMAT_RESP 0x0020 + +struct qlistnode +{ + struct qlistnode *next; + struct qlistnode *prev; +}; + +#define qnode_to_item(node, container, member) \ + (container *) (((char*) (node)) - offsetof(container, member)) + +#define qlist_for_each(node, list) \ + for (node = (list)->next; node != (list); node = node->next) + +#define qlist_empty(list) ((list) == (list)->next) +#define qlist_head(list) ((list)->next) +#define qlist_tail(list) ((list)->prev) + +typedef struct { + struct qlistnode qnode; + uint8_t ClientFd; + QCQMIMSG qmi[0]; +} QMI_PROXY_MSG; + +typedef struct { + struct qlistnode qnode; + uint8_t QMIType; + uint8_t ClientId; + unsigned AccessTime; +} QMI_PROXY_CLINET; + +typedef struct { + struct qlistnode qnode; + struct qlistnode client_qnode; + uint8_t ClientFd; + unsigned AccessTime; +} QMI_PROXY_CONNECTION; + +static int qmi_proxy_quit = 0; +static pthread_t thread_id = 0; +static int cdc_wdm_fd = -1; +static int qmi_proxy_server_fd = -1; +static struct qlistnode qmi_proxy_connection; +static struct qlistnode qmi_proxy_ctl_msg; +static PQCQMIMSG s_pCtlReq; +static PQCQMIMSG s_pCtlRsq; +static pthread_mutex_t s_ctlmutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t s_ctlcond = PTHREAD_COND_INITIALIZER; +static int verbose_debug = 0; + +static void qlist_init(struct qlistnode *node) +{ + node->next = node; + node->prev = node; +} + +static void qlist_add_tail(struct qlistnode *head, struct qlistnode *item) +{ + item->next = head; + item->prev = head->prev; + head->prev->next = item; + head->prev = item; +} + +static void qlist_remove(struct qlistnode *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; +} + + + +static void setTimespecRelative(struct timespec *p_ts, long long msec) +{ + struct timeval tv; + + gettimeofday(&tv, (struct timezone *) NULL); + + /* what's really funny about this is that I know + pthread_cond_timedwait just turns around and makes this + a relative time again */ + p_ts->tv_sec = tv.tv_sec + (msec / 1000); + p_ts->tv_nsec = (tv.tv_usec + (msec % 1000) * 1000L ) * 1000L; +} + +static int pthread_cond_timeout_np(pthread_cond_t *cond, pthread_mutex_t * mutex, unsigned msecs) { + if (msecs != 0) { + struct timespec ts; + setTimespecRelative(&ts, msecs); + return pthread_cond_timedwait(cond, mutex, &ts); + } else { + return pthread_cond_wait(cond, mutex); + } +} + +static int create_local_server(const char *name) { + int sockfd = -1; + int reuse_addr = 1; + struct sockaddr_un sockaddr; + socklen_t alen; + + /*Create server socket*/ + SYSCHECK(sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)); + + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sun_family = AF_LOCAL; + sockaddr.sun_path[0] = 0; + memcpy(sockaddr.sun_path + 1, name, strlen(name) ); + + alen = strlen(name) + offsetof(struct sockaddr_un, sun_path) + 1; + SYSCHECK(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,sizeof(reuse_addr))); + if(bind(sockfd, (struct sockaddr *)&sockaddr, alen) < 0) { + close(sockfd); + dprintf("%s bind %s errno: %d (%s)\n", __func__, name, errno, strerror(errno)); + return -1; + } + + dprintf("local server: %s sockfd = %d\n", name, sockfd); + cfmakenoblock(sockfd); + listen(sockfd, 1); + + return sockfd; +} + +static void accept_qmi_connection(int serverfd) { + int clientfd = -1; + unsigned char addr[128]; + socklen_t alen = sizeof(addr); + QMI_PROXY_CONNECTION *qmi_con; + + clientfd = accept(serverfd, (struct sockaddr *)addr, &alen); + + qmi_con = (QMI_PROXY_CONNECTION *)malloc(sizeof(QMI_PROXY_CONNECTION)); + if (qmi_con) { + qlist_init(&qmi_con->qnode); + qlist_init(&qmi_con->client_qnode); + qmi_con->ClientFd= clientfd; + qmi_con->AccessTime = 0; + dprintf("+++ ClientFd=%d\n", qmi_con->ClientFd); + qlist_add_tail(&qmi_proxy_connection, &qmi_con->qnode); + } + + cfmakenoblock(clientfd); +} + +static void cleanup_qmi_connection(int clientfd) { + struct qlistnode *con_node, *qmi_node; + + qlist_for_each(con_node, &qmi_proxy_connection) { + QMI_PROXY_CONNECTION *qmi_con = qnode_to_item(con_node, QMI_PROXY_CONNECTION, qnode); + + if (qmi_con->ClientFd == clientfd) { + while (!qlist_empty(&qmi_con->client_qnode)) { + QMI_PROXY_CLINET *qmi_client = qnode_to_item(qlist_head(&qmi_con->client_qnode), QMI_PROXY_CLINET, qnode); + + dprintf("xxx ClientFd=%d QMIType=%d ClientId=%d\n", qmi_con->ClientFd, qmi_client->QMIType, qmi_client->ClientId); + + qlist_remove(&qmi_client->qnode); + free(qmi_client); + } + + qlist_for_each(qmi_node, &qmi_proxy_ctl_msg) { + QMI_PROXY_MSG *qmi_msg = qnode_to_item(qmi_node, QMI_PROXY_MSG, qnode); + + if (qmi_msg->ClientFd == qmi_con->ClientFd) { + qlist_remove(&qmi_msg->qnode); + free(qmi_msg); + break; + } + } + + dprintf("--- ClientFd=%d\n", qmi_con->ClientFd); + close(qmi_con->ClientFd); + qlist_remove(&qmi_con->qnode); + free(qmi_con); + break; + } + } +} + +static void get_client_id(QMI_PROXY_CONNECTION *qmi_con, PQMICTL_GET_CLIENT_ID_RESP_MSG pClient) { + if (pClient->QMIResult == 0 && pClient->QMIError == 0) { + QMI_PROXY_CLINET *qmi_client = (QMI_PROXY_CLINET *)malloc(sizeof(QMI_PROXY_CLINET)); + + qlist_init(&qmi_client->qnode); + qmi_client->QMIType = pClient->QMIType; + qmi_client->ClientId = pClient->ClientId; + qmi_client->AccessTime = 0; + + dprintf("+++ ClientFd=%d QMIType=%d ClientId=%d\n", qmi_con->ClientFd, qmi_client->QMIType, qmi_client->ClientId); + qlist_add_tail(&qmi_con->client_qnode, &qmi_client->qnode); + } +} + +static void release_client_id(QMI_PROXY_CONNECTION *qmi_con, PQMICTL_RELEASE_CLIENT_ID_RESP_MSG pClient) { + struct qlistnode *client_node; + + if (pClient->QMIResult == 0 && pClient->QMIError == 0) { + qlist_for_each (client_node, &qmi_con->client_qnode) { + QMI_PROXY_CLINET *qmi_client = qnode_to_item(client_node, QMI_PROXY_CLINET, qnode); + + if (pClient->QMIType == qmi_client->QMIType && pClient->ClientId == qmi_client->ClientId) { + dprintf("--- ClientFd=%d QMIType=%d ClientId=%d\n", qmi_con->ClientFd, qmi_client->QMIType, qmi_client->ClientId); + qlist_remove(&qmi_client->qnode); + free(qmi_client); + break; + } + } + } +} + + +static int send_qmi_to_cdc_wdm(PQCQMIMSG pQMI) { + struct pollfd pollfds[]= {{cdc_wdm_fd, POLLOUT, 0}}; + ssize_t ret = 0; + + do { + ret = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), 5000); + } while ((ret < 0) && (errno == EINTR)); + + if (pollfds[0].revents & POLLOUT) { + ssize_t size = le16_to_cpu(pQMI->QMIHdr.Length) + 1; + ret = write(cdc_wdm_fd, pQMI, size); + if (verbose_debug) + { + ssize_t i; + printf("w %d %zd: ", cdc_wdm_fd, ret); + for (i = 0; i < 16; i++) + printf("%02x ", ((uint8_t *)pQMI)[i]); + printf("\n"); + } + } + + return ret; +} + +static int send_qmi_to_client(PQCQMIMSG pQMI, int clientFd) { + struct pollfd pollfds[]= {{clientFd, POLLOUT, 0}}; + ssize_t ret = 0; + + do { + ret = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), 5000); + } while ((ret < 0) && (errno == EINTR)); + + if (pollfds[0].revents & POLLOUT) { + ssize_t size = le16_to_cpu(pQMI->QMIHdr.Length) + 1; + ret = write(clientFd, pQMI, size); + if (verbose_debug) + { + ssize_t i; + printf("w %d %zd: ", clientFd, ret); + for (i = 0; i < 16; i++) + printf("%02x ", ((uint8_t *)pQMI)[i]); + printf("\n"); + } + } + + return ret; +} + + +static void recv_qmi(PQCQMIMSG pQMI, unsigned size) { + struct qlistnode *con_node, *client_node; + + if (qmi_proxy_server_fd <= 0) { + pthread_mutex_lock(&s_ctlmutex); + + if (s_pCtlReq != NULL) { + if (pQMI->QMIHdr.QMIType == QMUX_TYPE_CTL + && s_pCtlReq->CTLMsg.QMICTLMsgHdrRsp.TransactionId == pQMI->CTLMsg.QMICTLMsgHdrRsp.TransactionId) { + s_pCtlRsq = malloc(size); + memcpy(s_pCtlRsq, pQMI, size); + pthread_cond_signal(&s_ctlcond); + } + else if (pQMI->QMIHdr.QMIType != QMUX_TYPE_CTL + && s_pCtlReq->MUXMsg.QMUXMsgHdr.TransactionId == pQMI->MUXMsg.QMUXMsgHdr.TransactionId) { + s_pCtlRsq = malloc(size); + memcpy(s_pCtlRsq, pQMI, size); + pthread_cond_signal(&s_ctlcond); + } + } + + pthread_mutex_unlock(&s_ctlmutex); + } + else if (pQMI->QMIHdr.QMIType == QMUX_TYPE_CTL) { + if (pQMI->CTLMsg.QMICTLMsgHdr.CtlFlags == QMICTL_CTL_FLAG_RSP) { + if (!qlist_empty(&qmi_proxy_ctl_msg)) { + QMI_PROXY_MSG *qmi_msg = qnode_to_item(qlist_head(&qmi_proxy_ctl_msg), QMI_PROXY_MSG, qnode); + + qlist_for_each(con_node, &qmi_proxy_connection) { + QMI_PROXY_CONNECTION *qmi_con = qnode_to_item(con_node, QMI_PROXY_CONNECTION, qnode); + + if (qmi_con->ClientFd == qmi_msg->ClientFd) { + send_qmi_to_client(pQMI, qmi_msg->ClientFd); + + if (pQMI->CTLMsg.QMICTLMsgHdrRsp.QMICTLType == QMICTL_GET_CLIENT_ID_RESP) + get_client_id(qmi_con, &pQMI->CTLMsg.GetClientIdRsp); + else if (pQMI->CTLMsg.QMICTLMsgHdrRsp.QMICTLType == QMICTL_RELEASE_CLIENT_ID_RESP) + release_client_id(qmi_con, &pQMI->CTLMsg.ReleaseClientIdRsp); + else { + } + } + } + + qlist_remove(&qmi_msg->qnode); + free(qmi_msg); + } + } + + if (!qlist_empty(&qmi_proxy_ctl_msg)) { + QMI_PROXY_MSG *qmi_msg = qnode_to_item(qlist_head(&qmi_proxy_ctl_msg), QMI_PROXY_MSG, qnode); + + qlist_for_each(con_node, &qmi_proxy_connection) { + QMI_PROXY_CONNECTION *qmi_con = qnode_to_item(con_node, QMI_PROXY_CONNECTION, qnode); + + if (qmi_con->ClientFd == qmi_msg->ClientFd) { + send_qmi_to_cdc_wdm(qmi_msg->qmi); + } + } + } + } + else { + qlist_for_each(con_node, &qmi_proxy_connection) { + QMI_PROXY_CONNECTION *qmi_con = qnode_to_item(con_node, QMI_PROXY_CONNECTION, qnode); + + qlist_for_each(client_node, &qmi_con->client_qnode) { + QMI_PROXY_CLINET *qmi_client = qnode_to_item(client_node, QMI_PROXY_CLINET, qnode); + if (pQMI->QMIHdr.QMIType == qmi_client->QMIType) { + if (pQMI->QMIHdr.ClientId == 0 || pQMI->QMIHdr.ClientId == qmi_client->ClientId) { + send_qmi_to_client(pQMI, qmi_con->ClientFd); + } + } + } + } + } +} + +static int send_qmi(PQCQMIMSG pQMI, unsigned size, int clientfd) { + if (qmi_proxy_server_fd <= 0) { + send_qmi_to_cdc_wdm(pQMI); + } + else if (pQMI->QMIHdr.QMIType == QMUX_TYPE_CTL) { + QMI_PROXY_MSG *qmi_msg; + + if (qlist_empty(&qmi_proxy_ctl_msg)) + send_qmi_to_cdc_wdm(pQMI); + + qmi_msg = malloc(sizeof(QMI_PROXY_MSG) + size); + qlist_init(&qmi_msg->qnode); + qmi_msg->ClientFd = clientfd; + memcpy(qmi_msg->qmi, pQMI, size); + qlist_add_tail(&qmi_proxy_ctl_msg, &qmi_msg->qnode); + } + else { + send_qmi_to_cdc_wdm(pQMI); + } + + return 0; +} + +static int send_qmi_timeout(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse, unsigned mseconds) { + int ret; + + pthread_mutex_lock(&s_ctlmutex); + + s_pCtlReq = pRequest; + s_pCtlRsq = NULL; + if (ppResponse) *ppResponse = NULL; + + send_qmi_to_cdc_wdm(pRequest); + ret = pthread_cond_timeout_np(&s_ctlcond, &s_ctlmutex, mseconds); + if (!ret) { + if (s_pCtlRsq && ppResponse) { + *ppResponse = s_pCtlRsq; + } else if (s_pCtlRsq) { + free(s_pCtlRsq); + } + } else { + dprintf("%s ret=%d\n", __func__, ret); + } + + s_pCtlReq = NULL; + pthread_mutex_unlock(&s_ctlmutex); + + return ret; +} + +static PQCQMUX_TLV qmi_find_tlv (PQCQMIMSG pQMI, uint8_t TLVType) { + int Length = 0; + + while (Length < le16_to_cpu(pQMI->MUXMsg.QMUXMsgHdr.Length)) { + PQCQMUX_TLV pTLV = (PQCQMUX_TLV)(&pQMI->MUXMsg.QMUXMsgHdr.payload[Length]); + + //dprintf("TLV {%02x, %04x}\n", pTLV->Type, pTLV->Length); + if (pTLV->Type == TLVType) { + return pTLV; + } + + Length += (le16_to_cpu((pTLV->Length)) + sizeof(QCQMUX_TLV)); + } + + return NULL; +} + +static int qmi_proxy_init(char *qmi_device, char* getidproduct) { + unsigned i; + int ret; + QCQMIMSG _QMI; + PQCQMIMSG pQMI = &_QMI; + PQCQMIMSG pRsp; + uint8_t TransactionId = 0xC1; + uint8_t WDAClientId = 0; + unsigned rx_urb_size = 0; + unsigned ep_type, iface_id; + + dprintf("%s enter\n", __func__); + + pQMI->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI; + pQMI->QMIHdr.CtlFlags = 0x00; + pQMI->QMIHdr.QMIType = QMUX_TYPE_CTL; + pQMI->QMIHdr.ClientId= 0x00; + + pQMI->CTLMsg.QMICTLMsgHdr.CtlFlags = QMICTL_FLAG_REQUEST; + + for (i = 0; i < 10; i++) { + pQMI->CTLMsg.SyncReq.TransactionId = TransactionId++; + pQMI->CTLMsg.SyncReq.QMICTLType = QMICTL_SYNC_REQ; + pQMI->CTLMsg.SyncReq.Length = 0; + + pQMI->QMIHdr.Length = pQMI->CTLMsg.QMICTLMsgHdr.Length + sizeof(QCQMI_HDR) + sizeof(QCQMICTL_MSG_HDR) - 1; + + ret = send_qmi_timeout(pQMI, NULL, 1000); + if (!ret) + break; + } + + if (ret) + goto qmi_proxy_init_fail; + + pQMI->CTLMsg.GetVersionReq.TransactionId = TransactionId++; + pQMI->CTLMsg.GetVersionReq.QMICTLType = QMICTL_GET_VERSION_REQ; + pQMI->CTLMsg.GetVersionReq.Length = 0x0004; + pQMI->CTLMsg.GetVersionReq.TLVType= QCTLV_TYPE_REQUIRED_PARAMETER; + pQMI->CTLMsg.GetVersionReq.TLVLength = 0x0001; + pQMI->CTLMsg.GetVersionReq.QMUXTypes = QMUX_TYPE_ALL; + + pQMI->QMIHdr.Length = pQMI->CTLMsg.QMICTLMsgHdr.Length + sizeof(QCQMI_HDR) + sizeof(QCQMICTL_MSG_HDR) - 1; + + ret = send_qmi_timeout(pQMI, &pRsp, 3000); + if (ret || (pRsp == NULL)) + goto qmi_proxy_init_fail; + + if (pRsp) { + uint8_t NumElements = 0; + if (pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXResult ||pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXError) { + dprintf("QMICTL_GET_VERSION_REQ QMUXResult=%d, QMUXError=%d\n", + pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXResult, pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXError); + goto qmi_proxy_init_fail; + } + + for (NumElements = 0; NumElements < pRsp->CTLMsg.GetVersionRsp.NumElements; NumElements++) { + if (verbose_debug) + dprintf("QMUXType = %02x Version = %d.%d\n", + pRsp->CTLMsg.GetVersionRsp.TypeVersion[NumElements].QMUXType, + pRsp->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MajorVersion, + pRsp->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MinorVersion); + } + } + free(pRsp); + + pQMI->CTLMsg.GetClientIdReq.TransactionId = TransactionId++; + pQMI->CTLMsg.GetClientIdReq.QMICTLType = QMICTL_GET_CLIENT_ID_REQ; + pQMI->CTLMsg.GetClientIdReq.Length = 0x0004; + pQMI->CTLMsg.GetClientIdReq.TLVType= QCTLV_TYPE_REQUIRED_PARAMETER; + pQMI->CTLMsg.GetClientIdReq.TLVLength = 0x0001; + pQMI->CTLMsg.GetClientIdReq.QMIType = QMUX_TYPE_WDS_ADMIN; + + pQMI->QMIHdr.Length = pQMI->CTLMsg.QMICTLMsgHdr.Length + sizeof(QCQMI_HDR) + sizeof(QCQMICTL_MSG_HDR) - 1; + + ret = send_qmi_timeout(pQMI, &pRsp, 3000); + if (ret || (pRsp == NULL)) + goto qmi_proxy_init_fail; + + if (pRsp) { + if (pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXResult ||pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXError) { + dprintf("QMICTL_GET_CLIENT_ID_REQ QMUXResult=%d, QMUXError=%d\n", + pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXResult, pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXError); + goto qmi_proxy_init_fail; + } + + WDAClientId = pRsp->CTLMsg.GetClientIdRsp.ClientId; + if (verbose_debug) dprintf("WDAClientId = %d\n", WDAClientId); + } + free(pRsp); + + if(qmidev_is_pciemhi(qmi_device)) + { + rx_urb_size = 32*1024; //must same as rx_urb_size defined in GobiNet&qmi_wwan driver //SDX24&SDX55 support 32KB + ep_type = 0x03; + } + else + { + rx_urb_size = 4*1024; //must same as rx_urb_size defined in GobiNet&qmi_wwan driver //SDX24&SDX55 support 32KB + ep_type = 0x02; + } + + printf("getidproduct=%s\n", getidproduct); + if(!strncasecmp(getidproduct, "0104", 4)|| !strncasecmp(getidproduct, "1001", 4) || !strncasecmp(getidproduct, "9025", 4)) + iface_id = 0x04; + else if(!strncasecmp(getidproduct, "0109", 4) || !strncasecmp(getidproduct, "1000", 4)) + iface_id = 0x02; + else if(!strncasecmp(getidproduct, "0113", 4)) + iface_id = 0x00; + + /*begin added by minchao.zhao@fibocom.com for mantis 91283 on 2021-09-27*/ + if(qmidev_is_pciemhi(qmi_device)) + iface_id = 0x04; //mhi_QMI0 enumeration arrangement + /*end added by minchao.zhao@fibocom.com for mantis 91283 on 2021-09-27*/ + printf("iface_id=%d\n", iface_id); + + pQMI->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI; + pQMI->QMIHdr.CtlFlags = 0x00; + pQMI->QMIHdr.QMIType = QMUX_TYPE_WDS_ADMIN; + pQMI->QMIHdr.ClientId= WDAClientId; + + pQMI->MUXMsg.QMUXMsgHdr.CtlFlags = QMICTL_FLAG_REQUEST; + pQMI->MUXMsg.QMUXMsgHdr.TransactionId = TransactionId++; + + pQMI->MUXMsg.SetDataFormatReq.Type = QMIWDS_ADMIN_SET_DATA_FORMAT_REQ; + pQMI->MUXMsg.SetDataFormatReq.Length = sizeof(QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG) - sizeof(QCQMUX_MSG_HDR); + +//Indicates whether the Quality of Service(QOS) data format is used by the client. + pQMI->MUXMsg.SetDataFormatReq.QosDataFormatTlv.TLVType = 0x10; + pQMI->MUXMsg.SetDataFormatReq.QosDataFormatTlv.TLVLength = cpu_to_le16(0x0001); + pQMI->MUXMsg.SetDataFormatReq.QosDataFormatTlv.QOSSetting = 0; /* no-QOS header */ +//Underlying Link Layer Protocol + pQMI->MUXMsg.SetDataFormatReq.UnderlyingLinkLayerProtocolTlv.TLVType = 0x11; + pQMI->MUXMsg.SetDataFormatReq.UnderlyingLinkLayerProtocolTlv.TLVLength = cpu_to_le16(4); + pQMI->MUXMsg.SetDataFormatReq.UnderlyingLinkLayerProtocolTlv.Value = cpu_to_le32(0x02); /* Set IP mode */ +//Uplink (UL) data aggregation protocol to be used for uplink data transfer. + pQMI->MUXMsg.SetDataFormatReq.UplinkDataAggregationProtocolTlv.TLVType = 0x12; + pQMI->MUXMsg.SetDataFormatReq.UplinkDataAggregationProtocolTlv.TLVLength = cpu_to_le16(4); + pQMI->MUXMsg.SetDataFormatReq.UplinkDataAggregationProtocolTlv.Value = cpu_to_le32(0x05); //UL QMAP is enabled +//Downlink (DL) data aggregation protocol to be used for downlink data transfer + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationProtocolTlv.TLVType = 0x13; + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationProtocolTlv.TLVLength = cpu_to_le16(4); + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationProtocolTlv.Value = cpu_to_le32(0x05); //UL QMAP is enabled +//Maximum number of datagrams in a single aggregated packet on downlink + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.TLVType = 0x15; + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.TLVLength = cpu_to_le16(4); + if(qmidev_is_pciemhi(qmi_device)) + { + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.Value = cpu_to_le32((rx_urb_size>2048)?(rx_urb_size/512):1); + } + else + { + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.Value = cpu_to_le32((rx_urb_size>2048)?(rx_urb_size/1024):1); + } +//Maximum size in bytes of a single aggregated packet allowed on downlink + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.TLVType = 0x16; + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.TLVLength = cpu_to_le16(4); + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.Value = cpu_to_le32(rx_urb_size); +//Peripheral End Point ID + pQMI->MUXMsg.SetDataFormatReq.epTlv.TLVType = 0x17; + pQMI->MUXMsg.SetDataFormatReq.epTlv.TLVLength = cpu_to_le16(8); + pQMI->MUXMsg.SetDataFormatReq.epTlv.ep_type = cpu_to_le32(ep_type); // DATA_EP_TYPE_BAM_DMUX + pQMI->MUXMsg.SetDataFormatReq.epTlv.iface_id = cpu_to_le32(iface_id); + + pQMI->QMIHdr.Length = pQMI->MUXMsg.QMUXMsgHdr.Length + sizeof(QCQMI_HDR) + sizeof(QCQMUX_MSG_HDR) - 1; + + ret = send_qmi_timeout(pQMI, &pRsp, 3000); + if (ret || (pRsp == NULL)) + goto qmi_proxy_init_fail; + + if (pRsp) { + PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV pFormat; + + if (pRsp->MUXMsg.QMUXMsgHdrResp.QMUXResult || pRsp->MUXMsg.QMUXMsgHdrResp.QMUXError) { + dprintf("QMIWDS_ADMIN_SET_DATA_FORMAT_REQ QMUXResult=%d, QMUXError=%d\n", + pRsp->MUXMsg.QMUXMsgHdrResp.QMUXResult, pRsp->MUXMsg.QMUXMsgHdrResp.QMUXError); + goto qmi_proxy_init_fail; + } + + pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x11); + if (pFormat) + dprintf("link_prot %d\n", le32_to_cpu(pFormat->Value)); + pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x12); + if (pFormat) + dprintf("ul_data_aggregation_protocol %d\n", le32_to_cpu(pFormat->Value)); + pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x13); + if (pFormat) + dprintf("dl_data_aggregation_protocol %d\n", le32_to_cpu(pFormat->Value)); + pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x15); + if (pFormat) + dprintf("dl_data_aggregation_max_datagrams %d\n", le32_to_cpu(pFormat->Value)); + pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x16); + if (pFormat) { + dprintf("dl_data_aggregation_max_size %d\n", le32_to_cpu(pFormat->Value)); + rx_urb_size = le32_to_cpu(pFormat->Value); + } + pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x17); + if (pFormat) + dprintf("ul_data_aggregation_max_datagrams %d\n", le32_to_cpu(pFormat->Value)); + pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x18); + if (pFormat) + dprintf("ul_data_aggregation_max_size %d\n", le32_to_cpu(pFormat->Value)); + } + free(pRsp); + + dprintf("%s finished, rx_urb_size is %u\n", __func__, rx_urb_size); + return 0; + +qmi_proxy_init_fail: + dprintf("%s failed\n", __func__); + return -1; +} +//2021-06-03 willa.liu@fibocom.com changed begin +static void qmi_start_server(void) { + qmi_proxy_server_fd = create_local_server("fibo_qmimsg_server"); + printf("%s: qmi_proxy_server_fd = %d\n", __func__, qmi_proxy_server_fd); + if (qmi_proxy_server_fd == -1) { + dprintf("%s Failed to create %s, errno: %d (%s)\n", __func__, "fibocom-qmi-proxy", errno, strerror(errno)); + } +} + +static void qmi_close_server(void) { + if (qmi_proxy_server_fd != -1) { + dprintf("%s %s close server\n", __func__, "fibocom-qmi-proxy"); + close(qmi_proxy_server_fd); + qmi_proxy_server_fd = -1; + } +} +//2021-06-03 willa.liu@fibocom.com changed end +static void *qmi_proxy_loop(void *param) +{ + static uint8_t qmi_buf[2048]; + PQCQMIMSG pQMI = (PQCQMIMSG)qmi_buf; + struct qlistnode *con_node; + QMI_PROXY_CONNECTION *qmi_con; + + dprintf("%s enter thread_id %ld\n", __func__, pthread_self()); + + qlist_init(&qmi_proxy_connection); + qlist_init(&qmi_proxy_ctl_msg); + + while (cdc_wdm_fd > 0 && qmi_proxy_quit == 0) { + struct pollfd pollfds[2+64]; + int ne, ret, nevents = 0; + ssize_t nreads; + + pollfds[nevents].fd = cdc_wdm_fd; + pollfds[nevents].events = POLLIN; + pollfds[nevents].revents= 0; + nevents++; + + if (qmi_proxy_server_fd > 0) { + pollfds[nevents].fd = qmi_proxy_server_fd; + pollfds[nevents].events = POLLIN; + pollfds[nevents].revents= 0; + nevents++; + } + + qlist_for_each(con_node, &qmi_proxy_connection) { + qmi_con = qnode_to_item(con_node, QMI_PROXY_CONNECTION, qnode); + + pollfds[nevents].fd = qmi_con->ClientFd; + pollfds[nevents].events = POLLIN; + pollfds[nevents].revents= 0; + nevents++; + + if (nevents == (sizeof(pollfds)/sizeof(pollfds[0]))) + break; + } + + do { + //ret = poll(pollfds, nevents, -1); + ret = poll(pollfds, nevents, (qmi_proxy_server_fd > 0) ? -1 : 200); + } while (ret < 0 && errno == EINTR && qmi_proxy_quit == 0); + + if (ret < 0) { + dprintf("%s poll=%d, errno: %d (%s)\n", __func__, ret, errno, strerror(errno)); + goto qmi_proxy_loop_exit; + } + + for (ne = 0; ne < nevents; ne++) { + int fd = pollfds[ne].fd; + short revents = pollfds[ne].revents; + + if (revents & (POLLERR | POLLHUP | POLLNVAL)) { + dprintf("%s poll fd = %d, revents = %04x\n", __func__, fd, revents); + if (fd == cdc_wdm_fd) { + goto qmi_proxy_loop_exit; + } else if(fd == qmi_proxy_server_fd) { + + } else { + cleanup_qmi_connection(fd); + } + + continue; + } + + if (!(pollfds[ne].revents & POLLIN)) { + continue; + } + + if (fd == qmi_proxy_server_fd) { + accept_qmi_connection(fd); + } + else if (fd == cdc_wdm_fd) { + nreads = read(fd, pQMI, sizeof(qmi_buf)); + if (verbose_debug) + { + ssize_t i; + printf("r %d %zd: ", fd, nreads); + for (i = 0; i < 16; i++) + printf("%02x ", ((uint8_t *)pQMI)[i]); + printf("\n"); + } + if (nreads <= 0) { + dprintf("%s read=%d errno: %d (%s)\n", __func__, (int)nreads, errno, strerror(errno)); + goto qmi_proxy_loop_exit; + } + + if (nreads != ((pQMI->QMIHdr.Length) + 1)) { + dprintf("%s nreads=%d, pQCQMI->QMIHdr.Length = %d\n", __func__, (int)nreads, (pQMI->QMIHdr.Length)); + continue; + } + + recv_qmi(pQMI, nreads); + } + else { + nreads = read(fd, pQMI, sizeof(qmi_buf)); + if (verbose_debug) + { + ssize_t i; + printf("r %d %zd: ", fd, nreads); + for (i = 0; i < 16; i++) + printf("%02x ", ((uint8_t *)pQMI)[i]); + printf("\n"); + } + if (nreads <= 0) { + dprintf("%s read=%d errno: %d (%s)", __func__, (int)nreads, errno, strerror(errno)); + cleanup_qmi_connection(fd); + break; + } + + if (nreads != ((pQMI->QMIHdr.Length) + 1)) { + dprintf("%s nreads=%d, pQCQMI->QMIHdr.Length = %d\n", __func__, (int)nreads, (pQMI->QMIHdr.Length)); + continue; + } + + send_qmi(pQMI, nreads, fd); + } + } + } + +qmi_proxy_loop_exit: + while (!qlist_empty(&qmi_proxy_connection)) { + QMI_PROXY_CONNECTION *qmi_con = qnode_to_item(qlist_head(&qmi_proxy_connection), QMI_PROXY_CONNECTION, qnode); + + cleanup_qmi_connection(qmi_con->ClientFd); + } + + dprintf("%s exit, thread_id %ld\n", __func__, pthread_self()); + + return NULL; +} + +static void qmidevice_detect(char **device_name, char **idproduct) { + struct dirent* ent = NULL; + DIR *pDir; + + char dir[255] = "/sys/bus/usb/devices"; + pDir = opendir(dir); + if (pDir) { + struct { + char subdir[255 * 2]; + char qmifile[255 * 2]; + } *pl; + pl = (typeof(pl)) malloc(sizeof(*pl)); + memset(pl, 0x00, sizeof(*pl)); + + while ((ent = readdir(pDir)) != NULL) { + struct dirent* subent = NULL; + DIR *psubDir; + char idVendor[4+1] = {0}; + char idProduct[4+1] = {0}; + int fd = 0; + + memset(pl, 0x00, sizeof(*pl)); + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s/idVendor", dir, ent->d_name); + fd = open(pl->subdir, O_RDONLY); + if (fd > 0) { + read(fd, idVendor, 4); + close(fd); + } + + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s/idProduct", dir, ent->d_name); + fd = open(pl->subdir, O_RDONLY); + if (fd > 0) { + read(fd, idProduct, 4); + close(fd); + } + + + if (strncasecmp(idVendor, "05c6", 4) && strncasecmp(idVendor, "2cb7", 4) && strncasecmp(idVendor, "1508", 4)) + continue; + + dprintf("Find %s/%s idVendor=%s idProduct=%s\n", dir, ent->d_name, idVendor, idProduct); + + if(((!strncasecmp(idVendor, "2cb7", 4)) && (!strncasecmp(idProduct, "0104", 4))) || ((!strncasecmp(idVendor, "1508", 4)) && (!strncasecmp(idProduct, "1001", 4))) || ((!strncasecmp(idVendor, "05c6", 4)) && (!strncasecmp(idProduct, "9025", 4)))) + { + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s:1.4/usbmisc", dir, ent->d_name); + if (access(pl->subdir, R_OK)) { + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s:1.4/usb", dir, ent->d_name); + if (access(pl->subdir, R_OK)) { + dprintf("no GobiQMI/usbmic/usb found in %s/%s:1.4\n", dir, ent->d_name); + continue; + } + } + } + else if((!strncasecmp(idVendor, "2cb7", 4) && !strncasecmp(idProduct, "0109", 4)) || (!strncasecmp(idVendor, "1508", 4) && !strncasecmp(idProduct, "1000", 4))) + { + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s:1.2/usbmisc", dir, ent->d_name); + if (access(pl->subdir, R_OK)) { + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s:1.2/usb", dir, ent->d_name); + if (access(pl->subdir, R_OK)) { + dprintf("no GobiQMI/usbmic/usb found in %s/%s:1.2\n", dir, ent->d_name); + continue; + } + } + } + else if(!strncasecmp(idVendor, "2cb7", 4) && !strncasecmp(idProduct, "0113", 4)) + { + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s:1.0/usbmisc", dir, ent->d_name); + if (access(pl->subdir, R_OK)) { + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s:1.0/usb", dir, ent->d_name); + if (access(pl->subdir, R_OK)) { + dprintf("no GobiQMI/usbmic/usb found in %s/%s:1.0\n", dir, ent->d_name); + continue; + } + } + } + + strncpy(idproduct,idProduct, sizeof(idProduct)); + psubDir = opendir(pl->subdir); + if (pDir == NULL) { + dprintf("Cannot open directory: %s, errno: %d (%s)\n", dir, errno, strerror(errno)); + continue; + } + + while ((subent = readdir(psubDir)) != NULL) { + if (subent->d_name[0] == '.') + continue; + dprintf("Find %s/%s\n", pl->subdir, subent->d_name); + snprintf(pl->qmifile, sizeof(pl->qmifile), "/dev/%s", subent->d_name); + break; + } + *device_name = strdup(pl->qmifile); + closedir(psubDir); + } + closedir(pDir); + free(pl); + } +} + +static void usage(void) { + dprintf(" -d A valid qmi device\n" + " default /dev/cdc-wdm0, but cdc-wdm0 may be invalid\n" + " -v Will show all details\n"); +} + +static void sig_action(int sig) { + if (qmi_proxy_quit == 0) { + qmi_proxy_quit = 1; + if (thread_id) + pthread_kill(thread_id, sig); + } +} + +int main(int argc, char *argv[]) { + int opt; + char *cdc_wdm = NULL; + int retry_times = 0; + char getidproduct[5] = {0}; + optind = 1; + + signal(SIGINT, sig_action); + + while ( -1 != (opt = getopt(argc, argv, "d:v"))) { + switch (opt) { + case 'd': + cdc_wdm = strdup(optarg); + break; + case 'v': + verbose_debug = 1; + break; + default: + usage(); + break; + } + } + + if (cdc_wdm == NULL) + qmidevice_detect(&cdc_wdm, &getidproduct); + if (cdc_wdm == NULL) { + dprintf("Fail to find any /dev/cdc-wdm device. break\n"); + return -1; + } + + if (access(cdc_wdm, R_OK | W_OK)) { + dprintf("Fail to access %s, errno: %d (%s). break\n", cdc_wdm, errno, strerror(errno)); + free(cdc_wdm); + return -1; + } + + while (qmi_proxy_quit == 0) { + if (access(cdc_wdm, R_OK | W_OK)) { + dprintf("Fail to access %s, errno: %d (%s). continue\n", cdc_wdm, errno, strerror(errno)); + // wait device + sleep(3); + continue; + } + + dprintf("Will use cdc-wdm %s\n", cdc_wdm); + + cdc_wdm_fd = open(cdc_wdm, O_RDWR | O_NONBLOCK | O_NOCTTY); + if (cdc_wdm_fd == -1) { + dprintf("Failed to open %s, errno: %d (%s). break\n", cdc_wdm, errno, strerror(errno)); + return -1; + } + cfmakenoblock(cdc_wdm_fd); + + /* no qmi_proxy_loop lives, create one */ + pthread_create(&thread_id, NULL, qmi_proxy_loop, NULL); + /* try to redo init if failed, init function must be successfully */ + while (qmi_proxy_init(cdc_wdm, getidproduct) != 0) { + if (retry_times < 5) { + dprintf("fail to init proxy, try again in 2 seconds.\n"); + sleep(2); + retry_times++; + } else { + dprintf("has failed too much times, restart the modem and have a try...\n"); + break; + } + /* break loop if modem is detached */ + if (access(cdc_wdm, F_OK|R_OK|W_OK)) + break; + } + retry_times = 0; + qmi_start_server(); + pthread_join(thread_id, NULL); + + /* close local server at last */ + qmi_close_server(); + close(cdc_wdm_fd); + } + + if (cdc_wdm) { + free(cdc_wdm); + } + return 0; +} diff --git a/fibocom-dial/src/libmnl/README b/fibocom-dial/src/libmnl/README new file mode 100644 index 000000000..fbac9d2a8 --- /dev/null +++ b/fibocom-dial/src/libmnl/README @@ -0,0 +1,28 @@ += What is libmnl? = + +libmnl is a minimalistic user-space library oriented to Netlink developers. +There are a lot of common tasks in parsing, validating, constructing of +both the Netlink header and TLVs that are repetitive and easy to get wrong. +This library aims to provide simple helpers that allows you to re-use code +and to avoid re-inventing the wheel. The main features of this library are: + +* Small: the shared library requires around 30KB for an x86-based computer. +* Simple: this library avoids complexity and elaborated abstractions that +tend to hide Netlink details. +* Easy to use: the library simplifies the work for Netlink-wise developers. +It provides functions to make socket handling, message building, validating, +parsing and sequence tracking, easier. +* Easy to re-use: you can use the library to build your own abstraction layer +on top of this library. +* Decoupling: the interdependency of the main bricks that compose the library +is reduced, i.e. the library provides many helpers, but the programmer is not +forced to use them. + += Example files = + +You can find several example files under examples/ that you can compile by +invoking `make check'. + +-- +08/sep/2010 +Pablo Neira Ayuso diff --git a/fibocom-dial/src/libmnl/attr.c b/fibocom-dial/src/libmnl/attr.c new file mode 100644 index 000000000..30eb537b4 --- /dev/null +++ b/fibocom-dial/src/libmnl/attr.c @@ -0,0 +1,722 @@ +/* + * (C) 2008-2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ +#include /* for INT_MAX */ +#include +#include + +#include "libmnl.h" + +/** + * \defgroup attr Netlink attribute helpers + * + * Netlink Type-Length-Value (TLV) attribute: + * \verbatim + |<-- 2 bytes -->|<-- 2 bytes -->|<-- variable -->| + ------------------------------------------------- + | length | type | value | + ------------------------------------------------- + |<--------- header ------------>|<-- payload --->| +\endverbatim + * The payload of the Netlink message contains sequences of attributes that are + * expressed in TLV format. + * + * @{ + */ + +/** + * mnl_attr_get_type - get type of netlink attribute + * \param attr pointer to netlink attribute + * + * This function returns the attribute type. + */ +uint16_t mnl_attr_get_type(const struct nlattr *attr) +{ + return attr->nla_type & NLA_TYPE_MASK; +} + +/** + * mnl_attr_get_len - get length of netlink attribute + * \param attr pointer to netlink attribute + * + * This function returns the attribute length that is the attribute header + * plus the attribute payload. + */ +uint16_t mnl_attr_get_len(const struct nlattr *attr) +{ + return attr->nla_len; +} + +/** + * mnl_attr_get_payload_len - get the attribute payload-value length + * \param attr pointer to netlink attribute + * + * This function returns the attribute payload-value length. + */ +uint16_t mnl_attr_get_payload_len(const struct nlattr *attr) +{ + return attr->nla_len - MNL_ATTR_HDRLEN; +} + +/** + * mnl_attr_get_payload - get pointer to the attribute payload + * \param attr pointer to netlink attribute + * + * This function return a pointer to the attribute payload. + */ +void *mnl_attr_get_payload(const struct nlattr *attr) +{ + return (void *)attr + MNL_ATTR_HDRLEN; +} + +/** + * mnl_attr_ok - check if there is room for an attribute in a buffer + * \param attr attribute that we want to check if there is room for + * \param len remaining bytes in a buffer that contains the attribute + * + * This function is used to check that a buffer, which is supposed to contain + * an attribute, has enough room for the attribute that it stores, i.e. this + * function can be used to verify that an attribute is neither malformed nor + * truncated. + * + * This function does not set errno in case of error since it is intended + * for iterations. Thus, it returns true on success and false on error. + * + * The len parameter may be negative in the case of malformed messages during + * attribute iteration, that is why we use a signed integer. + */ +bool mnl_attr_ok(const struct nlattr *attr, int len) +{ + return len >= (int)sizeof(struct nlattr) && + attr->nla_len >= sizeof(struct nlattr) && + (int)attr->nla_len <= len; +} + +/** + * mnl_attr_next - get the next attribute in the payload of a netlink message + * \param attr pointer to the current attribute + * + * This function returns a pointer to the next attribute after the one passed + * as parameter. You have to use mnl_attr_ok() to ensure that the next + * attribute is valid. + */ +struct nlattr *mnl_attr_next(const struct nlattr *attr) +{ + return (struct nlattr *)((void *)attr + MNL_ALIGN(attr->nla_len)); +} + +/** + * mnl_attr_type_valid - check if the attribute type is valid + * \param attr pointer to attribute to be checked + * \param max maximum attribute type + * + * This function allows to check if the attribute type is higher than the + * maximum supported type. If the attribute type is invalid, this function + * returns -1 and errno is explicitly set. On success, this function returns 1. + * + * Strict attribute checking in user-space is not a good idea since you may + * run an old application with a newer kernel that supports new attributes. + * This leads to backward compatibility breakages in user-space. Better check + * if you support an attribute, if not, skip it. + */ +int mnl_attr_type_valid(const struct nlattr *attr, uint16_t max) +{ + if (mnl_attr_get_type(attr) > max) { + errno = EOPNOTSUPP; + return -1; + } + return 1; +} + +static int __mnl_attr_validate(const struct nlattr *attr, + enum mnl_attr_data_type type, size_t exp_len) +{ + uint16_t attr_len = mnl_attr_get_payload_len(attr); + const char *attr_data = mnl_attr_get_payload(attr); + + if (attr_len < exp_len) { + errno = ERANGE; + return -1; + } + switch(type) { + case MNL_TYPE_FLAG: + if (attr_len > 0) { + errno = ERANGE; + return -1; + } + break; + case MNL_TYPE_NUL_STRING: + if (attr_len == 0) { + errno = ERANGE; + return -1; + } + if (attr_data[attr_len-1] != '\0') { + errno = EINVAL; + return -1; + } + break; + case MNL_TYPE_STRING: + if (attr_len == 0) { + errno = ERANGE; + return -1; + } + break; + case MNL_TYPE_NESTED: + /* empty nested attributes are OK. */ + if (attr_len == 0) + break; + /* if not empty, they must contain one header, eg. flag */ + if (attr_len < MNL_ATTR_HDRLEN) { + errno = ERANGE; + return -1; + } + break; + default: + /* make gcc happy. */ + break; + } + if (exp_len && attr_len > exp_len) { + errno = ERANGE; + return -1; + } + return 0; +} + +static const size_t mnl_attr_data_type_len[MNL_TYPE_MAX] = { + [MNL_TYPE_U8] = sizeof(uint8_t), + [MNL_TYPE_U16] = sizeof(uint16_t), + [MNL_TYPE_U32] = sizeof(uint32_t), + [MNL_TYPE_U64] = sizeof(uint64_t), + [MNL_TYPE_MSECS] = sizeof(uint64_t), +}; + +/** + * mnl_attr_validate - validate netlink attribute (simplified version) + * \param attr pointer to netlink attribute that we want to validate + * \param type data type (see enum mnl_attr_data_type) + * + * The validation is based on the data type. Specifically, it checks that + * integers (u8, u16, u32 and u64) have enough room for them. This function + * returns -1 in case of error, and errno is explicitly set. + */ +int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type) +{ + int exp_len; + + if (type >= MNL_TYPE_MAX) { + errno = EINVAL; + return -1; + } + exp_len = mnl_attr_data_type_len[type]; + return __mnl_attr_validate(attr, type, exp_len); +} + +/** + * mnl_attr_validate2 - validate netlink attribute (extended version) + * \param attr pointer to netlink attribute that we want to validate + * \param type attribute type (see enum mnl_attr_data_type) + * \param exp_len expected attribute data size + * + * This function allows to perform a more accurate validation for attributes + * whose size is variable. If the size of the attribute is not what we expect, + * this functions returns -1 and errno is explicitly set. + */ +int mnl_attr_validate2(const struct nlattr *attr, + enum mnl_attr_data_type type, + size_t exp_len) +{ + if (type >= MNL_TYPE_MAX) { + errno = EINVAL; + return -1; + } + return __mnl_attr_validate(attr, type, exp_len); +} + +/** + * mnl_attr_parse - parse attributes + * \param nlh pointer to netlink message + * \param offset offset to start parsing from (if payload is after any header) + * \param cb callback function that is called for each attribute + * \param data pointer to data that is passed to the callback function + * + * This function allows to iterate over the sequence of attributes that compose + * the Netlink message. You can then put the attribute in an array as it + * usually happens at this stage or you can use any other data structure (such + * as lists or trees). + * + * This function propagates the return value of the callback, which can be + * MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP. + */ +int mnl_attr_parse(const struct nlmsghdr *nlh, + unsigned int offset, mnl_attr_cb_t cb, + void *data) +{ + int ret = MNL_CB_OK; + const struct nlattr *attr; + + mnl_attr_for_each(attr, nlh, offset) + if ((ret = cb(attr, data)) <= MNL_CB_STOP) + return ret; + return ret; +} + +/** + * mnl_attr_parse_nested - parse attributes inside a nest + * \param nested pointer to netlink attribute that contains a nest + * \param cb callback function that is called for each attribute in the nest + * \param data pointer to data passed to the callback function + * + * This function allows to iterate over the sequence of attributes that compose + * the Netlink message. You can then put the attribute in an array as it + * usually happens at this stage or you can use any other data structure (such + * as lists or trees). + * + * This function propagates the return value of the callback, which can be + * MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP. + */ +int mnl_attr_parse_nested(const struct nlattr *nested, + mnl_attr_cb_t cb, void *data) +{ + int ret = MNL_CB_OK; + const struct nlattr *attr; + + mnl_attr_for_each_nested(attr, nested) + if ((ret = cb(attr, data)) <= MNL_CB_STOP) + return ret; + return ret; +} + +/** + * mnl_attr_parse_payload - parse attributes in payload of Netlink message + * \param payload pointer to payload of the Netlink message + * \param payload_len payload length that contains the attributes + * \param cb callback function that is called for each attribute + * \param data pointer to data that is passed to the callback function + * + * This function takes a pointer to the area that contains the attributes, + * commonly known as the payload of the Netlink message. Thus, you have to + * pass a pointer to the Netlink message payload, instead of the entire + * message. + * + * This function allows you to iterate over the sequence of attributes that are + * located at some payload offset. You can then put the attributes in one array + * as usual, or you can use any other data structure (such as lists or trees). + * + * This function propagates the return value of the callback, which can be + * MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP. + */ +int mnl_attr_parse_payload(const void *payload, + size_t payload_len, + mnl_attr_cb_t cb, void *data) +{ + int ret = MNL_CB_OK; + const struct nlattr *attr; + + mnl_attr_for_each_payload(payload, payload_len) + if ((ret = cb(attr, data)) <= MNL_CB_STOP) + return ret; + return ret; +} + +/** + * mnl_attr_get_u8 - returns 8-bit unsigned integer attribute payload + * \param attr pointer to netlink attribute + * + * This function returns the 8-bit value of the attribute payload. + */ +uint8_t mnl_attr_get_u8(const struct nlattr *attr) +{ + return *((uint8_t *)mnl_attr_get_payload(attr)); +} + +/** + * mnl_attr_get_u16 - returns 16-bit unsigned integer attribute payload + * \param attr pointer to netlink attribute + * + * This function returns the 16-bit value of the attribute payload. + */ +uint16_t mnl_attr_get_u16(const struct nlattr *attr) +{ + return *((uint16_t *)mnl_attr_get_payload(attr)); +} + +/** + * mnl_attr_get_u32 - returns 32-bit unsigned integer attribute payload + * \param attr pointer to netlink attribute + * + * This function returns the 32-bit value of the attribute payload. + */ +uint32_t mnl_attr_get_u32(const struct nlattr *attr) +{ + return *((uint32_t *)mnl_attr_get_payload(attr)); +} + +/** + * mnl_attr_get_u64 - returns 64-bit unsigned integer attribute. + * \param attr pointer to netlink attribute + * + * This function returns the 64-bit value of the attribute payload. This + * function is align-safe, since accessing 64-bit Netlink attributes is a + * common source of alignment issues. + */ +uint64_t mnl_attr_get_u64(const struct nlattr *attr) +{ + uint64_t tmp; + memcpy(&tmp, mnl_attr_get_payload(attr), sizeof(tmp)); + return tmp; +} + +/** + * mnl_attr_get_str - returns pointer to string attribute. + * \param attr pointer to netlink attribute + * + * This function returns the payload of string attribute value. + */ +const char *mnl_attr_get_str(const struct nlattr *attr) +{ + return mnl_attr_get_payload(attr); +} + +/** + * mnl_attr_put - add an attribute to netlink message + * \param nlh pointer to the netlink message + * \param type netlink attribute type that you want to add + * \param len netlink attribute payload length + * \param data pointer to the data that will be stored by the new attribute + * + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type, + size_t len, const void *data) +{ + struct nlattr *attr = mnl_nlmsg_get_payload_tail(nlh); + uint16_t payload_len = MNL_ALIGN(sizeof(struct nlattr)) + len; + int pad; + + attr->nla_type = type; + attr->nla_len = payload_len; + memcpy(mnl_attr_get_payload(attr), data, len); + pad = MNL_ALIGN(len) - len; + if (pad > 0) + memset(mnl_attr_get_payload(attr) + len, 0, pad); + + nlh->nlmsg_len += MNL_ALIGN(payload_len); +} + +/** + * mnl_attr_put_u8 - add 8-bit unsigned integer attribute to netlink message + * \param nlh pointer to the netlink message + * \param type netlink attribute type + * \param data 8-bit unsigned integer data that is stored by the new attribute + * + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +void mnl_attr_put_u8(struct nlmsghdr *nlh, uint16_t type, + uint8_t data) +{ + mnl_attr_put(nlh, type, sizeof(uint8_t), &data); +} + +/** + * mnl_attr_put_u16 - add 16-bit unsigned integer attribute to netlink message + * \param nlh pointer to the netlink message + * \param type netlink attribute type + * \param data 16-bit unsigned integer data that is stored by the new attribute + * + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type, + uint16_t data) +{ + mnl_attr_put(nlh, type, sizeof(uint16_t), &data); +} + +/** + * mnl_attr_put_u32 - add 32-bit unsigned integer attribute to netlink message + * \param nlh pointer to the netlink message + * \param type netlink attribute type + * \param data 32-bit unsigned integer data that is stored by the new attribute + * + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type, + uint32_t data) +{ + mnl_attr_put(nlh, type, sizeof(uint32_t), &data); +} + +/** + * mnl_attr_put_u64 - add 64-bit unsigned integer attribute to netlink message + * \param nlh pointer to the netlink message + * \param type netlink attribute type + * \param data 64-bit unsigned integer data that is stored by the new attribute + * + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +void mnl_attr_put_u64(struct nlmsghdr *nlh, uint16_t type, + uint64_t data) +{ + mnl_attr_put(nlh, type, sizeof(uint64_t), &data); +} + +/** + * mnl_attr_put_str - add string attribute to netlink message + * \param nlh pointer to the netlink message + * \param type netlink attribute type + * \param data pointer to string data that is stored by the new attribute + * + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +void mnl_attr_put_str(struct nlmsghdr *nlh, uint16_t type, + const char *data) +{ + mnl_attr_put(nlh, type, strlen(data), data); +} + +/** + * mnl_attr_put_strz - add string attribute to netlink message + * \param nlh pointer to the netlink message + * \param type netlink attribute type + * \param data pointer to string data that is stored by the new attribute + * + * This function is similar to mnl_attr_put_str, but it includes the + * NUL/zero ('\0') terminator at the end of the string. + * + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type, + const char *data) +{ + mnl_attr_put(nlh, type, strlen(data)+1, data); +} + +/** + * mnl_attr_nest_start - start an attribute nest + * \param nlh pointer to the netlink message + * \param type netlink attribute type + * + * This function adds the attribute header that identifies the beginning of + * an attribute nest. This function always returns a valid pointer to the + * beginning of the nest. + */ +struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh, + uint16_t type) +{ + struct nlattr *start = mnl_nlmsg_get_payload_tail(nlh); + + /* set start->nla_len in mnl_attr_nest_end() */ + start->nla_type = NLA_F_NESTED | type; + nlh->nlmsg_len += MNL_ALIGN(sizeof(struct nlattr)); + + return start; +} + +/** + * mnl_attr_put_check - add an attribute to netlink message + * \param nlh pointer to the netlink message + * \param buflen size of buffer which stores the message + * \param type netlink attribute type that you want to add + * \param len netlink attribute payload length + * \param data pointer to the data that will be stored by the new attribute + * + * This function first checks that the data can be added to the message + * (fits into the buffer) and then updates the length field of the Netlink + * message (nlmsg_len) by adding the size (header + payload) of the new + * attribute. The function returns true if the attribute could be added + * to the message, otherwise false is returned. + */ +bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen, + uint16_t type, size_t len, + const void *data) +{ + if (nlh->nlmsg_len + MNL_ATTR_HDRLEN + MNL_ALIGN(len) > buflen) + return false; + mnl_attr_put(nlh, type, len, data); + return true; +} + +/** + * mnl_attr_put_u8_check - add 8-bit unsigned int attribute to netlink message + * \param nlh pointer to the netlink message + * \param buflen size of buffer which stores the message + * \param type netlink attribute type + * \param data 8-bit unsigned integer data that is stored by the new attribute + * + * This function first checks that the data can be added to the message + * (fits into the buffer) and then updates the length field of the Netlink + * message (nlmsg_len) by adding the size (header + payload) of the new + * attribute. The function returns true if the attribute could be added + * to the message, otherwise false is returned. + */ +bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen, + uint16_t type, uint8_t data) +{ + return mnl_attr_put_check(nlh, buflen, type, sizeof(uint8_t), &data); +} + +/** + * mnl_attr_put_u16_check - add 16-bit unsigned int attribute to netlink message + * \param nlh pointer to the netlink message + * \param buflen size of buffer which stores the message + * \param type netlink attribute type + * \param data 16-bit unsigned integer data that is stored by the new attribute + * + * This function first checks that the data can be added to the message + * (fits into the buffer) and then updates the length field of the Netlink + * message (nlmsg_len) by adding the size (header + payload) of the new + * attribute. The function returns true if the attribute could be added + * to the message, otherwise false is returned. + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen, + uint16_t type, uint16_t data) +{ + return mnl_attr_put_check(nlh, buflen, type, sizeof(uint16_t), &data); +} + +/** + * mnl_attr_put_u32_check - add 32-bit unsigned int attribute to netlink message + * \param nlh pointer to the netlink message + * \param buflen size of buffer which stores the message + * \param type netlink attribute type + * \param data 32-bit unsigned integer data that is stored by the new attribute + * + * This function first checks that the data can be added to the message + * (fits into the buffer) and then updates the length field of the Netlink + * message (nlmsg_len) by adding the size (header + payload) of the new + * attribute. The function returns true if the attribute could be added + * to the message, otherwise false is returned. + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen, + uint16_t type, uint32_t data) +{ + return mnl_attr_put_check(nlh, buflen, type, sizeof(uint32_t), &data); +} + +/** + * mnl_attr_put_u64_check - add 64-bit unsigned int attribute to netlink message + * \param nlh pointer to the netlink message + * \param buflen size of buffer which stores the message + * \param type netlink attribute type + * \param data 64-bit unsigned integer data that is stored by the new attribute + * + * This function first checks that the data can be added to the message + * (fits into the buffer) and then updates the length field of the Netlink + * message (nlmsg_len) by adding the size (header + payload) of the new + * attribute. The function returns true if the attribute could be added + * to the message, otherwise false is returned. + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +bool mnl_attr_put_u64_check(struct nlmsghdr *nlh, size_t buflen, + uint16_t type, uint64_t data) +{ + return mnl_attr_put_check(nlh, buflen, type, sizeof(uint64_t), &data); +} + +/** + * mnl_attr_put_str_check - add string attribute to netlink message + * \param nlh pointer to the netlink message + * \param buflen size of buffer which stores the message + * \param type netlink attribute type + * \param data pointer to string data that is stored by the new attribute + * + * This function first checks that the data can be added to the message + * (fits into the buffer) and then updates the length field of the Netlink + * message (nlmsg_len) by adding the size (header + payload) of the new + * attribute. The function returns true if the attribute could be added + * to the message, otherwise false is returned. + * This function updates the length field of the Netlink message (nlmsg_len) + * by adding the size (header + payload) of the new attribute. + */ +bool mnl_attr_put_str_check(struct nlmsghdr *nlh, size_t buflen, + uint16_t type, const char *data) +{ + return mnl_attr_put_check(nlh, buflen, type, strlen(data), data); +} + +/** + * mnl_attr_put_strz_check - add string attribute to netlink message + * \param nlh pointer to the netlink message + * \param buflen size of buffer which stores the message + * \param type netlink attribute type + * \param data pointer to string data that is stored by the new attribute + * + * This function is similar to mnl_attr_put_str, but it includes the + * NUL/zero ('\0') terminator at the end of the string. + * + * This function first checks that the data can be added to the message + * (fits into the buffer) and then updates the length field of the Netlink + * message (nlmsg_len) by adding the size (header + payload) of the new + * attribute. The function returns true if the attribute could be added + * to the message, otherwise false is returned. + */ +bool mnl_attr_put_strz_check(struct nlmsghdr *nlh, size_t buflen, + uint16_t type, const char *data) +{ + return mnl_attr_put_check(nlh, buflen, type, strlen(data)+1, data); +} + +/** + * mnl_attr_nest_start_check - start an attribute nest + * \param buflen size of buffer which stores the message + * \param nlh pointer to the netlink message + * \param type netlink attribute type + * + * This function adds the attribute header that identifies the beginning of + * an attribute nest. If the nested attribute cannot be added then NULL, + * otherwise valid pointer to the beginning of the nest is returned. + */ +struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh, + size_t buflen, + uint16_t type) +{ + if (nlh->nlmsg_len + MNL_ATTR_HDRLEN > buflen) + return NULL; + return mnl_attr_nest_start(nlh, type); +} + +/** + * mnl_attr_nest_end - end an attribute nest + * \param nlh pointer to the netlink message + * \param start pointer to the attribute nest returned by mnl_attr_nest_start() + * + * This function updates the attribute header that identifies the nest. + */ +void mnl_attr_nest_end(struct nlmsghdr *nlh, + struct nlattr *start) +{ + start->nla_len = mnl_nlmsg_get_payload_tail(nlh) - (void *)start; +} + +/** + * mnl_attr_nest_cancel - cancel an attribute nest + * \param nlh pointer to the netlink message + * \param start pointer to the attribute nest returned by mnl_attr_nest_start() + * + * This function updates the attribute header that identifies the nest. + */ +void mnl_attr_nest_cancel(struct nlmsghdr *nlh, + struct nlattr *start) +{ + nlh->nlmsg_len -= mnl_nlmsg_get_payload_tail(nlh) - (void *)start; +} + +/** + * @} + */ diff --git a/fibocom-dial/src/libmnl/callback.c b/fibocom-dial/src/libmnl/callback.c new file mode 100644 index 000000000..8283a93b9 --- /dev/null +++ b/fibocom-dial/src/libmnl/callback.c @@ -0,0 +1,167 @@ +/* + * (C) 2008-2010 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include + +#include "libmnl.h" + +static int mnl_cb_noop(const struct nlmsghdr *nlh, void *data) +{ + return MNL_CB_OK; +} + +static int mnl_cb_error(const struct nlmsghdr *nlh, void *data) +{ + const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh); + + if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) { + errno = EBADMSG; + return MNL_CB_ERROR; + } + /* Netlink subsystems returns the errno value with different signess */ + if (err->error < 0) + errno = -err->error; + else + errno = err->error; + + return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR; +} + +static int mnl_cb_stop(const struct nlmsghdr *nlh, void *data) +{ + return MNL_CB_STOP; +} + +static const mnl_cb_t default_cb_array[NLMSG_MIN_TYPE] = { + [NLMSG_NOOP] = mnl_cb_noop, + [NLMSG_ERROR] = mnl_cb_error, + [NLMSG_DONE] = mnl_cb_stop, + [NLMSG_OVERRUN] = mnl_cb_noop, +}; + +static inline int __mnl_cb_run(const void *buf, size_t numbytes, + unsigned int seq, unsigned int portid, + mnl_cb_t cb_data, void *data, + const mnl_cb_t *cb_ctl_array, + unsigned int cb_ctl_array_len) +{ + int ret = MNL_CB_OK, len = numbytes; + const struct nlmsghdr *nlh = buf; + + while (mnl_nlmsg_ok(nlh, len)) { + /* check message source */ + if (!mnl_nlmsg_portid_ok(nlh, portid)) { + errno = ESRCH; + return -1; + } + /* perform sequence tracking */ + if (!mnl_nlmsg_seq_ok(nlh, seq)) { + errno = EPROTO; + return -1; + } + + /* dump was interrupted */ + if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) { + errno = EINTR; + return -1; + } + + /* netlink data message handling */ + if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) { + if (cb_data){ + ret = cb_data(nlh, data); + if (ret <= MNL_CB_STOP) + goto out; + } + } else if (nlh->nlmsg_type < cb_ctl_array_len) { + if (cb_ctl_array && cb_ctl_array[nlh->nlmsg_type]) { + ret = cb_ctl_array[nlh->nlmsg_type](nlh, data); + if (ret <= MNL_CB_STOP) + goto out; + } + } else if (default_cb_array[nlh->nlmsg_type]) { + ret = default_cb_array[nlh->nlmsg_type](nlh, data); + if (ret <= MNL_CB_STOP) + goto out; + } + nlh = mnl_nlmsg_next(nlh, &len); + } +out: + return ret; +} + +/** + * \defgroup callback Callback helpers + * @{ + */ + +/** + * mnl_cb_run2 - callback runqueue for netlink messages + * \param buf buffer that contains the netlink messages + * \param numbytes number of bytes stored in the buffer + * \param seq sequence number that we expect to receive + * \param portid Netlink PortID that we expect to receive + * \param cb_data callback handler for data messages + * \param data pointer to data that will be passed to the data callback handler + * \param cb_ctl_array array of custom callback handlers from control messages + * \param cb_ctl_array_len array length of custom control callback handlers + * + * You can set the cb_ctl_array to NULL if you want to use the default control + * callback handlers, in that case, the parameter cb_ctl_array_len is not + * checked. + * + * Your callback may return three possible values: + * - MNL_CB_ERROR (<=-1): an error has occurred. Stop callback runqueue. + * - MNL_CB_STOP (=0): stop callback runqueue. + * - MNL_CB_OK (>=1): no problem has occurred. + * + * This function propagates the callback return value. On error, it returns + * -1 and errno is explicitly set. If the portID is not the expected, errno + * is set to ESRCH. If the sequence number is not the expected, errno is set + * to EPROTO. If the dump was interrupted, errno is set to EINTR and you should + * request a new fresh dump again. + */ +int mnl_cb_run2(const void *buf, size_t numbytes, + unsigned int seq, unsigned int portid, + mnl_cb_t cb_data, void *data, + const mnl_cb_t *cb_ctl_array, + unsigned int cb_ctl_array_len) +{ + return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data, + cb_ctl_array, cb_ctl_array_len); +} + +/** + * mnl_cb_run - callback runqueue for netlink messages (simplified version) + * \param buf buffer that contains the netlink messages + * \param numbytes number of bytes stored in the buffer + * \param seq sequence number that we expect to receive + * \param portid Netlink PortID that we expect to receive + * \param cb_data callback handler for data messages + * \param data pointer to data that will be passed to the data callback handler + * + * This function is like mnl_cb_run2() but it does not allow you to set + * the control callback handlers. + * + * Your callback may return three possible values: + * - MNL_CB_ERROR (<=-1): an error has occurred. Stop callback runqueue. + * - MNL_CB_STOP (=0): stop callback runqueue. + * - MNL_CB_OK (>=1): no problems has occurred. + * + * This function propagates the callback return value. + */ +int mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq, + unsigned int portid, mnl_cb_t cb_data, void *data) +{ + return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data, NULL, 0); +} + +/** + * @} + */ diff --git a/fibocom-dial/src/libmnl/dhcp/dhcp.h b/fibocom-dial/src/libmnl/dhcp/dhcp.h new file mode 100644 index 000000000..f48028552 --- /dev/null +++ b/fibocom-dial/src/libmnl/dhcp/dhcp.h @@ -0,0 +1,5 @@ +#ifndef __DHCP_H__ +#define __DHCP_H__ + +int do_dhcp(char *iname); +#endif //__DHCP_H__ \ No newline at end of file diff --git a/fibocom-dial/src/libmnl/dhcp/dhcpclient.c b/fibocom-dial/src/libmnl/dhcp/dhcpclient.c new file mode 100644 index 000000000..ccb71b5d8 --- /dev/null +++ b/fibocom-dial/src/libmnl/dhcp/dhcpclient.c @@ -0,0 +1,515 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../ifutils.h" +#include "dhcpmsg.h" +#include "packet.h" + +#define VERBOSE 2 + +static int verbose = 1; +static char errmsg[2048]; + +typedef unsigned long long msecs_t; +#if VERBOSE +void dump_dhcp_msg(); +#endif + +msecs_t get_msecs(void) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts)) { + return 0; + } else { + return (((msecs_t) ts.tv_sec) * ((msecs_t) 1000)) + + (((msecs_t) ts.tv_nsec) / ((msecs_t) 1000000)); + } +} + +void printerr(char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsnprintf(errmsg, sizeof(errmsg), fmt, ap); + va_end(ap); + + printf("%s\n", errmsg); +} + +const char *dhcp_lasterror() +{ + return errmsg; +} + +int fatal(const char *reason) +{ + printerr("%s: %s\n", reason, strerror(errno)); + return -1; +// exit(1); +} + +typedef struct dhcp_info dhcp_info; + +struct dhcp_info { + uint32_t type; + + uint32_t ipaddr; + uint32_t gateway; + uint32_t prefixLength; + + uint32_t dns1; + uint32_t dns2; + + uint32_t serveraddr; + uint32_t lease; +}; + +dhcp_info last_good_info; + +void get_dhcp_info(uint32_t *ipaddr, uint32_t *gateway, uint32_t *prefixLength, + uint32_t *dns1, uint32_t *dns2, uint32_t *server, + uint32_t *lease) +{ + *ipaddr = last_good_info.ipaddr; + *gateway = last_good_info.gateway; + *prefixLength = last_good_info.prefixLength; + *dns1 = last_good_info.dns1; + *dns2 = last_good_info.dns2; + *server = last_good_info.serveraddr; + *lease = last_good_info.lease; +} + +static int dhcp_configure(const char *ifname, dhcp_info *info) +{ + last_good_info = *info; + return if_set_network_v4(ifname, info->ipaddr, info->prefixLength, info->gateway, + info->dns1, info->dns2); +} + +static const char *dhcp_type_to_name(uint32_t type) +{ + switch(type) { + case DHCPDISCOVER: return "discover"; + case DHCPOFFER: return "offer"; + case DHCPREQUEST: return "request"; + case DHCPDECLINE: return "decline"; + case DHCPACK: return "ack"; + case DHCPNAK: return "nak"; + case DHCPRELEASE: return "release"; + case DHCPINFORM: return "inform"; + default: return "???"; + } +} + +void dump_dhcp_info(dhcp_info *info) +{ + char addr[20], gway[20]; + printf("--- dhcp %s (%d) ---\n", + dhcp_type_to_name(info->type), info->type); + strcpy(addr, ipaddr_to_string_v4(info->ipaddr)); + strcpy(gway, ipaddr_to_string_v4(info->gateway)); + printf("ip %s gw %s prefixLength %d\n", addr, gway, info->prefixLength); + if (info->dns1) printf("dns1: %s\n", ipaddr_to_string_v4(info->dns1)); + if (info->dns2) printf("dns2: %s\n", ipaddr_to_string_v4(info->dns2)); + printf("server %s, lease %d seconds\n", + ipaddr_to_string_v4(info->serveraddr), info->lease); +} + + +int decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info) +{ + uint8_t *x; + unsigned int opt; + int optlen; + + memset(info, 0, sizeof(dhcp_info)); + if (len < (DHCP_MSG_FIXED_SIZE + 4)) return -1; + + len -= (DHCP_MSG_FIXED_SIZE + 4); + + if (msg->options[0] != OPT_COOKIE1) return -1; + if (msg->options[1] != OPT_COOKIE2) return -1; + if (msg->options[2] != OPT_COOKIE3) return -1; + if (msg->options[3] != OPT_COOKIE4) return -1; + + x = msg->options + 4; + + while (len > 2) { + opt = *x++; + if (opt == OPT_PAD) { + len--; + continue; + } + if (opt == OPT_END) { + break; + } + optlen = *x++; + len -= 2; + if (optlen > len) { + break; + } + switch(opt) { + case OPT_SUBNET_MASK: + if (optlen >= 4) { + in_addr_t mask; + memcpy(&mask, x, 4); + info->prefixLength = mask_to_prefix_v4(mask); + } + break; + case OPT_GATEWAY: + if (optlen >= 4) memcpy(&info->gateway, x, 4); + break; + case OPT_DNS: + if (optlen >= 4) memcpy(&info->dns1, x + 0, 4); + if (optlen >= 8) memcpy(&info->dns2, x + 4, 4); + break; + case OPT_LEASE_TIME: + if (optlen >= 4) { + memcpy(&info->lease, x, 4); + info->lease = ntohl(info->lease); + } + break; + case OPT_SERVER_ID: + if (optlen >= 4) memcpy(&info->serveraddr, x, 4); + break; + case OPT_MESSAGE_TYPE: + info->type = *x; + break; + default: + break; + } + x += optlen; + len -= optlen; + } + + info->ipaddr = msg->yiaddr; + + return 0; +} + +#if VERBOSE + +static void hex2str(char *buf, size_t buf_size, const unsigned char *array, int len) +{ + int i; + char *cp = buf; + char *buf_end = buf + buf_size; + for (i = 0; i < len; i++) { + cp += snprintf(cp, buf_end - cp, " %02x ", array[i]); + } +} + +void dump_dhcp_msg(dhcp_msg *msg, int len) +{ + unsigned char *x; + unsigned int n,c; + int optsz; + const char *name; + char buf[2048]; + + if (len < DHCP_MSG_FIXED_SIZE) { + printf("Invalid length %d, should be %d", len, DHCP_MSG_FIXED_SIZE); + return; + } + + len -= DHCP_MSG_FIXED_SIZE; + + if (msg->op == OP_BOOTREQUEST) + name = "BOOTREQUEST"; + else if (msg->op == OP_BOOTREPLY) + name = "BOOTREPLY"; + else + name = "????"; + + c = msg->hlen > 16 ? 16 : msg->hlen; + hex2str(buf, sizeof(buf), msg->chaddr, c); + + for (n = 0; n < 64; n++) { + unsigned char x = msg->sname[n]; + if ((x < ' ') || (x > 127)) { + if (x == 0) break; + msg->sname[n] = '.'; + } + } + msg->sname[63] = 0; + + for (n = 0; n < 128; n++) { + unsigned char x = msg->file[n]; + if ((x < ' ') || (x > 127)) { + if (x == 0) break; + msg->file[n] = '.'; + } + } + msg->file[127] = 0; + + if (len < 4) return; + len -= 4; + x = msg->options + 4; + + while (len > 2) { + if (*x == 0) { + x++; + len--; + continue; + } + if (*x == OPT_END) { + break; + } + len -= 2; + optsz = x[1]; + if (optsz > len) break; + if (x[0] == OPT_DOMAIN_NAME || x[0] == OPT_MESSAGE) { + if ((unsigned int)optsz < sizeof(buf) - 1) { + n = optsz; + } else { + n = sizeof(buf) - 1; + } + memcpy(buf, &x[2], n); + buf[n] = '\0'; + } else { + hex2str(buf, sizeof(buf), &x[2], optsz); + } + if (x[0] == OPT_MESSAGE_TYPE) + name = dhcp_type_to_name(x[2]); + else + name = NULL; + len -= optsz; + x = x + optsz + 2; + } +} + +#endif + +static int send_message(int sock, int if_index, dhcp_msg *msg, int size) +{ +#if VERBOSE > 1 + dump_dhcp_msg(msg, size); +#endif + return send_packet(sock, if_index, msg, size, INADDR_ANY, INADDR_BROADCAST, + PORT_BOOTP_CLIENT, PORT_BOOTP_SERVER); +} + +static int is_valid_reply(dhcp_msg *msg, dhcp_msg *reply, int sz) +{ + if (sz < DHCP_MSG_FIXED_SIZE) { + if (verbose) printf("Wrong size %d != %d\n", sz, DHCP_MSG_FIXED_SIZE); + return 0; + } + if (reply->op != OP_BOOTREPLY) { + if (verbose) printf("Wrong Op %d != %d\n", reply->op, OP_BOOTREPLY); + return 0; + } + if (reply->xid != msg->xid) { + if (verbose) printf("Wrong Xid 0x%x != 0x%x\n", ntohl(reply->xid), + ntohl(msg->xid)); + return 0; + } + if (reply->htype != msg->htype) { + if (verbose) printf("Wrong Htype %d != %d\n", reply->htype, msg->htype); + return 0; + } + if (reply->hlen != msg->hlen) { + if (verbose) printf("Wrong Hlen %d != %d\n", reply->hlen, msg->hlen); + return 0; + } + if (memcmp(msg->chaddr, reply->chaddr, msg->hlen)) { + if (verbose) printf("Wrong chaddr %x != %x\n", *(reply->chaddr),*(msg->chaddr)); + return 0; + } + return 1; +} + +#define STATE_SELECTING 1 +#define STATE_REQUESTING 2 + +#define TIMEOUT_INITIAL 4000 +#define TIMEOUT_MAX 32000 + +int dhcp_init_ifc(const char *ifname) +{ + dhcp_msg discover_msg; + dhcp_msg request_msg; + dhcp_msg reply; + dhcp_msg *msg; + dhcp_info info; + int s, r, size; + int valid_reply; + uint32_t xid; + unsigned char hwaddr[6]; + struct pollfd pfd; + unsigned int state; + unsigned int timeout; + int if_index; + + xid = (uint32_t) get_msecs(); + + if (if_get_hwaddr(ifname, hwaddr)) { + return fatal("cannot obtain interface address"); + } + if ((if_index = if_nametoindex(ifname)) == 0) { + return fatal("cannot obtain interface index"); + } + + s = open_raw_socket(ifname, hwaddr, if_index); + + timeout = TIMEOUT_INITIAL; + state = STATE_SELECTING; + info.type = 0; + goto transmit; + + for (;;) { + pfd.fd = s; + pfd.events = POLLIN; + pfd.revents = 0; + r = poll(&pfd, 1, timeout); + + if (r == 0) { +#if VERBOSE + printerr("TIMEOUT\n"); +#endif + if (timeout >= TIMEOUT_MAX) { + printerr("timed out\n"); + if ( info.type == DHCPOFFER ) { + printerr("no acknowledgement from DHCP server\nconfiguring %s with offered parameters\n", ifname); + return dhcp_configure(ifname, &info); + } + errno = ETIME; + close(s); + return -1; + } + timeout = timeout * 2; + + transmit: + size = 0; + msg = NULL; + switch(state) { + case STATE_SELECTING: + msg = &discover_msg; + size = init_dhcp_discover_msg(msg, hwaddr, xid); + break; + case STATE_REQUESTING: + msg = &request_msg; + size = init_dhcp_request_msg(msg, hwaddr, xid, info.ipaddr, info.serveraddr); + break; + default: + r = 0; + } + if (size != 0) { + r = send_message(s, if_index, msg, size); + if (r < 0) { + printerr("error sending dhcp msg: %s\n", strerror(errno)); + } + } + continue; + } + + if (r < 0) { + if ((errno == EAGAIN) || (errno == EINTR)) { + continue; + } + return fatal("poll failed"); + } + + errno = 0; + r = receive_packet(s, &reply); + if (r < 0) { + if (errno != 0) { + printf("receive_packet failed (%d): %s", r, strerror(errno)); + if (errno == ENETDOWN || errno == ENXIO) { + return -1; + } + } + continue; + } + +#if VERBOSE > 1 + dump_dhcp_msg(&reply, r); +#endif + decode_dhcp_msg(&reply, r, &info); + + if (state == STATE_SELECTING) { + valid_reply = is_valid_reply(&discover_msg, &reply, r); + } else { + valid_reply = is_valid_reply(&request_msg, &reply, r); + } + if (!valid_reply) { + printerr("invalid reply\n"); + continue; + } + + if (verbose) dump_dhcp_info(&info); + + switch(state) { + case STATE_SELECTING: + if (info.type == DHCPOFFER) { + state = STATE_REQUESTING; + timeout = TIMEOUT_INITIAL; + xid++; + goto transmit; + } + break; + case STATE_REQUESTING: + if (info.type == DHCPACK) { + printerr("configuring %s\n", ifname); + close(s); + return dhcp_configure(ifname, &info); + } else if (info.type == DHCPNAK) { + printerr("configuration request denied\n"); + close(s); + return -1; + } else { + printerr("ignoring %s message in state %d\n", + dhcp_type_to_name(info.type), state); + } + break; + } + } + close(s); + return 0; +} + +int do_dhcp(char *iname) +{ + if (if_set_addr_v4(iname, 0, 32)) { + printerr("failed to set ip addr for %s to 0.0.0.0: %s\n", iname, strerror(errno)); + return -1; + } + + if (if_link_up(iname)) { + printerr("failed to bring up interface %s: %s\n", iname, strerror(errno)); + return -1; + } + + return dhcp_init_ifc(iname); +} diff --git a/fibocom-dial/src/libmnl/dhcp/dhcpmsg.c b/fibocom-dial/src/libmnl/dhcp/dhcpmsg.c new file mode 100644 index 000000000..1e0a233fc --- /dev/null +++ b/fibocom-dial/src/libmnl/dhcp/dhcpmsg.c @@ -0,0 +1,100 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "dhcpmsg.h" + +static void *init_dhcp_msg(dhcp_msg *msg, int type, void *hwaddr, uint32_t xid) +{ + uint8_t *x; + + memset(msg, 0, sizeof(dhcp_msg)); + + msg->op = OP_BOOTREQUEST; + msg->htype = HTYPE_ETHER; + msg->hlen = 6; + msg->hops = 0; + + msg->flags = htons(FLAGS_BROADCAST); + + msg->xid = xid; + + memcpy(msg->chaddr, hwaddr, 6); + + x = msg->options; + + *x++ = OPT_COOKIE1; + *x++ = OPT_COOKIE2; + *x++ = OPT_COOKIE3; + *x++ = OPT_COOKIE4; + + *x++ = OPT_MESSAGE_TYPE; + *x++ = 1; + *x++ = type; + + return x; +} + +int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid) +{ + uint8_t *x; + + x = init_dhcp_msg(msg, DHCPDISCOVER, hwaddr, xid); + + *x++ = OPT_PARAMETER_LIST; + *x++ = 4; + *x++ = OPT_SUBNET_MASK; + *x++ = OPT_GATEWAY; + *x++ = OPT_DNS; + *x++ = OPT_BROADCAST_ADDR; + + *x++ = OPT_END; + + return DHCP_MSG_FIXED_SIZE + (x - msg->options); +} + +int init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid, + uint32_t ipaddr, uint32_t serveraddr) +{ + uint8_t *x; + + x = init_dhcp_msg(msg, DHCPREQUEST, hwaddr, xid); + + *x++ = OPT_PARAMETER_LIST; + *x++ = 4; + *x++ = OPT_SUBNET_MASK; + *x++ = OPT_GATEWAY; + *x++ = OPT_DNS; + *x++ = OPT_BROADCAST_ADDR; + + *x++ = OPT_REQUESTED_IP; + *x++ = 4; + memcpy(x, &ipaddr, 4); + x += 4; + + *x++ = OPT_SERVER_ID; + *x++ = 4; + memcpy(x, &serveraddr, 4); + x += 4; + + *x++ = OPT_END; + + return DHCP_MSG_FIXED_SIZE + (x - msg->options); +} diff --git a/fibocom-dial/src/libmnl/dhcp/dhcpmsg.h b/fibocom-dial/src/libmnl/dhcp/dhcpmsg.h new file mode 100644 index 000000000..fb99490ab --- /dev/null +++ b/fibocom-dial/src/libmnl/dhcp/dhcpmsg.h @@ -0,0 +1,106 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _WIFI_DHCP_H_ +#define _WIFI_DHCP_H_ + +#include + +#define PORT_BOOTP_SERVER 67 +#define PORT_BOOTP_CLIENT 68 + +/* RFC 2131 p 9 */ +typedef struct dhcp_msg dhcp_msg; + +#define OP_BOOTREQUEST 1 +#define OP_BOOTREPLY 2 + +#define FLAGS_BROADCAST 0x8000 + +#define HTYPE_ETHER 1 + +struct dhcp_msg +{ + uint8_t op; /* BOOTREQUEST / BOOTREPLY */ + uint8_t htype; /* hw addr type */ + uint8_t hlen; /* hw addr len */ + uint8_t hops; /* client set to 0 */ + + uint32_t xid; /* transaction id */ + + uint16_t secs; /* seconds since start of acq */ + uint16_t flags; + + uint32_t ciaddr; /* client IP addr */ + uint32_t yiaddr; /* your (client) IP addr */ + uint32_t siaddr; /* ip addr of next server */ + /* (DHCPOFFER and DHCPACK) */ + uint32_t giaddr; /* relay agent IP addr */ + + uint8_t chaddr[16]; /* client hw addr */ + char sname[64]; /* asciiz server hostname */ + char file[128]; /* asciiz boot file name */ + + uint8_t options[1024]; /* optional parameters */ +}; + +#define DHCP_MSG_FIXED_SIZE 236 + +/* first four bytes of options are a cookie to indicate that +** the payload are DHCP options as opposed to some other BOOTP +** extension. +*/ +#define OPT_COOKIE1 0x63 +#define OPT_COOKIE2 0x82 +#define OPT_COOKIE3 0x53 +#define OPT_COOKIE4 0x63 + +/* BOOTP/DHCP options - see RFC 2132 */ +#define OPT_PAD 0 + +#define OPT_SUBNET_MASK 1 /* 4 */ +#define OPT_TIME_OFFSET 2 /* 4 */ +#define OPT_GATEWAY 3 /* 4*n * n */ +#define OPT_DNS 6 /* 4*n * n */ +#define OPT_DOMAIN_NAME 15 /* n */ +#define OPT_BROADCAST_ADDR 28 /* 4 */ + +#define OPT_REQUESTED_IP 50 /* 4 */ +#define OPT_LEASE_TIME 51 /* 4 */ +#define OPT_MESSAGE_TYPE 53 /* 1 */ +#define OPT_SERVER_ID 54 /* 4 */ +#define OPT_PARAMETER_LIST 55 /* n * n */ +#define OPT_MESSAGE 56 /* n */ +#define OPT_CLASS_ID 60 /* n */ +#define OPT_CLIENT_ID 61 /* n */ +#define OPT_END 255 + +/* DHCP message types */ +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 + +int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid); + +int init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid, + uint32_t ipaddr, uint32_t serveraddr); + +#endif diff --git a/fibocom-dial/src/libmnl/dhcp/packet.c b/fibocom-dial/src/libmnl/dhcp/packet.c new file mode 100644 index 000000000..9515dd1eb --- /dev/null +++ b/fibocom-dial/src/libmnl/dhcp/packet.c @@ -0,0 +1,247 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dhcpmsg.h" + +int fatal(); + +int open_raw_socket(const char *ifname __attribute__((unused)), uint8_t *hwaddr, int if_index) +{ + int s; + struct sockaddr_ll bindaddr; + + if((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { + return fatal("socket(PF_PACKET)"); + } + + memset(&bindaddr, 0, sizeof(bindaddr)); + bindaddr.sll_family = AF_PACKET; + bindaddr.sll_protocol = htons(ETH_P_IP); + bindaddr.sll_halen = ETH_ALEN; + memcpy(bindaddr.sll_addr, hwaddr, ETH_ALEN); + bindaddr.sll_ifindex = if_index; + + if (bind(s, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) { + return fatal("Cannot bind raw socket to interface"); + } + + return s; +} + +static uint32_t checksum(void *buffer, unsigned int count, uint32_t startsum) +{ + uint16_t *up = (uint16_t *)buffer; + uint32_t sum = startsum; + uint32_t upper16; + + while (count > 1) { + sum += *up++; + count -= 2; + } + if (count > 0) { + sum += (uint16_t) *(uint8_t *)up; + } + while ((upper16 = (sum >> 16)) != 0) { + sum = (sum & 0xffff) + upper16; + } + return sum; +} + +static uint32_t finish_sum(uint32_t sum) +{ + return ~sum & 0xffff; +} + +int send_packet(int s, int if_index, struct dhcp_msg *msg, int size, + uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport) +{ + struct iphdr ip; + struct udphdr udp; + struct iovec iov[3]; + uint32_t udpsum; + uint16_t temp; + struct msghdr msghdr; + struct sockaddr_ll destaddr; + + ip.version = IPVERSION; + ip.ihl = sizeof(ip) >> 2; + ip.tos = 0; + ip.tot_len = htons(sizeof(ip) + sizeof(udp) + size); + ip.id = 0; + ip.frag_off = 0; + ip.ttl = IPDEFTTL; + ip.protocol = IPPROTO_UDP; + ip.check = 0; + ip.saddr = saddr; + ip.daddr = daddr; + ip.check = finish_sum(checksum(&ip, sizeof(ip), 0)); + + udp.source = htons(sport); + udp.dest = htons(dport); + udp.len = htons(sizeof(udp) + size); + udp.check = 0; + + /* Calculate checksum for pseudo header */ + udpsum = checksum(&ip.saddr, sizeof(ip.saddr), 0); + udpsum = checksum(&ip.daddr, sizeof(ip.daddr), udpsum); + temp = htons(IPPROTO_UDP); + udpsum = checksum(&temp, sizeof(temp), udpsum); + temp = udp.len; + udpsum = checksum(&temp, sizeof(temp), udpsum); + + /* Add in the checksum for the udp header */ + udpsum = checksum(&udp, sizeof(udp), udpsum); + + /* Add in the checksum for the data */ + udpsum = checksum(msg, size, udpsum); + udp.check = finish_sum(udpsum); + + iov[0].iov_base = (char *)&ip; + iov[0].iov_len = sizeof(ip); + iov[1].iov_base = (char *)&udp; + iov[1].iov_len = sizeof(udp); + iov[2].iov_base = (char *)msg; + iov[2].iov_len = size; + memset(&destaddr, 0, sizeof(destaddr)); + destaddr.sll_family = AF_PACKET; + destaddr.sll_protocol = htons(ETH_P_IP); + destaddr.sll_ifindex = if_index; + destaddr.sll_halen = ETH_ALEN; + memcpy(destaddr.sll_addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN); + + msghdr.msg_name = &destaddr; + msghdr.msg_namelen = sizeof(destaddr); + msghdr.msg_iov = iov; + msghdr.msg_iovlen = sizeof(iov) / sizeof(struct iovec); + msghdr.msg_flags = 0; + msghdr.msg_control = 0; + msghdr.msg_controllen = 0; + return sendmsg(s, &msghdr, 0); +} + +int receive_packet(int s, struct dhcp_msg *msg) +{ + int nread; + int is_valid; + struct dhcp_packet { + struct iphdr ip; + struct udphdr udp; + struct dhcp_msg dhcp; + } packet; + int dhcp_size; + uint32_t sum; + uint16_t temp; + uint32_t saddr, daddr; + + nread = read(s, &packet, sizeof(packet)); + if (nread < 0) { + return -1; + } + /* + * The raw packet interface gives us all packets received by the + * network interface. We need to filter out all packets that are + * not meant for us. + */ + is_valid = 0; + if (nread < (int)(sizeof(struct iphdr) + sizeof(struct udphdr))) { +#if VERBOSE + ALOGD("Packet is too small (%d) to be a UDP datagram", nread); +#endif + } else if (packet.ip.version != IPVERSION || packet.ip.ihl != (sizeof(packet.ip) >> 2)) { +#if VERBOSE + ALOGD("Not a valid IP packet"); +#endif + } else if (nread < ntohs(packet.ip.tot_len)) { +#if VERBOSE + ALOGD("Packet was truncated (read %d, needed %d)", nread, ntohs(packet.ip.tot_len)); +#endif + } else if (packet.ip.protocol != IPPROTO_UDP) { +#if VERBOSE + ALOGD("IP protocol (%d) is not UDP", packet.ip.protocol); +#endif + } else if (packet.udp.dest != htons(PORT_BOOTP_CLIENT)) { +#if VERBOSE + ALOGD("UDP dest port (%d) is not DHCP client", ntohs(packet.udp.dest)); +#endif + } else { + is_valid = 1; + } + + if (!is_valid) { + return -1; + } + + /* Seems like it's probably a valid DHCP packet */ + /* validate IP header checksum */ + sum = finish_sum(checksum(&packet.ip, sizeof(packet.ip), 0)); + if (sum != 0) { + printf("IP header checksum failure (0x%x)\n", packet.ip.check); + return -1; + } + /* + * Validate the UDP checksum. + * Since we don't need the IP header anymore, we "borrow" it + * to construct the pseudo header used in the checksum calculation. + */ + dhcp_size = ntohs(packet.udp.len) - sizeof(packet.udp); + /* + * check validity of dhcp_size. + * 1) cannot be negative or zero. + * 2) src buffer contains enough bytes to copy + * 3) cannot exceed destination buffer + */ + if ((dhcp_size <= 0) || + ((int)(nread - sizeof(struct iphdr) - sizeof(struct udphdr)) < dhcp_size) || + ((int)sizeof(struct dhcp_msg) < dhcp_size)) { +#if VERBOSE + printf("Malformed Packet\n"); +#endif + return -1; + } + saddr = packet.ip.saddr; + daddr = packet.ip.daddr; + nread = ntohs(packet.ip.tot_len); + memset(&packet.ip, 0, sizeof(packet.ip)); + packet.ip.saddr = saddr; + packet.ip.daddr = daddr; + packet.ip.protocol = IPPROTO_UDP; + packet.ip.tot_len = packet.udp.len; + temp = packet.udp.check; + packet.udp.check = 0; + sum = finish_sum(checksum(&packet, nread, 0)); + packet.udp.check = temp; + if (!sum) + sum = finish_sum(sum); + if (temp != sum) { + printf("UDP header checksum failure (0x%x should be 0x%x)\n", sum, temp); + return -1; + } + memcpy(msg, &packet.dhcp, dhcp_size); + return dhcp_size; +} diff --git a/fibocom-dial/src/libmnl/dhcp/packet.h b/fibocom-dial/src/libmnl/dhcp/packet.h new file mode 100644 index 000000000..aade392df --- /dev/null +++ b/fibocom-dial/src/libmnl/dhcp/packet.h @@ -0,0 +1,25 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _WIFI_PACKET_H_ +#define _WIFI_PACKET_H_ + +int open_raw_socket(const char *ifname, uint8_t *hwaddr, int if_index); +int send_packet(int s, int if_index, struct dhcp_msg *msg, int size, + uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport); +int receive_packet(int s, struct dhcp_msg *msg); + +#endif diff --git a/fibocom-dial/src/libmnl/ifutils.c b/fibocom-dial/src/libmnl/ifutils.c new file mode 100644 index 000000000..51ac9d85f --- /dev/null +++ b/fibocom-dial/src/libmnl/ifutils.c @@ -0,0 +1,748 @@ +/* This example is placed in the public domain. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +//#include + +#include "libmnl.h" +#include "ifutils.h" + +#define ERRMSG(v...) printf("%s-%d: error=%s %s\n", __func__, __LINE__, strerror(errno), ##v) +extern void dbg_time(const char *fmt, ...); +int mask_to_prefix_v4(uint32_t mask) +{ + int ret = 0; + while (mask) + { + mask = mask & (mask - 1); + ret++; + } + return ret; +} + +const char *ipaddr_to_string_v4(in_addr_t ipaddr) +{ + static char buf[INET6_ADDRSTRLEN] = {'\0'}; + buf[0] = '\0'; + uint32_t addr = ipaddr; + return inet_ntop(AF_INET, &addr, buf, sizeof(buf)); +} + +const char *ipaddr_to_string_v6(uint8_t *ipaddr) +{ + static char buf[INET6_ADDRSTRLEN] = {'\0'}; + buf[0] = '\0'; + return inet_ntop(AF_INET6, ipaddr, buf, sizeof(buf)); +} + +static void ifc_init_ifr(const char *name, struct ifreq *ifr) +{ + memset(ifr, 0, sizeof(struct ifreq)); + strncpy(ifr->ifr_name, name, IFNAMSIZ); + ifr->ifr_name[IFNAMSIZ - 1] = 0; +} + +int if_get_hwaddr(const char *name, void *ptr) +{ + int r; + struct ifreq ifr; + ifc_init_ifr(name, &ifr); + + int ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (ifc_ctl_sock < 0) + { + return -1; + } + r = ioctl(ifc_ctl_sock, SIOCGIFHWADDR, &ifr); + if (r < 0) + return -1; + + memcpy(ptr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN); + return 0; +} + +static int if_act_on_link(const char *ifname, int state) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct ifinfomsg *ifm; + int ret; + unsigned int seq, portid, change = 0, flags = 0; + static int oldstate = -1; + + if (state == oldstate) + return 0; + oldstate = state; + + if (state) + { + change |= IFF_UP; + flags |= IFF_UP; + } + else + { + change |= IFF_UP; + flags &= ~IFF_UP; + } + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_NEWLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm)); + ifm->ifi_family = AF_UNSPEC; + ifm->ifi_change = change; + ifm->ifi_flags = flags; + + mnl_attr_put_str(nlh, IFLA_IFNAME, ifname); + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) + { + ERRMSG("mnl_socket_open"); + return -1; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) + { + ERRMSG(" mnl_socket_bind"); + return -1; + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) + { + ERRMSG(" mnl_socket_sendto"); + return -1; + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) + { + ERRMSG(" mnl_socket_recvfrom"); + return -1; + } + + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret == -1) + { + ERRMSG(" mnl_cb_run"); + return -1; + } + + mnl_socket_close(nl); + + return 0; +} + +int if_link_up(const char *ifname) +{ + dbg_time("if_link_up %s",ifname); + return if_act_on_link(ifname, 1); +} + +int if_link_down(const char *ifname) +{ + + dbg_time("if_link_down %s",ifname); + return if_act_on_link(ifname, 0); +} + +int if_set_mtu(const char *ifname, uint32_t mtu) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + unsigned int seq, portid; + struct mnl_socket *nl; + struct nlmsghdr *nlh; + struct ifinfomsg *ifm; + int ret; + int iface; + static uint32_t oldmtu = 1500; + + if (mtu == oldmtu) + return 0; + oldmtu = mtu; + + iface = if_nametoindex(ifname); + if (iface == 0) + { + ERRMSG(" if_nametoindex"); + return -1; + } + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_NEWLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifinfomsg)); + ifm->ifi_family = AF_UNSPEC; + ifm->ifi_index = iface; + ifm->ifi_change = 0xFFFFFFFF; + ifm->ifi_type = 0; + ifm->ifi_flags = IFF_NOARP | IFF_MULTICAST; + + mnl_attr_put_u32(nlh, IFLA_MTU, mtu); + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) + { + ERRMSG(" mnl_socket_open"); + return -1; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) + { + ERRMSG(" mnl_socket_bind"); + return -1; + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) + { + ERRMSG(" mnl_socket_sendto"); + return -1; + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) + { + ERRMSG(" mnl_socket_recvfrom"); + return -1; + } + + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret == -1) + { + ERRMSG(" mnl_cb_run"); + return -1; + } + + mnl_socket_close(nl); + + return 0; +} + +/** + * @brief Set the ip addr object + * + * @param operate + * 0 -> add address on interface + * 1 -> delete address on interface + * @param ifname + * @param ipaddr + * @param prefix + * @return int + */ +static int if_act_on_addr(bool operate, int proto, const char *ifname, addr_t *ipaddr, uint32_t prefix) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct ifaddrmsg *ifm; + uint32_t seq, portid; + int ret, family = proto; + + int iface; + + iface = if_nametoindex(ifname); + if (iface == 0) + { + ERRMSG(" if_nametoindex"); + return -1; + } + + nlh = mnl_nlmsg_put_header(buf); + if (operate) + nlh->nlmsg_type = RTM_NEWADDR; + else + nlh->nlmsg_type = RTM_DELADDR; + + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + + ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifaddrmsg)); + + ifm->ifa_family = family; + ifm->ifa_prefixlen = prefix; + ifm->ifa_flags = IFA_F_PERMANENT; + + ifm->ifa_scope = RT_SCOPE_UNIVERSE; + ifm->ifa_index = iface; + + /* + * The exact meaning of IFA_LOCAL and IFA_ADDRESS depend + * on the address family being used and the device type. + * For broadcast devices (like the interfaces we use), + * for IPv4 we specify both and they are used interchangeably. + * For IPv6, only IFA_ADDRESS needs to be set. + */ + if (family == AF_INET) + { + mnl_attr_put_u32(nlh, IFA_LOCAL, ipaddr->ip); + mnl_attr_put_u32(nlh, IFA_ADDRESS, ipaddr->ip); + } + else + { + mnl_attr_put(nlh, IFA_ADDRESS, sizeof(struct in6_addr), ipaddr); + } + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) + { + ERRMSG(" mnl_socket_open"); + return -1; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) + { + ERRMSG(" mnl_socket_bind"); + return -1; + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) + { + ERRMSG(" mnl_socket_sendto"); + return -1; + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret < 0) + { + ERRMSG(" mnl_socket_recvfrom"); + return -1; + } + + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret < 0) + { + ERRMSG(" mnl_cb_run"); + return -1; + } + + mnl_socket_close(nl); + + return 0; +} + +int if_set_addr_v4(const char *ifname, in_addr_t ipaddr, uint32_t prefix) +{ + addr_t addr; + addr.ip = ipaddr; + return if_act_on_addr(1, AF_INET, ifname, &addr, prefix); +} + +int if_del_addr_v4(const char *ifname, in_addr_t ipaddr, uint32_t prefix) +{ + addr_t addr; + addr.ip = ipaddr; + return if_act_on_addr(0, AF_INET, ifname, &addr, prefix); +} + +int if_set_addr_v6(const char *ifname, uint8_t *ipaddr, uint32_t prefix) +{ + addr_t addr; + memcpy(&addr.ip6, ipaddr, 16); + return if_act_on_addr(1, AF_INET6, ifname, &addr, prefix); +} + +int if_del_addr_v6(const char *ifname, uint8_t *ipaddr, uint32_t prefix) +{ + addr_t addr; + memcpy(&addr.ip6, ipaddr, 16); + return if_act_on_addr(0, AF_INET6, ifname, &addr, prefix); +} + +static int data_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, IFA_MAX) < 0) + return MNL_CB_OK; + + switch (type) + { + case IFA_ADDRESS: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) + { + ERRMSG(" mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[IFA_MAX + 1] = {}; + struct ifaddrmsg *ifa = mnl_nlmsg_get_payload(nlh); + struct addrinfo_t *addrinfo = (struct addrinfo_t *)data; + void *addr = NULL; + + mnl_attr_parse(nlh, sizeof(*ifa), data_attr_cb, tb); + if (tb[IFA_ADDRESS]) + { + char out[INET6_ADDRSTRLEN]; + + addr = mnl_attr_get_payload(tb[IFLA_ADDRESS]); + addr = mnl_attr_get_payload(tb[IFA_ADDRESS]); + if (!inet_ntop(ifa->ifa_family, addr, out, sizeof(out))) + ERRMSG("inet_ntop"); + // printf("%d %d-> %d %s\n", addrinfo->iface, ifa->ifa_index, ifa->ifa_scope, out); + + addrinfo->addrs[addrinfo->num].prefix = ifa->ifa_prefixlen; + if (ifa->ifa_index == addrinfo->iface) + { + if (ifa->ifa_family == AF_INET6) + memcpy(addrinfo->addrs[addrinfo->num].address.ip6.s6_addr, addr, 16); + if (ifa->ifa_family == AF_INET) + memcpy(&(addrinfo->addrs[addrinfo->num].address.ip), addr, 4); + addrinfo->num++; + } + } + + // ifa->ifa_scope + // 0: global + // 200: site + // 253: link + // 254: host + // 255: nowhere + + return MNL_CB_OK; +} + +/** + * @brief + * + * @param ifname + * @param proto + * AF_INET -> for IPv4 + * AF_INET6 -> for IPv6 + * @return int + */ +static int if_get_addr(const char *ifname, int proto, struct addrinfo_t *addrinfo) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + unsigned int seq, portid; + struct mnl_socket *nl; + struct nlmsghdr *nlh; + struct rtgenmsg *rt; + int ret; + + addrinfo->iface = if_nametoindex(ifname); + if (addrinfo->iface == 0) + { + ERRMSG(" if_nametoindex"); + return -1; + } + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_GETADDR; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlh->nlmsg_seq = seq = time(NULL); + rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg)); + if (proto == AF_INET) + rt->rtgen_family = AF_INET; + else if (proto == AF_INET6) + rt->rtgen_family = AF_INET6; + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) + { + ERRMSG(" mnl_socket_open"); + return -1; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) + { + ERRMSG(" mnl_socket_bind"); + return -1; + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) + { + ERRMSG(" mnl_socket_sendto"); + return -1; + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) + { + ret = mnl_cb_run(buf, ret, seq, portid, data_cb, addrinfo); + if (ret <= MNL_CB_STOP) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) + { + ERRMSG(" error"); + return -1; + } + mnl_socket_close(nl); + + return 0; +} + +int if_flush_v4_addr(const char *ifname) +{ + struct addrinfo_t addrinfo; + int i = 0; + + memset(&addrinfo, 0, sizeof(struct addrinfo_t)); + if_get_addr(ifname, AF_INET, &addrinfo); + for (; i < addrinfo.num; i++) + { + // printf("remove address: %s\n", ipaddr_to_string_v4(addrinfo.addrs[i].address.ip)); + if_del_addr_v4(ifname, addrinfo.addrs[i].address.ip, addrinfo.addrs[i].prefix); + } + return 0; +} + +int if_flush_v6_addr(const char *ifname) +{ + struct addrinfo_t addrinfo; + int i = 0; + + memset(&addrinfo, 0, sizeof(struct addrinfo_t)); + if_get_addr(ifname, AF_INET6, &addrinfo); + for (; i < addrinfo.num; i++) + { + // printf("remove address: %s\n", ipaddr_to_string_v6(addrinfo.addrs[i].address.ip6.s6_addr)); + if_del_addr_v6(ifname, addrinfo.addrs[i].address.ip6.s6_addr, addrinfo.addrs[i].prefix); + } + return 0; +} + +/** + * @brief Set the route addr object + * Usage: + * iface destination cidr [gateway] + * Example: + * eth0 10.0.1.12 32 10.0.1.11 + * eth0 ffff::10.0.1.12 128 fdff::1 + * @param operate + * add or del + * @param ifname + * @param dstaddr + * @param prefix + * @param gwaddr + * @return int + */ +int if_act_on_route(bool operate, int proto, const char *ifname, addr_t *dstaddr, uint32_t prefix, addr_t *gwaddr) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct rtmsg *rtm; + uint32_t seq, portid; + int iface, ret, family = proto; + + iface = if_nametoindex(ifname); + if (iface == 0) + { + ERRMSG(" if_nametoindex"); + return -1; + } + + nlh = mnl_nlmsg_put_header(buf); + if (operate) + nlh->nlmsg_type = RTM_NEWROUTE; + else + nlh->nlmsg_type = RTM_DELROUTE; + + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + + rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg)); + rtm->rtm_family = family; + rtm->rtm_dst_len = prefix; + rtm->rtm_src_len = 0; + rtm->rtm_tos = 0; + rtm->rtm_protocol = RTPROT_STATIC; + rtm->rtm_table = RT_TABLE_MAIN; + rtm->rtm_type = RTN_UNICAST; + /* is there any gateway? */ + rtm->rtm_scope = gwaddr ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK; + rtm->rtm_flags = 0; + + if (family == AF_INET) + mnl_attr_put_u32(nlh, RTA_DST, dstaddr->ip); + else + mnl_attr_put(nlh, RTA_DST, sizeof(struct in6_addr), dstaddr); + + mnl_attr_put_u32(nlh, RTA_OIF, iface); + if (gwaddr) + { + if (family == AF_INET) + mnl_attr_put_u32(nlh, RTA_GATEWAY, gwaddr->ip); + else + { + mnl_attr_put(nlh, RTA_GATEWAY, sizeof(struct in6_addr), gwaddr); + } + } + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) + { + ERRMSG(" mnl_socket_open"); + return -1; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) + { + ERRMSG(" mnl_socket_bind"); + return -1; + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) + { + ERRMSG(" mnl_socket_sendto"); + return -1; + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret < 0) + { + ERRMSG(" mnl_socket_recvfrom"); + return -1; + } + + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret < 0) + { + ERRMSG(" mnl_cb_run"); + return -1; + } + + mnl_socket_close(nl); + + return 0; +} + +int if_set_default_route_v4(const char *ifname) +{ + return if_act_on_route(1, AF_INET, ifname, (addr_t *)&in6addr_any, 0, NULL); +} + +int if_del_default_route_v4(const char *ifname) +{ + return if_act_on_route(0, AF_INET, ifname, (addr_t *)&in6addr_any, 0, NULL); +} + +int if_set_default_route_v6(const char *ifname) +{ + return if_act_on_route(1, AF_INET6, ifname, (addr_t *)&in6addr_any, 0, NULL); +} + +int if_del_default_route_v6(const char *ifname) +{ + return if_act_on_route(0, AF_INET6, ifname, (addr_t *)&in6addr_any, 0, NULL); +} + +/** + * @brief Set the default gwaddr object + * set default gw + * @param operate + * @param ifname + * @param gwaddr + * gateway ip + * @return int + */ +int if_set_route_gw_v4(const char *ifname, in_addr_t gwaddr) +{ + addr_t addr; + memset(&addr, 0, sizeof(addr_t)); + addr.ip = gwaddr; + return if_act_on_route(1, AF_INET, ifname, (addr_t *)&in6addr_any, 0, &addr); +} + +int if_del_route_gw_v4(const char *ifname, in_addr_t gwaddr) +{ + addr_t addr; + memset(&addr, 0, sizeof(addr_t)); + addr.ip = gwaddr; + return if_act_on_route(0, AF_INET, ifname, (addr_t *)&in6addr_any, 0, &addr); +} + +int if_set_route_gw_v6(const char *ifname, uint8_t *gwaddr) +{ + addr_t addr; + memset(&addr, 0, sizeof(addr_t)); + memcpy(&addr.ip6, gwaddr, 16); + return if_act_on_route(1, AF_INET6, ifname, (addr_t *)&in6addr_any, 0, &addr); +} + +int if_del_route_gw_v6(const char *ifname, uint8_t *gwaddr) +{ + addr_t addr; + memset(&addr, 0, sizeof(addr_t)); + memcpy(&addr.ip6, gwaddr, 16); + return if_act_on_route(0, AF_INET6, ifname, (addr_t *)&in6addr_any, 0, &addr); +} + +int if_set_dns(const char *dns1, const char *dns2) +{ + int ret = 0; + char buf[128] = {'\0'}; + int fd = open("/etc/resolv.conf", O_CREAT | O_WRONLY | O_TRUNC); + if (fd < 0) + { + ERRMSG(" fail to open /etc/resolv.conf"); + return -1; + } + + if (dns1) + snprintf(buf, sizeof(buf), "nameserver %s\n", dns1); + if (dns2) + snprintf(buf, sizeof(buf), "nameserver %s\n", dns2); + ret = write(fd, buf, strlen(buf)); + if (ret < 0) + { + ERRMSG(" write dns"); + } + close(fd); + return ret > 0 ? 0 : -1; +} + +int if_set_network_v4(const char *ifname, in_addr_t ipaddr, uint32_t prefix, + in_addr_t gwaddr, in_addr_t dns1, in_addr_t dns2) +{ + if_link_up(ifname); + sleep(2); + if_set_addr_v4(ifname, ipaddr, prefix); + if_set_default_route_v4(ifname); + if_set_dns(ipaddr_to_string_v4(dns1), ipaddr_to_string_v4(dns2)); + return 0; +} + +int if_set_network_v6(const char *ifname, uint8_t *ipaddr, uint32_t prefix, + uint8_t *gwaddr, uint8_t *dns1, uint8_t *dns2) +{ + if_link_up(ifname); + sleep(2); + if_set_addr_v6(ifname, ipaddr, prefix); + if_set_default_route_v6(ifname); + if_set_dns(ipaddr_to_string_v6(dns1), ipaddr_to_string_v6(dns2)); + return 0; +} diff --git a/fibocom-dial/src/libmnl/ifutils.h b/fibocom-dial/src/libmnl/ifutils.h new file mode 100644 index 000000000..4c2b5650d --- /dev/null +++ b/fibocom-dial/src/libmnl/ifutils.h @@ -0,0 +1,53 @@ +#ifndef __IFUTILS_H__ +#define __IFUTILS_H__ + +typedef union { + in_addr_t ip; + struct in6_addr ip6; +} addr_t; + +#define MAX_IP_NUM 32 +struct addrinfo_t +{ + int iface; + int num; + struct + { + int prefix; + addr_t address; + } addrs[MAX_IP_NUM]; +}; + +const char *ipaddr_to_string_v4(in_addr_t ipaddr); +const char *ipaddr_to_string_v6(uint8_t *ipaddr); +int mask_to_prefix_v4(in_addr_t mask); + +int if_get_hwaddr(const char *name, void *ptr); + +int if_link_down(const char *ifname); +int if_link_up(const char *ifname); +int if_set_mtu(const char *ifname, uint32_t mtu); + +int if_set_addr_v4(const char *name, in_addr_t address, uint32_t prefixlen); +int if_del_addr_v4(const char *name, in_addr_t address, uint32_t prefixlen); +int if_set_addr_v6(const char *name, uint8_t *address, uint32_t prefixlen); +int if_del_addr_v6(const char *name, uint8_t *address, uint32_t prefixlen); +int if_flush_v4_addr(const char *ifname); +int if_flush_v6_addr(const char *ifname); + +int if_set_route_gw_v4(const char *ifname, in_addr_t gwaddr); +int if_del_route_gw_v4(const char *ifname, in_addr_t gwaddr); +int if_set_default_route_v4(const char *ifname); +int if_del_default_route_v4(const char *ifname); + +int if_set_route_gw_v6(const char *ifname, uint8_t *gwaddr); +int if_del_route_gw_v6(const char *ifname, uint8_t *gwaddr); +int if_set_default_route_v6(const char *ifname); +int if_del_default_route_v6(const char *ifname); + +int if_set_network_v4(const char *ifname, in_addr_t ipaddr, uint32_t prefix, + in_addr_t gwaddr, in_addr_t dns1, in_addr_t dns2); +int if_set_network_v6(const char *ifname, uint8_t *ipaddr, uint32_t prefix, + uint8_t *gwaddr, uint8_t *dns1, uint8_t *dns2); + +#endif //__IFUTILS_H__ \ No newline at end of file diff --git a/fibocom-dial/src/libmnl/libmnl.h b/fibocom-dial/src/libmnl/libmnl.h new file mode 100644 index 000000000..4bd0b92e8 --- /dev/null +++ b/fibocom-dial/src/libmnl/libmnl.h @@ -0,0 +1,202 @@ +#ifndef _LIBMNL_H_ +#define _LIBMNL_H_ + +#include +#include +#include +#include +#include /* for sa_family_t */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Netlink socket API + */ + +#define MNL_SOCKET_AUTOPID 0 +#define MNL_SOCKET_BUFFER_SIZE (sysconf(_SC_PAGESIZE) < 8192L ? sysconf(_SC_PAGESIZE) : 8192L) +#define MNL_SOCKET_DUMP_SIZE 32768 + +struct mnl_socket; + +extern struct mnl_socket *mnl_socket_open(int bus); +extern struct mnl_socket *mnl_socket_open2(int bus, int flags); +extern struct mnl_socket *mnl_socket_fdopen(int fd); +extern int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid); +extern int mnl_socket_close(struct mnl_socket *nl); +extern int mnl_socket_get_fd(const struct mnl_socket *nl); +extern unsigned int mnl_socket_get_portid(const struct mnl_socket *nl); +extern ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *req, size_t siz); +extern ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf, size_t siz); +extern int mnl_socket_setsockopt(const struct mnl_socket *nl, int type, void *buf, socklen_t len); +extern int mnl_socket_getsockopt(const struct mnl_socket *nl, int type, void *buf, socklen_t *len); + +/* + * Netlink message API + */ + +#define MNL_ALIGNTO 4 +#define MNL_ALIGN(len) (((len)+MNL_ALIGNTO-1) & ~(MNL_ALIGNTO-1)) +#define MNL_NLMSG_HDRLEN MNL_ALIGN(sizeof(struct nlmsghdr)) + +extern size_t mnl_nlmsg_size(size_t len); +extern size_t mnl_nlmsg_get_payload_len(const struct nlmsghdr *nlh); + +/* Netlink message header builder */ +extern struct nlmsghdr *mnl_nlmsg_put_header(void *buf); +extern void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh, size_t size); + +/* Netlink message iterators */ +extern bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len); +extern struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh, int *len); + +/* Netlink sequence tracking */ +extern bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh, unsigned int seq); + +/* Netlink portID checking */ +extern bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh, unsigned int portid); + +/* Netlink message getters */ +extern void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh); +extern void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh, size_t offset); +extern void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh); + +/* Netlink message printer */ +extern void mnl_nlmsg_fprintf(FILE *fd, const void *data, size_t datalen, size_t extra_header_size); + +/* Message batch helpers */ +struct mnl_nlmsg_batch; +extern struct mnl_nlmsg_batch *mnl_nlmsg_batch_start(void *buf, size_t bufsiz); +extern bool mnl_nlmsg_batch_next(struct mnl_nlmsg_batch *b); +extern void mnl_nlmsg_batch_stop(struct mnl_nlmsg_batch *b); +extern size_t mnl_nlmsg_batch_size(struct mnl_nlmsg_batch *b); +extern void mnl_nlmsg_batch_reset(struct mnl_nlmsg_batch *b); +extern void *mnl_nlmsg_batch_head(struct mnl_nlmsg_batch *b); +extern void *mnl_nlmsg_batch_current(struct mnl_nlmsg_batch *b); +extern bool mnl_nlmsg_batch_is_empty(struct mnl_nlmsg_batch *b); + +/* + * Netlink attributes API + */ +#define MNL_ATTR_HDRLEN MNL_ALIGN(sizeof(struct nlattr)) + +/* TLV attribute getters */ +extern uint16_t mnl_attr_get_type(const struct nlattr *attr); +extern uint16_t mnl_attr_get_len(const struct nlattr *attr); +extern uint16_t mnl_attr_get_payload_len(const struct nlattr *attr); +extern void *mnl_attr_get_payload(const struct nlattr *attr); +extern uint8_t mnl_attr_get_u8(const struct nlattr *attr); +extern uint16_t mnl_attr_get_u16(const struct nlattr *attr); +extern uint32_t mnl_attr_get_u32(const struct nlattr *attr); +extern uint64_t mnl_attr_get_u64(const struct nlattr *attr); +extern const char *mnl_attr_get_str(const struct nlattr *attr); + +/* TLV attribute putters */ +extern void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type, size_t len, const void *data); +extern void mnl_attr_put_u8(struct nlmsghdr *nlh, uint16_t type, uint8_t data); +extern void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type, uint16_t data); +extern void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type, uint32_t data); +extern void mnl_attr_put_u64(struct nlmsghdr *nlh, uint16_t type, uint64_t data); +extern void mnl_attr_put_str(struct nlmsghdr *nlh, uint16_t type, const char *data); +extern void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type, const char *data); + +/* TLV attribute putters with buffer boundary checkings */ +extern bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, size_t len, const void *data); +extern bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint8_t data); +extern bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint16_t data); +extern bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint32_t data); +extern bool mnl_attr_put_u64_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint64_t data); +extern bool mnl_attr_put_str_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, const char *data); +extern bool mnl_attr_put_strz_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, const char *data); + +/* TLV attribute nesting */ +extern struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh, uint16_t type); +extern struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type); +extern void mnl_attr_nest_end(struct nlmsghdr *nlh, struct nlattr *start); +extern void mnl_attr_nest_cancel(struct nlmsghdr *nlh, struct nlattr *start); + +/* TLV validation */ +extern int mnl_attr_type_valid(const struct nlattr *attr, uint16_t maxtype); + +enum mnl_attr_data_type { + MNL_TYPE_UNSPEC, + MNL_TYPE_U8, + MNL_TYPE_U16, + MNL_TYPE_U32, + MNL_TYPE_U64, + MNL_TYPE_STRING, + MNL_TYPE_FLAG, + MNL_TYPE_MSECS, + MNL_TYPE_NESTED, + MNL_TYPE_NESTED_COMPAT, + MNL_TYPE_NUL_STRING, + MNL_TYPE_BINARY, + MNL_TYPE_MAX, +}; + +extern int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type); +extern int mnl_attr_validate2(const struct nlattr *attr, enum mnl_attr_data_type type, size_t len); + +/* TLV iterators */ +extern bool mnl_attr_ok(const struct nlattr *attr, int len); +extern struct nlattr *mnl_attr_next(const struct nlattr *attr); + +#define mnl_attr_for_each(attr, nlh, offset) \ + for ((attr) = mnl_nlmsg_get_payload_offset((nlh), (offset)); \ + mnl_attr_ok((attr), (char *)mnl_nlmsg_get_payload_tail(nlh) - (char *)(attr)); \ + (attr) = mnl_attr_next(attr)) + +#define mnl_attr_for_each_nested(attr, nest) \ + for ((attr) = mnl_attr_get_payload(nest); \ + mnl_attr_ok((attr), (char *)mnl_attr_get_payload(nest) + mnl_attr_get_payload_len(nest) - (char *)(attr)); \ + (attr) = mnl_attr_next(attr)) + +#define mnl_attr_for_each_payload(payload, payload_size) \ + for ((attr) = (payload); \ + mnl_attr_ok((attr), (char *)(payload) + payload_size - (char *)(attr)); \ + (attr) = mnl_attr_next(attr)) + +/* TLV callback-based attribute parsers */ +typedef int (*mnl_attr_cb_t)(const struct nlattr *attr, void *data); + +extern int mnl_attr_parse(const struct nlmsghdr *nlh, unsigned int offset, mnl_attr_cb_t cb, void *data); +extern int mnl_attr_parse_nested(const struct nlattr *attr, mnl_attr_cb_t cb, void *data); +extern int mnl_attr_parse_payload(const void *payload, size_t payload_len, mnl_attr_cb_t cb, void *data); + +/* + * callback API + */ +#define MNL_CB_ERROR -1 +#define MNL_CB_STOP 0 +#define MNL_CB_OK 1 + +typedef int (*mnl_cb_t)(const struct nlmsghdr *nlh, void *data); + +extern int mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq, + unsigned int portid, mnl_cb_t cb_data, void *data); + +extern int mnl_cb_run2(const void *buf, size_t numbytes, unsigned int seq, + unsigned int portid, mnl_cb_t cb_data, void *data, + const mnl_cb_t *cb_ctl_array, + unsigned int cb_ctl_array_len); + +/* + * other declarations + */ + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + +#ifndef MNL_ARRAY_SIZE +#define MNL_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/fibocom-dial/src/libmnl/nlmsg.c b/fibocom-dial/src/libmnl/nlmsg.c new file mode 100644 index 000000000..d960cf338 --- /dev/null +++ b/fibocom-dial/src/libmnl/nlmsg.c @@ -0,0 +1,556 @@ +/* + * (C) 2008-2010 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include + +#include "libmnl.h" + +/** + * \defgroup nlmsg Netlink message helpers + * + * Netlink message: + * \verbatim + |<----------------- 4 bytes ------------------->| + |<----- 2 bytes ------>|<------- 2 bytes ------>| + |-----------------------------------------------| + | Message length (including header) | + |-----------------------------------------------| + | Message type | Message flags | + |-----------------------------------------------| + | Message sequence number | + |-----------------------------------------------| + | Netlink PortID | + |-----------------------------------------------| + | | + . Payload . + |_______________________________________________| +\endverbatim + * + * There is usually an extra header after the the Netlink header (at the + * beginning of the payload). This extra header is specific of the Netlink + * subsystem. After this extra header, it comes the sequence of attributes + * that are expressed in Type-Length-Value (TLV) format. + * + * @{ + */ + +/** + * mnl_nlmsg_size - calculate the size of Netlink message (without alignment) + * \param len length of the Netlink payload + * + * This function returns the size of a netlink message (header plus payload) + * without alignment. + */ +size_t mnl_nlmsg_size(size_t len) +{ + return len + MNL_NLMSG_HDRLEN; +} + +/** + * mnl_nlmsg_get_payload_len - get the length of the Netlink payload + * \param nlh pointer to the header of the Netlink message + * + * This function returns the Length of the netlink payload, ie. the length + * of the full message minus the size of the Netlink header. + */ +size_t mnl_nlmsg_get_payload_len(const struct nlmsghdr *nlh) +{ + return nlh->nlmsg_len - MNL_NLMSG_HDRLEN; +} + +/** + * mnl_nlmsg_put_header - reserve and prepare room for Netlink header + * \param buf memory already allocated to store the Netlink header + * + * This function sets to zero the room that is required to put the Netlink + * header in the memory buffer passed as parameter. This function also + * initializes the nlmsg_len field to the size of the Netlink header. This + * function returns a pointer to the Netlink header structure. + */ +struct nlmsghdr *mnl_nlmsg_put_header(void *buf) +{ + int len = MNL_ALIGN(sizeof(struct nlmsghdr)); + struct nlmsghdr *nlh = buf; + + memset(buf, 0, len); + nlh->nlmsg_len = len; + return nlh; +} + +/** + * mnl_nlmsg_put_extra_header - reserve and prepare room for an extra header + * \param nlh pointer to Netlink header + * \param size size of the extra header that we want to put + * + * This function sets to zero the room that is required to put the extra + * header after the initial Netlink header. This function also increases + * the nlmsg_len field. You have to invoke mnl_nlmsg_put_header() before + * you call this function. This function returns a pointer to the extra + * header. + */ +void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh, + size_t size) +{ + char *ptr = (char *)nlh + nlh->nlmsg_len; + size_t len = MNL_ALIGN(size); + nlh->nlmsg_len += len; + memset(ptr, 0, len); + return ptr; +} + +/** + * mnl_nlmsg_get_payload - get a pointer to the payload of the netlink message + * \param nlh pointer to a netlink header + * + * This function returns a pointer to the payload of the netlink message. + */ +void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh) +{ + return (void *)nlh + MNL_NLMSG_HDRLEN; +} + +/** + * mnl_nlmsg_get_payload_offset - get a pointer to the payload of the message + * \param nlh pointer to a netlink header + * \param offset offset to the payload of the attributes TLV set + * + * This function returns a pointer to the payload of the netlink message plus + * a given offset. + */ +void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh, + size_t offset) +{ + return (void *)nlh + MNL_NLMSG_HDRLEN + MNL_ALIGN(offset); +} + +/** + * mnl_nlmsg_ok - check a there is room for netlink message + * \param nlh netlink message that we want to check + * \param len remaining bytes in a buffer that contains the netlink message + * + * This function is used to check that a buffer that contains a netlink + * message has enough room for the netlink message that it stores, ie. this + * function can be used to verify that a netlink message is not malformed nor + * truncated. + * + * This function does not set errno in case of error since it is intended + * for iterations. Thus, it returns true on success and false on error. + * + * The len parameter may become negative in malformed messages during message + * iteration, that is why we use a signed integer. + */ +bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len) +{ + return len >= (int)sizeof(struct nlmsghdr) && + nlh->nlmsg_len >= sizeof(struct nlmsghdr) && + (int)nlh->nlmsg_len <= len; +} + +/** + * mnl_nlmsg_next - get the next netlink message in a multipart message + * \param nlh current netlink message that we are handling + * \param len length of the remaining bytes in the buffer (passed by reference). + * + * This function returns a pointer to the next netlink message that is part + * of a multi-part netlink message. Netlink can batch several messages into + * one buffer so that the receiver has to iterate over the whole set of + * Netlink messages. + * + * You have to use mnl_nlmsg_ok() to check if the next Netlink message is + * valid. + */ +struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh, + int *len) +{ + *len -= MNL_ALIGN(nlh->nlmsg_len); + return (struct nlmsghdr *)((void *)nlh + MNL_ALIGN(nlh->nlmsg_len)); +} + +/** + * mnl_nlmsg_get_payload_tail - get the ending of the netlink message + * \param nlh pointer to netlink message + * + * This function returns a pointer to the netlink message tail. This is useful + * to build a message since we continue adding attributes at the end of the + * message. + */ +void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh) +{ + return (void *)nlh + MNL_ALIGN(nlh->nlmsg_len); +} + +/** + * mnl_nlmsg_seq_ok - perform sequence tracking + * \param nlh current netlink message that we are handling + * \param seq last sequence number used to send a message + * + * This functions returns true if the sequence tracking is fulfilled, otherwise + * false is returned. We skip the tracking for netlink messages whose sequence + * number is zero since it is usually reserved for event-based kernel + * notifications. On the other hand, if seq is set but the message sequence + * number is not set (i.e. this is an event message coming from kernel-space), + * then we also skip the tracking. This approach is good if we use the same + * socket to send commands to kernel-space (that we want to track) and to + * listen to events (that we do not track). + */ +bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh, + unsigned int seq) +{ + return nlh->nlmsg_seq && seq ? nlh->nlmsg_seq == seq : true; +} + +/** + * mnl_nlmsg_portid_ok - perform portID origin check + * \param nlh current netlink message that we are handling + * \param portid netlink portid that we want to check + * + * This functions returns true if the origin is fulfilled, otherwise + * false is returned. We skip the tracking for netlink message whose portID + * is zero since it is reserved for event-based kernel notifications. On the + * other hand, if portid is set but the message PortID is not (i.e. this + * is an event message coming from kernel-space), then we also skip the + * tracking. This approach is good if we use the same socket to send commands + * to kernel-space (that we want to track) and to listen to events (that we + * do not track). + */ +bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh, + unsigned int portid) +{ + return nlh->nlmsg_pid && portid ? nlh->nlmsg_pid == portid : true; +} + +static void mnl_nlmsg_fprintf_header(FILE *fd, const struct nlmsghdr *nlh) +{ + fprintf(fd, "----------------\t------------------\n"); + fprintf(fd, "| %.010u |\t| message length |\n", nlh->nlmsg_len); + fprintf(fd, "| %.05u | %c%c%c%c |\t| type | flags |\n", + nlh->nlmsg_type, + nlh->nlmsg_flags & NLM_F_REQUEST ? 'R' : '-', + nlh->nlmsg_flags & NLM_F_MULTI ? 'M' : '-', + nlh->nlmsg_flags & NLM_F_ACK ? 'A' : '-', + nlh->nlmsg_flags & NLM_F_ECHO ? 'E' : '-'); + fprintf(fd, "| %.010u |\t| sequence number|\n", nlh->nlmsg_seq); + fprintf(fd, "| %.010u |\t| port ID |\n", nlh->nlmsg_pid); + fprintf(fd, "----------------\t------------------\n"); +} + +static void mnl_nlmsg_fprintf_payload(FILE *fd, const struct nlmsghdr *nlh, + size_t extra_header_size) +{ + int rem = 0; + unsigned int i; + + for (i=sizeof(struct nlmsghdr); inlmsg_len; i+=4) { + char *b = (char *) nlh; + struct nlattr *attr = (struct nlattr *) (b+i); + + /* netlink control message. */ + if (nlh->nlmsg_type < NLMSG_MIN_TYPE) { + fprintf(fd, "| %.2x %.2x %.2x %.2x |\t", + 0xff & b[i], 0xff & b[i+1], + 0xff & b[i+2], 0xff & b[i+3]); + fprintf(fd, "| |\n"); + /* special handling for the extra header. */ + } else if (extra_header_size > 0) { + extra_header_size -= 4; + fprintf(fd, "| %.2x %.2x %.2x %.2x |\t", + 0xff & b[i], 0xff & b[i+1], + 0xff & b[i+2], 0xff & b[i+3]); + fprintf(fd, "| extra header |\n"); + /* this seems like an attribute header. */ + } else if (rem == 0 && (attr->nla_type & NLA_TYPE_MASK) != 0) { + fprintf(fd, "|%c[%d;%dm" + "%.5u" + "%c[%dm" + "|" + "%c[%d;%dm" + "%c%c" + "%c[%dm" + "|" + "%c[%d;%dm" + "%.5u" + "%c[%dm|\t", + 27, 1, 31, + attr->nla_len, + 27, 0, + 27, 1, 32, + attr->nla_type & NLA_F_NESTED ? 'N' : '-', + attr->nla_type & + NLA_F_NET_BYTEORDER ? 'B' : '-', + 27, 0, + 27, 1, 34, + attr->nla_type & NLA_TYPE_MASK, + 27, 0); + fprintf(fd, "|len |flags| type|\n"); + + if (!(attr->nla_type & NLA_F_NESTED)) { + rem = NLA_ALIGN(attr->nla_len) - + sizeof(struct nlattr); + } + /* this is the attribute payload. */ + } else if (rem > 0) { + rem -= 4; + fprintf(fd, "| %.2x %.2x %.2x %.2x |\t", + 0xff & b[i], 0xff & b[i+1], + 0xff & b[i+2], 0xff & b[i+3]); + fprintf(fd, "| data |"); + fprintf(fd, "\t %c %c %c %c\n", + isprint(b[i]) ? b[i] : ' ', + isprint(b[i+1]) ? b[i+1] : ' ', + isprint(b[i+2]) ? b[i+2] : ' ', + isprint(b[i+3]) ? b[i+3] : ' '); + } + } + fprintf(fd, "----------------\t------------------\n"); +} + +/** + * mnl_nlmsg_fprintf - print netlink message to file + * \param fd pointer to file type + * \param data pointer to the buffer that contains messages to be printed + * \param datalen length of data stored in the buffer + * \param extra_header_size size of the extra header (if any) + * + * This function prints the netlink header to a file handle. + * It may be useful for debugging purposes. One example of the output + * is the following: + * + *\verbatim +---------------- ------------------ +| 0000000040 | | message length | +| 00016 | R-A- | | type | flags | +| 1289148991 | | sequence number| +| 0000000000 | | port ID | +---------------- ------------------ +| 00 00 00 00 | | extra header | +| 00 00 00 00 | | extra header | +| 01 00 00 00 | | extra header | +| 01 00 00 00 | | extra header | +|00008|--|00003| |len |flags| type| +| 65 74 68 30 | | data | e t h 0 +---------------- ------------------ +\endverbatim + * + * This example above shows the netlink message that is send to kernel-space + * to set up the link interface eth0. The netlink and attribute header data + * are displayed in base 10 whereas the extra header and the attribute payload + * are expressed in base 16. The possible flags in the netlink header are: + * + * - R, that indicates that NLM_F_REQUEST is set. + * - M, that indicates that NLM_F_MULTI is set. + * - A, that indicates that NLM_F_ACK is set. + * - E, that indicates that NLM_F_ECHO is set. + * + * The lack of one flag is displayed with '-'. On the other hand, the possible + * attribute flags available are: + * + * - N, that indicates that NLA_F_NESTED is set. + * - B, that indicates that NLA_F_NET_BYTEORDER is set. + */ +void mnl_nlmsg_fprintf(FILE *fd, const void *data, size_t datalen, + size_t extra_header_size) +{ + const struct nlmsghdr *nlh = data; + int len = datalen; + + while (mnl_nlmsg_ok(nlh, len)) { + mnl_nlmsg_fprintf_header(fd, nlh); + mnl_nlmsg_fprintf_payload(fd, nlh, extra_header_size); + nlh = mnl_nlmsg_next(nlh, &len); + } +} + +/** + * @} + */ + +/** + * \defgroup batch Netlink message batch helpers + * + * This library provides helpers to batch several messages into one single + * datagram. These helpers do not perform strict memory boundary checkings. + * + * The following figure represents a Netlink message batch: + * + * |<-------------- MNL_SOCKET_BUFFER_SIZE ------------->| + * |<-------------------- batch ------------------>| | + * |-----------|-----------|-----------|-----------|-----------| + * |<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|<- nlmsg ->| + * |-----------|-----------|-----------|-----------|-----------| + * ^ ^ + * | | + * message N message N+1 + * + * To start the batch, you have to call mnl_nlmsg_batch_start() and you can + * use mnl_nlmsg_batch_stop() to release it. + * + * You have to invoke mnl_nlmsg_batch_next() to get room for a new message + * in the batch. If this function returns NULL, it means that the last + * message that was added (message N+1 in the figure above) does not fit the + * batch. Thus, you have to send the batch (which includes until message N) + * and, then, you have to call mnl_nlmsg_batch_reset() to re-initialize + * the batch (this moves message N+1 to the head of the buffer). For that + * reason, the buffer that you have to use to store the batch must be double + * of MNL_SOCKET_BUFFER_SIZE to ensure that the last message (message N+1) + * that did not fit into the batch is written inside valid memory boundaries. + * + * @{ + */ + +struct mnl_nlmsg_batch { + /* the buffer that is used to store the batch. */ + void *buf; + size_t limit; + size_t buflen; + /* the current netlink message in the batch. */ + void *cur; + bool overflow; +}; + +/** + * mnl_nlmsg_batch_start - initialize a batch + * \param buf pointer to the buffer that will store this batch + * \param limit maximum size of the batch (should be MNL_SOCKET_BUFFER_SIZE). + * + * The buffer that you pass must be double of MNL_SOCKET_BUFFER_SIZE. The + * limit must be half of the buffer size, otherwise expect funny memory + * corruptions 8-). + * + * You can allocate the buffer that you use to store the batch in the stack or + * the heap, no restrictions in this regard. This function returns NULL on + * error. + */ +struct mnl_nlmsg_batch *mnl_nlmsg_batch_start(void *buf, + size_t limit) +{ + struct mnl_nlmsg_batch *b; + + b = malloc(sizeof(struct mnl_nlmsg_batch)); + if (b == NULL) + return NULL; + + b->buf = buf; + b->limit = limit; + b->buflen = 0; + b->cur = buf; + b->overflow = false; + + return b; +} + +/** + * mnl_nlmsg_batch_stop - release a batch + * \param b pointer to batch + * + * This function releases the batch allocated by mnl_nlmsg_batch_start(). + */ +void mnl_nlmsg_batch_stop(struct mnl_nlmsg_batch *b) +{ + free(b); +} + +/** + * mnl_nlmsg_batch_next - get room for the next message in the batch + * \param b pointer to batch + * + * This function returns false if the last message did not fit into the + * batch. Otherwise, it prepares the batch to provide room for the new + * Netlink message in the batch and returns true. + * + * You have to put at least one message in the batch before calling this + * function, otherwise your application is likely to crash. + */ +bool mnl_nlmsg_batch_next(struct mnl_nlmsg_batch *b) +{ + struct nlmsghdr *nlh = b->cur; + + if (b->buflen + nlh->nlmsg_len > b->limit) { + b->overflow = true; + return false; + } + b->cur = b->buf + b->buflen + nlh->nlmsg_len; + b->buflen += nlh->nlmsg_len; + return true; +} + +/** + * mnl_nlmsg_batch_reset - reset the batch + * \param b pointer to batch + * + * This function allows to reset a batch, so you can reuse it to create a + * new one. This function moves the last message which does not fit the + * batch to the head of the buffer, if any. + */ +void mnl_nlmsg_batch_reset(struct mnl_nlmsg_batch *b) +{ + if (b->overflow) { + struct nlmsghdr *nlh = b->cur; + memcpy(b->buf, b->cur, nlh->nlmsg_len); + b->buflen = nlh->nlmsg_len; + b->cur = b->buf + b->buflen; + b->overflow = false; + } else { + b->buflen = 0; + b->cur = b->buf; + } +} + +/** + * mnl_nlmsg_batch_size - get current size of the batch + * \param b pointer to batch + * + * This function returns the current size of the batch. + */ +size_t mnl_nlmsg_batch_size(struct mnl_nlmsg_batch *b) +{ + return b->buflen; +} + +/** + * mnl_nlmsg_batch_head - get head of this batch + * \param b pointer to batch + * + * This function returns a pointer to the head of the batch, which is the + * beginning of the buffer that is used. + */ +void *mnl_nlmsg_batch_head(struct mnl_nlmsg_batch *b) +{ + return b->buf; +} + +/** + * mnl_nlmsg_batch_current - returns current position in the batch + * \param b pointer to batch + * + * This function returns a pointer to the current position in the buffer + * that is used to store the batch. + */ +void *mnl_nlmsg_batch_current(struct mnl_nlmsg_batch *b) +{ + return b->cur; +} + +/** + * mnl_nlmsg_batch_is_empty - check if there is any message in the batch + * \param b pointer to batch + * + * This function returns true if the batch is empty. + */ +bool mnl_nlmsg_batch_is_empty(struct mnl_nlmsg_batch *b) +{ + return b->buflen == 0; +} + +/** + * @} + */ diff --git a/fibocom-dial/src/libmnl/socket.c b/fibocom-dial/src/libmnl/socket.c new file mode 100644 index 000000000..dd5ab664c --- /dev/null +++ b/fibocom-dial/src/libmnl/socket.c @@ -0,0 +1,351 @@ +/* + * (C) 2008-2010 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include "libmnl.h" + +/** + * \mainpage + * + * libmnl is a minimalistic user-space library oriented to Netlink developers. + * There are a lot of common tasks in parsing, validating, constructing of + * both the Netlink header and TLVs that are repetitive and easy to get wrong. + * This library aims to provide simple helpers that allows you to avoid + * re-inventing the wheel in common Netlink tasks. + * + * \verbatim +"Simplify, simplify" -- Henry David Thoureau. Walden (1854) +\endverbatim + * + * The acronym libmnl stands for LIBrary Minimalistic NetLink. + * + * libmnl homepage is: + * http://www.netfilter.org/projects/libmnl/ + * + * \section features Main Features + * - Small: the shared library requires around 30KB for an x86-based computer. + * - Simple: this library avoids complex abstractions that tend to hide Netlink + * details. It avoids elaborated object-oriented infrastructure and complex + * callback-based workflow. + * - Easy to use: the library simplifies the work for Netlink-wise developers. + * It provides functions to make socket handling, message building, + * validating, parsing and sequence tracking, easier. + * - Easy to re-use: you can use this library to build your own abstraction + * layer upon this library, if you want to provide another library that + * hides Netlink details to your users. + * - Decoupling: the interdependency of the main bricks that compose this + * library is reduced, i.e. the library provides many helpers, but the + * programmer is not forced to use them. + * + * \section licensing Licensing terms + * This library is released under the LGPLv2.1 or any later (at your option). + * + * \section Dependencies + * You have to install the Linux kernel headers that you want to use to develop + * your application. Moreover, this library requires that you have some basics + * on Netlink. + * + * \section scm Git Tree + * The current development version of libmnl can be accessed at: + * http://git.netfilter.org/cgi-bin/gitweb.cgi?p=libmnl.git;a=summary + * + * \section using Using libmnl + * You can access several example files under examples/ in the libmnl source + * code tree. + */ + +struct mnl_socket { + int fd; + struct sockaddr_nl addr; +}; + +/** + * \defgroup socket Netlink socket helpers + * @{ + */ + +/** + * mnl_socket_get_fd - obtain file descriptor from netlink socket + * \param nl netlink socket obtained via mnl_socket_open() + * + * This function returns the file descriptor of a given netlink socket. + */ +int mnl_socket_get_fd(const struct mnl_socket *nl) +{ + return nl->fd; +} + +/** + * mnl_socket_get_portid - obtain Netlink PortID from netlink socket + * \param nl netlink socket obtained via mnl_socket_open() + * + * This function returns the Netlink PortID of a given netlink socket. + * It's a common mistake to assume that this PortID equals the process ID + * which is not always true. This is the case if you open more than one + * socket that is binded to the same Netlink subsystem from the same process. + */ +unsigned int mnl_socket_get_portid(const struct mnl_socket *nl) +{ + return nl->addr.nl_pid; +} + +static struct mnl_socket *__mnl_socket_open(int bus, int flags) +{ + struct mnl_socket *nl; + + nl = calloc(1, sizeof(struct mnl_socket)); + if (nl == NULL) + return NULL; + + nl->fd = socket(AF_NETLINK, SOCK_RAW | flags, bus); + if (nl->fd == -1) { + free(nl); + return NULL; + } + + return nl; +} + +/** + * mnl_socket_open - open a netlink socket + * \param bus the netlink socket bus ID (see NETLINK_* constants) + * + * On error, it returns NULL and errno is appropriately set. Otherwise, it + * returns a valid pointer to the mnl_socket structure. + */ +struct mnl_socket *mnl_socket_open(int bus) +{ + return __mnl_socket_open(bus, 0); +} + +/** + * mnl_socket_open2 - open a netlink socket with appropriate flags + * \param bus the netlink socket bus ID (see NETLINK_* constants) + * \param flags the netlink socket flags (see SOCK_* constants in socket(2)) + * + * This is similar to mnl_socket_open(), but allows to set flags like + * SOCK_CLOEXEC at socket creation time (useful for multi-threaded programs + * performing exec calls). + * + * On error, it returns NULL and errno is appropriately set. Otherwise, it + * returns a valid pointer to the mnl_socket structure. + */ +struct mnl_socket *mnl_socket_open2(int bus, int flags) +{ + return __mnl_socket_open(bus, flags); +} + +/** + * mnl_socket_fdopen - associates a mnl_socket object with pre-existing socket. + * \param fd pre-existing socket descriptor. + * + * On error, it returns NULL and errno is appropriately set. Otherwise, it + * returns a valid pointer to the mnl_socket structure. It also sets the portID + * if the socket fd is already bound and it is AF_NETLINK. + * + * Note that mnl_socket_get_portid() returns 0 if this function is used with + * non-netlink socket. + */ +struct mnl_socket *mnl_socket_fdopen(int fd) +{ + int ret; + struct mnl_socket *nl; + struct sockaddr_nl addr; + socklen_t addr_len = sizeof(struct sockaddr_nl); + + ret = getsockname(fd, (struct sockaddr *) &addr, &addr_len); + if (ret == -1) + return NULL; + + nl = calloc(1, sizeof(struct mnl_socket)); + if (nl == NULL) + return NULL; + + nl->fd = fd; + if (addr.nl_family == AF_NETLINK) + nl->addr = addr; + + return nl; +} + +/** + * mnl_socket_bind - bind netlink socket + * \param nl netlink socket obtained via mnl_socket_open() + * \param groups the group of message you're interested in + * \param pid the port ID you want to use (use zero for automatic selection) + * + * On error, this function returns -1 and errno is appropriately set. On + * success, 0 is returned. You can use MNL_SOCKET_AUTOPID which is 0 for + * automatic port ID selection. + */ +int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, + pid_t pid) +{ + int ret; + socklen_t addr_len; + + nl->addr.nl_family = AF_NETLINK; + nl->addr.nl_groups = groups; + nl->addr.nl_pid = pid; + + ret = bind(nl->fd, (struct sockaddr *) &nl->addr, sizeof (nl->addr)); + if (ret < 0) + return ret; + + addr_len = sizeof(nl->addr); + ret = getsockname(nl->fd, (struct sockaddr *) &nl->addr, &addr_len); + if (ret < 0) + return ret; + + if (addr_len != sizeof(nl->addr)) { + errno = EINVAL; + return -1; + } + if (nl->addr.nl_family != AF_NETLINK) { + errno = EINVAL; + return -1; + } + return 0; +} + +/** + * mnl_socket_sendto - send a netlink message of a certain size + * \param nl netlink socket obtained via mnl_socket_open() + * \param buf buffer containing the netlink message to be sent + * \param len number of bytes in the buffer that you want to send + * + * On error, it returns -1 and errno is appropriately set. Otherwise, it + * returns the number of bytes sent. + */ +ssize_t mnl_socket_sendto(const struct mnl_socket *nl, + const void *buf, size_t len) +{ + static const struct sockaddr_nl snl = { + .nl_family = AF_NETLINK + }; + return sendto(nl->fd, buf, len, 0, + (struct sockaddr *) &snl, sizeof(snl)); +} + +/** + * mnl_socket_recvfrom - receive a netlink message + * \param nl netlink socket obtained via mnl_socket_open() + * \param buf buffer that you want to use to store the netlink message + * \param bufsiz size of the buffer passed to store the netlink message + * + * On error, it returns -1 and errno is appropriately set. If errno is set + * to ENOSPC, it means that the buffer that you have passed to store the + * netlink message is too small, so you have received a truncated message. + * To avoid this, you have to allocate a buffer of MNL_SOCKET_BUFFER_SIZE + * (which is 8KB, see linux/netlink.h for more information). Using this + * buffer size ensures that your buffer is big enough to store the netlink + * message without truncating it. + */ +ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, + void *buf, size_t bufsiz) +{ + ssize_t ret; + struct sockaddr_nl addr; + struct iovec iov = { + .iov_base = buf, + .iov_len = bufsiz, + }; + struct msghdr msg = { + .msg_name = &addr, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0, + }; + ret = recvmsg(nl->fd, &msg, 0); + if (ret == -1) + return ret; + + if (msg.msg_flags & MSG_TRUNC) { + errno = ENOSPC; + return -1; + } + if (msg.msg_namelen != sizeof(struct sockaddr_nl)) { + errno = EINVAL; + return -1; + } + return ret; +} + +/** + * mnl_socket_close - close a given netlink socket + * \param nl netlink socket obtained via mnl_socket_open() + * + * On error, this function returns -1 and errno is appropriately set. + * On success, it returns 0. + */ +int mnl_socket_close(struct mnl_socket *nl) +{ + int ret = close(nl->fd); + free(nl); + return ret; +} + +/** + * mnl_socket_setsockopt - set Netlink socket option + * \param nl netlink socket obtained via mnl_socket_open() + * \param type type of Netlink socket options + * \param buf the buffer that contains the data about this option + * \param len the size of the buffer passed + * + * This function allows you to set some Netlink socket option. As of writing + * this (see linux/netlink.h), the existing options are: + * + * - \#define NETLINK_ADD_MEMBERSHIP 1 + * - \#define NETLINK_DROP_MEMBERSHIP 2 + * - \#define NETLINK_PKTINFO 3 + * - \#define NETLINK_BROADCAST_ERROR 4 + * - \#define NETLINK_NO_ENOBUFS 5 + * + * In the early days, Netlink only supported 32 groups expressed in a + * 32-bits mask. However, since 2.6.14, Netlink may have up to 2^32 multicast + * groups but you have to use setsockopt() with NETLINK_ADD_MEMBERSHIP to + * join a given multicast group. This function internally calls setsockopt() + * to join a given netlink multicast group. You can still use mnl_bind() + * and the 32-bit mask to join a set of Netlink multicast groups. + * + * On error, this function returns -1 and errno is appropriately set. + */ +int mnl_socket_setsockopt(const struct mnl_socket *nl, int type, + void *buf, socklen_t len) +{ + return setsockopt(nl->fd, SOL_NETLINK, type, buf, len); +} + +/** + * mnl_socket_getsockopt - get a Netlink socket option + * \param nl netlink socket obtained via mnl_socket_open() + * \param type type of Netlink socket options + * \param buf pointer to the buffer to store the value of this option + * \param len size of the information written in the buffer + * + * On error, this function returns -1 and errno is appropriately set. + */ +int mnl_socket_getsockopt(const struct mnl_socket *nl, int type, + void *buf, socklen_t *len) +{ + return getsockopt(nl->fd, SOL_NETLINK, type, buf, len); +} + +/** + * @} + */ diff --git a/fibocom-dial/src/main.c b/fibocom-dial/src/main.c new file mode 100644 index 000000000..dd6429350 --- /dev/null +++ b/fibocom-dial/src/main.c @@ -0,0 +1,1770 @@ +#include +#include +#include +#include + +//2021-02-25 willa.liu@fibocom.com changed begin for support eipd SN-20210129001 +#include +#include +//2021-02-25 willa.liu@fibocom.com changed end for support eipd SN-20210129001 + +//2020-12-23 willa.liu@fibocom.com changed begin for support mantis 0065286 +#include +#include +//2020-12-23 willa.liu@fibocom.com changed end for support mantis 0065286 + +#include "QMIThread.h" +#include "util.h" +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 +#include "query_pcie_mode.h" +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 + +#define MAJOR 2 +#define MINOR 0 +#define REVISION 10 +/* + * Generally, we do not modify version info, so several modifications will share + * the same version code. SUBVERSION is used for customized modification to + * distinguise this version from previous one. SUBVERSION adds up before you + * send the code to customers and it should be set to 0 if VERSION_STRING info + * is changed. + */ +#define SUBVERSION 0 +#define STRINGIFY_HELPER(v) #v +#define STRINGIFY(v) STRINGIFY_HELPER(v) +#define VERSION_STRING() \ + STRINGIFY(MAJOR) "." STRINGIFY(MINOR) "." STRINGIFY(REVISION) +#define MAX_PATH 256 + +//2020-12-23 willa.liu@fibocom.com changed begin for support mantis 0065286 +struct sockaddr_un { + sa_family_t sun_family; + char sun_path[100]; +}; +//2020-12-23 willa.liu@fibocom.com changed end for support mantis 0065286 + +int query_pcie_mode = 0; +int debug_qmi = 0; +int main_loop = 0; +int qmidevice_control_fd[2]; +static int signal_control_fd[2]; +pthread_t gQmiThreadID; +USHORT g_MobileCountryCode = 0; +USHORT g_MobileNetworkCode = 0; + +//2020-12-23 willa.liu@fibocom.com changed begin for support mantis 0065286 +sem_t sem; +int socket_server_fd = 0; +int cli_accept_fd = 0; +char fibo_dial_filepath[100] = {0}; +//2020-12-23 willa.liu@fibocom.com changed end for support mantis 0065286 + +extern const struct qmi_device_ops qmiwwan_qmidev_ops; +extern const struct qmi_device_ops gobi_qmidev_ops; + +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 +//extern int *speed_arr; +//extern int *name_arr; +//extern int get_private_gateway_debug; +//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817 + +static int s_link = -1; + +static int is_pcie_dial() +{ + return (access("/dev/mhi_QMI0", F_OK) == 0); +} + +//2021-02-25 willa.liu@fibocom.com changed begin for support eipd SN-20210129001 +int split_gateway(char *str ,char* split, int index, char *outgateway) +{ + char chBuffer[1024]; + char *pchStrTmpIn = NULL; + char *pchTmp = NULL; + int i = 1; + strncpy(chBuffer, str, sizeof(chBuffer)-1); + pchTmp = chBuffer; + //printf("pchTmp is [%s]\n", pchTmp); + while(NULL != (pchTmp = strtok_r( pchTmp, split, &pchStrTmpIn))) + { + //printf("line %d::::::::::::[%s]\n", i, pchTmp); + if(i == index) + { + memcpy(outgateway, pchTmp, strlen(pchTmp)); + break; + } + //if(strstr(pchTmp, "+CGCONTRDP") != -1) + i++; + pchTmp = NULL; + } + return strlen(outgateway); +} + +int get_private_gateway(char *outgateway) +{ + int i; + int fd; + int ret; + char buffer[1024] = {0}; + int rate; + char *sendbuffer; + char prigateways[1024*2] = {0}; + char prigateway[256] = {0}; + int totallen = 0; + fd_set readfds; + struct timeval timeout; + struct termios tiosfd, tio; + + int timeoutVal = 5; + + char *dev = "/dev/ttyUSB1"; //The port under Linux is operated by opening the device file + rate = 115200; + sendbuffer = "AT+CGCONTRDP=1"; + + fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK); + if(fd < 0) + goto ERR; + fcntl(fd, F_SETFL, O_RDWR); + + xget1(fd, &tio, &tiosfd); + + for(i = 0; i < sizeof(speed_arr)/sizeof(int); i++) + { + if(rate == name_arr[i])//Judge if the pass is equal to the pass + { + break; + } + } + + if(i >= sizeof(speed_arr)/sizeof(int)) + { + printf("bound rate set failed\n"); + goto ERR; + } + + cfsetspeed(&tio, speed_arr[i]); + if(xset1(fd, &tio, dev)) + goto ERR; + + tcflush(fd, TCIOFLUSH); + + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + sprintf(buffer, "%s\r", sendbuffer); + ret = write (fd, buffer, strlen(buffer)); + if(ret < 0) + { + printf("write failed\n"); + goto ERR; + } + printf("dev: %s\nrate:%d\nsendbuffer:%s\n", dev, rate, sendbuffer); + sleep(1); + if(get_private_gateway_debug)printf("write %d\n", ret); + while(1) + { + timeout.tv_sec = timeoutVal; + timeout.tv_usec = 0; + + ret = select(fd+1, &readfds, (fd_set *)0, (fd_set *)0, &timeout); + if(ret > 0) + { + ret = read(fd, buffer+totallen, sizeof(buffer)-totallen-1); + if(ret < 0) + { + printf("read failed\n"); + goto ERR; + } + + if(ret == 0) + { + goto ERR; + } + + totallen += ret; + buffer[totallen] = '\0'; + if(get_private_gateway_debug)printf("read %d(%s)\n", ret, &buffer[totallen-ret]); + //printf("buffer is %s\n", buffer); + + if(totallen == sizeof(buffer)-1) + break; + + if(strstr(buffer, "\nOK")) + { + split_gateway(buffer, "\r", 3, prigateways); + //printf("test1 %s\n", prigateways); + split_gateway(prigateways, ",", 5, prigateway); + //printf("test2 %s\n", prigateway); + break; + } + else + { + goto ERR; + } + + } + else + { + printf("select timeout\n"); + goto ERR; + } + + } + + tcsetattr(fd, TCSAFLUSH, &tiosfd); + if(strstr(buffer, "\nERROR") || strstr(buffer, "\n+CME ERROR:") || strstr(buffer, "\n+CMS ERROR:")) + goto ERR; + + close(fd); + + memcpy(outgateway, prigateway+1, strlen(prigateway)-2); + printf("private gateway is %s\n", outgateway); + return 0; +ERR: + if(fd > 0) + close(fd); + return -1; +} +//2021-02-25 willa.liu@fibocom.com changed end for support eipd SN-20210129001 + +// UINT ifc_get_addr(const char *ifname); +static void usbnet_link_change(int link, PROFILE_T *profile) +{ + // static int s_link = 0; + dbg_time("%s :link:%d ", __func__, link); + + if (s_link == link) + return; + + s_link = link; + + if (link) { + if (profile->ipv4_flag) + requestGetIPAddress(profile, IpFamilyV4); + if (profile->ipv6_flag) + requestGetIPAddress(profile, IpFamilyV6); + if (qmidev_is_pciemhi(profile->qmichannel)) + udhcpc_start_pcie(profile); + else + udhcpc_start(profile); + } else { + if (qmidev_is_pciemhi(profile->qmichannel)) + udhcpc_stop_pcie(profile); + else + udhcpc_stop(profile); + } +} + +static int check_address(PROFILE_T *now_profile,int ipfamily) +{ + PROFILE_T new_profile_v; + PROFILE_T *new_profile = &new_profile_v; + static int time_out_count = 0; + int ret = 0; + #define QMITHREADSENDQMITIMEOUT 110 + + memcpy(new_profile, now_profile, sizeof(PROFILE_T)); + if(ipfamily == IpFamilyV4) + { + ret = requestGetIPAddress(new_profile, 0x04); + if (ret == 0) + { + time_out_count = 0; + if (new_profile->ipv4.Address != now_profile->ipv4.Address || debug_qmi) + { + unsigned char *l = (unsigned char *)&now_profile->ipv4.Address; + unsigned char *r = (unsigned char *)&new_profile->ipv4.Address; + dbg_time("localIP: %d.%d.%d.%d VS remoteIP: %d.%d.%d.%d", l[3], + l[2], l[1], l[0], r[3], r[2], r[1], r[0]); + } + return (new_profile->ipv4.Address == now_profile->ipv4.Address); + } + else if (ret == QMITHREADSENDQMITIMEOUT) + { + time_out_count++; + dbg_time("%s %d time_out_count %d\n", __func__, __LINE__, time_out_count); + if (time_out_count >= 3) + { + return 0; + } + else + { + return 1; + } + } + } + if(ipfamily == IpFamilyV6) + { + return 1; + } + + return 0; +} + +static void main_send_event_to_qmidevice(int triger_event) +{ + write(qmidevice_control_fd[0], &triger_event, sizeof(triger_event)); +} + +static void send_signo_to_main(int signo) +{ + write(signal_control_fd[0], &signo, sizeof(signo)); + dbg_time("write signo: %d to signal_control_fd",signo); +} + +void qmidevice_send_event_to_main(int triger_event) +{ + write(qmidevice_control_fd[1], &triger_event, sizeof(triger_event)); + dbg_time("write triger_event: %d to qmidevice_control_fd",triger_event); +} + +static int ls_dir(const char *dir, + int (*match)(const char *dir, const char *file, void *argv[]), + void *argv[]) +{ + DIR *pDir; + struct dirent *ent = NULL; + int match_times = 0; + + pDir = opendir(dir); + if (pDir == NULL) { + dbg_time("Cannot open directory: %s, errno: %d (%s)", dir, errno, + strerror(errno)); + return 0; + } + + while ((ent = readdir(pDir)) != NULL) { + match_times += match(dir, ent->d_name, argv); + } + closedir(pDir); + + return match_times; +} + +static int is_same_linkfile(const char *dir, const char *file, void *argv[]) +{ + const char *qmichannel = (const char *)argv[1]; + char linkname[MAX_PATH]; + char filename[MAX_PATH]; + int linksize; + + snprintf(linkname, MAX_PATH, "%s/%s", dir, file); + linksize = readlink(linkname, filename, MAX_PATH); + if (linksize <= 0) + return 0; + + filename[linksize] = 0; + if (strcmp(filename, qmichannel)) + return 0; + + dbg_time("%s -> %s", linkname, filename); + return 1; +} + +static int is_brother_process(const char *dir, const char *file, void *argv[]) +{ + // const char *myself = (const char *)argv[0]; + char linkname[MAX_PATH]; + char filename[MAX_PATH]; + int linksize; + int i = 0, kill_timeout = 15; + pid_t pid; + + // dbg_time("%s", file); + while (file[i]) { + if (!isdigit(file[i])) + break; + i++; + } + + if (file[i]) { + // dbg_time("%s not digit", file); + return 0; + } + + snprintf(linkname, MAX_PATH, "%s/%s/exe", dir, file); + linksize = readlink(linkname, filename, MAX_PATH); + if (linksize <= 0) + return 0; + + filename[linksize] = 0; + + pid = atoi(file); + if (pid >= getpid()) + return 0; + + snprintf(linkname, MAX_PATH, "%s/%s/fd", dir, file); + if (!ls_dir(linkname, is_same_linkfile, argv)) + return 0; + + dbg_time("%s/%s/exe -> %s", dir, file, filename); + while (kill_timeout-- && !kill(pid, 0)) { + kill(pid, SIGTERM); + sleep(1); + } + if (!kill(pid, 0)) { + dbg_time("force kill %s/%s/exe -> %s", dir, file, filename); + kill(pid, SIGKILL); + sleep(1); + } + + return 1; +} + +static int kill_brothers(const char *qmichannel) +{ + char myself[MAX_PATH]; + int filenamesize; + void *argv[2] = {myself, (void *)qmichannel}; + + filenamesize = readlink("/proc/self/exe", myself, MAX_PATH); + if (filenamesize <= 0) + return 0; + myself[filenamesize] = 0; + + if (ls_dir("/proc", is_brother_process, argv)) + sleep(1); + + return 0; +} + +static void fibo_sigaction(int signo) +{ + if (SIGCHLD == signo) + waitpid(-1, NULL, WNOHANG); + else if (SIGALRM == signo) + send_signo_to_main(SIGUSR1); + else { + if (SIGTERM == signo || SIGHUP == signo || SIGINT == signo) + main_loop = 0; + send_signo_to_main(signo); + main_send_event_to_qmidevice(signo); // main may be wating qmi response + } +} + +static int usage(const char *progname) +{ + dbg_time("Usage: %s [options]", progname); + dbg_time( + "-s [apn [user password auth]] Set " + "apn/user/password/auth get from your network provider"); + dbg_time( + "-p pincode Verify sim card pin if " + "sim card is locked"); + dbg_time( + "-f logfilename Save log message of this " + "program to file"); + dbg_time( + "-i interface Specify network " + "interface(default auto-detect)"); + dbg_time("-4 IPv4 protocol"); + dbg_time("-6 IPv6 protocol"); + +//2021-02-25 willa.liu@fibocom.com changed begin for support eipd SN-20210129001 + dbg_time("-g IPv6 private gateway"); +//2021-02-25 willa.liu@fibocom.com changed begin for support eipd SN-20210129001 + +//2020-12-29 willa.liu@fibocom.com changed begin for support mantis 0066901 + //dbg_time( + //"-P profile index Specify profile index " + //"when start network data connection(default 1)."); +//2020-12-29 willa.liu@fibocom.com changed end for support mantis 0066901 + + dbg_time( + "-m muxID Specify muxid when set " + "multi-pdn data connection."); + dbg_time( + "-n channelID Specify channelID when " + "set multi-pdn data connection(default 1)."); + dbg_time( + "-N Number of channel Specify total channels " + "when set multi-pdn data connection(default 1)."); + +//2020-12-23 willa.liu@fibocom.com changed begin for support mantis 0065286 + dbg_time( + "-k number kill specified process "); +//2020-12-23 willa.liu@fibocom.com changed end for support mantis 0065286 + + dbg_time("[Examples]"); + dbg_time("Example 1: %s ", progname); + dbg_time("Example 2: %s -s 3gnet ", progname); + dbg_time("Example 3: %s -s 3gnet carl 1234 0 -p 1234 -f gobinet_log.txt", + progname); + return 0; +} + +static int charsplit(const char *src, char *desc, int n, const char *splitStr) +{ + char *p; + char *p1; + int len; + + len = strlen(splitStr); + p = strstr(src, splitStr); + if (p == NULL) + return -1; + p1 = strstr(p, "\n"); + if (p1 == NULL) + return -1; + memset(desc, 0, n); + memcpy(desc, p + len, p1 - p - len); + + return 0; +} + +static int get_dev_major_minor(char *path, int *major, int *minor) +{ + int fd = -1; + char desc[128] = {0}; + char devmajor[64], devminor[64]; + int n = 0; + if (access(path, R_OK | W_OK)) { + return 1; + } + if ((fd = open(path, O_RDWR)) < 0) { + return 1; + } + n = read(fd, desc, sizeof(desc)); + if (n == sizeof(desc)) { + dbg_time("may be overflow"); + } + close(fd); + if (charsplit(desc, devmajor, 64, "MAJOR=") == -1 || + charsplit(desc, devminor, 64, "MINOR=") == -1) { + return 2; + } + *major = atoi(devmajor); + *minor = atoi(devminor); + return 0; +} +#ifdef GHT_FEATURE_PCIE_AUTO +//begin modified by zhangkaibo add PCIe auto detect feature on x55/x24 platform. 20200605 +static int qmipciedevice_detect(char **pp_qmichannel, char **pp_usbnet_adapter) +{ + struct dirent *ent = NULL; + DIR *pDir; + char dir[255] = "/sys/bus/pci/devices"; + + struct { + char subdir[255 * 2]; + char subdir2[255 * 3 + 16]; + char qmifile[255 * 2]; + } * pl; + pl = (typeof(pl))malloc(sizeof(*pl)); + memset(pl, 0x00, sizeof(*pl)); + + pDir = opendir(dir); + if (pDir) { + while ((ent = readdir(pDir)) != NULL) { + + struct dirent *subent = NULL; + DIR *psubDir; + char idVendor[4 + 1] = {0}; + char idDevice[4 + 1] = {0}; + char tempNetpath[256] = {0}; + int fd = 0; + char netcard[32] = "\0"; + + memset(pl, 0x00, sizeof(*pl)); + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s/vendor", dir, + ent->d_name); + fd = open(pl->subdir, O_RDONLY); + if (fd > 0) { + read(fd, idVendor, 6); + close(fd); + } + + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s/device", dir, + ent->d_name); + fd = open(pl->subdir, O_RDONLY); + if (fd > 0) { + read(fd, idDevice, 6); + close(fd); + } + + if (!strncasecmp(idVendor, "0x17cb", 6)) + ; + else + continue; + + dbg_time("Find %s/%s vendor=%s device=%s", dir, ent->d_name, + idVendor,idDevice); + //find pcie netdevicename + snprintf(tempNetpath,256,"%s%s_IP_HW0", idDevice+2 , ent->d_name+2); + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s/%s/net", dir, + ent->d_name,tempNetpath); + psubDir = opendir(pl->subdir); + if (psubDir == NULL) { + dbg_time("Cannot open directory: %s, errno: %d (%s)", + pl->subdir, errno, strerror(errno)); + continue; + } + + while ((subent = readdir(psubDir)) != NULL) { + if (subent->d_name[0] == '.') + continue; + dbg_time("Find pcienet_adapter = %s", subent->d_name); + strcpy(netcard, subent->d_name); + break; + } + + closedir(psubDir); + + if (netcard[0]) { + } else { + continue; + } + + *pp_usbnet_adapter = strdup(netcard); + break; + } + closedir(pDir); + } + //find pcie qmi control name + if ((pDir = opendir("/dev")) == NULL) { + dbg_time("Cannot open directory: %s, errno:%d (%s)", "/dev", errno, + strerror(errno)); + return -ENODEV; + } + + while ((ent = readdir(pDir)) != NULL) { + if ( strncmp(ent->d_name, "mhi_QMI", strlen("mhi_QMI")) == 0 ) { + *pp_qmichannel = (char *)malloc(32); + sprintf(*pp_qmichannel, "/dev/%s", ent->d_name); + dbg_time("Find qmichannel = %s", *pp_qmichannel); + break; + } + } + closedir(pDir); + free(pl); + + return (*pp_qmichannel && *pp_usbnet_adapter); +} +//end modified by zhangkaibo add PCIe auto detect feature on x55/x24 platform. 20200605 +#endif +static int qmidevice_detect(char **pp_qmichannel, char **pp_usbnet_adapter,PROFILE_T *profile) +{ + struct dirent *ent = NULL; + DIR *pDir; + char dir[255] = "/sys/bus/usb/devices"; + int major = 0, minor = 0; + int indexid = 4; + struct { + char subdir[255 * 3]; + char subdir2[255 * 4 + 16]; + char qmifile[255 * 2]; + } * pl; +#ifdef GHT_FEATURE_PCIE_AUTO +//begin modified by zhangkaibo add PCIe auto detect feature on x55/x24 platform. 20200605 + if (qmipciedevice_detect(pp_qmichannel, pp_usbnet_adapter)) + { + return (*pp_qmichannel && *pp_usbnet_adapter); + } +//end modified by zhangkaibo add PCIe auto detect feature on x55/x24 platform. 20200605 +#endif + pl = (typeof(pl))malloc(sizeof(*pl)); + memset(pl, 0x00, sizeof(*pl)); + + pDir = opendir(dir); + if (pDir) { + while ((ent = readdir(pDir)) != NULL) { + struct dirent *subent = NULL; + DIR *psubDir; + char idVendor[4 + 1] = {0}; + char idProduct[4 + 1] = {0}; + int fd = 0; + char netcard[32] = "\0"; + + memset(pl, 0x00, sizeof(*pl)); + + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s/idVendor", dir, + ent->d_name); + fd = open(pl->subdir, O_RDONLY); + if (fd > 0) { + read(fd, idVendor, 4); + close(fd); + } + + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s/idProduct", dir, + ent->d_name); + fd = open(pl->subdir, O_RDONLY); + if (fd > 0) { + read(fd, idProduct, 4); + close(fd); + } + + if (!strncasecmp(idVendor, "05c6", 4) || + !strncasecmp(idVendor, "2cb7", 4)) + ; + else + continue; + + dbg_time("Find %s/%s idVendor=%s idProduct=%s", dir, ent->d_name, + idVendor, idProduct); +//2021-02-08 zhangkaibo@fibocom.com changed start for mantis 0070613 + if(!strncasecmp(idProduct, "0109", 4)) + { + indexid = 2; + profile->interfacenum = 2; + } +//2021-09-09 willa.liu@fibocom.com changed start for mantis 0086219 + else if(!strncasecmp(idProduct, "0113", 4)) + { + indexid = 0; + profile->interfacenum = 0; + } +//2021-09-09 willa.liu@fibocom.com changed end for mantis 0086219 + else + { + profile->interfacenum = 4; + } + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s:1.%d/net", dir, + ent->d_name,indexid); + psubDir = opendir(pl->subdir); + if (psubDir == NULL) { + dbg_time("Cannot open directory: %s, errno: %d (%s)", + pl->subdir, errno, strerror(errno)); + continue; + } + + while ((subent = readdir(psubDir)) != NULL) { + if (subent->d_name[0] == '.') + continue; + dbg_time("Find %s/%s", pl->subdir, subent->d_name); + dbg_time("Find usbnet_adapter = %s", subent->d_name); + strcpy(netcard, subent->d_name); + break; + } + + closedir(psubDir); + + if (netcard[0]) { + } else { + continue; + } + + if (*pp_usbnet_adapter && strcmp(*pp_usbnet_adapter, netcard)) + continue; + + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s:1.%d/GobiQMI", dir, + ent->d_name,indexid); + if (access(pl->subdir, R_OK)) { + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s:1.%d/usbmisc", + dir, ent->d_name,indexid); + if (access(pl->subdir, R_OK)) { + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s:1.%d/usb", + dir, ent->d_name,indexid); + if (access(pl->subdir, R_OK)) { + dbg_time("no GobiQMI/usbmic/usb found in %s/%s:1.%d", + dir, ent->d_name,indexid); + continue; + } + } + } + +//2021-02-08 zhangkaibo@fibocom.com changed end for mantis 0070613 + + psubDir = opendir(pl->subdir); + if (pDir == NULL) { + dbg_time("Cannot open directory: %s, errno: %d (%s)", dir, + errno, strerror(errno)); + continue; + } + + while ((subent = readdir(psubDir)) != NULL) { + if (subent->d_name[0] == '.') + continue; + dbg_time("Find %s/%s", pl->subdir, subent->d_name); + dbg_time("Find qmichannel = /dev/%s", subent->d_name); + snprintf(pl->qmifile, sizeof(pl->qmifile), "/dev/%s", + subent->d_name); + + // get major minor + snprintf(pl->subdir2, sizeof(pl->subdir2), "%s/%s/uevent", + pl->subdir, subent->d_name); + if (!get_dev_major_minor(pl->subdir2, &major, &minor)) { + // dbg_time("%s major = %d, minor = %d\n",pl->qmifile, + // major, minor); + } else { + dbg_time("get %s major and minor failed\n", pl->qmifile); + } + // get major minor + + if ((fd = open(pl->qmifile, R_OK)) < 0) { + dbg_time("%s open failed", pl->qmifile); + dbg_time("please mknod %s c %d %d", pl->qmifile, major, + minor); + } else { + close(fd); + } + break; + } + + closedir(psubDir); + + if (netcard[0] && pl->qmifile[0]) { + *pp_qmichannel = strdup(pl->qmifile); + *pp_usbnet_adapter = strdup(netcard); + closedir(pDir); + return 1; + } + } + + closedir(pDir); + } + + if ((pDir = opendir("/dev")) == NULL) { + dbg_time("Cannot open directory: %s, errno:%d (%s)", "/dev", errno, + strerror(errno)); + return -ENODEV; + } + + while ((ent = readdir(pDir)) != NULL) { + if ((strncmp(ent->d_name, "cdc-wdm", strlen("cdc-wdm")) == 0) || + (strncmp(ent->d_name, "qcqmi", strlen("qcqmi")) == 0)) { + char net_path[255*2]; + + *pp_qmichannel = (char *)malloc(32); + sprintf(*pp_qmichannel, "/dev/%s", ent->d_name); + dbg_time("Find qmichannel = %s", *pp_qmichannel); + + if (strncmp(ent->d_name, "cdc-wdm", strlen("cdc-wdm")) == 0) + sprintf(net_path, "/sys/class/net/wwan%s", + &ent->d_name[strlen("cdc-wdm")]); + else { + sprintf(net_path, "/sys/class/net/usb%s", + &ent->d_name[strlen("qcqmi")]); + + if (access(net_path, R_OK) && errno == ENOENT) + sprintf(net_path, "/sys/class/net/eth%s", + &ent->d_name[strlen("qcqmi")]); + } + + if (access(net_path, R_OK) == 0) { + if (*pp_usbnet_adapter && + strcmp(*pp_usbnet_adapter, + (net_path + strlen("/sys/class/net/")))) { + free(*pp_qmichannel); + *pp_qmichannel = NULL; + continue; + } + *pp_usbnet_adapter = + strdup(net_path + strlen("/sys/class/net/")); + dbg_time("Find usbnet_adapter = %s", *pp_usbnet_adapter); + break; + } else { + dbg_time("Failed to access %s, errno:%d (%s)", net_path, errno, + strerror(errno)); + free(*pp_qmichannel); + *pp_qmichannel = NULL; + } + } + } + closedir(pDir); + free(pl); + + return (*pp_qmichannel && *pp_usbnet_adapter); +} +//begin modified by zhangkaibo limit input parameters. mantis 0048965 20200605 +int IsInt(char* str) +{ + int len; + len = strlen(str); + int i=0; + for(; i < len ; i++) + { + if(!(isdigit(str[i]))) + return 0; + } + return 1; +} +//end modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + +//2020-12-23 willa.liu@fibocom.com changed begin for support mantis 0065286 +int rdwr_init(int clienfd, char * msg) +{ + char buf[512]; + int rv = 0; + memset(buf, 0, 512); + while(1) + { + if ((rv = read(clienfd, buf, 512)) < 0) + { + dbg_time("Read by socket[%d] error:%s", clienfd, strerror(errno)); + goto STOP; + } + else if (0 == rv) + { + goto STOP; + } + else if (rv > 0) + { + dbg_time("Read %d bytes data from client, there are :[%s]", rv, buf); + } + if ((rv =write(clienfd, msg, strlen(msg))) < 0) + { + dbg_time("write to cilent by socket[%d] error:%s",clienfd, strerror(errno)); + goto STOP; + } + } +STOP: + dbg_time("The socket[%d] will exit!", clienfd); + return 0; +} + +void *thread_socket_server(void *args) +{ + int on = 1; + int rv = 0; + struct sockaddr_un serv_addr; + struct sockaddr_un cli_addr; + socklen_t len = sizeof(serv_addr); + sprintf(fibo_dial_filepath,"/tmp/fibocom_dial_%d",*(int*)args); + char* msg = "receive quit request\n"; + if ((socket_server_fd = socket(AF_UNIX,SOCK_STREAM,0))< 0) + { + dbg_time("Socket error:%s\a\n", strerror(errno)); + return ; + } + dbg_time("socket[%d] successfuly!", socket_server_fd); + if (!access(fibo_dial_filepath, F_OK)) + { + unlink(fibo_dial_filepath); + } + setsockopt(socket_server_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sun_family = AF_UNIX; + strncpy(serv_addr.sun_path, fibo_dial_filepath, sizeof(serv_addr.sun_path) - 1); + if ((rv = bind(socket_server_fd, (struct sockaddr *)&serv_addr, len)) < 0) + { + dbg_time("Bind error %s", strerror(errno)); + goto EXIT; + } + if ((rv = listen(socket_server_fd, 13)) < 0) + { + dbg_time("Listen error:%s", strerror(errno)); + goto EXIT; + } + dbg_time("Waiting client to connect....."); + sem_post(&sem); + while(1) + { + if ((cli_accept_fd = accept(socket_server_fd, (struct sockaddr *)&cli_addr, &len))< 0) + { + dbg_time("accept error:%s", strerror(errno)); + goto EXIT; + } + dbg_time("client to connected"); + rv = rdwr_init(cli_accept_fd, msg); + goto EXIT; + } +EXIT: + unlink(fibo_dial_filepath); + close(socket_server_fd ); + close(cli_accept_fd ); + send_signo_to_main(SIGTERM); + return ((void *)0); + +} + +//2020-12-29 willa.liu@fibocom.com changed begin for support mantis 0066901 +int startSocketserver(int pdpindex) +{ + pthread_t ntid; + //sem_init(&sem, 0, 2); + sem_init(&sem, 0, 0); + if (pthread_create(&ntid, 0, thread_socket_server, + (void *)(&pdpindex)) != 0) { + dbg_time("%s Failed to create QMIThread: %d (%s)", __func__, errno, + strerror(errno)); + } + sem_wait(&sem); + sem_destroy(&sem); + return 0; +} +//2020-12-29 willa.liu@fibocom.com changed begin for support mantis 0066901 + +int startSocketclient(int pdpindex) +{ + int socket_client_fd; + int rv = -1; + struct sockaddr_un servaddr; + char fibo_dial_filepath[100] = {0}; + sprintf(fibo_dial_filepath,"/tmp/fibocom_dial_%d",pdpindex); + dbg_time("enter startSocketclient %s.",fibo_dial_filepath); + char* message = "fibocom-dial quit request"; + char buf[1024]; + + if(access(fibo_dial_filepath, F_OK) != 0) + { + return -1; + } + socket_client_fd = socket(AF_UNIX,SOCK_STREAM,0); + if(socket_client_fd < 0) + { + dbg_time("Cearte socket failure :%s\a",strerror(errno)); + return -1; + } + dbg_time("Create socket [%d] sucessfully.",socket_client_fd); + memset(&servaddr,0,sizeof(servaddr)); + servaddr.sun_family = AF_UNIX; + strncpy(servaddr.sun_path, fibo_dial_filepath, sizeof(servaddr.sun_path) - 1); + rv = connect(socket_client_fd,(struct sockaddr *)&servaddr,sizeof(servaddr)); + if(rv <0) + { + dbg_time("connect to server [%s] failure :%s\a",fibo_dial_filepath,strerror(errno)); + return -2; + } + dbg_time("connect to server [%s] successfully!",fibo_dial_filepath); + rv = write(socket_client_fd,message,strlen(message)); + if(rv <0) + { + dbg_time("Write to server failure by socket[%d] failure: %s",socket_client_fd,strerror(errno)); + return -3; + } + else + { + dbg_time("Write [%s] to server by socket[%d] successfuly!", message, socket_client_fd); + } + memset(buf,0,sizeof(buf)); + rv = read(socket_client_fd,buf,1024); + if(rv < 0) + { + dbg_time("Read to server socket[%d] failure: %s",socket_client_fd,strerror(errno)); + return -4; + } + else if(rv == 0) + { + dbg_time("socket [%d] get disconnected",socket_client_fd); + return -4; + } + else if(rv >0) + { + dbg_time("Read [%d] bytes data from Server: [%s]",rv,buf); + } + close(socket_client_fd); + unlink(fibo_dial_filepath); + + return 0; +} +//2020-12-23 willa.liu@fibocom.com changed begin for support mantis 0065286 + +int main(int argc, char *argv[]) +{ + int triger_event = 0; + int opt = 1; + int signo; +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 + int sim_number = 0; +//begin modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + char argvtemp[256] = {0}; +//end modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + +//2020-12-23 willa.liu@fibocom.com changed begin for support mantis 0065286 + int killnumber = -1; +//2020-12-23 willa.liu@fibocom.com changed end for support mantis 0065286 + +#ifdef CONFIG_SIM + SIM_Select SIMNumber; +//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817 + SIM_Status SIMStatus; +#endif + UCHAR PSAttachedState; + UCHAR IPv4ConnectionStatus = 0xff; // unknow state + UCHAR IPV6ConnectionStatus = 0xff; // unknow state + int qmierr = 0; + char *save_usbnet_adapter = NULL; + PROFILE_T profile; +#ifdef CONFIG_RESET_RADIO + struct timeval resetRadioTime = {0}; + struct timeval FailDailRadioTime = {0}; + struct timeval nowTime; + gettimeofday(&resetRadioTime, (struct timezone *)NULL); +#endif + + dbg_time("Fibocom-dial_Linux_Tool_V%s", VERSION_STRING()); + memset(&profile, 0x00, sizeof(profile)); + profile.pdp = CONFIG_DEFAULT_PDP; + profile.pdpindex = CONFIG_DEFAULT_PDPINDEX; + profile.pdpnum = 1; + + if (!strcmp(argv[argc - 1], "&")) + argc--; + + opt = 1; + while (opt < argc) { + if (argv[opt][0] != '-') + return usage(argv[0]); + + switch (argv[opt++][1]) { +#define has_more_argv() ((opt < argc) && (argv[opt][0] != '-')) + case 's': + profile.apn = profile.user = profile.password = ""; + if (has_more_argv()) + profile.apn = argv[opt++]; + if (has_more_argv()) + profile.user = argv[opt++]; + if (has_more_argv()) { + profile.password = argv[opt++]; + if (profile.password && profile.password[0]) + profile.auth = 2; // default chap, customers may miss auth + } + if (has_more_argv()) +//begin modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + { + memset(argvtemp,0,256); + strcpy(argvtemp,argv[opt++]); + if(strlen(argvtemp) > 1 || !IsInt(argvtemp)) + { + dbg_time("Invalid parament for \"-s\""); + return -1; + } + profile.auth = argvtemp[0] - '0'; +//begin modified by zhangkaibo limit input parameters. mantis 0077949 20200513 + if(profile.auth < 0 || profile.auth > 3) + { + dbg_time("auth must be 0~3"); +//end modified by zhangkaibo limit input parameters. mantis 0077949 20200513 + return -1; + } + } +//end modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + break; + + case 'm': + if (has_more_argv()) + { + if (is_pcie_dial()) + profile.muxid = argv[opt++][0] - '0'; + else + { + memset(argvtemp,0,256); + strcpy(argvtemp,argv[opt++]); + if(strlen(argvtemp) > 1 || !IsInt(argvtemp)) + { + dbg_time("Invalid parament for \"-P\""); + return -1; + } + profile.pdpindex = argvtemp[0] - '0'; + if(profile.pdpindex < 1 || profile.pdpindex > 10) + { + dbg_time("pdpindex must be 1~10"); + return -1; + } + } + } + break; + + case 'p': + if (has_more_argv()) + profile.pincode = argv[opt++]; + break; +#if 0 + case 'P': + if (has_more_argv()) +//begin modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + { + memset(argvtemp,0,256); + strcpy(argvtemp,argv[opt++]); + if(strlen(argvtemp) > 1 || !IsInt(argvtemp)) + { + dbg_time("Invalid parament for \"-P\""); + return -1; + } + profile.pdpindex = argvtemp[0] - '0'; + if(profile.pdpindex < 1 || profile.pdpindex > 10) + { + dbg_time("pdpindex must be 1~10"); + return -1; + } + } +//end modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + break; +#endif + case 'n': + if (has_more_argv()) +//begin modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + { + memset(argvtemp,0,256); + strcpy(argvtemp,argv[opt++]); + if(strlen(argvtemp) > 1 || !IsInt(argvtemp)) + { + dbg_time("Invalid parament for \"-n\""); + return -1; + } + profile.pdp = argvtemp[0] - '0'; + if(profile.pdp < 1 || profile.pdp > 8) + { + dbg_time("pdpindex must be 1~8"); + return -1; + } + + if (is_pcie_dial()) + profile.pdpindex = profile.pdp; + } + break; +//end modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + case 'N': + if (has_more_argv()) +//begin modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + { + memset(argvtemp,0,256); + strcpy(argvtemp,argv[opt++]); + if(strlen(argvtemp) > 1 || !IsInt(argvtemp)) + { + dbg_time("Invalid parament for \"-N\""); + return -1; + } + profile.pdpnum = argvtemp[0] - '0'; + if(profile.pdpnum < 0 || profile.pdpnum > 8) + { + dbg_time("pdpnum must be 0~8"); + return -1; + } + } +//end modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + break; + + case 'f': + if (has_more_argv()) { + const char *filename = argv[opt++]; + logfilefp = fopen(filename, "a+"); + if (!logfilefp) { + dbg_time("Fail to open %s, errno: %d(%s)", filename, errno, + strerror(errno)); + } + } + break; + + case 'i': + if (has_more_argv()) + profile.usbnet_adapter = save_usbnet_adapter = argv[opt++]; + break; + + case 'v': + debug_qmi = 1; + break; + + case 'l': + if (has_more_argv()) { + profile.replication_factor = atoi(argv[opt++]); + if (profile.replication_factor > 0) + profile.loopback_state = 1; + } + else + main_loop = 1; + + break; + + case '4': + profile.ipv4_flag = 1; + break; + + case '6': + profile.ipv6_flag = 1; + break; + +//2021-02-25 willa.liu@fibocom.com changed begin for support eipd SN-20210129001 + case 'g': + profile.ipv6_prigateway_flag = 1; + break; +//2021-02-25 willa.liu@fibocom.com changed end for support eipd SN-20210129001 + + case 'd': + if (has_more_argv()) { + profile.qmichannel = argv[opt++]; + if (qmidev_is_pciemhi(profile.qmichannel)) + profile.usbnet_adapter = "pcie_mhi0"; + } + break; + + case 'U': + query_pcie_mode = 1; + break; + +//2020-12-23 willa.liu@fibocom.com changed begin for support mantis 0065286 + case 'k': + if (has_more_argv()) + { + memset(argvtemp,0,256); + strcpy(argvtemp,argv[opt++]); + if(strlen(argvtemp) > 1 || !IsInt(argvtemp)) + { + dbg_time("Invalid parament for \"-n\""); + return -1; + } + killnumber = argvtemp[0] - '0'; +//2021-02-01 willa.liu@fibocom.com changed begin for support mantis 0069837 + //if(killnumber < 1 || killnumber > 5) + if(killnumber < 1 || killnumber > 8) + { + dbg_time("pdpindex must be 1~8"); + return -1; + } +//2021-02-01 willa.liu@fibocom.com changed end for support mantis 0069837 + } + break; +//2020-12-23 willa.liu@fibocom.com changed end for support mantis 0065286 + + default: + return usage(argv[0]); + break; + } + } + + if (profile.ipv4_flag != 1 && + profile.ipv6_flag != 1) { // default enable IPv4 + profile.ipv4_flag = 1; + } + + dbg_time("%s profile[%d] = %s/%s/%s/%d, pincode = %s", argv[0], profile.pdp, + profile.apn, profile.user, profile.password, profile.auth, + profile.pincode); + signal(SIGUSR1, fibo_sigaction); + signal(SIGUSR2, fibo_sigaction); + signal(SIGINT, fibo_sigaction); + signal(SIGTERM, fibo_sigaction); + signal(SIGHUP, fibo_sigaction); + signal(SIGCHLD, fibo_sigaction); + signal(SIGALRM, fibo_sigaction); + + //2020-12-23 willa.liu@fibocom.com changed begin for support mantis 0065286 + if(killnumber == -1) + { + startSocketserver(profile.pdp); + } + else{ + startSocketclient(killnumber); + return 0; + } + //2020-12-23 willa.liu@fibocom.com changed end for support mantis 0065286 + + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, signal_control_fd) < 0) { + dbg_time("%s Faild to create main_control_fd: %d (%s)", __func__, errno, + strerror(errno)); + return -1; + } + + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, qmidevice_control_fd) < 0) { + dbg_time("%s Failed to create thread control socket pair: %d (%s)", + __func__, errno, strerror(errno)); + return 0; + } + +// sudo apt-get install udhcpc +// sudo apt-get remove ModemManager +__main_loop: + while (!profile.qmichannel) { +//2021-02-08 zhangkaibo@fibocom.com changed start for mantis 0070613 + if (qmidevice_detect(&profile.qmichannel, &profile.usbnet_adapter,&profile)) +//2021-02-08 zhangkaibo@fibocom.com changed end for mantis 0070613 + { + if (query_pcie_mode) + get_pcie_mode(); + break; + } + if (main_loop) { + int wait_for_device = 3000; + dbg_time("Wait for fibo modules connect"); + while (wait_for_device && main_loop) { + wait_for_device -= 100; + usleep(100 * 1000); + } + continue; + } + dbg_time( + "Cannot find qmichannel(%s) usbnet_adapter(%s) for fibo modules", + profile.qmichannel, profile.usbnet_adapter); + return -ENODEV; + } + + if (access(profile.qmichannel, R_OK | W_OK)) { + dbg_time("Fail to access %s, errno: %d (%s)", profile.qmichannel, errno, + strerror(errno)); + return errno; + } + dbg_time( + "qmichannel(%s) usbnet_adapter(%s) ", + profile.qmichannel, profile.usbnet_adapter); + + if (qmidev_is_gobinet(profile.qmichannel)) { + profile.qmi_ops = &gobi_qmidev_ops; + dbg_time("usb rmnet mode"); + } else { + profile.qmi_ops = &qmiwwan_qmidev_ops; + dbg_time("pcie mode"); + } + qmidev_send = profile.qmi_ops->send; + fibo_qmap_mode_detect(&profile); + if (profile.pdpnum > 1 && profile.pdpnum != profile.qmap_mode && !qmidev_is_pciemhi(profile.qmichannel)) { + if (profile.qmap_mode > 1) { + dbg_time("qmap_mode=%d,profile.pdpnum %d",profile.qmap_mode,profile.pdpnum); + kill_brothers(profile.qmichannel); + } + fibo_qmap_mode_set(&profile); + } + fibo_qmap_mode_detect(&profile); +//begin modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + if(profile.qmap_mode < profile.pdp && profile.qmap_mode > 0 && !qmidev_is_pciemhi(profile.qmichannel)) + { + dbg_time("invalid pdpindex"); + return -1; + } +//end modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + if (profile.qmap_mode == 0 || profile.qmap_mode == 1) + { + dbg_time("qmap_mode=%d",profile.qmap_mode); + kill_brothers(profile.qmichannel); + } + if (pthread_create(&gQmiThreadID, 0, profile.qmi_ops->read, + (void *)&profile) != 0) { + dbg_time("%s Failed to create QMIThread: %d (%s)", __func__, errno, + strerror(errno)); + return 0; + } + + if ((read(qmidevice_control_fd[0], &triger_event, sizeof(triger_event)) != + sizeof(triger_event)) || + (triger_event != RIL_INDICATE_DEVICE_CONNECTED)) { + dbg_time("%s Failed to init QMIThread: %d (%s)", __func__, errno, + strerror(errno)); + return 0; + } + + if (profile.qmi_ops->init && profile.qmi_ops->init(&profile)) { + dbg_time("%s Failed to qmi init: %d (%s)", __func__, errno, + strerror(errno)); + return 0; + } + +#ifdef CONFIG_VERSION + requestBaseBandVersion(NULL); +#endif + requestSetEthMode(&profile); + +#ifdef CONFIG_SIM +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 + sim_number = requestGetSIMCardNumber(&profile); + if(sim_number == 1){ + requestSimBindSubscription_NAS_WMS(); + requestSimBindSubscription_WDS_DMS_QOS(); + } + //qmierr = requestGetSIMStatus(&SIMStatus); + qmierr = requestGetSIMStatus(&SIMStatus, sim_number); + while (qmierr == QMI_ERR_OP_DEVICE_UNSUPPORTED) { + sleep(1); + //qmierr = requestGetSIMStatus(&SIMStatus); + qmierr = requestGetSIMStatus(&SIMStatus, sim_number); +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 + } + if ((SIMStatus == SIM_PIN) && profile.pincode) { + requestEnterSimPin(profile.pincode); + } +#ifdef CONFIG_IMSI_ICCID + if (SIMStatus == SIM_READY) { + requestGetICCID(); + requestGetIMSI(); + } +#endif +#endif +#ifdef CONFIG_APN + if (profile.apn || profile.user || profile.password) { + requestSetProfile(&profile); + } + requestGetProfile(&profile); +#endif + requestRegistrationState2(&PSAttachedState); + /* + if (!requestQueryDataCall(&IPv4ConnectionStatus, IpFamilyV4) && + (QWDS_PKT_DATA_CONNECTED == IPv4ConnectionStatus)) usbnet_link_change(1, + &profile); else usbnet_link_change(0, &profile); + */ + send_signo_to_main(SIGUSR2); + + while (1) { + struct pollfd pollfds[] = {{signal_control_fd[1], POLLIN, 0}, + {qmidevice_control_fd[0], POLLIN, 0}}; + int ne, ret, nevents = sizeof(pollfds) / sizeof(pollfds[0]); + + do { + ret = poll(pollfds, nevents, 15 * 1000); + } while ((ret < 0) && (errno == EINTR)); + + if (ret == 0) { + send_signo_to_main(SIGUSR2); + continue; + } + + if (ret <= 0) { + dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, + strerror(errno)); + goto __main_quit; + } + + for (ne = 0; ne < nevents; ne++) { + int fd = pollfds[ne].fd; + short revents = pollfds[ne].revents; + dbg_time("epoll fd = %d, events = 0x%04x", fd, revents); + if (revents & (POLLERR | POLLHUP | POLLNVAL)) { + dbg_time("%s poll err/hup", __func__); + dbg_time("epoll fd = %d, events = 0x%04x", fd, revents); + main_send_event_to_qmidevice(RIL_REQUEST_QUIT); + if (revents & POLLHUP) + goto __main_quit; + } + + if ((revents & POLLIN) == 0) + continue; + + if (fd == signal_control_fd[1]) { + if (read(fd, &signo, sizeof(signo)) == sizeof(signo)) { + alarm(0); + dbg_time("get signo: %d ",signo); + switch (signo) { + case SIGUSR1: + { + if(profile.ipv4_flag) + { + if (QWDS_PKT_DATA_CONNECTED != IPv4ConnectionStatus) { + usbnet_link_change(0, &profile); + requestRegistrationState2(&PSAttachedState); + +//2021-02-05 willa.liu@fibocom.com changed begin for support mantis 0070460 + //if (PSAttachedState == 1 || profile.loopback_state == 0) { + if (PSAttachedState == 1 || profile.loopback_state == 1) { + qmierr = + requestSetupDataCall(&profile, IpFamilyV4); + + if ((qmierr > 0) && profile.user && + profile.user[0] && profile.password && + profile.password[0]) { + int old_auto = profile.auth; + + // may be fail because wrong auth mode, try + // pap->chap, or chap->pap + profile.auth = (profile.auth == 1) ? 2 : 1; + qmierr = requestSetupDataCall(&profile, + IpFamilyV4); + + if (qmierr) + profile.auth = + old_auto; // still fail, restore + // old auth moe + } + if (!qmierr) { + requestGetIPAddress(&profile, IpFamilyV4); + } + if (!qmierr){ + IPv4ConnectionStatus = QWDS_PKT_DATA_CONNECTED; + FailDailRadioTime.tv_sec = 0; + } + else if(FailDailRadioTime.tv_sec == 0) { + gettimeofday(&FailDailRadioTime, (struct timezone *)NULL); + } + } + } + } + if(profile.ipv6_flag) + { + if (QWDS_PKT_DATA_CONNECTED != IPV6ConnectionStatus) { + if(!profile.ipv4_flag) + { + usbnet_link_change(0, &profile); + requestRegistrationState2(&PSAttachedState); + } + else + { + profile.dual_flag = 1; + //PSAttachedState = 1; // for dual stack + requestRegistrationState2(&PSAttachedState); + } +//2021-02-05 willa.liu@fibocom.com changed end for support mantis 0070460 + + if (PSAttachedState == 1) { + qmierr = + requestSetupDataCall(&profile, IpFamilyV6); + if ((g_MobileCountryCode == 460) && (g_MobileNetworkCode == 1 || g_MobileNetworkCode == 6)) { + if (qmierr > 0 && profile.dual_flag == 1) { + profile.dual_flag = 0; + profile.ipv6_flag = 0; + dbg_time("%s Dual stack dial IPv6 failed, quit", __func__); + break; + } + } + if ((qmierr > 0) && profile.user && + profile.user[0] && profile.password && + profile.password[0]) { + int old_auto = profile.auth; + + // may be fail because wrong auth mode, try + // pap->chap, or chap->pap + profile.auth = (profile.auth == 1) ? 2 : 1; + qmierr = requestSetupDataCall(&profile, + IpFamilyV6); + + if (qmierr) + profile.auth = + old_auto; // still fail, restore + // old auth moe + } + if (!qmierr) { + requestGetIPAddress(&profile, IpFamilyV6); + } + + if (!qmierr){ + IPV6ConnectionStatus = QWDS_PKT_DATA_CONNECTED; + FailDailRadioTime.tv_sec = 0; + } + else if(FailDailRadioTime.tv_sec == 0) { + gettimeofday(&FailDailRadioTime, (struct timezone *)NULL); + } + } + } + } +#ifdef CONFIG_RESET_RADIO + if( ((profile.ipv4_flag)&&(IPv4ConnectionStatus != QWDS_PKT_DATA_CONNECTED))|| + ((profile.ipv6_flag)&&(IPV6ConnectionStatus != QWDS_PKT_DATA_CONNECTED))) + { + gettimeofday(&nowTime, (struct timezone *)NULL); + if( ((abs(nowTime.tv_sec - FailDailRadioTime.tv_sec) > CONFIG_RESET_RADIO)) + &&(FailDailRadioTime.tv_sec > resetRadioTime.tv_sec)){ + resetRadioTime = nowTime; + // requestSetOperatingMode(0x06); //same as + // AT+CFUN=0 + requestSetOperatingMode( + 0x01); // same as AT+CFUN=4 + requestSetOperatingMode( + 0x00); // same as AT+CFUN=1 + } + } +#endif + alarm(5); // try to setup data call 5 seconds later + } + break; + + case SIGUSR2: + { + + if (profile.ipv4_flag) + { + requestQueryDataCall(&IPv4ConnectionStatus, IpFamilyV4); + //local ip is different with remote ip + if (QWDS_PKT_DATA_CONNECTED == IPv4ConnectionStatus && check_address(&profile,IpFamilyV4) == 0) { + requestDeactivateDefaultPDP(&profile, IpFamilyV4); + IPv4ConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; + } + } + else + IPv4ConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; + if (profile.ipv6_flag) + requestQueryDataCall(&IPV6ConnectionStatus, IpFamilyV6); + else + IPV6ConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; + if(IPv4ConnectionStatus == QWDS_PKT_DATA_DISCONNECTED && IPV6ConnectionStatus == QWDS_PKT_DATA_DISCONNECTED) + { + usbnet_link_change(0, &profile); + } + if((profile.ipv6_flag && profile.ipv6_flag ) && ((IPv4ConnectionStatus == QWDS_PKT_DATA_CONNECTED && IPV6ConnectionStatus == QWDS_PKT_DATA_CONNECTED)) ) + { + usbnet_link_change(1, &profile); + } + else + if(IPv4ConnectionStatus == QWDS_PKT_DATA_CONNECTED || IPV6ConnectionStatus == QWDS_PKT_DATA_CONNECTED) + { + usbnet_link_change(1, &profile); + } + if((profile.ipv4_flag && IPv4ConnectionStatus == QWDS_PKT_DATA_DISCONNECTED ) || + (profile.ipv6_flag && IPV6ConnectionStatus == QWDS_PKT_DATA_DISCONNECTED )) + { + send_signo_to_main(SIGUSR1); + } + } + break; + + case SIGTERM: + case SIGHUP: + case SIGINT: + if(profile.ipv4_flag && IPv4ConnectionStatus == QWDS_PKT_DATA_CONNECTED) + requestDeactivateDefaultPDP(&profile, IpFamilyV4); + if(profile.ipv6_flag && IPV6ConnectionStatus == QWDS_PKT_DATA_CONNECTED) + requestDeactivateDefaultPDP(&profile, IpFamilyV6); + + usbnet_link_change(0, &profile); + if (profile.qmi_ops->deinit) + profile.qmi_ops->deinit(); + main_send_event_to_qmidevice(RIL_REQUEST_QUIT); + goto __main_quit; + break; + + default: + break; + } + } + } + + if (fd == qmidevice_control_fd[0]) { + if (read(fd, &triger_event, sizeof(triger_event)) == + sizeof(triger_event)) { + switch (triger_event) { + case RIL_INDICATE_DEVICE_DISCONNECTED: + usbnet_link_change(0, &profile); + if (main_loop) { + if (pthread_join(gQmiThreadID, NULL)) { + dbg_time( + "%s Error joining to listener thread (%s)", + __func__, strerror(errno)); + } + profile.qmichannel = NULL; + profile.usbnet_adapter = save_usbnet_adapter; + goto __main_loop; + } + goto __main_quit; + break; + + case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED: + requestRegistrationState2(&PSAttachedState); + if (PSAttachedState == 1) + { +//begin modify for mantis 0050112 by kaibo.zhangkaibo@fibocom.com + if((profile.ipv4_flag && IPv4ConnectionStatus == QWDS_PKT_DATA_DISCONNECTED) + ||(profile.ipv6_flag && IPV6ConnectionStatus == QWDS_PKT_DATA_DISCONNECTED)) +//end modify for mantis 0050112 by kaibo.zhangkaibo@fibocom.com + send_signo_to_main(SIGUSR1); + } + break; + + case RIL_UNSOL_DATA_CALL_LIST_CHANGED: + if (IPv4ConnectionStatus == QWDS_PKT_DATA_CONNECTED || IPV6ConnectionStatus == QWDS_PKT_DATA_CONNECTED) + send_signo_to_main(SIGUSR2); + break; + + default: + break; + } + } + } + } + } + +__main_quit: + usbnet_link_change(0, &profile); + if (pthread_join(gQmiThreadID, NULL)) { + dbg_time("%s Error joining to listener thread (%s)", __func__, + strerror(errno)); + } + close(signal_control_fd[0]); + close(signal_control_fd[1]); + close(qmidevice_control_fd[0]); + close(qmidevice_control_fd[1]); + dbg_time("%s exit", __func__); + if (logfilefp) + fclose(logfilefp); + +//2020-12-23 willa.liu@fibocom.com changed begin for support mantis 0065286 + unlink(fibo_dial_filepath); + close(socket_server_fd ); + close(cli_accept_fd ); +//2020-12-23 willa.liu@fibocom.com changed end for support mantis 0065286 + + return 0; +} diff --git a/fibocom-dial/src/multi-pdn-manager.c b/fibocom-dial/src/multi-pdn-manager.c new file mode 100644 index 000000000..20d05e732 --- /dev/null +++ b/fibocom-dial/src/multi-pdn-manager.c @@ -0,0 +1,1019 @@ +#include +#include +#include +#include +#include +#include +#include +#include +//begin modified by zhangkaibo get qmap_num process flow. mantis 0048920,0048917 20200605 +#include +//end modified by zhangkaibo get qmap_num process flow. mantis 0048920,0048917 20200605 +#include "util.h" +#include "query_pcie_mode.h" +#define MAJOR 1 +#define MINOR 0 +#define REVISION 4 +/* + * Generally, we do not modify version info, so several modifications will share + * the same version code. SUBVERSION is used for customized modification to + * distinguise this version from previous one. SUBVERSION adds up before you + * send the code to customers and it should be set to 0 if VERSION_STRING info + * is changed. + */ +#define SUBVERSION 0 +#define STRINGIFY_HELPER(v) #v +#define STRINGIFY(v) STRINGIFY_HELPER(v) +#define VERSION_STRING() \ + STRINGIFY(MAJOR) "." STRINGIFY(MINOR) "." STRINGIFY(REVISION) +#define MAX_PATH 256 + +#define _PARAM_GLOBALS_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +//begin modified by zhangkaibo get qmap_num process flow. mantis 0048920,0048917 20200605 +#include +//end modified by zhangkaibo get qmap_num process flow. mantis 0048920,0048917 20200605 +#define SECTION_MAX_LEN 256 +#define STRVALUE_MAX_LEN 256 +#define LINE_CONTENT_MAX_LEN 256 +#define READ_STR_ERR -1 +#define READ_STR_OK 0 +//begin modified by zhangkaibo get qmap_num process flow. mantis 0048920,0048917 20200605 + +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 +//#include "query_pcie_mode.h" +//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817 + +static int is_pcie_dial() +{ + return (access("/dev/mhi_QMI0", F_OK) == 0); +} + +static int is_gobinet_dial() +{ + return (access("/dev/qcqmi0", F_OK) == 0); +} + +static int is_qmiwwan_dial() +{ + return (access("/dev/cdc-wdm0", F_OK) == 0); +} + +static size_t fibo_fread(const char *filename, void *buf, size_t size) +{ + FILE *fp = fopen(filename, "r"); + size_t n = 0; + + memset(buf, 0x00, size); + + if (fp) { + n = fread(buf, 1, size, fp); + if (n <= 0 || n == size) { + dbg_time( + "warnning: fail to fread(%s), fread=%zd, buf_size=%zd, " + "errno: %d (%s)", + __func__, filename, n, size, errno, strerror(errno)); + } + fclose(fp); + } + + return n > 0 ? n : 0; +} + +int get_qmap_num() +{ + int n; + int qmap_num=-1; + char buf[128]; + struct { + char filename[255 * 2]; + char linkname[255 * 2]; + } * pl; + + if (is_pcie_dial()) + return 8; + + pl = (typeof(pl))malloc(sizeof(*pl)); + + if(is_gobinet_dial()) + { + snprintf(pl->linkname, sizeof(pl->linkname), + "/sys/class/net/usb0/device/driver"); + n = readlink(pl->linkname, pl->filename, sizeof(pl->filename)); + pl->filename[n] = '\0'; + while (pl->filename[n] != '/') n--; + + snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/usb0/qmap_num"); + //2021-02-22 kaibo.zhangkaibo@fibocom.com changed begin for support mantis 0071171 + if (access(pl->filename, F_OK) == 0) { + dbg_time("access %s", pl->filename); + if (errno != ENOENT) { + dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno, + strerror(errno)); + goto _out; + } + } + else + { + snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/usb0/qmap_mode"); + if (access(pl->filename, F_OK) == 0) { + dbg_time("access %s", pl->filename); + if (errno != ENOENT) { + dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno, + strerror(errno)); + goto _out; + } + } + } + } + if(is_qmiwwan_dial()) + { + snprintf(pl->linkname, sizeof(pl->linkname), + "/sys/class/net/wwan0/device/driver"); + n = readlink(pl->linkname, pl->filename, sizeof(pl->filename)); + pl->filename[n] = '\0'; + while (pl->filename[n] != '/') n--; + + snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/wwan0/qmap_mode"); + if (access(pl->filename, F_OK) == 0) { + dbg_time("access %s", pl->filename); + if (errno != ENOENT) { + dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno, + strerror(errno)); + goto _out; + } + } + } + + dbg_time("access %s", pl->filename); + + if (!access(pl->filename, R_OK)) { + n = fibo_fread(pl->filename, buf, sizeof(buf)); + if (n > 0) { + qmap_num = atoi(buf); + } + } +_out: + free(pl); + + return qmap_num; +} +//end modified by zhangkaibo get qmap_num process flow. mantis 0048920,0048917 20200605 +//begin modified by zhangkaibo limit input parameters. mantis 0048965 20200605 +int IsInt(char* str) +{ + int len; + len = strlen(str-1); + len = len>=1?len:1; + int i=0; + for(; i < len ; i++) + { + if(!(isdigit(str[i]))) + return 0; + } + return 1; +} + +//end modified by zhangkaibo limit input parameters. mantis 0048965 20200605 +// read value from .ini +void IniReadValue(char *section, char *key, char *val, const char *file) +{ + FILE *fp; + int i = 0; + int lineContentLen = 0; + int position = 0; + char lineContent[LINE_CONTENT_MAX_LEN]; + bool bFoundSection = false; + bool bFoundKey = false; + fp = fopen(file, "r"); + if (fp == NULL) { + printf("%s: Opent file %s failed.\n", __FILE__, file); + return; + } + while (feof(fp) == 0) { + memset(lineContent, 0, LINE_CONTENT_MAX_LEN); + fgets(lineContent, LINE_CONTENT_MAX_LEN, fp); + if ((lineContent[0] == ';') || (lineContent[0] == '\0') || + (lineContent[0] == '\r') || (lineContent[0] == '\n')) { + continue; + } + + // check section + if (strncmp(lineContent, section, strlen(section)) == 0) { + bFoundSection = true; + // printf("Found section = %s\n", lineContent); + while (feof(fp) == 0) { + memset(lineContent, 0, LINE_CONTENT_MAX_LEN); + fgets(lineContent, LINE_CONTENT_MAX_LEN, fp); + // check key + if (strncmp(lineContent, key, strlen(key)) == 0) { + bFoundKey = true; + lineContentLen = strlen(lineContent); + // find value + for (i = strlen(key); i < lineContentLen; i++) { + if (lineContent[i] == '=') { + position = i + 1; + break; + } + } + if (i >= lineContentLen) + break; + strncpy(val, lineContent + position, + strlen(lineContent + position)); + lineContentLen = strlen(val); + for (i = 0; i < lineContentLen; i++) { + if ((val[i] == '\0') || (val[i] == '\r') || + (val[i] == '\n')) { + val[i] = '\0'; + // break; + } + } + } else if (lineContent[0] == '[') { + break; + } + } + break; + } + } + if (!bFoundSection) { + printf("No section = %s\n", section); + } else if (!bFoundKey) { + printf("No key = %s\n", key); + } + + fclose(fp); +} + +int readStringValue(const char *section, char *key, char *val, const char *file) +{ + char sect[SECTION_MAX_LEN]; + // printf("section = %s, key = %s, file = %s\n", section, key, file); + if (section == NULL || key == NULL || val == NULL || file == NULL) { + printf("%s: input parameter(s) is NULL!\n", __func__); + return READ_STR_ERR; + } + + memset(sect, 0, SECTION_MAX_LEN); + sprintf(sect, "[%s]", section); + // printf("reading value...\n"); + IniReadValue(sect, key, val, file); + + return READ_STR_OK; +} + +char fileContents[80][LINE_CONTENT_MAX_LEN]; +int fileIndex = 0; + +void IniWriteValue(const char *section, char *key, char *val, const char *file) +{ + int err = 0; + char lineContent[LINE_CONTENT_MAX_LEN]; + char strWrite[LINE_CONTENT_MAX_LEN]; + bool bFoundSection = false; + bool bFoundKey = false; + int fileIndextemp = 0; + memset(lineContent, '\0', LINE_CONTENT_MAX_LEN); + memset(strWrite, '\0', LINE_CONTENT_MAX_LEN); + sprintf(strWrite, "%s=%s\n", key, val); + + while (fileIndextemp <= fileIndex) { + memset(lineContent, 0, LINE_CONTENT_MAX_LEN); + memcpy(lineContent, fileContents[fileIndextemp++], + LINE_CONTENT_MAX_LEN); + if ((lineContent[0] == ';') || (lineContent[0] == '\0') || + (lineContent[0] == '\r') || (lineContent[0] == '\n')) { + continue; + } + // check section + if (strncmp(lineContent, section, strlen(section)) == 0) { + bFoundSection = true; + while (fileIndextemp <= fileIndex) { + memset(lineContent, 0, LINE_CONTENT_MAX_LEN); + memcpy(lineContent, fileContents[fileIndextemp], + LINE_CONTENT_MAX_LEN); + // check key + if (strncmp(lineContent, key, strlen(key)) == 0) { + bFoundKey = true; + // printf("%s: %s=%s\n", __func__, key, val); + memset(fileContents[fileIndextemp], 0, + LINE_CONTENT_MAX_LEN); + memcpy(fileContents[fileIndextemp], strWrite, + strlen(strWrite)); + // printf("%s\n",strWrite); + if (err < 0) { + printf("%s err.\n", __func__); + } + break; + } else if (lineContent[0] == '[') { + fileIndextemp++; + break; + } + fileIndextemp++; + } + break; + } + } + if (!bFoundSection) { + printf("No section = %s\n", section); + } else if (!bFoundKey) { + printf("No key = %s\n", key); + } +} + +int writeStringVlaue(const char *section, char *key, char *val, + const char *file) +{ + char sect[SECTION_MAX_LEN]; + + // printf("section = %s, key = %s, value=%s , file = %s\n", section, key, + // val, file); + if (section == NULL || key == NULL || val == NULL || file == NULL) { + printf("%s: input parameter(s) is NULL!\n", __func__); + return READ_STR_ERR; + } + memset(sect, '\0', SECTION_MAX_LEN); + sprintf(sect, "[%s]", section); + IniWriteValue(sect, key, val, file); + return READ_STR_OK; +} + +int writeIntValue(const char *section, char *key, int val, const char *file) +{ + char strValue[STRVALUE_MAX_LEN]; + memset(strValue, '\0', STRVALUE_MAX_LEN); + sprintf(strValue, "%d", val); + + return writeStringVlaue(section, key, strValue, file); +} + +int readIntValue(const char *section, char *key, const char *file) +{ + char strValue[STRVALUE_MAX_LEN]; + memset(strValue, '\0', STRVALUE_MAX_LEN); + if (readStringValue(section, key, strValue, file) != READ_STR_OK) { + printf("%s: error", __func__); + return 0; + } + return (atoi(strValue)); +} + +int mygetch() +{ + struct termios oldt, newt; + int ch; + tcgetattr(STDIN_FILENO, &oldt); + newt = oldt; + newt.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &newt); + ch = getchar(); + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + return ch; +} + +#define CONFIG_FILE "multi-pdn.ini" +#define DIAL_PROCESS "fibocom-dial" +#define MAX_QMAP_NUM 8 + +struct profile { + char apn[MAX_PATH]; + char user[MAX_PATH]; + char password[MAX_PATH]; + int auth; + int ipfamily; +//begin modified by zhangkaibo add sim pin operate. mantis 0049142 20200612 + char pin[MAX_PATH]; +//end modified by zhangkaibo add sim pin operate. mantis 0049142 20200612 +}; + +int shmid; +void *shm; +int qmap_num = -1; + +bool connectState[MAX_QMAP_NUM]; +int lock_file = -1; + +void initShareMemory() +{ + shmid = shmget((key_t)9527, sizeof(int) * 8, 0666 | IPC_CREAT); + if (shmid == -1) { + fprintf(stderr, "shmget failed\n"); + exit(EXIT_FAILURE); + } + shm = shmat(shmid, 0, 0); + if (shm == (void *)-1) { + fprintf(stderr, "shmat failed\n"); + exit(EXIT_FAILURE); + } +} + +void destoryShareMemory() +{ + if (shmdt(shm) == -1) { + fprintf(stderr, "shmdt failed\n"); + exit(EXIT_FAILURE); + } + + if (shmctl(shmid, IPC_RMID, 0) == -1) { + fprintf(stderr, "shmctl(IPC_RMID) failed\n"); + exit(EXIT_FAILURE); + } +} + +void SIGINT_handle(int sig_num) + +{ + printf("reveice signal %d \n", sig_num); + { + int *connectPids = (int *)shm; + int cIndex; + for(cIndex = 1 ; cIndex <= qmap_num ; cIndex++) + { + dbg_time("kill pid %d", connectPids[cIndex - 1]); + kill(connectPids[cIndex - 1], SIGINT); + connectState[cIndex - 1] = false; + } + } + destoryShareMemory(); + close(lock_file); // 不要忘记释放文件指针 + exit(0); +} + +int loadConfigFromIni(struct profile *profileList, bool *connectState) +{ + int profileIndex; +//begin modified by zhangkaibo get qmap_num process flow. mantis 0048920,0048917 20200605 + //qmap_num = readIntValue("Setting", "qmap_num", CONFIG_FILE); + qmap_num = get_qmap_num(); + if (qmap_num == 0 || qmap_num == -1 ) + return -1; +//end modified by zhangkaibo get qmap_num process flow. mantis 0048920,0048917 20200605 + for (profileIndex = 0; profileIndex < 8; profileIndex++) { + char profilename[MAX_PATH]; + sprintf(profilename, "profile%d", profileIndex + 1); + memset(profileList[profileIndex].apn, 0, MAX_PATH); + readStringValue(profilename, "apn", profileList[profileIndex].apn, + CONFIG_FILE); + memset(profileList[profileIndex].user, 0, MAX_PATH); + readStringValue(profilename, "user", profileList[profileIndex].user, + CONFIG_FILE); + memset(profileList[profileIndex].password, 0, MAX_PATH); + readStringValue(profilename, "password", + profileList[profileIndex].password, CONFIG_FILE); + profileList[profileIndex].auth = + readIntValue(profilename, "auth", CONFIG_FILE); + profileList[profileIndex].ipfamily = + readIntValue(profilename, "ipfamily", CONFIG_FILE); +//begin modified by zhangkaibo add sim pin operate. mantis 0049142 20200612 + memset(profileList[profileIndex].pin, 0, MAX_PATH); + readStringValue(profilename, "pin", profileList[profileIndex].pin, + CONFIG_FILE); +//end modified by zhangkaibo add sim pin operate. mantis 0049142 20200612 + } + FILE *fp; + fileIndex = 0; + char lineContent[LINE_CONTENT_MAX_LEN]; + fp = fopen(CONFIG_FILE, "r+"); + if (fp == NULL) { + printf("%s: Opent file %s failed.\n", __FILE__, CONFIG_FILE); + return -2; + } + // read all + while (feof(fp) == 0) { + memset(lineContent, 0, LINE_CONTENT_MAX_LEN); + fgets(lineContent, LINE_CONTENT_MAX_LEN, fp); + memcpy(fileContents[fileIndex], lineContent, strlen(lineContent)); + // printf("%s",fileContents[fileIndex]); + fileIndex++; + } + fclose(fp); + return 0; +} + +void saveConfigToIni() +{ + FILE *fp; + fp = fopen(CONFIG_FILE, "w"); + int fileIndextemp = 0; + fseek(fp, 0, SEEK_SET); + while (fileIndextemp <= fileIndex) { + fwrite(fileContents[fileIndextemp], strlen(fileContents[fileIndextemp]), + 1, fp); + // printf("%s",fileContents[fileIndextemp]); + fileIndextemp++; + } + fclose(fp); +} + +void modifyProfile(struct profile *profileList) +{ + int profileIndex; +//begin modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + struct profile tempprofile; +//end modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + char *f; + // show current fprfile setting + for (profileIndex = 0; profileIndex < 8; profileIndex++) { + dbg_time( + "Fibocom manager \nprofile%d apn:\"%s\" username:\"%s\" password: " + "\"%s\" auth: \"%d\" ip family: \"%d\"", + profileIndex + 1, profileList[profileIndex].apn, + profileList[profileIndex].user, profileList[profileIndex].password, + profileList[profileIndex].auth, profileList[profileIndex].ipfamily); + } + // select instance to be set + int connectProfile; + printf("set profile index:"); + f = malloc(sizeof(f)); + fgets(f,sizeof(f),stdin); + connectProfile = atoi(f); +//begin modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + if(!IsInt(f)){ + dbg_time("invalid input"); + return; + } +//end modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + if (connectProfile < 0 || connectProfile > 8) { + dbg_time("error profile id"); + return; + } + free(f); + /* + * Begin: 2020-11-16, Lizhengyang for multi-pdn-manager, + * Solve the problem of setting length + * of apn/username/password, mantis 0062495. + */ + printf("profile apn:"); + f = malloc(MAX_PATH); + if (f == NULL) + { + printf("profile apn f malloc null\n"); + return; + } + fgets(f, MAX_PATH, stdin); + f[strlen(f) - 1] = '\0'; + strcpy(tempprofile.apn, f); + free(f); + + printf("profile username:"); + f = malloc(MAX_PATH); + if (f == NULL) + { + printf("profile username f malloc null\n"); + return; + } + fgets(f, MAX_PATH, stdin); + f[strlen(f) - 1] = '\0'; + strcpy(tempprofile.user, f); + free(f); + + printf("profile password:"); + f = malloc(MAX_PATH); + if (f == NULL) + { + printf("profile password f malloc null\n"); + return; + } + fgets(f, MAX_PATH, stdin); + f[strlen(f) - 1] = '\0'; + strcpy(tempprofile.password, f); + free(f); + /* + * End: 2020-11-16, Lizhengyang for multi-pdn-manager, + * Solve the problem of setting length + * of apn/username/password, mantis 0062495. + */ + printf("profile auth[0-3]:"); + f = malloc(sizeof(f)); + fgets(f,sizeof(f),stdin); +//begin modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + tempprofile.auth = atoi(f); + if(!IsInt(f)){ + dbg_time("invalid input"); + return; + } + if(tempprofile.auth < 0 || tempprofile.auth > 3){ + dbg_time("invalid auth type"); + return; + } +//end modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + free(f); + + printf("profile ipfamily[1-3]:"); + f = malloc(sizeof(f)); + fgets(f,sizeof(f),stdin); +//begin modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + tempprofile.ipfamily = atoi(f); + if(!IsInt(f)){ + dbg_time("invalid input"); + return; + } + free(f); + if(tempprofile.ipfamily < 1 || tempprofile.ipfamily > 3){ + dbg_time("invalid auth type"); + return; + } +//begin modified by zhangkaibo add sim pin operate. mantis 0049142 20200612 + printf("pin:"); + f = malloc(sizeof(f)); + fgets(f,sizeof(f),stdin); + f[strlen(f)-1]='\0'; + strcpy(tempprofile.pin, f); + free(f); +//end modified by zhangkaibo add sim pin operate. mantis 0049142 20200612 + memset(profileList[connectProfile-1].apn, 0, MAX_PATH); + memset(profileList[connectProfile-1].user, 0, MAX_PATH); + memset(profileList[connectProfile-1].password, 0, MAX_PATH); + + strcpy(profileList[connectProfile-1].apn , tempprofile.apn); + strcpy(profileList[connectProfile-1].user , tempprofile.user); + strcpy(profileList[connectProfile-1].password , tempprofile.password); + profileList[connectProfile-1].auth = tempprofile.auth; + profileList[connectProfile-1].ipfamily = tempprofile.ipfamily; +//end modified by zhangkaibo limit input parameters. mantis 0048965 20200605 +//begin modified by zhangkaibo add sim pin operate. mantis 0049142 20200612 + //strcpy(profileList[connectProfile].pin , tempprofile.pin); + strcpy(profileList[connectProfile-1].pin , tempprofile.pin); +//end modified by zhangkaibo add sim pin operate. mantis 0049142 20200612 + // save in + for (profileIndex = 0; profileIndex < qmap_num; profileIndex++) { + dbg_time( + "Fibocom manager \nprofile%d apn:\"%s\" username:\"%s\" pawssword: " + "\"%s\" auth: \"%d\" ip family: \"%d\"", + profileIndex + 1, profileList[profileIndex].apn, + profileList[profileIndex].user, profileList[profileIndex].password, + profileList[profileIndex].auth, profileList[profileIndex].ipfamily); + char profilename[MAX_PATH]; + sprintf(profilename, "profile%d", profileIndex + 1); + writeStringVlaue(profilename, "apn", profileList[profileIndex].apn, + CONFIG_FILE); + writeStringVlaue(profilename, "user", profileList[profileIndex].user, + CONFIG_FILE); + writeStringVlaue(profilename, "password", + profileList[profileIndex].password, CONFIG_FILE); + writeIntValue(profilename, "auth", profileList[profileIndex].auth, + CONFIG_FILE); + writeIntValue(profilename, "ipfamily", + profileList[profileIndex].ipfamily, CONFIG_FILE); +//begin modified by zhangkaibo add sim pin operate. mantis 0049142 20200612 + writeStringVlaue(profilename, "pin", + profileList[profileIndex].pin, CONFIG_FILE); +//end modified by zhangkaibo add sim pin operate. mantis 0049142 20200612 + } +} + +void startConnectCM(struct profile *profileList, bool *connectState) +{ + int connectIndex; + int connectProfile; + char *f; + printf("connect visual net interface use:"); + f = malloc(sizeof(f)); + fgets(f,sizeof(f),stdin); + connectIndex = atoi(f); + free(f); + if (connectIndex < 1 || connectIndex > qmap_num) { + dbg_time("error visual net interface "); + return; + } + printf("connect profile use:"); + f = malloc(sizeof(f)); + fgets(f,sizeof(f),stdin); + connectProfile = atoi(f); + free(f); + if (connectProfile < 1 || connectProfile > 8) { + dbg_time("error profile id"); + return; + } +//begin modified by zhangkaibo modify mantis0048806. 20200605 + if (connectState[connectIndex - 1] == true) { + dbg_time( + "visual net interface %d now is connected", + connectIndex); + return; + } +//end modified by zhangkaibo modify mantis0048806. 20200605 + dbg_time("Start connect network use instance %d profile %d", connectIndex, + connectProfile); + char cmd[1024]; + //./fibicom-dial -n -s -s apn user password auth -4 -6 + + sprintf(cmd, "-n %d -m %d -s %s %s %s %d", connectIndex, connectProfile, + profileList[connectProfile - 1].apn, + profileList[connectProfile - 1].user, + profileList[connectProfile - 1].password, + profileList[connectProfile - 1].auth); + char *argvCmd[30]; + int index = 0; + // DIAL_PROCESS,","-N","4,""-n","2","-m","1","-s","ctnet","","","","-4","-6","-f","temp.txt",NULL; + argvCmd[index] = malloc(256); + sprintf(argvCmd[index++], "%s", DIAL_PROCESS); + argvCmd[index] = malloc(5); + sprintf(argvCmd[index++], "-N"); + argvCmd[index] = malloc(256); + sprintf(argvCmd[index++], "%d", qmap_num); + argvCmd[index] = malloc(5); + sprintf(argvCmd[index++], "-n"); + argvCmd[index] = malloc(256); + sprintf(argvCmd[index++], "%d", connectIndex); + argvCmd[index] = malloc(5); + sprintf(argvCmd[index++], "-m"); + argvCmd[index] = malloc(256); + sprintf(argvCmd[index++], "%d", connectProfile); + argvCmd[index] = malloc(5); + sprintf(argvCmd[index++], "-s"); + argvCmd[index] = malloc(256); + sprintf(argvCmd[index++], "%s", profileList[connectProfile - 1].apn); + if (strlen(profileList[connectProfile - 1].user) > 0) { + argvCmd[index] = malloc(256); + sprintf(argvCmd[index++], "%s", profileList[connectProfile - 1].user); + argvCmd[index] = malloc(256); + sprintf(argvCmd[index++], "%s", + profileList[connectProfile - 1].password); + argvCmd[index] = malloc(256); + sprintf(argvCmd[index++], "%d", profileList[connectProfile - 1].auth); + } + + if (profileList[connectProfile - 1].ipfamily == 2) { + argvCmd[index] = malloc(5); +//begin modified by zhangkaibo fix ipv6 dial process flow. mantis 0048789 20200605 + sprintf(argvCmd[index++], "-6"); +//end modified by zhangkaibo fix ipv6 dial process flow. mantis 0048789 20200605 + } else if (profileList[connectProfile - 1].ipfamily == 3) { + argvCmd[index] = malloc(5); + sprintf(argvCmd[index++], "-4"); + argvCmd[index] = malloc(5); + sprintf(argvCmd[index++], "-6"); + } else { + argvCmd[index] = malloc(5); + sprintf(argvCmd[index++], "-4"); + } +//begin modified by zhangkaibo add sim pin operate. mantis 0049142 20200612 + if (strlen(profileList[connectProfile - 1].pin) > 0) { + argvCmd[index] = malloc(5); + sprintf(argvCmd[index++], "-p"); + argvCmd[index] = malloc(256); + sprintf(argvCmd[index++], "%s", profileList[connectProfile - 1].pin); + } + + argvCmd[index] = malloc(5); +//end modified by zhangkaibo add sim pin operate. mantis 0049142 20200612 + sprintf(argvCmd[index++], "-f"); + argvCmd[index] = malloc(256); + sprintf(argvCmd[index++], "instance%d.txt", connectIndex); + argvCmd[index] = NULL; + char temp[MAX_PATH]; + sprintf(temp, "rm -f instance%d.txt", connectIndex); + system(temp); + if (0 == fork()) { + int *connectPids = (int *)shm; + connectPids[connectIndex - 1] = getpid(); + dbg_time("exec pid %d", connectPids[connectIndex - 1]); + char tempcmd[MAX_PATH]; + sprintf(tempcmd, "./%s", DIAL_PROCESS); + execvp(tempcmd, argvCmd); + // system(cmd); +//begin modified by zhangkaibo add sim pin operate. mantis 0049142 20200612 + } +//2021-02-01 willa.liu@fibocom.com changed begin for support mantis 0069837 + //for(int ii=0; ii < index;ii++) + int ii; + for(ii = 0;ii < index;ii++) +//2021-02-01 willa.liu@fibocom.com changed end for support mantis 0069837 + { + printf("%s ",argvCmd[ii]); + free(argvCmd[ii]); + } + printf("\r\n"); + connectState[connectIndex - 1] = true; +} + +int main(int argc, char *argv[]) +{ + struct profile profileList[MAX_QMAP_NUM]; + char *f; + int profileIndex; + int opsCode = -1; + int ret=0; + + /* Exit if the process is already running */ + lock_file = open("/tmp/single_proc.lock", O_CREAT|O_RDWR, 0666); + ret = flock(lock_file, LOCK_EX|LOCK_NB); + if (ret) + { + if (EWOULDBLOCK == errno) + { + dbg_time("The Fibocom multi-pdn-manager is already running! So killall multi-pdn-manager!"); + system("killall -9 fibocom-dial"); + system("killall -9 multi-pdn-manager"); + } + } + else + { + char buffer[64]; + sprintf(buffer, "pid:%d\n", getpid()); + write(lock_file, buffer, strlen(buffer)); + system("killall -9 fibocom-dial"); + dbg_time("Start Fibocom multi-pdn-manager!\n"); + } + + system("clear"); + + get_pcie_mode(); + + signal(SIGCHLD, SIG_IGN); + + // read from config + ret = loadConfigFromIni(profileList, connectState); + if(ret == -1) + { + dbg_time("Fibocom multi-pdn-manager %s", VERSION_STRING()); + dbg_time("Fibocom manager current qmap_num is %d,not support multi-pdn", qmap_num); + dbg_time("please use \"echo 4 > /sys/class/net/usb0/qmap_num\",set driver to multi mode"); + return -1; + } + if(ret == -2) + return -2; + initShareMemory(); + signal(SIGINT, SIGINT_handle); + if (is_pcie_dial()) + { + dbg_time("start qmi proxy server"); + //system("./fibo_qmimsg_server -d /dev/mhi_QMI0 &"); + } + while (1) + { + // show tips + dbg_time("Fibocom multi-pdn-manager %s", VERSION_STRING()); + dbg_time("Fibocom manager current qmap_num is %d", qmap_num); + for (profileIndex = 0; profileIndex < qmap_num; profileIndex++) + { + dbg_time("Fibocom manager sub-instance%d %s", profileIndex + 1, + connectState[profileIndex] ? "Connected" : "Disconnected"); + } + printf("Please select an action[0-9]\n"); + printf("1.show profile setting\n"); + printf("2.set profile setting\n"); + printf("3.connect to network\n"); + printf("4.disconnect to network\n"); + printf("5.show connect log\n"); + printf("6.reload profile setting\n"); + printf("7.save profile setting\n"); + printf("8.show max instance number\n"); + printf("9.set max instance number\n"); + printf("0.exit\n"); + + // get operate + printf("input [0-9]: "); + f = malloc(sizeof(f)); + //begin modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + fgets(f,sizeof(f),stdin); + if(!IsInt(f)) + { + dbg_time("invalid input"); + printf("Press any key to continus"); + getchar(); + system("clear"); + continue; + } + //end modified by zhangkaibo limit input parameters. mantis 0048965 20200605 + opsCode = atoi(f); + free(f); + + // select operate + switch (opsCode) + { + case 1: + //begin modified by zhangkaibo add sim pin operate. mantis 0049142 20200612 + for (profileIndex = 0; profileIndex < qmap_num; profileIndex++) + { + dbg_time( + "Fibocom manager \nprofile%d apn:\"%s\" username:\"%s\" " + "password: \"%s\" auth: \"%d\" ip family: \"%d\" pin:\"%s\"", + profileIndex + 1, profileList[profileIndex].apn, + profileList[profileIndex].user, + profileList[profileIndex].password, + profileList[profileIndex].auth, + profileList[profileIndex].ipfamily, + profileList[profileIndex].pin); + } + //end modified by zhangkaibo add sim pin operate. mantis 0049142 20200612 + break; + case 2: + { + modifyProfile(profileList); + } + break; + case 3: + { + startConnectCM(profileList, connectState); + break; + } + case 4: + { + int connectIndex; + printf("disconnect visual net interface :"); + f = malloc(sizeof(f)); + fgets(f,sizeof(f),stdin); + connectIndex = atoi(f); + free(f); + int *connectPids = (int *)shm; + if (connectIndex < 1 || connectIndex > qmap_num) + { + dbg_time("error visual net interface id"); + break; + } + if (connectState[connectIndex - 1] == false) + { + dbg_time("visual net interface %d now is disconnected", connectIndex); + break; + } + dbg_time("kill pid %d", connectPids[connectIndex - 1]); + kill(connectPids[connectIndex - 1], SIGINT); + connectState[connectIndex - 1] = false; + } + break; + case 5: + { + int connectIndex; + printf("show visual net interface :"); + f = malloc(sizeof(f)); + fgets(f,sizeof(f),stdin); + connectIndex = atoi(f); + free(f); + if (connectIndex < 1 || connectIndex > qmap_num) + { + dbg_time("error visual net interface id"); + break; + } + char temp[MAX_PATH]; + sprintf(temp, "cat instance%d.txt", connectIndex); + system(temp); + } + break; + case 6: + { + loadConfigFromIni(profileList, connectState); + } + break; + case 7: + { + saveConfigToIni(profileList); + } + break; + case 8: + printf("qmap_num : %d", qmap_num); + break; + case 9: + { + int temp_qmap; + printf("set qmap_num :"); + f = malloc(sizeof(f)); + fgets(f,sizeof(f),stdin); + temp_qmap = atoi(f); + free(f); + if (temp_qmap < 1 || temp_qmap > 8) + { + dbg_time("error qmap_num id"); + break; + } + qmap_num = temp_qmap; + } + break; + case 0: + printf("exit? [Y/N] :"); + f = malloc(sizeof(f)); + fgets(f,sizeof(f),stdin); + if (f[0] == 'y' || f[0] == 'Y') + goto __main_exit; + break; + default: + dbg_time("invalid input"); + }; + printf("Press any key to continus"); + getchar(); + system("clear"); + } +__main_exit: + { + int *connectPids = (int *)shm; + int cIndex; + for(cIndex = 1 ; cIndex <= qmap_num ; cIndex++) + { + dbg_time("kill pid %d", connectPids[cIndex - 1]); + kill(connectPids[cIndex - 1], SIGINT); + connectState[cIndex - 1] = false; + } + //system("ps -ef|grep fibo|grep -v grep|cut -c 9-15|xargs kill"); + } + destoryShareMemory(); + close(lock_file); // 不要忘记释放文件指针 + return 0; +} diff --git a/fibocom-dial/src/multi-pdn.ini b/fibocom-dial/src/multi-pdn.ini new file mode 100644 index 000000000..182a7ef47 --- /dev/null +++ b/fibocom-dial/src/multi-pdn.ini @@ -0,0 +1,67 @@ +[Setting] +qmap_num=4 + +[profile1] +apn=1234567890 +user=qwertyuio +password=asdfghjkl +auth=2 +ipfamily=1 +pin= + +[profile2] +apn=ctnet +user= +password= +auth=0 +ipfamily=1 +pin= + +[profile3] +apn=ctnet +user= +password= +auth=0 +ipfamily=1 +pin= + +[profile4] +apn=ctnet +user= +password= +auth=0 +ipfamily=1 +pin= + +[profile5] +apn=ctnet +user= +password= +auth= +ipfamily=1 +pin= + +[profile6] +apn=ctnet +user= +password= +auth= +ipfamily=1 +pin= + +[profile7] +apn=ctnet +user= +password= +auth= +ipfamily=1 +pin= + +[profile8] +apn=ctnet +user= +password= +auth= +ipfamily=1 +pin= + diff --git a/fibocom-dial/src/qmap_bridge_mode.c b/fibocom-dial/src/qmap_bridge_mode.c new file mode 100644 index 000000000..cabf76d52 --- /dev/null +++ b/fibocom-dial/src/qmap_bridge_mode.c @@ -0,0 +1,438 @@ +#include "QMIThread.h" +//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037 +#include +//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037 + +static size_t fibo_fread(const char *filename, void *buf, size_t size) +{ + FILE *fp = fopen(filename, "r"); + size_t n = 0; + + memset(buf, 0x00, size); + + if (fp) { + n = fread(buf, 1, size, fp); + if (n <= 0 || n == size) { + dbg_time( + "warnning: fail to fread(%s), fread=%zd, buf_size=%zd, " + "errno: %d (%s)", + __func__, filename, n, size, errno, strerror(errno)); + } + fclose(fp); + } + + return n > 0 ? n : 0; +} + +static size_t fibo_fwrite(const char *filename, const void *buf, size_t size) +{ + FILE *fp = fopen(filename, "w"); + size_t n = 0; + + if (fp) { + n = fwrite(buf, 1, size, fp); + if (n != size) { + dbg_time( + "warnning: fail to fwrite(%s), fwrite=%zd, buf_size=%zd, " + "errno: %d (%s)", + __func__, filename, n, size, errno, strerror(errno)); + } + fclose(fp); + } + + return n > 0 ? n : 0; +} + +static int fibo_iface_is_in_bridge(const char *iface) +{ + char filename[256]; + + snprintf(filename, sizeof(filename), "/sys/class/net/%s/brport", iface); + return (access(filename, F_OK) == 0 || errno != ENOENT); +} + +int fibo_bridge_mode_detect(PROFILE_T *profile) +{ + const char *ifname = profile->qmapnet_adapter ? profile->qmapnet_adapter + : profile->usbnet_adapter; + const char *driver; + char bridge_mode[128]; + char bridge_ipv4[128]; + char ipv4[128]; + char buf[64]; + size_t n; + int in_bridge; + + driver = profile->driver_name; + snprintf(bridge_mode, sizeof(bridge_mode), "/sys/class/net/%s/bridge_mode", + ifname); + snprintf(bridge_ipv4, sizeof(bridge_ipv4), "/sys/class/net/%s/bridge_ipv4", + ifname); + + if (access(bridge_mode, F_OK) && errno == ENOENT) { + snprintf(bridge_mode, sizeof(bridge_mode), + "/sys/module/%s/parameters/bridge_mode", driver); + snprintf(bridge_ipv4, sizeof(bridge_ipv4), + "/sys/module/%s/parameters/bridge_ipv4", driver); + + if (access(bridge_mode, F_OK) && errno == ENOENT) { + bridge_mode[0] = '\0'; + } + } + + in_bridge = fibo_iface_is_in_bridge(ifname); + if (in_bridge) { + dbg_time("notice: iface %s had add to bridge\n", ifname); + } + + if (in_bridge && bridge_mode[0] == '\0') { + dbg_time("warnning: can not find bride_mode file for %s\n", ifname); + return 1; + } + + n = fibo_fread(bridge_mode, buf, sizeof(buf)); + + if (in_bridge) { + if (n <= 0 || buf[0] == '0') { + dbg_time("warnning: should set 1 to bride_mode file for %s\n", + ifname); + return 1; + } + } else { + if (n <= 0 || buf[0] == '0') { + return 0; + } + } + + memset(ipv4, 0, sizeof(ipv4)); + + if (strstr(bridge_ipv4, "/sys/class/net/") || profile->qmap_mode == 0 || + profile->qmap_mode == 1) { + snprintf(ipv4, sizeof(ipv4), "0x%x", profile->ipv4.Address); + dbg_time("echo '%s' > %s", ipv4, bridge_ipv4); + fibo_fwrite(bridge_ipv4, ipv4, strlen(ipv4)); + } else { + snprintf(ipv4, sizeof(ipv4), "0x%x:%d", profile->ipv4.Address, + profile->muxid); + dbg_time("echo '%s' > %s", ipv4, bridge_ipv4); + fibo_fwrite(bridge_ipv4, ipv4, strlen(ipv4)); + } + + return 1; +} +//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037 +void fibo_get_driver_info(PROFILE_T *profile, RMNET_INFO *rmnet_info) { + int ifc_ctl_sock; + struct ifreq ifr; + int rc; + int request = 0x89F3; + unsigned char data[512]; + + memset(rmnet_info, 0x00, sizeof(*rmnet_info)); + + ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (ifc_ctl_sock <= 0) { + dbg_time("socket() failed: %s\n", strerror(errno)); + return; + } + + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_name, profile->usbnet_adapter, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ - 1] = 0; + ifr.ifr_ifru.ifru_data = (void *)data; + rc = ioctl(ifc_ctl_sock, request, &ifr); + if (rc < 0) { + dbg_time("ioctl(0x%x, qmap_settings) failed: %s, rc=%d", request, strerror(errno), rc); + } + else { + memcpy(rmnet_info, data, sizeof(*rmnet_info)); + } + + close(ifc_ctl_sock); +} +//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037 +int fibo_qmap_mode_detect(PROFILE_T *profile) +{ + int n; + char buf[128]; + char qmap_netcard[128]; + struct { + char filename[255 * 2]; + char linkname[255 * 2]; + } * pl; + + pl = (typeof(pl))malloc(sizeof(*pl)); + + snprintf(pl->linkname, sizeof(pl->linkname), + "/sys/class/net/%s/device/driver", profile->usbnet_adapter); + n = readlink(pl->linkname, pl->filename, sizeof(pl->filename)); + pl->filename[n] = '\0'; + while (pl->filename[n] != '/') n--; + profile->driver_name = strdup(&pl->filename[n + 1]); + +//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037 + fibo_get_driver_info(profile, &profile->rmnet_info); + if (profile->rmnet_info.size) { + profile->qmap_mode = profile->rmnet_info.qmap_mode; + if (profile->qmap_mode) { + int offset_id = profile->pdp - 1; + + if (profile->qmap_mode == 1) + offset_id = 0; + profile->muxid = profile->rmnet_info.mux_id[offset_id]; + profile->qmapnet_adapter = strdup( profile->rmnet_info.ifname[offset_id]); + profile->qmap_size = profile->rmnet_info.rx_urb_size; + profile->qmap_version = profile->rmnet_info.qmap_version; + } + + goto _out; + } +//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037 + + if (qmidev_is_pciemhi(profile->qmichannel)) { + profile->qmap_mode = 1; + + if (profile->muxid == 0 || profile->muxid == 0x81) + { + profile->muxid = 0x81; + } + else + { + if (profile->muxid < 0x80) + profile->muxid += 0x81; + profile->qmap_mode = 2; + } + + profile->qmapnet_adapter = strdup(profile->usbnet_adapter); + goto _final_process; + } + + snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_num", + profile->usbnet_adapter); + +//2021-01-27 willa.liu@fibocom.com changed begin for support mantis 0068849 + if (access(pl->filename, F_OK) == 0) { + dbg_time("access %s", pl->filename); + if (errno != ENOENT) { + dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno, + strerror(errno)); + goto _out; + } +//2021-02-01 willa.liu@fibocom.com changed begin for support mantis 0069837 +/* + snprintf(pl->filename, sizeof(pl->filename), + "/sys/module/%s/parameters/qmap_num", profile->driver_name); + if (access(pl->filename, R_OK)) { + if (errno != ENOENT) { + dbg_time("fail to access %s, errno: %d (%s)", pl->filename, + errno, strerror(errno)); + goto _out; + } + + snprintf( + pl->filename, sizeof(pl->filename), + "/sys/class/net/%s/device/driver/module/parameters/qmap_num", + profile->usbnet_adapter); + if (access(pl->filename, R_OK)) { + if (errno != ENOENT) { + dbg_time("fail to access %s, errno: %d (%s)", pl->filename, + errno, strerror(errno)); + goto _out; + } + } + } +*/ +//2021-02-01 willa.liu@fibocom.com changed end for support mantis 0069837 + } + else + { + snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_mode", + profile->usbnet_adapter); + + if (access(pl->filename, F_OK) == 0) { + dbg_time("access %s", pl->filename); + if (errno != ENOENT) { + dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno, + strerror(errno)); + goto _out; + } + } + } + + if(!access(pl->filename, R_OK)) + { + n = fibo_fread(pl->filename, buf, sizeof(buf)); + if(n > 0) + { + profile->qmap_mode = atoi(buf); + if(profile->qmap_mode >= 1 && qmidev_is_pciemhi(profile->qmichannel)) + { + profile->muxid = + profile->pdp + 0x80; + sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter,profile->pdp); + profile->qmapnet_adapter = strdup(qmap_netcard); + } + if(qmidev_is_gobinet(profile->qmichannel) || qmidev_is_qmiwwan(profile->qmichannel)) + { + if(profile->qmap_mode > 1) + { + profile->muxid = + profile->pdp + 0x80; + sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter,profile->pdp); + profile->qmapnet_adapter = strdup(qmap_netcard); + } + if(profile->qmap_mode == 1) + { + profile->muxid = 0x81; + profile->qmapnet_adapter = strdup(profile->usbnet_adapter); + } + } + } + + + if (0) { + profile->qmap_mode = atoi(buf); + + if (profile->qmap_mode > 1 && qmidev_is_gobinet(profile->qmichannel)) { + profile->muxid = + profile->pdp + 0x80; // muxis is 0x8X for PDN-X + sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter,profile->pdp); + profile->qmapnet_adapter = strdup(qmap_netcard); + } + if (profile->qmap_mode >= 1 && !qmidev_is_gobinet(profile->qmichannel)) { + profile->muxid = + profile->pdp + 0x80; // muxis is 0x8X for PDN-X + sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter,profile->pdp); + profile->qmapnet_adapter = strdup(qmap_netcard); + } + if (profile->qmap_mode == 1 && qmidev_is_gobinet(profile->qmichannel)) { + profile->muxid = 0x81; + profile->qmapnet_adapter = strdup(profile->usbnet_adapter); +//2021-01-27 willa.liu@fibocom.com changed end for support mantis 0068849 + + } + } + } else if (qmidev_is_qmiwwan(profile->qmichannel)) { + snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/qmimux%d", + profile->pdp - 1); + if (access(pl->filename, R_OK)) { + if (errno != ENOENT) { + dbg_time("fail to access %s, errno: %d (%s)", pl->filename, + errno, strerror(errno)); + } + goto _out; + } + + // upstream Kernel Style QMAP qmi_wwan.c + snprintf(pl->filename, sizeof(pl->filename), + "/sys/class/net/%s/qmi/add_mux", profile->usbnet_adapter); + n = fibo_fread(pl->filename, buf, sizeof(buf)); + if (n >= 5) { + profile->qmap_mode = n / 5; // 0x81\n0x82\n0x83\n + if (profile->qmap_mode > 1) { + // PDN-X map to qmimux-X + profile->muxid = (buf[5 * (profile->pdp - 1) + 2] - '0') * 16 + + (buf[5 * (profile->pdp - 1) + 3] - '0'); + sprintf(qmap_netcard, "qmimux%d", profile->pdp - 1); + profile->qmapnet_adapter = strdup(qmap_netcard); + } else if (profile->qmap_mode == 1) { + profile->muxid = + (buf[5 * 0 + 2] - '0') * 16 + (buf[5 * 0 + 3] - '0'); + sprintf(qmap_netcard, "qmimux%d", 0); + profile->qmapnet_adapter = strdup(qmap_netcard); + } + } + } + +_out: + if (profile->qmap_mode) { + profile->qmap_size = 16 * 1024; + snprintf(pl->filename, sizeof(pl->filename), + "/sys/class/net/%s/qmap_size", profile->usbnet_adapter); + if (!access(pl->filename, R_OK)) { + size_t n; + char buf[32]; + n = fibo_fread(pl->filename, buf, sizeof(buf)); + if (n > 0) { + profile->qmap_size = atoi(buf); + } + } + } + +_final_process: + if (profile->qmap_mode) + dbg_time("qmap_mode = %d, muxid = 0x%02x, qmap_netcard = %s", + profile->qmap_mode, profile->muxid, profile->qmapnet_adapter); + + free(pl); + + return 0; +} + +int fibo_qmap_mode_set(PROFILE_T *profile) +{ + int n; + char buf[128]; + struct { + char filename[255 * 2]; + char linkname[255 * 2]; + } * pl; + + if (qmidev_is_pciemhi(profile->qmichannel)) + { + dbg_time("pcie mode exit fibo_qmap_mode_set "); + return 0; + } + + pl = (typeof(pl))malloc(sizeof(*pl)); + + snprintf(pl->linkname, sizeof(pl->linkname), + "/sys/class/net/%s/device/driver", profile->usbnet_adapter); + n = readlink(pl->linkname, pl->filename, sizeof(pl->filename)); + pl->filename[n] = '\0'; + while (pl->filename[n] != '/') n--; + profile->driver_name = strdup(&pl->filename[n + 1]); + + snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_num", + profile->usbnet_adapter); + if (access(pl->filename, R_OK)) { + if (errno != ENOENT) { + dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno, + strerror(errno)); + goto _out; + } + + snprintf(pl->filename, sizeof(pl->filename), + "/sys/module/%s/parameters/qmap_mnum", profile->driver_name); + if (access(pl->filename, R_OK)) { + if (errno != ENOENT) { + dbg_time("fail to access %s, errno: %d (%s)", pl->filename, + errno, strerror(errno)); + goto _out; + } + + snprintf( + pl->filename, sizeof(pl->filename), + "/sys/class/net/%s/device/driver/module/parameters/qmap_num", + profile->usbnet_adapter); + if (access(pl->filename, R_OK)) { + if (errno != ENOENT) { + dbg_time("fail to access %s, errno: %d (%s)", pl->filename, + errno, strerror(errno)); + goto _out; + } + } + } + } + + if (!access(pl->filename, R_OK)) { + snprintf(buf, sizeof(buf), "%d", profile->pdpnum); + n = fibo_fwrite(pl->filename, buf, strlen(buf)); + } + +_out: + + free(pl); + + return 0; +} diff --git a/fibocom-dial/src/query_pcie_mode.c b/fibocom-dial/src/query_pcie_mode.c new file mode 100644 index 000000000..de3451d3a --- /dev/null +++ b/fibocom-dial/src/query_pcie_mode.c @@ -0,0 +1,160 @@ +//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817 +#include "QMIThread.h" +#include "query_pcie_mode.h" +int speed_arr[] = { B115200, B19200, B9600, B4800, B2400, B1200, B300,// + B38400, B19200, B9600, B4800, B2400, B1200, B300, + }; +int name_arr[] = {115200, 19200, 9600, 4800, 2400, 1200, 300, + 38400, 19200, 9600, 4800, 2400, 1200, 300, + }; + +int get_pcie_mode_debug = 0; + +//2021-02-24 willa.liu@fibocom.com changed begin for support eipd SN-20210129001 +int get_private_gateway_debug = 1; +//2021-02-24 willa.liu@fibocom.com changed end for support eipd SN-20210129001 + +//static int xset1(int fd, struct termios *tio, const char *device) +int xset1(int fd, struct termios *tio, const char *device) +{ + int ret = tcsetattr(fd, TCSAFLUSH, tio); + + if (ret) { + printf("can't tcsetattr for %s", device); + } + return ret; +} + +// set raw tty mode +//static void xget1(int fd, struct termios *t, struct termios *oldt) +void xget1(int fd, struct termios *t, struct termios *oldt) +{ + tcgetattr(fd, oldt); + *t = *oldt; + cfmakeraw(t); +// t->c_lflag &= ~(ISIG|ICANON|ECHO|IEXTEN); +// t->c_iflag &= ~(BRKINT|IXON|ICRNL); +// t->c_oflag &= ~(ONLCR); +// t->c_cc[VMIN] = 1; +// t->c_cc[VTIME] = 0; +} + +//static int get_pcie_mode() +int get_pcie_mode() +//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817 +{ + int i; + int fd; + int ret; + char buffer[409600] = {0}; + int rate; + char* sendbuffer; + int totallen = 0; + fd_set readfds; + struct timeval timeout; + struct termios tio0, tiosfd, tio; + + int timeoutVal = 5; + + char *dev = "/dev/ttyUSB1"; //The port under Linux is operated by opening the device file + rate = 115200; + sendbuffer = "at+gtpcie=3"; + printf ( "dev: %s\nrate:%d\nsendbuffer:%s\n", dev,rate,sendbuffer); + + fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (fd < 0) + goto ERR; + fcntl(fd, F_SETFL, O_RDWR); + + // put device to "raw mode" + xget1(fd, &tio, &tiosfd); + // set device speed + for ( i = 0;i < sizeof ( speed_arr ) / sizeof ( int );i++ ) + { + if ( rate == name_arr[i] ) //Judge if the pass is equal to the pass + { + break; + } + } + + if(i >= sizeof ( speed_arr ) / sizeof ( int )) + { + printf("bound rate set failed\n"); + goto ERR; + } + + cfsetspeed(&tio, speed_arr[i]); + if (xset1(fd, &tio, dev)) + goto ERR; + + tcflush ( fd, TCIOFLUSH ); + + sprintf(buffer, "%s\r", sendbuffer); + ret = write ( fd, buffer, strlen(buffer) ); + if(ret < 0) + { + printf ( "write failed\n" ); + goto ERR; + } + if(get_pcie_mode_debug)printf("write %d\n", ret); + + FD_ZERO ( &readfds ); + FD_SET ( fd, &readfds ); + + while(1) + { + timeout.tv_sec = timeoutVal; + timeout.tv_usec = 0; + + ret = select ( fd+1, &readfds, ( fd_set * ) 0, ( fd_set * ) 0, &timeout ); + if(ret > 0) + { + ret = read ( fd, buffer+totallen, sizeof(buffer)-totallen-1 ); + if(ret < 0) + { + printf ( "read failed\n" ); + goto ERR; + } + + if(ret == 0) + { + goto ERR; + } + + totallen += ret; + buffer[totallen] = '\0'; + if(get_pcie_mode_debug)printf("read %d(%s)\n", ret, &buffer[totallen-ret]); + if(totallen == sizeof(buffer)-1) + break; + + if(strstr(buffer,"\nOK") || strstr(buffer,"\nERROR") + || strstr(buffer,"\n+CME ERROR:") || strstr(buffer,"\n+CMS ERROR:")) + { + if(get_pcie_mode_debug)printf("match OK/ERROR"); + if(get_pcie_mode_debug)printf("%s", buffer); + break; + } + + } + else + { + printf ( "select timeout\n" ); + goto ERR; + } + + } + + tcsetattr(fd, TCSAFLUSH, &tiosfd); + //printf("buffer:\n %s\n", buffer); + printf("%s\n", buffer); + if(strstr(buffer,"\nERROR") || strstr(buffer,"\n+CME ERROR:") || strstr(buffer,"\n+CMS ERROR:")) + goto ERR; + + close ( fd ); + return 0; + ERR: + if(fd > 0) + close(fd); + return -1; +} + diff --git a/fibocom-dial/src/query_pcie_mode.h b/fibocom-dial/src/query_pcie_mode.h new file mode 100644 index 000000000..c6e9b9d0e --- /dev/null +++ b/fibocom-dial/src/query_pcie_mode.h @@ -0,0 +1,17 @@ +#ifndef __QUERY_PCIE_MODE__ +#define __QUERY_PCIE_MODE__ + +extern int speed_arr[14]; +extern int name_arr[14]; + +extern int get_pcie_mode_debug; + +extern int get_private_gateway_debug; + +extern int xset1(int fd, struct termios *tio, const char *device); + +// set raw tty mode +extern void xget1(int fd, struct termios *t, struct termios *oldt); +extern int get_pcie_mode(); + +#endif diff --git a/fibocom-dial/src/udhcpc.c b/fibocom-dial/src/udhcpc.c new file mode 100644 index 000000000..966614b6b --- /dev/null +++ b/fibocom-dial/src/udhcpc.c @@ -0,0 +1,289 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "QMIThread.h" +#include "util.h" + +static int dibbler_client_alive = 0; + +static int fibo_system(const char *shell_cmd) +{ + int ret = 0; + dbg_time("%s", shell_cmd); + ret = system(shell_cmd); + if (ret) { + // dbg_time("Fail to system(\"%s\") = %d, errno: %d (%s)", shell_cmd, + // ret, errno, strerror(errno)); + } + return ret; +} + +static void fibo_set_mtu(const char *ifname, int ifru_mtu) +{ + int inet_sock; + struct ifreq ifr; + + inet_sock = socket(AF_INET, SOCK_DGRAM, 0); + + if (inet_sock > 0) { + strcpy(ifr.ifr_name, ifname); + + if (!ioctl(inet_sock, SIOCGIFMTU, &ifr)) { + if (ifr.ifr_ifru.ifru_mtu != ifru_mtu) { + dbg_time("change mtu %d -> %d", ifr.ifr_ifru.ifru_mtu, + ifru_mtu); + ifr.ifr_ifru.ifru_mtu = ifru_mtu; + ioctl(inet_sock, SIOCSIFMTU, &ifr); + } + } + + close(inet_sock); + } +} + +static void *udhcpc_thread_function(void *arg) +{ + FILE *udhcpc_fp; + char *udhcpc_cmd = (char *)arg; + + if (udhcpc_cmd == NULL) + return NULL; + + dbg_time("%s", udhcpc_cmd); + udhcpc_fp = popen(udhcpc_cmd, "r"); + free(udhcpc_cmd); + if (udhcpc_fp) { + char buf[0xff]; + + buf[sizeof(buf) - 1] = '\0'; + while ((fgets(buf, sizeof(buf) - 1, udhcpc_fp)) != NULL) { + if ((strlen(buf) > 1) && (buf[strlen(buf) - 1] == '\n')) + buf[strlen(buf) - 1] = '\0'; + dbg_time("%s", buf); + } + + pclose(udhcpc_fp); + } + + return NULL; +} + +void fibo_set_driver_link_state(PROFILE_T *profile, int link_state) +{ + char link_file[128]; + int fd; + int new_state = 0; + dbg_time("enter %s ", __func__); + + snprintf(link_file, sizeof(link_file), "/sys/class/net/%s/link_state", + profile->usbnet_adapter); + fd = open(link_file, O_RDWR | O_NONBLOCK | O_NOCTTY); + if (fd == -1) { + if (errno != ENOENT) + dbg_time("Fail to access %s, errno: %d (%s)", link_file, errno, + strerror(errno)); + return; + } + + if (profile->qmap_mode <= 1) + new_state = !!link_state; + else { + // 0x80 means link off this pdp + new_state = (link_state ? 0x00 : 0x80) + profile->pdp; + } + + snprintf(link_file, sizeof(link_file), "%d\n", new_state); + write(fd, link_file, sizeof(link_file)); + + if (link_state == 0 && profile->qmap_mode > 1) { + size_t rc; + + lseek(fd, 0, SEEK_SET); + rc = read(fd, link_file, sizeof(link_file)); + if (rc > 1 && (!strcasecmp(link_file, "0\n") || + !strcasecmp(link_file, "0x0\n"))) { + // snprintf(link_file, sizeof(link_file), "busybox ifconfig %s down", + // profile->usbnet_adapter); + // fibo_system(link_file); + } + } + + close(fd); +} + +int fibo_raw_ip_mode_check(const char *ifname) +{ + int fd; + char raw_ip[128]; + char shell_cmd[128]; + char mode[2] = "X"; + int mode_change = 0; + + snprintf(raw_ip, sizeof(raw_ip), "/sys/class/net/%s/qmi/raw_ip", ifname); + if (access(raw_ip, F_OK)) + return 0; + + fd = open(raw_ip, O_RDWR | O_NONBLOCK | O_NOCTTY); + if (fd < 0) { + dbg_time("%s %d fail to open(%s), errno:%d (%s)", __FILE__, __LINE__, + raw_ip, errno, strerror(errno)); + return 0; + } + + read(fd, mode, 2); + if (mode[0] == '0' || mode[0] == 'N') { + snprintf(shell_cmd, sizeof(shell_cmd), "busybox ifconfig %s down", ifname); + fibo_system(shell_cmd); + dbg_time("echo Y > /sys/class/net/%s/qmi/raw_ip", ifname); + mode[0] = 'Y'; + write(fd, mode, 2); + mode_change = 1; + snprintf(shell_cmd, sizeof(shell_cmd), "busybox ifconfig %s up", ifname); + fibo_system(shell_cmd); + } + + close(fd); + return mode_change; +} + +void udhcpc_start(PROFILE_T *profile) +{ + char *ifname = profile->usbnet_adapter; + char shell_cmd[128]; + + fibo_set_driver_link_state(profile, 1); + if (profile->qmapnet_adapter) { + ifname = profile->qmapnet_adapter; + } + dbg_time("1 %s", profile->qmichannel); + if (qmidev_is_qmiwwan(profile->qmichannel)) { + dbg_time("2 %s", ifname); + fibo_raw_ip_mode_check(ifname); + } + + if (profile->rawIP && profile->ipv4.Address && profile->ipv4.Mtu) { + fibo_set_mtu(ifname, (profile->ipv4.Mtu)); + } + + fibo_system("echo 1 > /sys/module/fibo_mhi/parameters/macaddr_check"); + +//begin modified by zhangkaibo fix ipv6 dial process flow. mantis 0048789 20200605 +// if (strcmp(ifname, profile->usbnet_adapter)) { +// snprintf(shell_cmd, sizeof(shell_cmd) - 1, "busybox ifconfig %s up", +// profile->usbnet_adapter); +// fibo_system(shell_cmd); +// } +//begin modified by zhangkaibo fix ipv6 dial process flow. mantis 0048789 20200605 + // For IPv6, down & up will send protocol packets, and that's needed. + if (profile->ipv6_flag) { + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "busybox ifconfig %s down", ifname); + fibo_system(shell_cmd); + } + + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "busybox ifconfig %s up", ifname); + fibo_system(shell_cmd); + + //begin modified by zhangming Added NOARP and Multilcast on flag bit commands. mantis 0050106 20200713 + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link set %s arp off multicast on", ifname); + fibo_system(shell_cmd); + //begin modified by zhangming Added NOARP and Multilcast on flag bit commands. mantis 0050106 20200713 + //Modified unicom dual stack dialing unsuccessful problem + + // for bridge mode, only one public IP, so do udhcpc manually + if (fibo_bridge_mode_detect(profile)) { + return; + } + + /* Do DHCP using busybox tools */ + { + char udhcpc_cmd[128]; + pthread_attr_t udhcpc_thread_attr; + pthread_t udhcpc_thread_id; + + pthread_attr_init(&udhcpc_thread_attr); + pthread_attr_setdetachstate(&udhcpc_thread_attr, + PTHREAD_CREATE_DETACHED); + + if (profile->ipv4.Address) { + if (access("/usr/share/udhcpc/default.script", X_OK)) { + dbg_time( + "Fail to access /usr/share/udhcpc/default.script, " + "errno: %d (%s)", + errno, strerror(errno)); + } + + //-f,--foreground Run in foreground + //-b,--background Background if lease is not obtained + //-n,--now Exit if lease is not obtained + //-q,--quit Exit after obtaining lease + //-t,--retries N Send up to N discover packets (default 3) + snprintf(udhcpc_cmd, sizeof(udhcpc_cmd), + "busybox udhcpc -f -n -q -t 5 -i %s", ifname); + + if (!access("/lib/netifd/dhcp.script", X_OK) && + !access("/sbin/ifup", X_OK) && + !access("/sbin/ifstatus", X_OK)) { + dbg_time("you are use OpenWrt?"); + dbg_time("should not calling udhcpc manually?"); + dbg_time("should modify /etc/config/network as below?"); + dbg_time("config interface wan"); + dbg_time("\toption ifname %s", ifname); + dbg_time("\toption proto dhcp"); + dbg_time( + "should use \"/sbin/ifstaus wan\" to check %s 's status?", + ifname); + } + + pthread_create(&udhcpc_thread_id, NULL, udhcpc_thread_function, + (void *)strdup(udhcpc_cmd)); + sleep(1); + } + + if (profile->ipv6.Address[0] && profile->ipv6.PrefixLengthIPAddr) { + // module do not support DHCPv6, only support 'Router Solicit' + // and it seem if enable /proc/sys/net/ipv6/conf/all/forwarding, + // Kernel do not send RS + const char *forward_file = "/proc/sys/net/ipv6/conf/all/forwarding"; + int forward_fd = open(forward_file, O_RDONLY); + if (forward_fd > 0) { + char forward_state[2]; + read(forward_fd, forward_state, 2); + if (forward_state[0] == '1') { + dbg_time( + "%s enabled, kernel maybe donot send 'Router Solicit'", + forward_file); + } + close(forward_fd); + } + } + } +} + +void udhcpc_stop(PROFILE_T *profile) +{ + char *ifname = profile->usbnet_adapter; + char shell_cmd[128]; + char reset_ip[128]; + dbg_time("enter %s ", __func__); + + if (profile->qmapnet_adapter) { + ifname = profile->qmapnet_adapter; + } + + if (dibbler_client_alive) { + system("killall dibbler-client"); + dibbler_client_alive = 0; + } + //snprintf(shell_cmd, sizeof(shell_cmd) - 1, "busybox ifconfig %s down", + // profile->usbnet_adapter); + //fibo_system(shell_cmd); + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "busybox ifconfig %s down", ifname); + fibo_system(shell_cmd); + snprintf(reset_ip, sizeof(reset_ip) - 1, "busybox ifconfig %s 0.0.0.0", ifname); + fibo_system(reset_ip); +} diff --git a/fibocom-dial/src/udhcpc_netlink.c b/fibocom-dial/src/udhcpc_netlink.c new file mode 100644 index 000000000..fddf16639 --- /dev/null +++ b/fibocom-dial/src/udhcpc_netlink.c @@ -0,0 +1,421 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libmnl/ifutils.h" +#include "libmnl/dhcp/dhcp.h" +#include "util.h" +#include "QMIThread.h" + +static int fibo_system(const char *shell_cmd) +{ + int ret = 0; + dbg_time("%s", shell_cmd); + ret = system(shell_cmd); + if (ret) { + // dbg_time("Fail to system(\"%s\") = %d, errno: %d (%s)", shell_cmd, + // ret, errno, strerror(errno)); + } + return ret; +} + +int fibo_raw_ip_mode_check(const char *ifname) +{ + int fd; + char raw_ip[128]; + char mode[2] = "X"; + int mode_change = 0; + + snprintf(raw_ip, sizeof(raw_ip), "/sys/class/net/%s/qmi/raw_ip", ifname); + if (access(raw_ip, F_OK)) + return 0; + + fd = open(raw_ip, O_RDWR | O_NONBLOCK | O_NOCTTY); + if (fd < 0) + { + dbg_time("%s %d fail to open(%s), errno:%d (%s)", __FILE__, __LINE__, raw_ip, errno, strerror(errno)); + return 0; + } + + read(fd, mode, 2); + if (mode[0] == '0' || mode[0] == 'N') + { + if_link_down(ifname); + dbg_time("echo Y > /sys/class/net/%s/qmi/raw_ip", ifname); + mode[0] = 'Y'; + write(fd, mode, 2); + mode_change = 1; + if_link_up(ifname); + } + + close(fd); + return mode_change; +} + +static void fibo_set_driver_link_state(PROFILE_T *profile, int link_state) +{ + char link_file[128]; + int fd; + int new_state = 0; + dbg_time("enter %s ", __func__); + + snprintf(link_file, sizeof(link_file), "/sys/class/net/%s/link_state", profile->usbnet_adapter); + fd = open(link_file, O_RDWR | O_NONBLOCK | O_NOCTTY); + if (fd == -1) + { + if (errno != ENOENT) + dbg_time("Fail to access %s, errno: %d (%s)", link_file, errno, strerror(errno)); + return; + } + + if (profile->qmap_mode <= 1) + new_state = !!link_state; + else + { + //0x80 means link off this pdp + new_state = (link_state ? 0x00 : 0x80) + profile->pdp; + } + + snprintf(link_file, sizeof(link_file), "%d\n", new_state); + write(fd, link_file, sizeof(link_file)); + + if (link_state == 0 && profile->qmap_mode > 1) + { + size_t rc; + + lseek(fd, 0, SEEK_SET); + rc = read(fd, link_file, sizeof(link_file)); + if (rc > 1 && (!strcasecmp(link_file, "0\n") || !strcasecmp(link_file, "0x0\n"))) + { + // if_link_down(profile->usbnet_adapter); + } + } + + close(fd); +} + +void udhcpc_start(PROFILE_T *profile) +{ +//2021-02-01 willa.liu@fibocom.com changed begin for support mantis 0069837 + char *ifname = profile->usbnet_adapter; + char shell_cmd[512]; + + fibo_set_driver_link_state(profile, 1); + fibo_raw_ip_mode_check(ifname); + + if (profile->qmapnet_adapter) + { + ifname = profile->qmapnet_adapter; + } + if (profile->rawIP && profile->ipv4.Address && profile->ipv4.Mtu) + { + if_set_mtu(ifname, (profile->ipv4.Mtu)); + } + + if (strcmp(ifname, profile->usbnet_adapter)) + { + //if_link_up(profile->usbnet_adapter); + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s up", profile->usbnet_adapter); + fibo_system(shell_cmd); + } +//2021-02-01 willa.liu@fibocom.com changed begin for support mantis 0069837 + if_link_up(ifname); + +#if 1 //for bridge mode, only one public IP, so do udhcpc manually + if (fibo_bridge_mode_detect(profile)) + { + return; + } +#endif + // if use DHCP(should make with ${DHCP} src files) + // do_dhcp(ifname); + // return 0; + /* IPv4 Addr Info */ + if (profile->ipv4.Address) + { + dbg_time("IPv4 MTU: %d", profile->ipv4.Mtu); + dbg_time("IPv4 Address: %s", ipaddr_to_string_v4(ntohl(profile->ipv4.Address))); + dbg_time("IPv4 Netmask: %d", mask_to_prefix_v4(ntohl(profile->ipv4.SubnetMask))); + dbg_time("IPv4 Gateway: %s", ipaddr_to_string_v4(ntohl(profile->ipv4.Gateway))); + dbg_time("IPv4 DNS1: %s", ipaddr_to_string_v4(ntohl(profile->ipv4.DnsPrimary))); + dbg_time("IPv4 DNS2: %s", ipaddr_to_string_v4(ntohl(profile->ipv4.DnsSecondary))); + if_set_network_v4(ifname, ntohl(profile->ipv4.Address), + mask_to_prefix_v4(profile->ipv4.SubnetMask), + ntohl(profile->ipv4.Gateway), + ntohl(profile->ipv4.DnsPrimary), + ntohl(profile->ipv4.DnsSecondary)); + } + else + { + dbg_time("The IPv4 Address in profile is NULL"); + } + + if (profile->ipv6.Address && (profile->ipv6.PrefixLengthIPAddr != 0)) + { + //module do not support DHCPv6, only support 'Router Solicit' + //and it seem if enable /proc/sys/net/ipv6/conf/all/forwarding, Kernel do not send RS + const char *forward_file = "/proc/sys/net/ipv6/conf/all/forwarding"; + int forward_fd = open(forward_file, O_RDONLY); + if (forward_fd > 0) + { + char forward_state[2]; + read(forward_fd, forward_state, 2); + if (forward_state[0] == '1') + { + dbg_time("%s enabled, kernel maybe donot send 'Router Solicit'", forward_file); + } + close(forward_fd); + } + + dbg_time("IPv6 MTU: %d", profile->ipv6.Mtu); + dbg_time("IPv6 Address: %s", ipaddr_to_string_v6(profile->ipv6.Address)); + dbg_time("IPv6 PrefixLengthIPAddr: %d", profile->ipv6.PrefixLengthIPAddr); + dbg_time("IPv6 Gateway: %s", ipaddr_to_string_v6(profile->ipv6.Gateway)); + dbg_time("IPv6 DNS1: %s", ipaddr_to_string_v6(profile->ipv6.DnsPrimary)); + dbg_time("IPv6 DNS2: %s", ipaddr_to_string_v6(profile->ipv6.DnsSecondary)); + if_set_network_v6(ifname, profile->ipv6.Address, profile->ipv6.PrefixLengthIPAddr, + profile->ipv6.Gateway, profile->ipv6.DnsPrimary, profile->ipv6.DnsSecondary); + } + else + { + dbg_time("The IPv6 Address in profile is NULL"); + } +} + +void udhcpc_stop(PROFILE_T *profile) +{ + char *ifname = profile->usbnet_adapter; + dbg_time("enter %s ", __func__); + + fibo_set_driver_link_state(profile, 0); + + if (profile->qmapnet_adapter) + { + ifname = profile->qmapnet_adapter; + } + + if_flush_v4_addr(ifname); + if_flush_v6_addr(ifname); + if_link_down(ifname); +} + +static void fibo_set_mtu(const char *ifname, int ifru_mtu) { + int inet_sock; + struct ifreq ifr; + + inet_sock = socket(AF_INET, SOCK_DGRAM, 0); + + if (inet_sock > 0) { + strcpy(ifr.ifr_name, ifname); + + if (!ioctl(inet_sock, SIOCGIFMTU, &ifr)) { + if (ifr.ifr_ifru.ifru_mtu != ifru_mtu) { + dbg_time("change mtu %d -> %d", ifr.ifr_ifru.ifru_mtu , ifru_mtu); + ifr.ifr_ifru.ifru_mtu = ifru_mtu; + ioctl(inet_sock, SIOCSIFMTU, &ifr); + } + } + + close(inet_sock); + } +} + +static void* udhcpc_thread_function(void* arg) { + FILE * udhcpc_fp; + char *udhcpc_cmd = (char *)arg; + + if (udhcpc_cmd == NULL) + return NULL; + + dbg_time("%s", udhcpc_cmd); + udhcpc_fp = popen(udhcpc_cmd, "r"); + free(udhcpc_cmd); + if (udhcpc_fp) { + char buf[0xff]; + + buf[sizeof(buf)-1] = '\0'; + while((fgets(buf, sizeof(buf)-1, udhcpc_fp)) != NULL) { + if ((strlen(buf) > 1) && (buf[strlen(buf) - 1] == '\n')) + buf[strlen(buf) - 1] = '\0'; + dbg_time("%s", buf); + } + + pclose(udhcpc_fp); + } + + return NULL; +} + +void udhcpc_start_pcie(PROFILE_T *profile) { + char *ifname = profile->usbnet_adapter; + char sub_intf_name[100] = {0}; + int intf_id = 0; + char shell_cmd[512]; + dbg_time("enter %s ", __func__); + + if (profile->qmapnet_adapter) { + ifname = profile->qmapnet_adapter; + } + + if (profile->muxid > 0x81) + { + intf_id = profile->muxid - 0x81; + snprintf(sub_intf_name, sizeof(sub_intf_name) - 1, "%s.%d", ifname, intf_id); + } + + if (profile->rawIP && profile->ipv4.Address && profile->ipv4.Mtu) { + fibo_set_mtu(ifname, (profile->ipv4.Mtu)); + } + + fibo_system("echo 1 > /sys/module/fibo_mhi/parameters/macaddr_check"); + + if (strcmp(ifname, profile->usbnet_adapter)) { + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s up", profile->usbnet_adapter); + fibo_system(shell_cmd); + } + + // For IPv6, down & up will send protocol packets, and that's needed. + if (profile->ipv6_flag && profile->muxid <= 0x81) { + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s down", ifname); + fibo_system(shell_cmd); + } + + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s up", ifname); + fibo_system(shell_cmd); + + //for bridge mode, only one public IP, so do udhcpc manually + if (fibo_bridge_mode_detect(profile)) { + return; + } + +/* Do DHCP using busybox tools */ + { + char udhcpc_cmd[128]; + pthread_attr_t udhcpc_thread_attr; + pthread_t udhcpc_thread_id; + + pthread_attr_init(&udhcpc_thread_attr); + pthread_attr_setdetachstate(&udhcpc_thread_attr, PTHREAD_CREATE_DETACHED); + + if (profile->ipv4.Address) + { + char v4add_str[32] = {0}; + char v4gw_str[32] = {0}; + char v4_netmask_str[32] = {0}; + uint32_t Address = ntohl(profile->ipv4.Address); + uint32_t Gateway = ntohl(profile->ipv4.Gateway); + uint32_t SubnetMask = ntohl(profile->ipv4.SubnetMask); + inet_ntop(AF_INET, &Address, v4add_str, sizeof(v4add_str)); + inet_ntop(AF_INET, &Gateway, v4gw_str, sizeof(v4gw_str)); + inet_ntop(AF_INET, &SubnetMask, v4_netmask_str, sizeof(v4_netmask_str)); + + if (profile->muxid == 0x81) + { + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s %s netmask %s", ifname, v4add_str, v4_netmask_str); + fibo_system(shell_cmd); + + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "route add default gw %s dev %s", v4gw_str, ifname); + fibo_system(shell_cmd); + } + else if (profile->muxid > 0x81) + { + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link add link %s name %s type vlan id %d", ifname, sub_intf_name, intf_id); + fibo_system(shell_cmd); + + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link set %s up", ifname); + fibo_system(shell_cmd); + + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link set %s up", sub_intf_name); + fibo_system(shell_cmd); + + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s %s netmask %s", sub_intf_name, v4add_str, v4_netmask_str); + fibo_system(shell_cmd); + } + } + + if (profile->ipv6.PrefixLengthIPAddr) + { + //module do not support DHCPv6, only support 'Router Solicit' + //and it seem if enable /proc/sys/net/ipv6/conf/all/forwarding, Kernel do not send RS + const char *forward_file = "/proc/sys/net/ipv6/conf/all/forwarding"; + int forward_fd = open(forward_file, O_RDONLY); + if (forward_fd > 0) { + char forward_state[2]; + read(forward_fd, forward_state, 2); + if (forward_state[0] == '1') { + dbg_time("%s enabled, kernel maybe donot send 'Router Solicit'", forward_file); + } + close(forward_fd); + } + + { + char v6add_str[100] = {0}; + char v6gw_str[100] = {0}; + inet_ntop(AF_INET6, profile->ipv6.Address, v6add_str, sizeof(v6add_str)); + inet_ntop(AF_INET6, profile->ipv6.Gateway, v6gw_str, sizeof(v6gw_str)); + + if (profile->muxid == 0x81) + { + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s add %s/%d", ifname, v6add_str, profile->ipv6.PrefixLengthIPAddr); + fibo_system(shell_cmd); + + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "route -A inet6 add ::/0 gw %s dev %s", v6gw_str, ifname); + fibo_system(shell_cmd); + } + else if (profile->muxid > 0x81) + { + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link add link %s name %s type vlan id %d", ifname, sub_intf_name, intf_id); + fibo_system(shell_cmd); + + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link set %s up", ifname); + fibo_system(shell_cmd); + + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link set %s up", sub_intf_name); + fibo_system(shell_cmd); + + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s add %s/%d", sub_intf_name, v6add_str, profile->ipv6.PrefixLengthIPAddr); + fibo_system(shell_cmd); + /* start 2021-01-21 add by haopengfei to fix mantis 69056 */ + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "route -A inet6 add ::/0 gw %s dev %s", v6gw_str, sub_intf_name); + fibo_system(shell_cmd); + /* end 2021-01-21 add by haopengfei to fix mantis 69056 */ + } + } + + } + } +} + +void udhcpc_stop_pcie(PROFILE_T *profile) { + char *ifname = profile->usbnet_adapter; + char shell_cmd[128]; + char reset_ip[128]; + dbg_time("enter %s ", __func__); + + if (profile->qmapnet_adapter) { + ifname = profile->qmapnet_adapter; + } + + if (profile->muxid == 0x81) + { + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s down", ifname); + fibo_system(shell_cmd); + snprintf(reset_ip, sizeof(reset_ip) - 1, "ifconfig %s 0.0.0.0", ifname); + fibo_system(reset_ip); + } + else if (profile->muxid > 0x81) + { + char sub_intf_name[100]; + int intf_id = profile->muxid - 0x81; + snprintf(sub_intf_name, sizeof(sub_intf_name) - 1, "%s.%d", ifname, intf_id); + + snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link del link dev %s", sub_intf_name); + fibo_system(shell_cmd); + } +} + diff --git a/fibocom-dial/src/util.c b/fibocom-dial/src/util.c new file mode 100644 index 000000000..f2a22281b --- /dev/null +++ b/fibocom-dial/src/util.c @@ -0,0 +1,142 @@ +#include + +#include "QMIThread.h" + +#if defined(__STDC__) +#include +#define __V(x) x +#else +#include +#define __V(x) (va_alist) va_dcl +#define const +#define volatile +#endif + +#include + +#define is_bigendian() ((*(char *)&i) == 0) +FILE *logfilefp = NULL; +static pthread_mutex_t printfMutex = PTHREAD_MUTEX_INITIALIZER; +static char line[1024]; +const int i = 1; + +// defined in atchannel.c +static void setTimespecRelative(struct timespec *p_ts, long long msec) +{ + struct timeval tv; + + gettimeofday(&tv, (struct timezone *)NULL); + + /* what's really funny about this is that I know + pthread_cond_timedwait just turns around and makes this + a relative time again */ + p_ts->tv_sec = tv.tv_sec + (msec / 1000); + p_ts->tv_nsec = (tv.tv_usec + (msec % 1000) * 1000L) * 1000L; +} + +int pthread_cond_timeout_np(pthread_cond_t *cond, pthread_mutex_t *mutex, + unsigned msecs) +{ + if (msecs != 0) { + struct timespec ts; + setTimespecRelative(&ts, msecs); + return pthread_cond_timedwait(cond, mutex, &ts); + } else { + return pthread_cond_wait(cond, mutex); + } +} + +static const char *get_time(void) +{ + static char time_buf[50]; + struct timeval tv; + time_t time; + suseconds_t millitm; + struct tm *ti; + + gettimeofday(&tv, NULL); + + time = tv.tv_sec; + millitm = (tv.tv_usec + 500) / 1000; + + if (millitm == 1000) { + ++time; + millitm = 0; + } + + ti = localtime(&time); + sprintf(time_buf, "[%02d-%02d_%02d:%02d:%02d:%03d]", ti->tm_mon + 1, + ti->tm_mday, ti->tm_hour, ti->tm_min, ti->tm_sec, (int)millitm); + return time_buf; +} + +void dbg_time(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + pthread_mutex_lock(&printfMutex); + + snprintf(line, sizeof(line), "%s ", get_time()); + + vsnprintf(line + strlen(line), sizeof(line) - strlen(line), fmt, args); + fprintf(stdout, "%s\n", line); + + if (logfilefp) { + fprintf(logfilefp, "%s\n", line); + } + fflush(logfilefp); + pthread_mutex_unlock(&printfMutex); +} + +USHORT le16_to_cpu(USHORT v16) +{ + USHORT tmp = v16; + if (is_bigendian()) { + unsigned char *s = (unsigned char *)(&v16); + unsigned char *d = (unsigned char *)(&tmp); + d[0] = s[1]; + d[1] = s[0]; + } + return tmp; +} + +UINT le32_to_cpu(UINT v32) +{ + UINT tmp = v32; + if (is_bigendian()) { + unsigned char *s = (unsigned char *)(&v32); + unsigned char *d = (unsigned char *)(&tmp); + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; + } + return tmp; +} + +USHORT cpu_to_le16(USHORT v16) +{ + USHORT tmp = v16; + if (is_bigendian()) { + unsigned char *s = (unsigned char *)(&v16); + unsigned char *d = (unsigned char *)(&tmp); + d[0] = s[1]; + d[1] = s[0]; + } + return tmp; +} + +UINT cpu_to_le32(UINT v32) +{ + UINT tmp = v32; + if (is_bigendian()) { + unsigned char *s = (unsigned char *)(&v32); + unsigned char *d = (unsigned char *)(&tmp); + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; + } + return tmp; +} diff --git a/fibocom-dial/src/util.h b/fibocom-dial/src/util.h new file mode 100644 index 000000000..0a0ec4803 --- /dev/null +++ b/fibocom-dial/src/util.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#include + +struct listnode { + struct listnode *next; + struct listnode *prev; +}; + +#define node_to_item(node, container, member) \ + (container *)(((char *)(node)) - offsetof(container, member)) + +#define list_declare(name) \ + struct listnode name = { \ + .next = &name, \ + .prev = &name, \ + } + +#define list_for_each(node, list) \ + for (node = (list)->next; node != (list); node = node->next) + +#define list_for_each_reverse(node, list) \ + for (node = (list)->prev; node != (list); node = node->prev) +void dbg_time(const char *fmt, ...); +void list_init(struct listnode *list); +void list_add_tail(struct listnode *list, struct listnode *item); +void list_add_head(struct listnode *head, struct listnode *item); +void list_remove(struct listnode *item); + +#define list_empty(list) ((list) == (list)->next) +#define list_head(list) ((list)->next) +#define list_tail(list) ((list)->prev) + +int epoll_register(int epoll_fd, int fd, unsigned int events); +int epoll_deregister(int epoll_fd, int fd); +#endif diff --git a/fibocom_QMI_WWAN/Makefile b/fibocom_QMI_WWAN/Makefile deleted file mode 100644 index 836085e20..000000000 --- a/fibocom_QMI_WWAN/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -# -# Copyright (C) 2015 OpenWrt.org -# -# This is free software, licensed under the GNU General Public License v2. -# See /LICENSE for more information. -# - -include $(TOPDIR)/rules.mk - -PKG_NAME:=qmi_wwan_f -PKG_VERSION:=1.0 -PKG_RELEASE:=2 - -include $(INCLUDE_DIR)/kernel.mk -include $(INCLUDE_DIR)/package.mk - -define KernelPackage/qmi_wwan_f - SUBMENU:=WWAN Support - TITLE:=Fibocom Linux USB QMI WWAN Driver - DEPENDS:=+kmod-usb-net +kmod-usb-wdm - FILES:=$(PKG_BUILD_DIR)/qmi_wwan_f.ko - AUTOLOAD:=$(call AutoLoad,82,qmi_wwan_f) -endef - -define KernelPackage/qmi_wwan_f/description - Fibocom Linux USB QMI WWAN Driver -endef - -MAKE_OPTS:= \ - ARCH="$(LINUX_KARCH)" \ - CROSS_COMPILE="$(TARGET_CROSS)" \ - CXXFLAGS="$(TARGET_CXXFLAGS)" \ - M="$(PKG_BUILD_DIR)" \ - $(EXTRA_KCONFIG) - -define Build/Prepare - mkdir -p $(PKG_BUILD_DIR) - $(CP) ./src/* $(PKG_BUILD_DIR)/ -endef - -define Build/Compile - $(MAKE) -C "$(LINUX_DIR)" \ - $(MAKE_OPTS) \ - modules -endef - -$(eval $(call KernelPackage,qmi_wwan_f)) diff --git a/fibocom_QMI_WWAN/src/Makefile b/fibocom_QMI_WWAN/src/Makefile deleted file mode 100644 index 00250a1c1..000000000 --- a/fibocom_QMI_WWAN/src/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -obj-m += qmi_wwan_f.o - -PWD := $(shell pwd) -OUTPUTDIR=/lib/modules/`uname -r`/kernel/drivers/net/usb/ - -ifeq ($(ARCH),) -ARCH := $(shell uname -m) -endif -ifeq ($(CROSS_COMPILE),) -CROSS_COMPILE := -endif -ifeq ($(KDIR),) -KDIR := /lib/modules/$(shell uname -r)/build -ifeq ($(ARCH),i686) -ifeq ($(wildcard $KDIR/arch/$ARCH),) -ARCH=i386 -endif -endif -endif - -ifneq ($(findstring &,${PWD}),) -$(warning "${PWD}") -$(warning "current directory contain special char '&' !") -$(error "please remove it!") -endif - -default: - $(MAKE) ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} -C $(KDIR) M=$(PWD) modules - -install: default - cp $(PWD)/qmi_wwan_f.ko /lib/modules/$(shell uname -r)/kernel/drivers/net/usb/ - depmod - modprobe -r qmi_wwan_f - modprobe -r qmi_wwan - modprobe qmi_wwan_f -clean: - rm -rf *~ .tmp_versions modules.order Module.symvers - find . -type f -name "*~" -o -name "*.o" -o -name "*.ko" -o -name "*.cmd" -o -name "*.mod.c" | xargs rm -rf diff --git a/fibocom_QMI_WWAN/src/qmi_wwan_f.c b/fibocom_QMI_WWAN/src/qmi_wwan_f.c deleted file mode 100644 index 48efef728..000000000 --- a/fibocom_QMI_WWAN/src/qmi_wwan_f.c +++ /dev/null @@ -1,2534 +0,0 @@ -/* - * Copyright (c) 2012 Bjørn Mork - * - * The probing code is heavily inspired by cdc_ether, which is: - * Copyright (C) 2003-2005 by David Brownell - * Copyright (C) 2006 by Ole Andre Vadla Ravnas (ActiveSync) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#if LINUX_VERSION_CODE > KERNEL_VERSION(3,16,0) //8b094cd03b4a3793220d8d8d86a173bfea8c285b -#include -#else -#define timespec64 timespec -#define ktime_get_ts64 ktime_get_ts -#define timespec64_sub timespec_sub -#endif -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef ETH_P_MAP -#define ETH_P_MAP 0xDA1A -#endif - -#if (ETH_P_MAP == 0x00F9) -#undef ETH_P_MAP -#define ETH_P_MAP 0xDA1A -#endif - -#ifndef ARPHRD_RAWIP -#define ARPHRD_RAWIP ARPHRD_NONE -#endif - -#ifdef CONFIG_PINCTRL_IPQ807x -#define CONFIG_QCA_NSS_DRV -#endif - -#if 1//def CONFIG_QCA_NSS_DRV -#define _RMNET_NSS_H_ -#define _RMENT_NSS_H_ -struct rmnet_nss_cb { - int (*nss_create)(struct net_device *dev); - int (*nss_free)(struct net_device *dev); - int (*nss_tx)(struct sk_buff *skb); -}; -static struct rmnet_nss_cb *rmnet_nss_callbacks __rcu __read_mostly; -#ifdef CONFIG_QCA_NSS_DRV -static uint __read_mostly qca_nss_enabled = 1; -module_param( qca_nss_enabled, uint, S_IRUGO); -#endif -#endif - -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 4,0,0 )) //1e9e39f4a29857a396ac7b669d109f697f66695e -#define usbnet_set_skb_tx_stats(skb, packets, bytes_delta) do { dev->net->stats.tx_packets += packets; } while(0) -#endif - -/* This driver supports wwan (3G/LTE/?) devices using a vendor - * specific management protocol called Qualcomm MSM Interface (QMI) - - * in addition to the more common AT commands over serial interface - * management - * - * QMI is wrapped in CDC, using CDC encapsulated commands on the - * control ("master") interface of a two-interface CDC Union - * resembling standard CDC ECM. The devices do not use the control - * interface for any other CDC messages. Most likely because the - * management protocol is used in place of the standard CDC - * notifications NOTIFY_NETWORK_CONNECTION and NOTIFY_SPEED_CHANGE - * - * Alternatively, control and data functions can be combined in a - * single USB interface. - * - * Handling a protocol like QMI is out of the scope for any driver. - * It is exported as a character device using the cdc-wdm driver as - * a subdriver, enabling userspace applications ("modem managers") to - * handle it. - * - * These devices may alternatively/additionally be configured using AT - * commands on a serial interface - */ -#define VERSION_NUMBER "V1.0.5" -#define FIBOCOM_WWAN_VERSION "Fibocom_QMI_WWAN_Driver_"VERSION_NUMBER -static const char driver_name[] = "qmi_wwan_f"; - -/* driver specific data */ -struct qmi_wwan_state { - struct usb_driver *subdriver; - atomic_t pmcount; - unsigned long unused; - struct usb_interface *control; - struct usb_interface *data; -}; - -/* default ethernet address used by the modem */ -static const u8 default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3}; - -#if 1 -#define FIBOCOM_WWAN_QMAP 4 //MAX is 7 - -#if defined(FIBOCOM_WWAN_QMAP) -#define FIBOCOM_QMAP_MUX_ID 0x81 - -static uint __read_mostly qmap_mode = 0; -module_param( qmap_mode, uint, S_IRUGO); -module_param_named( rx_qmap, qmap_mode, uint, S_IRUGO ); -#endif - -#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) -#define FIBOCOM_BRIDGE_MODE -#endif - -#ifdef FIBOCOM_BRIDGE_MODE -static uint __read_mostly bridge_mode = 0/*|BIT(1)*/; -module_param( bridge_mode, uint, S_IRUGO ); -#endif - -#if defined(FIBOCOM_WWAN_QMAP) -#define FIBOCOM_UL_DATA_AGG 1 - -#if defined(FIBOCOM_UL_DATA_AGG) -struct tx_agg_ctx { - /* QMIWDS_ADMIN_SET_DATA_FORMAT_RESP TLV_0x17 and TLV_0x18 */ - uint ul_data_aggregation_max_datagrams; //UplinkDataAggregationMaxDatagramsTlv - uint ul_data_aggregation_max_size; //UplinkDataAggregationMaxSizeTlv - uint dl_minimum_padding; //0x1A -}; -#endif - -typedef struct { - unsigned int size; - unsigned int rx_urb_size; - unsigned int ep_type; - unsigned int iface_id; - unsigned int qmap_mode; - unsigned int qmap_version; - unsigned int dl_minimum_padding; - char ifname[8][16]; - unsigned char mux_id[8]; -} RMNET_INFO; - -typedef struct sQmiWwanQmap -{ - struct usbnet *mpNetDev; - struct driver_info driver_info; - atomic_t refcount; - struct net_device *mpQmapNetDev[FIBOCOM_WWAN_QMAP]; - uint link_state; - uint qmap_mode; - uint qmap_size; - uint qmap_version; - struct sk_buff_head skb_chain; - -#if defined(FIBOCOM_UL_DATA_AGG) - struct tx_agg_ctx tx_ctx; - struct tasklet_struct txq; -#endif - -#ifdef FIBOCOM_BRIDGE_MODE - uint bridge_mode; - uint bridge_ipv4; - unsigned char bridge_mac[ETH_ALEN]; -#endif - uint use_rmnet_usb; - RMNET_INFO rmnet_info; -} sQmiWwanQmap; -//2021-03-19 willa.liu@fibocom.com changed begin for support mantis 0073129 -//#if LINUX_VERSION_CODE > KERNEL_VERSION(3,10,0) -#if LINUX_VERSION_CODE > KERNEL_VERSION(3,15,0) -//2021-03-19 willa.liu@fibocom.com changed end for support mantis 0073129 -#define MHI_NETDEV_STATUS64 -#endif -struct qmap_priv { - struct usbnet *dev; - struct net_device *real_dev; - struct net_device *self_dev; - u8 offset_id; - u8 mux_id; - u8 qmap_version; - u8 link_state; - -#if defined(MHI_NETDEV_STATUS64) - struct pcpu_sw_netstats __percpu *stats64; -#endif - - spinlock_t agg_lock; - struct sk_buff *agg_skb; - unsigned agg_count; - struct timespec64 agg_time; - struct hrtimer agg_hrtimer; - struct work_struct agg_wq; - -#ifdef FIBOCOM_BRIDGE_MODE - uint bridge_mode; - uint bridge_ipv4; - unsigned char bridge_mac[ETH_ALEN]; -#endif -}; - -struct qmap_hdr { - u8 cd_rsvd_pad; - u8 mux_id; - u16 pkt_len; -} __packed; - -enum rmnet_map_v5_header_type { - RMNET_MAP_HEADER_TYPE_UNKNOWN, - RMNET_MAP_HEADER_TYPE_COALESCING = 0x1, - RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD = 0x2, - RMNET_MAP_HEADER_TYPE_ENUM_LENGTH -}; - -/* Main QMAP header */ -struct rmnet_map_header { - u8 pad_len:6; - u8 next_hdr:1; - u8 cd_bit:1; - u8 mux_id; - __be16 pkt_len; -} __aligned(1); - -/* QMAP v5 headers */ -struct rmnet_map_v5_csum_header { - u8 next_hdr:1; - u8 header_type:7; - u8 hw_reserved:7; - u8 csum_valid_required:1; - __be16 reserved; -} __aligned(1); - -#ifdef FIBOCOM_BRIDGE_MODE -static int is_qmap_netdev(const struct net_device *netdev); -#endif -#endif - -#ifdef FIBOCOM_BRIDGE_MODE -static int bridge_arp_reply(struct net_device *net, struct sk_buff *skb, uint bridge_ipv4) { - struct arphdr *parp; - u8 *arpptr, *sha; - u8 sip[4], tip[4], ipv4[4]; - struct sk_buff *reply = NULL; - - ipv4[0] = (bridge_ipv4 >> 24) & 0xFF; - ipv4[1] = (bridge_ipv4 >> 16) & 0xFF; - ipv4[2] = (bridge_ipv4 >> 8) & 0xFF; - ipv4[3] = (bridge_ipv4 >> 0) & 0xFF; - - parp = arp_hdr(skb); - - if (parp->ar_hrd == htons(ARPHRD_ETHER) && parp->ar_pro == htons(ETH_P_IP) - && parp->ar_op == htons(ARPOP_REQUEST) && parp->ar_hln == 6 && parp->ar_pln == 4) { - arpptr = (u8 *)parp + sizeof(struct arphdr); - sha = arpptr; - arpptr += net->addr_len; /* sha */ - memcpy(sip, arpptr, sizeof(sip)); - arpptr += sizeof(sip); - arpptr += net->addr_len; /* tha */ - memcpy(tip, arpptr, sizeof(tip)); - - pr_info("%s sip = %d.%d.%d.%d, tip=%d.%d.%d.%d, ipv4=%d.%d.%d.%d\n", netdev_name(net), - sip[0], sip[1], sip[2], sip[3], tip[0], tip[1], tip[2], tip[3], ipv4[0], ipv4[1], ipv4[2], ipv4[3]); - //wwan0 sip = 10.151.137.255, tip=10.151.138.0, ipv4=10.151.137.255 - if (tip[0] == ipv4[0] && tip[1] == ipv4[1] && (tip[2]&0xFC) == (ipv4[2]&0xFC) && tip[3] != ipv4[3]) - reply = arp_create(ARPOP_REPLY, ETH_P_ARP, *((__be32 *)sip), net, *((__be32 *)tip), sha, default_modem_addr, sha); - - if (reply) { - skb_reset_mac_header(reply); - __skb_pull(reply, skb_network_offset(reply)); - reply->ip_summed = CHECKSUM_UNNECESSARY; - reply->pkt_type = PACKET_HOST; -#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0) - netif_rx_ni(reply); -#else - netif_rx(reply); -#endif - } - return 1; - } - - return 0; -} - -static struct sk_buff *bridge_mode_tx_fixup(struct net_device *net, struct sk_buff *skb, uint bridge_ipv4, unsigned char *bridge_mac) { - struct ethhdr *ehdr; - const struct iphdr *iph; - - skb_reset_mac_header(skb); - ehdr = eth_hdr(skb); - - if (ehdr->h_proto == htons(ETH_P_ARP)) { - if (bridge_ipv4) - bridge_arp_reply(net, skb, bridge_ipv4); - return NULL; - } - - iph = ip_hdr(skb); - //DBG("iphdr: "); - //PrintHex((void *)iph, sizeof(struct iphdr)); - -// 1 0.000000000 0.0.0.0 255.255.255.255 DHCP 362 DHCP Request - Transaction ID 0xe7643ad7 - if (ehdr->h_proto == htons(ETH_P_IP) && iph->protocol == IPPROTO_UDP && iph->saddr == 0x00000000 && iph->daddr == 0xFFFFFFFF) { - //if (udp_hdr(skb)->dest == htons(67)) //DHCP Request - { - memcpy(bridge_mac, ehdr->h_source, ETH_ALEN); - pr_info("%s PC Mac Address: %02x:%02x:%02x:%02x:%02x:%02x\n", netdev_name(net), - bridge_mac[0], bridge_mac[1], bridge_mac[2], bridge_mac[3], bridge_mac[4], bridge_mac[5]); - } - } - - if (memcmp(ehdr->h_source, bridge_mac, ETH_ALEN)) { - return NULL; - } - - return skb; -} - -static void bridge_mode_rx_fixup(sQmiWwanQmap *pQmapDev, struct net_device *net, struct sk_buff *skb) { - uint bridge_mode = 0; - unsigned char *bridge_mac; - - if (pQmapDev->qmap_mode > 1 || pQmapDev->use_rmnet_usb == 1) { - struct qmap_priv *priv = netdev_priv(net); - bridge_mode = priv->bridge_mode; - bridge_mac = priv->bridge_mac; - } - else { - bridge_mode = pQmapDev->bridge_mode; - bridge_mac = pQmapDev->bridge_mac; - } - - if (bridge_mode) - memcpy(eth_hdr(skb)->h_dest, bridge_mac, ETH_ALEN); - else - memcpy(eth_hdr(skb)->h_dest, net->dev_addr, ETH_ALEN); -} -#endif - -#if defined(FIBOCOM_WWAN_QMAP) -static ssize_t qmap_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *netdev = to_net_dev(dev); - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - - return snprintf(buf, PAGE_SIZE, "%d\n", pQmapDev->qmap_mode); -} - -static DEVICE_ATTR(qmap_mode, S_IRUGO, qmap_mode_show, NULL); - -static ssize_t qmap_size_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *netdev = to_net_dev(dev); - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - - return snprintf(buf, PAGE_SIZE, "%u\n", pQmapDev->qmap_size); -} - -static DEVICE_ATTR(qmap_size, S_IRUGO, qmap_size_show, NULL); - -static ssize_t link_state_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *netdev = to_net_dev(dev); - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - - return snprintf(buf, PAGE_SIZE, "0x%x\n", pQmapDev->link_state); -} - -static ssize_t link_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct net_device *netdev = to_net_dev(dev); - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - unsigned link_state = 0; - unsigned old_link = pQmapDev->link_state; - uint offset_id = 0; - - link_state = simple_strtoul(buf, NULL, 0); - - if (pQmapDev->qmap_mode == 1) { - pQmapDev->link_state = !!link_state; - } - else if (pQmapDev->qmap_mode > 1) { - offset_id = ((link_state&0x7F) - 1); - - if (offset_id >= pQmapDev->qmap_mode) { - dev_info(dev, "%s offset_id is %d. but qmap_mode is %d\n", __func__, offset_id, pQmapDev->qmap_mode); - return count; - } - - if (link_state&0x80) - pQmapDev->link_state &= ~(1 << offset_id); - else - pQmapDev->link_state |= (1 << offset_id); - } - - if (old_link != pQmapDev->link_state) { - struct net_device *qmap_net = pQmapDev->mpQmapNetDev[offset_id]; - - if (pQmapDev->link_state) { - netif_carrier_on(usbnetdev->net); - } else { - netif_carrier_off(usbnetdev->net); - } - - if (qmap_net && qmap_net != netdev) { - struct qmap_priv *priv = netdev_priv(qmap_net); - - priv->link_state = !!(pQmapDev->link_state & (1 << offset_id)); - if (priv->link_state) { - netif_carrier_on(qmap_net); - if (netif_queue_stopped(qmap_net) && !netif_queue_stopped(usbnetdev->net)) - netif_wake_queue(qmap_net); - } - else { - netif_carrier_off(qmap_net); - } - } - - dev_info(dev, "link_state 0x%x -> 0x%x\n", old_link, pQmapDev->link_state); - } - - return count; -} - -#ifdef FIBOCOM_BRIDGE_MODE -static ssize_t bridge_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct net_device *netdev = to_net_dev(dev); - uint old_mode = 0; - uint bridge_mode = simple_strtoul(buf, NULL, 0); - - if (netdev->type != ARPHRD_ETHER) { - return count; - } - - if (is_qmap_netdev(netdev)) { - struct qmap_priv *priv = netdev_priv(netdev); - old_mode = priv->bridge_mode; - priv->bridge_mode = bridge_mode; - } - else { - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - old_mode = pQmapDev->bridge_mode; - pQmapDev->bridge_mode = bridge_mode; - } - - if (old_mode != bridge_mode) { - dev_info(dev, "bridge_mode change to 0x%x\n", bridge_mode); - } - - return count; -} - -static ssize_t bridge_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *netdev = to_net_dev(dev); - uint bridge_mode = 0; - - if (is_qmap_netdev(netdev)) { - struct qmap_priv *priv = netdev_priv(netdev); - bridge_mode = priv->bridge_mode; - } - else { - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - bridge_mode = pQmapDev->bridge_mode; - } - - return snprintf(buf, PAGE_SIZE, "%u\n", bridge_mode); -} - -static ssize_t bridge_ipv4_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *netdev = to_net_dev(dev); - unsigned int bridge_ipv4 = 0; - unsigned char ipv4[4]; - - if (is_qmap_netdev(netdev)) { - struct qmap_priv *priv = netdev_priv(netdev); - bridge_ipv4 = priv->bridge_ipv4; - } - else { - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - bridge_ipv4 = pQmapDev->bridge_ipv4; - } - - ipv4[0] = (bridge_ipv4 >> 24) & 0xFF; - ipv4[1] = (bridge_ipv4 >> 16) & 0xFF; - ipv4[2] = (bridge_ipv4 >> 8) & 0xFF; - ipv4[3] = (bridge_ipv4 >> 0) & 0xFF; - - return snprintf(buf, PAGE_SIZE, "%d.%d.%d.%d\n", ipv4[0], ipv4[1], ipv4[2], ipv4[3]); -} - -static ssize_t bridge_ipv4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct net_device *netdev = to_net_dev(dev); - - if (is_qmap_netdev(netdev)) { - struct qmap_priv *priv = netdev_priv(netdev); - priv->bridge_ipv4 = simple_strtoul(buf, NULL, 16); - } - else { - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - pQmapDev->bridge_ipv4 = simple_strtoul(buf, NULL, 16); - } - - return count; -} -#endif - -static DEVICE_ATTR(link_state, S_IWUSR | S_IRUGO, link_state_show, link_state_store); -#ifdef FIBOCOM_BRIDGE_MODE -static DEVICE_ATTR(bridge_mode, S_IWUSR | S_IRUGO, bridge_mode_show, bridge_mode_store); -static DEVICE_ATTR(bridge_ipv4, S_IWUSR | S_IRUGO, bridge_ipv4_show, bridge_ipv4_store); -#endif - -static struct attribute *qmi_wwan_sysfs_attrs[] = { - &dev_attr_link_state.attr, - &dev_attr_qmap_mode.attr, - &dev_attr_qmap_size.attr, -#ifdef FIBOCOM_BRIDGE_MODE - &dev_attr_bridge_mode.attr, - &dev_attr_bridge_ipv4.attr, -#endif - NULL, -}; - -static struct attribute_group qmi_wwan_sysfs_attr_group = { - .attrs = qmi_wwan_sysfs_attrs, -}; - -#ifdef FIBOCOM_BRIDGE_MODE -static struct attribute *qmi_qmap_sysfs_attrs[] = { - &dev_attr_bridge_mode.attr, - &dev_attr_bridge_ipv4.attr, - NULL, -}; - -static struct attribute_group qmi_qmap_sysfs_attr_group = { - .attrs = qmi_qmap_sysfs_attrs, -}; -#endif - -static int qmap_open(struct net_device *dev) -{ - struct qmap_priv *priv = netdev_priv(dev); - struct net_device *real_dev = priv->real_dev; - - if (!(priv->real_dev->flags & IFF_UP)) - return -ENETDOWN; - - if (netif_carrier_ok(real_dev) && priv->link_state) - netif_carrier_on(dev); - - if (netif_carrier_ok(dev)) { - if (netif_queue_stopped(dev) && !netif_queue_stopped(real_dev)) - netif_wake_queue(dev); - } - - return 0; -} - -static int qmap_stop(struct net_device *pNet) -{ - netif_carrier_off(pNet); - return 0; -} - -static struct sk_buff * add_qhdr(struct sk_buff *skb, u8 mux_id) { - struct qmap_hdr *qhdr; - int pad = 0; - - pad = skb->len%4; - if (pad) { - pad = 4 - pad; - if (skb_tailroom(skb) < pad) { - printk("skb_tailroom small!\n"); - pad = 0; - } - if (pad) - __skb_put(skb, pad); - } - - qhdr = (struct qmap_hdr *)skb_push(skb, sizeof(struct qmap_hdr)); - qhdr->cd_rsvd_pad = pad; - qhdr->mux_id = mux_id; - qhdr->pkt_len = cpu_to_be16(skb->len - sizeof(struct qmap_hdr)); - - return skb; -} - -static struct sk_buff * add_qhdr_v5(struct sk_buff *skb, u8 mux_id) { - struct rmnet_map_header *map_header; - struct rmnet_map_v5_csum_header *ul_header; - u32 padding, map_datalen; - - map_datalen = skb->len; - padding = map_datalen%4; - if (padding) { - padding = 4 - padding; - if (skb_tailroom(skb) < padding) { - printk("skb_tailroom small!\n"); - padding = 0; - } - if (padding) - __skb_put(skb, padding); - } - - map_header = (struct rmnet_map_header *)skb_push(skb, (sizeof(struct rmnet_map_header) + sizeof(struct rmnet_map_v5_csum_header))); - map_header->cd_bit = 0; - map_header->next_hdr = 1; - map_header->pad_len = padding; - map_header->mux_id = mux_id; - map_header->pkt_len = htons(map_datalen + padding); - - ul_header = (struct rmnet_map_v5_csum_header *)(map_header + 1); - memset(ul_header, 0, sizeof(*ul_header)); - ul_header->header_type = RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD; - if (skb->ip_summed == CHECKSUM_PARTIAL) { -#if 0 //TODO - skb->ip_summed = CHECKSUM_NONE; - /* Ask for checksum offloading */ - ul_header->csum_valid_required = 1; -#endif - } - - return skb; -} - -static void rmnet_vnd_update_rx_stats(struct net_device *net, - unsigned rx_packets, unsigned rx_bytes) { -#if defined(MHI_NETDEV_STATUS64) - struct qmap_priv *dev = netdev_priv(net); - struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->stats64); - - u64_stats_update_begin(&stats64->syncp); -#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0) - stats64->rx_packets += rx_packets; - stats64->rx_bytes += rx_bytes; -#else - u64_stats_add(&stats64->rx_packets, rx_packets); - u64_stats_add(&stats64->rx_bytes, rx_bytes); -#endif - u64_stats_update_end(&stats64->syncp); -#else - net->stats.rx_packets += rx_packets; - net->stats.rx_bytes += rx_bytes; -#endif -} - -static void rmnet_vnd_update_tx_stats(struct net_device *net, - unsigned tx_packets, unsigned tx_bytes) { -#if defined(MHI_NETDEV_STATUS64) - struct qmap_priv *dev = netdev_priv(net); - struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->stats64); - - u64_stats_update_begin(&stats64->syncp); -#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0) - stats64->tx_packets += tx_packets; - stats64->tx_bytes += tx_bytes; -#else - u64_stats_add(&stats64->tx_packets, tx_packets); - u64_stats_add(&stats64->tx_bytes, tx_bytes); -#endif - u64_stats_update_end(&stats64->syncp); -#else - net->stats.tx_packets += tx_packets; - net->stats.tx_bytes += tx_bytes; -#endif -} - -#if defined(MHI_NETDEV_STATUS64) -static struct rtnl_link_stats64 *_rmnet_vnd_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats) -{ - struct qmap_priv *dev = netdev_priv(net); - unsigned int start; - int cpu; - struct rmnet_nss_cb *nss_cb; - - netdev_stats_to_stats64(stats, &net->stats); - - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (nss_cb) { // rmnet_nss.c:rmnet_nss_tx() will update rx stats - stats->rx_packets = 0; - stats->rx_bytes = 0; - } - - for_each_possible_cpu(cpu) { - struct pcpu_sw_netstats *stats64; - u64 rx_packets, rx_bytes; - u64 tx_packets, tx_bytes; - - stats64 = per_cpu_ptr(dev->stats64, cpu); - - do { - start = u64_stats_fetch_begin_irq(&stats64->syncp); -#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0) - rx_packets = stats64->rx_packets; - rx_bytes = stats64->rx_bytes; - tx_packets = stats64->tx_packets; - tx_bytes = stats64->tx_bytes; -#else - rx_packets = u64_stats_read(&stats64->rx_packets); - rx_bytes = u64_stats_read(&stats64->rx_bytes); - tx_packets = u64_stats_read(&stats64->tx_packets); - tx_bytes = u64_stats_read(&stats64->tx_bytes); -#endif - } while (u64_stats_fetch_retry_irq(&stats64->syncp, start)); - - stats->rx_packets += rx_packets; - stats->rx_bytes += rx_bytes; - stats->tx_packets += tx_packets; - stats->tx_bytes += tx_bytes; - } - - return stats; -} - -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 4,10,0 )) //bc1f44709cf27fb2a5766cadafe7e2ad5e9cb221 -static void rmnet_vnd_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats) { - _rmnet_vnd_get_stats64(net, stats); -} -#else -static struct rtnl_link_stats64 *rmnet_vnd_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats) { - return _rmnet_vnd_get_stats64(net, stats); -} -#endif -#endif - -#if defined(FIBOCOM_UL_DATA_AGG) -static void rmnet_usb_tx_wake_queue(unsigned long data) { - sQmiWwanQmap *pQmapDev = (void *)data; - int i; - - for (i = 0; i < pQmapDev->qmap_mode; i++) { - struct net_device *qmap_net = pQmapDev->mpQmapNetDev[i]; - if (qmap_net) { - if (netif_queue_stopped(qmap_net) && !netif_queue_stopped(pQmapDev->mpNetDev->net)) { - netif_wake_queue(qmap_net); - } - } - } -} - -static void rmnet_usb_tx_skb_destructor(struct sk_buff *skb) { - struct net_device *net = skb->dev; - struct usbnet * dev = netdev_priv( net ); - struct qmi_wwan_state *info = (void *)&dev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - - if (pQmapDev && pQmapDev->use_rmnet_usb) { - int i; - - for (i = 0; i < pQmapDev->qmap_mode; i++) { - struct net_device *qmap_net = pQmapDev->mpQmapNetDev[i]; - - if (qmap_net) { - if (netif_queue_stopped(qmap_net)) { - tasklet_schedule(&pQmapDev->txq); - break; - } - } - } - } -} - -static int rmnet_usb_tx_agg_skip(struct sk_buff *skb, int offset) -{ - u8 *packet_start = skb->data + offset; - int ready2send = 0; - - if (skb->protocol == htons(ETH_P_IP)) { - struct iphdr *ip4h = (struct iphdr *)(packet_start); - - if (ip4h->protocol == IPPROTO_TCP) { - const struct tcphdr *th = (const struct tcphdr *)(packet_start + sizeof(struct iphdr)); - if (th->psh) { - ready2send = 1; - } - } - else if (ip4h->protocol == IPPROTO_ICMP) - ready2send = 1; - - } else if (skb->protocol == htons(ETH_P_IPV6)) { - struct ipv6hdr *ip6h = (struct ipv6hdr *)(packet_start); - - if (ip6h->nexthdr == NEXTHDR_TCP) { - const struct tcphdr *th = (const struct tcphdr *)(packet_start + sizeof(struct ipv6hdr)); - if (th->psh) { - ready2send = 1; - } - } else if (ip6h->nexthdr == NEXTHDR_ICMP) { - ready2send = 1; - } else if (ip6h->nexthdr == NEXTHDR_FRAGMENT) { - struct frag_hdr *frag; - - frag = (struct frag_hdr *)(packet_start - + sizeof(struct ipv6hdr)); - if (frag->nexthdr == IPPROTO_ICMPV6) - ready2send = 1; - } - } - - return ready2send; -} - -static void rmnet_usb_tx_agg_work(struct work_struct *work) -{ - struct qmap_priv *priv = - container_of(work, struct qmap_priv, agg_wq); - struct sk_buff *skb = NULL; - unsigned long flags; - - spin_lock_irqsave(&priv->agg_lock, flags); - if (likely(priv->agg_skb)) { - skb = priv->agg_skb; - priv->agg_skb = NULL; - priv->agg_count = 0; - skb->protocol = htons(ETH_P_MAP); - skb->dev = priv->real_dev; - ktime_get_ts64(&priv->agg_time); - } - spin_unlock_irqrestore(&priv->agg_lock, flags); - - if (skb) { - int err = dev_queue_xmit(skb); - if (err != NET_XMIT_SUCCESS) { - priv->self_dev->stats.tx_errors++; - } - } -} - -static enum hrtimer_restart rmnet_usb_tx_agg_timer_cb(struct hrtimer *timer) -{ - struct qmap_priv *priv = - container_of(timer, struct qmap_priv, agg_hrtimer); - - schedule_work(&priv->agg_wq); - return HRTIMER_NORESTART; -} - -static long agg_time_limit __read_mostly = 1000000L; //reduce this time, can get better TPUT performance, but will increase USB interrupts -module_param(agg_time_limit, long, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(agg_time_limit, "Maximum time packets sit in the agg buf"); - -static long agg_bypass_time __read_mostly = 10000000L; -module_param(agg_bypass_time, long, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(agg_bypass_time, "Skip agg when apart spaced more than this"); - -static int rmnet_usb_tx_agg(struct sk_buff *skb, struct qmap_priv *priv) { - struct qmi_wwan_state *info = (void *)&priv->dev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - struct tx_agg_ctx *ctx = &pQmapDev->tx_ctx; - int ready2send = 0; - int xmit_more = 0; - struct timespec64 diff, now; - struct sk_buff *agg_skb = NULL; - unsigned long flags; - int err; - struct net_device *pNet = priv->self_dev; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(5,1,0) //6b16f9ee89b8d5709f24bc3ac89ae8b5452c0d7c -#if LINUX_VERSION_CODE > KERNEL_VERSION(3,16,0) - xmit_more = skb->xmit_more; -#endif -#else - xmit_more = netdev_xmit_more(); -#endif - - rmnet_vnd_update_tx_stats(pNet, 1, skb->len); - - if (ctx->ul_data_aggregation_max_datagrams == 1) { - skb->protocol = htons(ETH_P_MAP); - skb->dev = priv->real_dev; - if (!skb->destructor) - skb->destructor = rmnet_usb_tx_skb_destructor; - err = dev_queue_xmit(skb); - if (err != NET_XMIT_SUCCESS) - pNet->stats.tx_errors++; - return NET_XMIT_SUCCESS; - } - -new_packet: - spin_lock_irqsave(&priv->agg_lock, flags); - agg_skb = NULL; - ready2send = 0; - ktime_get_ts64(&now); - diff = timespec64_sub(now, priv->agg_time); - - if (priv->agg_skb) { - if ((priv->agg_skb->len + skb->len) < ctx->ul_data_aggregation_max_size) { - memcpy(skb_put(priv->agg_skb, skb->len), skb->data, skb->len); - priv->agg_count++; - - if (diff.tv_sec > 0 || diff.tv_nsec > agg_time_limit) { - ready2send = 1; - } - else if (priv->agg_count == ctx->ul_data_aggregation_max_datagrams) { - ready2send = 1; - } - else if (xmit_more == 0) { - struct rmnet_map_header *map_header = (struct rmnet_map_header *)skb->data; - size_t offset = sizeof(struct rmnet_map_header); - if (map_header->next_hdr) - offset += sizeof(struct rmnet_map_v5_csum_header); - - ready2send = rmnet_usb_tx_agg_skip(skb, offset); - } - - dev_kfree_skb_any(skb); - skb = NULL; - } - else { - ready2send = 1; - } - - if (ready2send) { - agg_skb = priv->agg_skb; - priv->agg_skb = NULL; - priv->agg_count = 0; - } - } - else if (skb) { - if (diff.tv_sec > 0 || diff.tv_nsec > agg_bypass_time) { - ready2send = 1; - } - else if (xmit_more == 0) { - struct rmnet_map_header *map_header = (struct rmnet_map_header *)skb->data; - size_t offset = sizeof(struct rmnet_map_header); - if (map_header->next_hdr) - offset += sizeof(struct rmnet_map_v5_csum_header); - - ready2send = rmnet_usb_tx_agg_skip(skb, offset); - } - - if (ready2send == 0) { - priv->agg_skb = alloc_skb(ctx->ul_data_aggregation_max_size, GFP_ATOMIC); - if (priv->agg_skb) { - memcpy(skb_put(priv->agg_skb, skb->len), skb->data, skb->len); - priv->agg_count++; - dev_kfree_skb_any(skb); - skb = NULL; - } - else { - ready2send = 1; - } - } - - if (ready2send) { - agg_skb = skb; - skb = NULL; - } - } - - if (ready2send) { - priv->agg_time = now; - } - spin_unlock_irqrestore(&priv->agg_lock, flags); - - if (agg_skb) { - agg_skb->protocol = htons(ETH_P_MAP); - agg_skb->dev = priv->real_dev; - if (!agg_skb->destructor) - agg_skb->destructor = rmnet_usb_tx_skb_destructor; - err = dev_queue_xmit(agg_skb); - if (err != NET_XMIT_SUCCESS) { - pNet->stats.tx_errors++; - } - } - - if (skb) { - goto new_packet; - } - - if (priv->agg_skb) { - if (!hrtimer_is_queued(&priv->agg_hrtimer)) - hrtimer_start(&priv->agg_hrtimer, ns_to_ktime(NSEC_PER_MSEC * 2), HRTIMER_MODE_REL); - } - - return NET_XMIT_SUCCESS; -} -#endif - -static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb, - struct net_device *pNet) -{ - int err; - struct qmap_priv *priv = netdev_priv(pNet); - - if (netif_queue_stopped(priv->real_dev)) { - netif_stop_queue(pNet); - return NETDEV_TX_BUSY; - } - - //printk("%s 1 skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - if (pNet->type == ARPHRD_ETHER) { - skb_reset_mac_header(skb); - -#ifdef FIBOCOM_BRIDGE_MODE - if (priv->bridge_mode && bridge_mode_tx_fixup(pNet, skb, priv->bridge_ipv4, priv->bridge_mac) == NULL) { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } -#endif - - if (skb_pull(skb, ETH_HLEN) == NULL) { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } - } - //printk("%s 2 skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - - if (priv->qmap_version == 5) { - add_qhdr(skb, priv->mux_id); - } - else if (priv->qmap_version == 9) { - add_qhdr_v5(skb, priv->mux_id); - } - else { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } - //printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - -#if 0 - skb->protocol = htons(ETH_P_MAP); - skb->dev = priv->real_dev; - err = dev_queue_xmit(skb); - - if (err == NET_XMIT_SUCCESS) { - rmnet_vnd_update_tx_stats(pNet, 1, skb->len); - } else { - pNet->stats.tx_errors++; - } -#else - err = rmnet_usb_tx_agg(skb, priv); -#endif - - return err; -} - -static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu) -{ - if (new_mtu < 0 || new_mtu > 1500) - return -EINVAL; - - rmnet_dev->mtu = new_mtu; - return 0; -} - -/* drivers may override default ethtool_ops in their bind() routine */ -static const struct ethtool_ops rmnet_vnd_ethtool_ops = { - .get_link = ethtool_op_get_link, -}; - -static int qmap_start_xmit(struct sk_buff *skb, struct net_device *pNet) -{ - int err; - struct qmap_priv *priv = netdev_priv(pNet); - - //printk("%s 1 skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - if (pNet->type == ARPHRD_ETHER) { - skb_reset_mac_header(skb); - -#ifdef FIBOCOM_BRIDGE_MODE - if (priv->bridge_mode && bridge_mode_tx_fixup(pNet, skb, priv->bridge_ipv4, priv->bridge_mac) == NULL) { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } -#endif - - if (skb_pull(skb, ETH_HLEN) == NULL) { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } - } - - - add_qhdr(skb, FIBOCOM_QMAP_MUX_ID + priv->offset_id); - - skb->dev = priv->real_dev; - err = dev_queue_xmit(skb); - if (err == NET_XMIT_SUCCESS) { - pNet->stats.tx_packets++; - pNet->stats.tx_bytes += skb->len; - } else { - pNet->stats.tx_errors++; - } - - return err; -} - -static const struct net_device_ops qmap_netdev_ops = { - .ndo_open = qmap_open, - .ndo_stop = qmap_stop, - .ndo_start_xmit = qmap_start_xmit, -}; - -static const struct net_device_ops rmnet_vnd_ops = { - .ndo_open = qmap_open, - .ndo_stop = qmap_stop, - .ndo_start_xmit = rmnet_vnd_start_xmit, - .ndo_change_mtu = rmnet_vnd_change_mtu, -#if defined(MHI_NETDEV_STATUS64) - .ndo_get_stats64 = rmnet_vnd_get_stats64, -#endif -}; - -static void rmnet_usb_vnd_setup(struct net_device *rmnet_dev) -{ - rmnet_dev->needed_headroom = 16; - - /* Raw IP mode */ - rmnet_dev->header_ops = NULL; /* No header */ - rmnet_dev->type = ARPHRD_RAWIP; - rmnet_dev->hard_header_len = 0; - rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); -} - -static rx_handler_result_t rmnet_usb_rx_priv_handler(struct sk_buff **pskb) -{ - struct sk_buff *skb = *pskb; - struct rmnet_nss_cb *nss_cb; - - if (!skb) - return RX_HANDLER_CONSUMED; - - //printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - - if (skb->pkt_type == PACKET_LOOPBACK) - return RX_HANDLER_PASS; - - /* Check this so that we dont loop around netif_receive_skb */ - if (skb->cb[0] == 1) { - skb->cb[0] = 0; - - return RX_HANDLER_PASS; - } - - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (nss_cb) { - nss_cb->nss_tx(skb); - return RX_HANDLER_CONSUMED; - } - - return RX_HANDLER_PASS; -} - -static int qmap_register_device(sQmiWwanQmap * pDev, u8 offset_id) -{ - struct net_device *real_dev = pDev->mpNetDev->net; - struct net_device *qmap_net; - struct qmap_priv *priv; - int err; - struct rmnet_nss_cb *nss_cb; - - qmap_net = alloc_etherdev(sizeof(*priv)); - if (!qmap_net) - return -ENOBUFS; - - SET_NETDEV_DEV(qmap_net, &real_dev->dev); - priv = netdev_priv(qmap_net); - priv->offset_id = offset_id; - priv->real_dev = real_dev; - priv->self_dev = qmap_net; - priv->dev = pDev->mpNetDev; - priv->qmap_version = pDev->qmap_version; - priv->mux_id = FIBOCOM_QMAP_MUX_ID + offset_id; - sprintf(qmap_net->name, "%s.%d", real_dev->name, offset_id + 1); - qmap_net->netdev_ops = &qmap_netdev_ops; - memcpy (qmap_net->dev_addr, real_dev->dev_addr, ETH_ALEN); - -#ifdef FIBOCOM_BRIDGE_MODE - priv->bridge_mode = !!(pDev->bridge_mode & BIT(offset_id)); - qmap_net->sysfs_groups[0] = &qmi_qmap_sysfs_attr_group; -#endif - - priv->agg_skb = NULL; - priv->agg_count = 0; - hrtimer_init(&priv->agg_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - priv->agg_hrtimer.function = rmnet_usb_tx_agg_timer_cb; - INIT_WORK(&priv->agg_wq, rmnet_usb_tx_agg_work); - ktime_get_ts64(&priv->agg_time); - spin_lock_init(&priv->agg_lock); - - if (pDev->use_rmnet_usb) { - qmap_net->ethtool_ops = &rmnet_vnd_ethtool_ops; - qmap_net->netdev_ops = &rmnet_vnd_ops; -#if defined(MHI_NETDEV_STATUS64) - priv->stats64 = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); - if (!priv->stats64) { - err = -ENOBUFS; - goto out_free_newdev; - } -#endif - } - - nss_cb = rcu_dereference(rmnet_nss_callbacks); -#ifdef FIBOCOM_BRIDGE_MODE - if(nss_cb && !priv->bridge_mode) { -#else - if (nss_cb) { -#endif - rmnet_usb_vnd_setup(qmap_net); - } - - err = register_netdev(qmap_net); - dev_info(&real_dev->dev, "%s(%s)=%d\n", __func__, qmap_net->name, err); - if (err < 0) - goto out_free_newdev; - netif_device_attach (qmap_net); - netif_carrier_off(qmap_net); - - nss_cb = rcu_dereference(rmnet_nss_callbacks); -#ifdef FIBOCOM_BRIDGE_MODE - if(nss_cb && !priv->bridge_mode) { -#else - if (nss_cb) { -#endif - int rc = nss_cb->nss_create(qmap_net); - if (rc) { - /* Log, but don't fail the device creation */ - netdev_err(qmap_net, "Device will not use NSS path: %d\n", rc); - } else { - netdev_info(qmap_net, "NSS context created\n"); - rtnl_lock(); - netdev_rx_handler_register(qmap_net, rmnet_usb_rx_priv_handler, NULL); - rtnl_unlock(); - } - } - - if (pDev->use_rmnet_usb) { - strcpy(pDev->rmnet_info.ifname[offset_id], qmap_net->name); - pDev->rmnet_info.mux_id[offset_id] = priv->mux_id; - } - - pDev->mpQmapNetDev[offset_id] = qmap_net; - qmap_net->flags |= IFF_NOARP; - qmap_net->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); - - dev_info(&real_dev->dev, "%s %s\n", __func__, qmap_net->name); - - return 0; - -out_free_newdev: - free_netdev(qmap_net); - return err; -} - -static void qmap_unregister_device(sQmiWwanQmap * pDev, u8 offset_id) { - struct net_device *qmap_net = pDev->mpQmapNetDev[offset_id]; - - if (qmap_net != NULL && qmap_net != pDev->mpNetDev->net) { - struct rmnet_nss_cb *nss_cb; - struct qmap_priv *priv = netdev_priv(qmap_net); - unsigned long flags; - - pr_info("qmap_unregister_device(%s)\n", qmap_net->name); - pDev->mpQmapNetDev[offset_id] = NULL; - netif_carrier_off( qmap_net ); - netif_stop_queue( qmap_net ); - - hrtimer_cancel(&priv->agg_hrtimer); - cancel_work_sync(&priv->agg_wq); - spin_lock_irqsave(&priv->agg_lock, flags); - if (priv->agg_skb) { - kfree_skb(priv->agg_skb); - } - spin_unlock_irqrestore(&priv->agg_lock, flags); - nss_cb = rcu_dereference(rmnet_nss_callbacks); - -#ifdef FIBOCOM_BRIDGE_MODE - if(nss_cb && !priv->bridge_mode) { -#else - if (nss_cb) { -#endif - rtnl_lock(); - netdev_rx_handler_unregister(qmap_net); - rtnl_unlock(); - nss_cb->nss_free(qmap_net); - } - -#if defined(MHI_NETDEV_STATUS64) - free_percpu(priv->stats64); -#endif - unregister_netdev (qmap_net); - free_netdev(qmap_net); - } -} - -#if 1//def CONFIG_ANDROID -typedef struct { - unsigned int size; - unsigned int rx_urb_size; - unsigned int ep_type; - unsigned int iface_id; - unsigned int MuxId; - unsigned int ul_data_aggregation_max_datagrams; //0x17 - unsigned int ul_data_aggregation_max_size ;//0x18 - unsigned int dl_minimum_padding; //0x1A -} QMAP_SETTING; - -int qma_setting_store(struct device *dev, QMAP_SETTING *qmap_settings, size_t size) { - struct net_device *netdev = to_net_dev(dev); - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - - if (qmap_settings->size != size) { - dev_err(dev, "ERROR: qmap_settings.size donot match!\n"); - return -EOPNOTSUPP; - } - -#ifdef FIBOCOM_UL_DATA_AGG - netif_tx_lock_bh(netdev); - if (pQmapDev->tx_ctx.ul_data_aggregation_max_datagrams == 1 && qmap_settings->ul_data_aggregation_max_datagrams > 1) { - pQmapDev->tx_ctx.ul_data_aggregation_max_datagrams = qmap_settings->ul_data_aggregation_max_datagrams; - pQmapDev->tx_ctx.ul_data_aggregation_max_size = qmap_settings->ul_data_aggregation_max_size; - pQmapDev->tx_ctx.dl_minimum_padding = qmap_settings->dl_minimum_padding; - dev_info(dev, "ul_data_aggregation_max_datagrams=%d, ul_data_aggregation_max_size=%d, dl_minimum_padding=%d\n", - pQmapDev->tx_ctx.ul_data_aggregation_max_datagrams, - pQmapDev->tx_ctx.ul_data_aggregation_max_size, - pQmapDev->tx_ctx.dl_minimum_padding); - } - netif_tx_unlock_bh(netdev); - return 0; -#endif - - return -EOPNOTSUPP; -} - -static int qmap_ndo_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - struct usbnet * usbnetdev = netdev_priv( dev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - int rc = -EOPNOTSUPP; - uint link_state = 0; - QMAP_SETTING qmap_settings = {0}; - - switch (cmd) { - case 0x89F1: //SIOCDEVPRIVATE - rc = copy_from_user(&link_state, ifr->ifr_ifru.ifru_data, sizeof(link_state)); - if (!rc) { - char buf[32]; - snprintf(buf, sizeof(buf), "%u", link_state); - link_state_store(&dev->dev, NULL, buf, strlen(buf)); - } - break; - - case 0x89F2: //SIOCDEVPRIVATE - rc = copy_from_user(&qmap_settings, ifr->ifr_ifru.ifru_data, sizeof(qmap_settings)); - if (!rc) { - rc = qma_setting_store(&dev->dev, &qmap_settings, sizeof(qmap_settings)); - } - break; - - case 0x89F3: //SIOCDEVPRIVATE - if (pQmapDev->use_rmnet_usb) { - uint i; - - for (i = 0; i < pQmapDev->qmap_mode; i++) { - struct net_device *qmap_net = pQmapDev->mpQmapNetDev[i]; - - if (!qmap_net) - break; - - strcpy(pQmapDev->rmnet_info.ifname[i], qmap_net->name); - } - rc = copy_to_user(ifr->ifr_ifru.ifru_data, &pQmapDev->rmnet_info, sizeof(pQmapDev->rmnet_info)); - } - break; - - default: - break; - } - - return rc; -} -#endif - -#ifdef FIBOCOM_BRIDGE_MODE -static int is_qmap_netdev(const struct net_device *netdev) { - return netdev->netdev_ops == &qmap_netdev_ops || netdev->netdev_ops == &rmnet_vnd_ops; -} -#endif -#endif - -static struct sk_buff *qmi_wwan_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) { - if ((dev->driver_info->flags & FLAG_NOARP) == 0) - return skb; - - // Skip Ethernet header from message - if (dev->net->hard_header_len == 0) - return skb; - else - skb_reset_mac_header(skb); - -#ifdef FIBOCOM_BRIDGE_MODE -{ - struct qmi_wwan_state *info = (void *)&dev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - - if (pQmapDev->bridge_mode && bridge_mode_tx_fixup(dev->net, skb, pQmapDev->bridge_ipv4, pQmapDev->bridge_mac) == NULL) { - dev_kfree_skb_any (skb); - return NULL; - } -} -#endif - - if (skb_pull(skb, ETH_HLEN)) { - return skb; - } else { - dev_err(&dev->intf->dev, "Packet Dropped "); - } - - // Filter the packet out, release it - dev_kfree_skb_any(skb); - return NULL; -} -#endif - -/* Make up an ethernet header if the packet doesn't have one. - * - * A firmware bug common among several devices cause them to send raw - * IP packets under some circumstances. There is no way for the - * driver/host to know when this will happen. And even when the bug - * hits, some packets will still arrive with an intact header. - * - * The supported devices are only capably of sending IPv4, IPv6 and - * ARP packets on a point-to-point link. Any packet with an ethernet - * header will have either our address or a broadcast/multicast - * address as destination. ARP packets will always have a header. - * - * This means that this function will reliably add the appropriate - * header if necessary, provided our hardware address does not start - * with 4 or 6. - * - * Another common firmware bug results in all packets being addressed - * to 00:a0:c6:00:00:00 despite the host address being different. - * This function will also fixup such packets. - */ -static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -{ - __be16 proto; - - /* This check is no longer done by usbnet */ - if (skb->len < dev->net->hard_header_len) - return 0; - - switch (skb->data[0] & 0xf0) { - case 0x40: - proto = htons(ETH_P_IP); - break; - case 0x60: - proto = htons(ETH_P_IPV6); - break; - case 0x00: - if (is_multicast_ether_addr(skb->data)) - return 1; - /* possibly bogus destination - rewrite just in case */ - skb_reset_mac_header(skb); - goto fix_dest; - default: - /* pass along other packets without modifications */ - return 1; - } - if (skb_headroom(skb) < ETH_HLEN) - return 0; - skb_push(skb, ETH_HLEN); - skb_reset_mac_header(skb); - eth_hdr(skb)->h_proto = proto; - memset(eth_hdr(skb)->h_source, 0, ETH_ALEN); -#if 1 //Added by Fibocom - //some kernel will drop ethernet packet which's souce mac is all zero - memcpy(eth_hdr(skb)->h_source, default_modem_addr, ETH_ALEN); -#endif - -fix_dest: -#ifdef FIBOCOM_BRIDGE_MODE -{ - struct qmi_wwan_state *info = (void *)&dev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - bridge_mode_rx_fixup(pQmapDev, dev->net, skb); -} -#else - memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN); -#endif - - return 1; -} - -#if defined(FIBOCOM_WWAN_QMAP) -static struct sk_buff *qmap_qmi_wwan_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) { - struct qmi_wwan_state *info = (void *)&dev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - struct qmap_hdr *qhdr; - - if (unlikely(pQmapDev == NULL)) { - goto drop_skb; - } else if (unlikely(pQmapDev->qmap_mode && !pQmapDev->link_state)) { - dev_dbg(&dev->net->dev, "link_state 0x%x, drop skb, len = %u\n", pQmapDev->link_state, skb->len); - goto drop_skb; - } else if (pQmapDev->qmap_mode == 0) { - skb = qmi_wwan_tx_fixup(dev, skb, flags); - } - else if (pQmapDev->qmap_mode > 1) { - if (likely(skb)) { - qhdr = (struct qmap_hdr *)skb->data; - if ((qhdr->mux_id&0xF0) != 0x80 || ((be16_to_cpu(qhdr->pkt_len) + sizeof(struct qmap_hdr)) != skb->len)) { - goto drop_skb; - } - } - } - else { - if (likely(skb)) { - skb = qmi_wwan_tx_fixup(dev, skb, flags); - - if (skb) { - add_qhdr(skb, FIBOCOM_QMAP_MUX_ID); - } - else { - return NULL; - } - } - } - - if (skb && (dev->driver_info->flags&FLAG_MULTI_PACKET)) { - usbnet_set_skb_tx_stats(skb, 1, 0); - } - - - return skb; -drop_skb: - dev_kfree_skb_any (skb); - return NULL; -} - -static int qmap_qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) -{ - struct qmi_wwan_state *info = (void *)&dev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - unsigned headroom = 0; - const unsigned need_headroot = ETH_HLEN; - struct sk_buff *qmap_skb; - - if (pQmapDev->qmap_mode == 0) - return qmi_wwan_rx_fixup(dev, skb_in); - - headroom = skb_headroom(skb_in); - - while (skb_in->len > sizeof(struct qmap_hdr)) { - struct qmap_hdr *qhdr = (struct qmap_hdr *)skb_in->data; - struct net_device *qmap_net; - int pkt_len = be16_to_cpu(qhdr->pkt_len); - int skb_len; - __be16 protocol; - int mux_id; - - skb_len = pkt_len - (qhdr->cd_rsvd_pad&0x3F); - if (skb_len > 1500) { - dev_info(&dev->net->dev, "drop skb_len=%x larger than 1500\n", skb_len); - goto error_pkt; - } - - if (skb_in->len < (pkt_len + sizeof(struct qmap_hdr))) { - dev_info(&dev->net->dev, "drop qmap unknow pkt, len=%d, pkt_len=%d\n", skb_in->len, pkt_len); - goto error_pkt; - } - - if (qhdr->cd_rsvd_pad & 0x80) { - dev_info(&dev->net->dev, "skip qmap command packet %x\n", qhdr->cd_rsvd_pad); - goto skip_pkt; - } - - switch (skb_in->data[sizeof(struct qmap_hdr)] & 0xf0) { - case 0x40: - protocol = htons(ETH_P_IP); - break; - case 0x60: - protocol = htons(ETH_P_IPV6); - break; - default: - dev_info(&dev->net->dev, "unknow skb->protocol %02x\n", skb_in->data[sizeof(struct qmap_hdr)]); - goto error_pkt; - } - - mux_id = qhdr->mux_id - FIBOCOM_QMAP_MUX_ID; - if (mux_id >= pQmapDev->qmap_mode) { - dev_info(&dev->net->dev, "drop qmap unknow mux_id %x\n", qhdr->mux_id); - goto error_pkt; - } - - qmap_net = pQmapDev->mpQmapNetDev[mux_id]; - - if (qmap_net == NULL) { - dev_info(&dev->net->dev, "drop qmap unknow mux_id %x\n", qhdr->mux_id); - goto skip_pkt; - } - - if (headroom >= need_headroot) { - qmap_skb = skb_clone(skb_in, GFP_ATOMIC); - if (qmap_skb) { - qmap_skb->dev = qmap_net; - skb_pull(qmap_skb, sizeof(struct qmap_hdr)); - skb_trim(qmap_skb, skb_len); - } - headroom = (qhdr->cd_rsvd_pad&0x3F); - } - else { - qmap_skb = netdev_alloc_skb(qmap_net, need_headroot + skb_len); - if (qmap_skb) { - skb_reserve(qmap_skb, need_headroot); - skb_put(qmap_skb, skb_len); - memcpy(qmap_skb->data, skb_in->data + sizeof(struct qmap_hdr), skb_len); - } - headroom = pkt_len; - } - - if (qmap_skb == NULL) { - dev_info(&dev->net->dev, "fail to alloc skb, pkt_len = %d\n", skb_len); - return 0; - } - - skb_push(qmap_skb, ETH_HLEN); - skb_reset_mac_header(qmap_skb); - memcpy(eth_hdr(qmap_skb)->h_source, default_modem_addr, ETH_ALEN); - memcpy(eth_hdr(qmap_skb)->h_dest, qmap_net->dev_addr, ETH_ALEN); - eth_hdr(qmap_skb)->h_proto = protocol; -#ifdef FIBOCOM_BRIDGE_MODE - bridge_mode_rx_fixup(pQmapDev, qmap_net, qmap_skb); -#endif - - if (qmap_net != dev->net) { - qmap_net->stats.rx_packets++; - qmap_net->stats.rx_bytes += qmap_skb->len; - } - - skb_queue_tail(&pQmapDev->skb_chain, qmap_skb); - -skip_pkt: - skb_pull(skb_in, pkt_len + sizeof(struct qmap_hdr)); - } - - while ((qmap_skb = skb_dequeue (&pQmapDev->skb_chain))) { - if (qmap_skb->dev != dev->net) { - qmap_skb->protocol = eth_type_trans (qmap_skb, qmap_skb->dev); - netif_rx(qmap_skb); - } - else { - qmap_skb->protocol = 0; - usbnet_skb_return(dev, qmap_skb); - } - } - -error_pkt: - return 0; -} -#endif - -/* very simplistic detection of IPv4 or IPv6 headers */ -static bool possibly_iphdr(const char *data) -{ - return (data[0] & 0xd0) == 0x40; -} - -/* disallow addresses which may be confused with IP headers */ -static int qmi_wwan_mac_addr(struct net_device *dev, void *p) -{ - int ret; - struct sockaddr *addr = p; - - ret = eth_prepare_mac_addr_change(dev, p); - if (ret < 0) - return ret; - if (possibly_iphdr(addr->sa_data)) - return -EADDRNOTAVAIL; - eth_commit_mac_addr_change(dev, p); - return 0; -} - -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 4,10,0 )) //bc1f44709cf27fb2a5766cadafe7e2ad5e9cb221 -static void (*_usbnet_get_stats64)(struct net_device *net, struct rtnl_link_stats64 *stats); - -static void qmi_wwan_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats) { - if (_usbnet_get_stats64) ////c8b5d129ee293bcf972e7279ac996bb8a138505c - return _usbnet_get_stats64(net, stats); - - netdev_stats_to_stats64(stats, &net->stats); -} -#else -static struct rtnl_link_stats64 * (*_usbnet_get_stats64)(struct net_device *net, struct rtnl_link_stats64 *stats); - -static struct rtnl_link_stats64 * qmi_wwan_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats) { - if (_usbnet_get_stats64) - return _usbnet_get_stats64(net, stats); - - netdev_stats_to_stats64(stats, &net->stats); - return stats; -} -#endif - -static netdev_tx_t qmi_wwan_start_xmit (struct sk_buff *skb, - struct net_device *net) -{ - struct usbnet * usbnetdev = netdev_priv( net ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - int retval; - - retval = usbnet_start_xmit(skb, net); - - if (netif_queue_stopped(net) && pQmapDev && pQmapDev->use_rmnet_usb) { - int i; - - for (i = 0; i < pQmapDev->qmap_mode; i++) { - struct net_device *qmap_net = pQmapDev->mpQmapNetDev[i]; - if (qmap_net) { - netif_stop_queue(qmap_net); - } - } - } - - return retval; -} - -static const struct net_device_ops qmi_wwan_netdev_ops = { - .ndo_open = usbnet_open, - .ndo_stop = usbnet_stop, - .ndo_start_xmit = qmi_wwan_start_xmit, - .ndo_tx_timeout = usbnet_tx_timeout, - .ndo_change_mtu = usbnet_change_mtu, - .ndo_get_stats64 = qmi_wwan_get_stats64, - .ndo_set_mac_address = qmi_wwan_mac_addr, - .ndo_validate_addr = eth_validate_addr, -#if defined(FIBOCOM_WWAN_QMAP)// && defined(CONFIG_ANDROID) - .ndo_do_ioctl = qmap_ndo_do_ioctl, -#endif -}; - -static void ql_net_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) -{ - /* Inherit standard device info */ - usbnet_get_drvinfo(net, info); - strlcpy(info->driver, driver_name, sizeof(info->driver)); - strlcpy(info->version, VERSION_NUMBER, sizeof(info->version)); -} - -static struct ethtool_ops ql_net_ethtool_ops; - -/* using a counter to merge subdriver requests with our own into a - * combined state - */ -static int qmi_wwan_manage_power(struct usbnet *dev, int on) -{ - struct qmi_wwan_state *info = (void *)&dev->data; - int rv; - - dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, - atomic_read(&info->pmcount), on); - - if ((on && atomic_add_return(1, &info->pmcount) == 1) || - (!on && atomic_dec_and_test(&info->pmcount))) { - /* need autopm_get/put here to ensure the usbcore sees - * the new value - */ - rv = usb_autopm_get_interface(dev->intf); - dev->intf->needs_remote_wakeup = on; - if (!rv) - usb_autopm_put_interface(dev->intf); - } - return 0; -} - -static int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on) -{ - struct usbnet *dev = usb_get_intfdata(intf); - - /* can be called while disconnecting */ - if (!dev) - return 0; - return qmi_wwan_manage_power(dev, on); -} - -/* collect all three endpoints and register subdriver */ -static int qmi_wwan_register_subdriver(struct usbnet *dev) -{ - int rv; - struct usb_driver *subdriver = NULL; - struct qmi_wwan_state *info = (void *)&dev->data; - - /* collect bulk endpoints */ - rv = usbnet_get_endpoints(dev, info->data); - if (rv < 0) - goto err; - - /* update status endpoint if separate control interface */ - if (info->control != info->data) - dev->status = &info->control->cur_altsetting->endpoint[0]; - - /* require interrupt endpoint for subdriver */ - if (!dev->status) { - rv = -EINVAL; - goto err; - } - - /* for subdriver power management */ - atomic_set(&info->pmcount, 0); - - /* register subdriver */ -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 5,12,0 )) //cac6fb015f719104e60b1c68c15ca5b734f57b9c - subdriver = usb_cdc_wdm_register(info->control, &dev->status->desc, - 4096, WWAN_PORT_QMI, &qmi_wwan_cdc_wdm_manage_power); -#else - subdriver = usb_cdc_wdm_register(info->control, &dev->status->desc, - 4096, &qmi_wwan_cdc_wdm_manage_power); - -#endif - if (IS_ERR(subdriver)) { - dev_err(&info->control->dev, "subdriver registration failed\n"); - rv = PTR_ERR(subdriver); - goto err; - } - - /* prevent usbnet from using status endpoint */ - dev->status = NULL; - - /* save subdriver struct for suspend/resume wrappers */ - info->subdriver = subdriver; - -err: - return rv; -} - -static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) -{ - int status = -1; - struct usb_driver *driver = driver_of(intf); - struct qmi_wwan_state *info = (void *)&dev->data; - - BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < - sizeof(struct qmi_wwan_state))); - - /* set up initial state */ - info->control = intf; - info->data = intf; - - status = qmi_wwan_register_subdriver(dev); - if (status < 0 && info->control != info->data) { - usb_set_intfdata(info->data, NULL); - usb_driver_release_interface(driver, info->data); - } - - /* Never use the same address on both ends of the link, even - * if the buggy firmware told us to. - */ - if (ether_addr_equal(dev->net->dev_addr, default_modem_addr)) - eth_hw_addr_random(dev->net); - - /* make MAC addr easily distinguishable from an IP header */ - if (possibly_iphdr(dev->net->dev_addr)) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0) - dev->net->dev_addr[0] |= 0x02; /* set local assignment bit */ - dev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */ -#else - u8 addr = dev->net->dev_addr[0]; - - addr |= 0x02; /* set local assignment bit */ - addr &= 0xbf; /* clear "IP" bit */ - dev_addr_mod(dev->net, 0, &addr, 1); -#endif - } - if (!_usbnet_get_stats64) - _usbnet_get_stats64 = dev->net->netdev_ops->ndo_get_stats64; - dev->net->netdev_ops = &qmi_wwan_netdev_ops; - - ql_net_ethtool_ops = *dev->net->ethtool_ops; - ql_net_ethtool_ops.get_drvinfo = ql_net_get_drvinfo; - dev->net->ethtool_ops = &ql_net_ethtool_ops; - -#if 1 //Added by Fibocom - if (dev->driver_info->flags & FLAG_NOARP) { - int ret; - char buf[32] = "Module"; - - ret = usb_string(dev->udev, dev->udev->descriptor.iProduct, buf, sizeof(buf)); - if (ret > 0) { - buf[ret] = '\0'; - } - - dev_err(&intf->dev, "Fibocom %s work on RawIP mode\n", buf); - dev->net->flags |= IFF_NOARP; - dev->net->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); - - usb_control_msg( - interface_to_usbdev(intf), - usb_sndctrlpipe(interface_to_usbdev(intf), 0), - 0x22, //USB_CDC_REQ_SET_CONTROL_LINE_STATE - 0x21, //USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE - 1, //active CDC DTR - intf->cur_altsetting->desc.bInterfaceNumber, - NULL, 0, 100); - } - - dev->rx_urb_size = ETH_DATA_LEN + ETH_HLEN + 6; - -#if defined(FIBOCOM_WWAN_QMAP) - if (qmap_mode > FIBOCOM_WWAN_QMAP) - qmap_mode = FIBOCOM_WWAN_QMAP; - - if (!status) - { - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)kzalloc(sizeof(sQmiWwanQmap), GFP_KERNEL); - - if (pQmapDev == NULL) - return -ENODEV; - -#ifdef FIBOCOM_BRIDGE_MODE - pQmapDev->bridge_mode = bridge_mode; -#endif - pQmapDev->mpNetDev = dev; - pQmapDev->link_state = 1; - - dev->net->features |= (NETIF_F_VLAN_CHALLENGED); - - skb_queue_head_init(&pQmapDev->skb_chain); - - if (dev->driver_info->flags & FLAG_NOARP) - { - int idProduct = le16_to_cpu(dev->udev->descriptor.idProduct); - int lte_a = (idProduct == 0x0104 || idProduct == 0x0109 || idProduct == 0x0113 || idProduct == 0x1000 || idProduct == 0x1001); - - pQmapDev->qmap_mode = qmap_mode; - if (lte_a || dev->udev->speed >= USB_SPEED_SUPER) { - if (pQmapDev->qmap_mode == 0) { - pQmapDev->qmap_mode = 1; - if(qmap_mode == 0) - qmap_mode = 1; - } - } - - if (pQmapDev->qmap_mode) { - pQmapDev->qmap_version = 5; - pQmapDev->qmap_size = (dev->udev->speed >= USB_SPEED_SUPER) ? 16*1024 : 4*1024; -/* - switch (idProduct) { - case 0x0104: - pQmapDev->qmap_version = 9; - pQmapDev->qmap_size = 31*1024; - break; - default: - break; - } -*/ - dev->rx_urb_size = pQmapDev->qmap_size; - //for these modules, if send pakcet before qmi_start_network, or cause host PC crash, or cause modules crash - if (lte_a || dev->udev->speed >= USB_SPEED_SUPER) - pQmapDev->link_state = 0; - } - -#if defined(FIBOCOM_UL_DATA_AGG) - if (pQmapDev->qmap_mode) { - struct tx_agg_ctx *ctx = &pQmapDev->tx_ctx; - ctx->ul_data_aggregation_max_datagrams = 1; - ctx->ul_data_aggregation_max_size = 1500; - } -#endif - - if (pQmapDev->qmap_mode == 0) { - pQmapDev->driver_info = *dev->driver_info; - pQmapDev->driver_info.flags &= ~(FLAG_MULTI_PACKET); //see usbnet.c rx_process() - dev->driver_info = &pQmapDev->driver_info; - } - } - - info->unused = (unsigned long)pQmapDev; - dev->net->sysfs_groups[0] = &qmi_wwan_sysfs_attr_group; - - dev_info(&intf->dev, "rx_urb_size = %zd\n", dev->rx_urb_size); - } -#endif -#endif - - return status; -} - -static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf) -{ - struct qmi_wwan_state *info = (void *)&dev->data; - struct usb_driver *driver = driver_of(intf); - struct usb_interface *other; - - if (dev->udev && dev->udev->state == USB_STATE_CONFIGURED) { - usb_control_msg( - interface_to_usbdev(intf), - usb_sndctrlpipe(interface_to_usbdev(intf), 0), - 0x22, //USB_CDC_REQ_SET_CONTROL_LINE_STATE - 0x21, //USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE - 0, //deactive CDC DTR - intf->cur_altsetting->desc.bInterfaceNumber, - NULL, 0, 100); - } - - if (info->subdriver && info->subdriver->disconnect) - info->subdriver->disconnect(info->control); - - /* allow user to unbind using either control or data */ - if (intf == info->control) - other = info->data; - else - other = info->control; - - /* only if not shared */ - if (other && intf != other) { - usb_set_intfdata(other, NULL); - usb_driver_release_interface(driver, other); - } - - info->subdriver = NULL; - info->data = NULL; - info->control = NULL; -} - -/* suspend/resume wrappers calling both usbnet and the cdc-wdm - * subdriver if present. - * - * NOTE: cdc-wdm also supports pre/post_reset, but we cannot provide - * wrappers for those without adding usbnet reset support first. - */ -static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct usbnet *dev = usb_get_intfdata(intf); - struct qmi_wwan_state *info = (void *)&dev->data; - int ret; - - /* Both usbnet_suspend() and subdriver->suspend() MUST return 0 - * in system sleep context, otherwise, the resume callback has - * to recover device from previous suspend failure. - */ - ret = usbnet_suspend(intf, message); - if (ret < 0) - goto err; - - if (intf == info->control && info->subdriver && - info->subdriver->suspend) - ret = info->subdriver->suspend(intf, message); - if (ret < 0) - usbnet_resume(intf); -err: - return ret; -} - -static int qmi_wwan_resume(struct usb_interface *intf) -{ - struct usbnet *dev = usb_get_intfdata(intf); - struct qmi_wwan_state *info = (void *)&dev->data; - int ret = 0; - bool callsub = (intf == info->control && info->subdriver && - info->subdriver->resume); - - if (callsub) - ret = info->subdriver->resume(intf); - if (ret < 0) - goto err; - ret = usbnet_resume(intf); - if (ret < 0 && callsub) - info->subdriver->suspend(intf, PMSG_SUSPEND); -err: - return ret; -} - -static int qmi_wwan_reset_resume(struct usb_interface *intf) -{ - dev_info(&intf->dev, "device do not support reset_resume\n"); - intf->needs_binding = 1; - return -EOPNOTSUPP; -} - -static int rmnet_usb_bind(struct usbnet *dev, struct usb_interface *intf) -{ - int status = qmi_wwan_bind(dev, intf); - dev_err(&intf->dev, "rmnet_usb_bind\n"); - - if (!status) { - struct qmi_wwan_state *info = (void *)&dev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - - if (pQmapDev && pQmapDev->qmap_mode) { - struct net_device *rmmet_usb = dev->net; - - pQmapDev->use_rmnet_usb = 1; - pQmapDev->rmnet_info.size = sizeof(RMNET_INFO); - pQmapDev->rmnet_info.rx_urb_size = pQmapDev->qmap_size; - pQmapDev->rmnet_info.ep_type = 2; //DATA_EP_TYPE_HSUSB - pQmapDev->rmnet_info.iface_id = 4; - pQmapDev->rmnet_info.qmap_mode = pQmapDev->qmap_mode; - pQmapDev->rmnet_info.qmap_version = pQmapDev->qmap_version; - pQmapDev->rmnet_info.dl_minimum_padding = 0; - - strcpy(rmmet_usb->name, "rmnet_usb%d"); - -#if 0 - rmmet_usb->header_ops = NULL; /* No header */ - rmmet_usb->type = ARPHRD_RAWIP; - rmmet_usb->hard_header_len = 0; - rmmet_usb->addr_len = 0; -#endif - rmmet_usb->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); - rmmet_usb->flags |= (IFF_NOARP); - } - } - - return status; -} - -static struct sk_buff *rmnet_usb_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) -{ - //printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - if (skb->protocol != htons(ETH_P_MAP)) { - dev_kfree_skb_any(skb); - return NULL; - } - - return skb; -} - -static int rmnet_usb_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -{ - struct net_device *net = dev->net; - - //printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - if (net->type == ARPHRD_ETHER && skb_headroom(skb) >= ETH_HLEN) { - //usbnet.c rx_process() usbnet_skb_return() eth_type_trans() - skb_push(skb, ETH_HLEN); - skb_reset_mac_header(skb); - memcpy(eth_hdr(skb)->h_source, default_modem_addr, ETH_ALEN); - memcpy(eth_hdr(skb)->h_dest, net->dev_addr, ETH_ALEN); - eth_hdr(skb)->h_proto = htons(ETH_P_MAP); - - return 1; - } - - return 0; -} - -static void _rmnet_usb_rx_handler(struct usbnet *dev, struct sk_buff *skb_in) -{ - struct qmi_wwan_state *info = (void *)&dev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - struct sk_buff *qmap_skb; - struct sk_buff_head skb_chain; - uint dl_minimum_padding = 0; - - if (pQmapDev->qmap_version == 9) - dl_minimum_padding = pQmapDev->tx_ctx.dl_minimum_padding; - - __skb_queue_head_init(&skb_chain); - - while (skb_in->len > sizeof(struct qmap_hdr)) { - struct rmnet_map_header *map_header = (struct rmnet_map_header *)skb_in->data; - struct rmnet_map_v5_csum_header *ul_header = NULL; - size_t hdr_size = sizeof(struct rmnet_map_header); - struct net_device *qmap_net; - int pkt_len = ntohs(map_header->pkt_len); - int skb_len; - __be16 protocol; - int mux_id; - - if (map_header->next_hdr) { - ul_header = (struct rmnet_map_v5_csum_header *)(map_header + 1); - hdr_size += sizeof(struct rmnet_map_v5_csum_header); - } - - skb_len = pkt_len - (map_header->pad_len&0x3F); - skb_len -= dl_minimum_padding; - if (skb_len > 1500) { - dev_info(&dev->net->dev, "drop skb_len=%x larger than 1500\n", skb_len); - goto error_pkt; - } - - if (skb_in->len < (pkt_len + hdr_size)) { - dev_info(&dev->net->dev, "drop qmap unknow pkt, len=%d, pkt_len=%d\n", skb_in->len, pkt_len); - goto error_pkt; - } - - if (map_header->cd_bit) { - dev_info(&dev->net->dev, "skip qmap command packet\n"); - goto skip_pkt; - } - - switch (skb_in->data[hdr_size] & 0xf0) { - case 0x40: - protocol = htons(ETH_P_IP); - break; - case 0x60: - protocol = htons(ETH_P_IPV6); - break; - default: - dev_info(&dev->net->dev, "unknow skb->protocol %02x\n", skb_in->data[hdr_size]); - goto error_pkt; - } - - mux_id = map_header->mux_id - FIBOCOM_QMAP_MUX_ID; - if (mux_id >= pQmapDev->qmap_mode) { - dev_info(&dev->net->dev, "drop qmap unknow mux_id %x\n", map_header->mux_id); - goto error_pkt; - } - - qmap_net = pQmapDev->mpQmapNetDev[mux_id]; - - if (qmap_net == NULL) { - dev_info(&dev->net->dev, "drop qmap unknow mux_id %x\n", map_header->mux_id); - goto skip_pkt; - } - - qmap_skb = netdev_alloc_skb(qmap_net, skb_len); - if (qmap_skb) { - skb_put(qmap_skb, skb_len); - memcpy(qmap_skb->data, skb_in->data + hdr_size, skb_len); - } - - if (qmap_skb == NULL) { - dev_info(&dev->net->dev, "fail to alloc skb, pkt_len = %d\n", skb_len); - goto error_pkt; - } - - skb_reset_transport_header(qmap_skb); - skb_reset_network_header(qmap_skb); - qmap_skb->pkt_type = PACKET_HOST; - skb_set_mac_header(qmap_skb, 0); - qmap_skb->protocol = protocol; - - if (ul_header && ul_header->header_type == RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD - && ul_header->csum_valid_required) { -#if 0 //TODO - qmap_skb->ip_summed = CHECKSUM_UNNECESSARY; -#endif - } - - if (qmap_skb->dev->type == ARPHRD_ETHER) { - skb_push(qmap_skb, ETH_HLEN); - skb_reset_mac_header(qmap_skb); - memcpy(eth_hdr(qmap_skb)->h_source, default_modem_addr, ETH_ALEN); - memcpy(eth_hdr(qmap_skb)->h_dest, qmap_net->dev_addr, ETH_ALEN); - eth_hdr(qmap_skb)->h_proto = protocol; -#ifdef FIBOCOM_BRIDGE_MODE - bridge_mode_rx_fixup(pQmapDev, qmap_net, qmap_skb); -#endif - __skb_pull(qmap_skb, ETH_HLEN); - } - - rmnet_vnd_update_rx_stats(qmap_net, 1, skb_len); - __skb_queue_tail(&skb_chain, qmap_skb); - -skip_pkt: - skb_pull(skb_in, pkt_len + hdr_size); - } - -error_pkt: - while ((qmap_skb = __skb_dequeue (&skb_chain))) { - netif_receive_skb(qmap_skb); - } -} - -static rx_handler_result_t rmnet_usb_rx_handler(struct sk_buff **pskb) -{ - struct sk_buff *skb = *pskb; - struct usbnet *dev; - - if (!skb) - goto done; - - //printk("%s skb=%p, protocol=%x, len=%d\n", __func__, skb, skb->protocol, skb->len); - - if (skb->pkt_type == PACKET_LOOPBACK) - return RX_HANDLER_PASS; - - if (skb->protocol != htons(ETH_P_MAP)) { - WARN_ON(1); - return RX_HANDLER_PASS; - } - /* when open hyfi function, run cm will make system crash */ - //dev = rcu_dereference(skb->dev->rx_handler_data); - dev = netdev_priv(skb->dev); - - if (dev == NULL) { - WARN_ON(1); - return RX_HANDLER_PASS; - } - - _rmnet_usb_rx_handler(dev, skb); - consume_skb(skb); - -done: - return RX_HANDLER_CONSUMED; -} - -static const struct driver_info qmi_wwan_info = { - .description = "WWAN/QMI device", - .flags = FLAG_WWAN, - .bind = qmi_wwan_bind, - .unbind = qmi_wwan_unbind, - .manage_power = qmi_wwan_manage_power, - .rx_fixup = qmi_wwan_rx_fixup, -}; - -static const struct driver_info qmi_wwan_raw_ip_info = { - .description = "WWAN/QMI device", - .flags = FLAG_WWAN | FLAG_RX_ASSEMBLE | FLAG_NOARP | FLAG_SEND_ZLP | FLAG_MULTI_PACKET, - .bind = qmi_wwan_bind, - .unbind = qmi_wwan_unbind, - .manage_power = qmi_wwan_manage_power, -#if defined(FIBOCOM_WWAN_QMAP) - .tx_fixup = qmap_qmi_wwan_tx_fixup, - .rx_fixup = qmap_qmi_wwan_rx_fixup, -#else - .tx_fixup = qmi_wwan_tx_fixup, - .rx_fixup = qmi_wwan_rx_fixup, -#endif -}; - -static const struct driver_info rmnet_usb_info = { - .description = "RMNET/USB device", - .flags = FLAG_NOARP | FLAG_SEND_ZLP, - .bind = rmnet_usb_bind, - .unbind = qmi_wwan_unbind, - .manage_power = qmi_wwan_manage_power, - .tx_fixup = rmnet_usb_tx_fixup, - .rx_fixup = rmnet_usb_rx_fixup, -}; - -/* map QMI/wwan function by a fixed interface number */ -#define QMI_FIXED_INTF(vend, prod, num) \ - USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \ - .driver_info = (unsigned long)&qmi_wwan_info - -#define QMI_FIXED_RAWIP_INTF(vend, prod, num) \ - USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \ - .driver_info = (unsigned long)&qmi_wwan_raw_ip_info - -#define RMNET_USB_INTF(vend, prod, num) \ - USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \ - .driver_info = (unsigned long) &rmnet_usb_info - -static const struct usb_device_id products[] = { -#if 1 //Added by Fibocom - { QMI_FIXED_RAWIP_INTF(0x2cb7, 0x0104, 4) }, /* Fibocom FG150/FM150/NL952/FG101 */ - { QMI_FIXED_RAWIP_INTF(0x2cb7, 0x0109, 2) }, /* Fibocom FG150/FM150 */ - { QMI_FIXED_RAWIP_INTF(0x2cb7, 0x0113, 0) }, /* Fibocom FG101 */ - { QMI_FIXED_RAWIP_INTF(0x1508, 0x1000, 2) }, /* Fibocom NL668 */ - { QMI_FIXED_RAWIP_INTF(0x1508, 0x1001, 4) }, /* Fibocom NL668 */ - { QMI_FIXED_RAWIP_INTF(0x05c6, 0x9025, 4) }, /* Fibocom NL668 */ -#endif - { } /* END */ -}; -MODULE_DEVICE_TABLE(usb, products); - -static int qmi_wwan_probe(struct usb_interface *intf, - const struct usb_device_id *prod) -{ - struct usb_device_id *id = (struct usb_device_id *)prod; - - /* Workaround to enable dynamic IDs. This disables usbnet - * blacklisting functionality. Which, if required, can be - * reimplemented here by using a magic "blacklist" value - * instead of 0 in the static device id table - */ - if (!id->driver_info) { - dev_dbg(&intf->dev, "setting defaults for dynamic device id\n"); - id->driver_info = (unsigned long)&qmi_wwan_info; - } - - if (intf->cur_altsetting->desc.bInterfaceClass != 0xff) { - dev_info(&intf->dev, "Fibocom module not qmi_wwan mode!\n"); - return -ENODEV; - } - - return usbnet_probe(intf, id); -} - -#if defined(FIBOCOM_WWAN_QMAP) -static int qmap_qmi_wwan_probe(struct usb_interface *intf, - const struct usb_device_id *prod) -{ - int status = qmi_wwan_probe(intf, prod); - - if (!status) { - struct usbnet *dev = usb_get_intfdata(intf); - struct qmi_wwan_state *info = (void *)&dev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - unsigned i; - - if (!pQmapDev) - return status; - - tasklet_init(&pQmapDev->txq, rmnet_usb_tx_wake_queue, (unsigned long)pQmapDev); - - if (pQmapDev->qmap_mode == 1) { - pQmapDev->mpQmapNetDev[0] = dev->net; - if (pQmapDev->use_rmnet_usb) { - pQmapDev->mpQmapNetDev[0] = NULL; - qmap_register_device(pQmapDev, 0); - } - } - else if (pQmapDev->qmap_mode > 1) { - for (i = 0; i < pQmapDev->qmap_mode; i++) { - qmap_register_device(pQmapDev, i); - } - } - - if (pQmapDev->use_rmnet_usb) { - rtnl_lock(); - /* when open hyfi function, run cm will make system crash */ - //netdev_rx_handler_register(dev->net, rmnet_usb_rx_handler, dev); - netdev_rx_handler_register(dev->net, rmnet_usb_rx_handler, NULL); - rtnl_unlock(); - } - - if (pQmapDev->link_state == 0) { - netif_carrier_off(dev->net); - } - } - - return status; -} - -static void qmap_qmi_wwan_disconnect(struct usb_interface *intf) -{ - struct usbnet *dev = usb_get_intfdata(intf); - struct qmi_wwan_state *info; - sQmiWwanQmap *pQmapDev; - uint i; - - if (!dev) - return; - - info = (void *)&dev->data; - pQmapDev = (sQmiWwanQmap *)info->unused; - - if (!pQmapDev) { - return usbnet_disconnect(intf); - } - - pQmapDev->link_state = 0; - - if (pQmapDev->qmap_mode > 1) { - for (i = 0; i < pQmapDev->qmap_mode; i++) { - qmap_unregister_device(pQmapDev, i); - } - } - - if (pQmapDev->use_rmnet_usb) { - qmap_unregister_device(pQmapDev, 0); - rtnl_lock(); - netdev_rx_handler_unregister(dev->net); - rtnl_unlock(); - } - - tasklet_kill(&pQmapDev->txq); - - usbnet_disconnect(intf); - info->unused = 0; - kfree(pQmapDev); -} -#endif - -static struct usb_driver qmi_wwan_driver = { - .name = "qmi_wwan_f", - .id_table = products, - .probe = qmi_wwan_probe, -#if defined(FIBOCOM_WWAN_QMAP) - .probe = qmap_qmi_wwan_probe, - .disconnect = qmap_qmi_wwan_disconnect, -#else - .probe = qmi_wwan_probe, - .disconnect = usbnet_disconnect, -#endif - .suspend = qmi_wwan_suspend, - .resume = qmi_wwan_resume, - .reset_resume = qmi_wwan_reset_resume, - .supports_autosuspend = 1, - .disable_hub_initiated_lpm = 1, -}; - -#ifdef CONFIG_QCA_NSS_DRV -/* - EXTRA_CFLAGS="-I$(STAGING_DIR)/usr/include/qca-nss-drv $(EXTRA_CFLAGS)" - qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c -*/ -#include "rmnet_nss.c" -#endif - -static int __init qmi_wwan_driver_init(void) -{ - RCU_INIT_POINTER(rmnet_nss_callbacks, NULL); -#ifdef CONFIG_QCA_NSS_DRV - if (qca_nss_enabled) - rmnet_nss_init(); -#endif - return usb_register(&qmi_wwan_driver); -} -module_init(qmi_wwan_driver_init); -static void __exit qmi_wwan_driver_exit(void) -{ -#ifdef CONFIG_QCA_NSS_DRV - if (qca_nss_enabled) - rmnet_nss_exit(); -#endif - usb_deregister(&qmi_wwan_driver); -} -module_exit(qmi_wwan_driver_exit); - -MODULE_AUTHOR("Bjørn Mork "); -MODULE_DESCRIPTION("Qualcomm MSM Interface (QMI) WWAN driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(FIBOCOM_WWAN_VERSION); diff --git a/quectel_MHI/Makefile b/quectel_MHI/Makefile deleted file mode 100644 index cd33b9c00..000000000 --- a/quectel_MHI/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -# -# Copyright (C) 2015 OpenWrt.org -# -# This is free software, licensed under the GNU General Public License v2. -# See /LICENSE for more information. -# - -include $(TOPDIR)/rules.mk - -PKG_NAME:=pcie_mhi -PKG_VERSION:=1.3.8 -PKG_RELEASE:=4 - -include $(INCLUDE_DIR)/kernel.mk -include $(INCLUDE_DIR)/package.mk - -define KernelPackage/pcie_mhi - SUBMENU:=WWAN Support - TITLE:=Kernel pcie driver for MHI device - DEPENDS:=+pciids +pciutils +quectel-cm - FILES:=$(PKG_BUILD_DIR)/pcie_mhi.ko - AUTOLOAD:=$(call AutoLoad,90,pcie_mhi) -endef - -define KernelPackage/pcie_mhi/description - Kernel module for register a custom pciemhi platform device. -endef - -MAKE_OPTS:= \ - ARCH="$(LINUX_KARCH)" \ - CROSS_COMPILE="$(TARGET_CROSS)" \ - CXXFLAGS="$(TARGET_CXXFLAGS)" \ - M="$(PKG_BUILD_DIR)" \ - $(EXTRA_KCONFIG) - -define Build/Prepare - mkdir -p $(PKG_BUILD_DIR) - $(CP) ./src/* $(PKG_BUILD_DIR)/ -endef - -define Build/Compile - $(MAKE) -C "$(LINUX_DIR)" \ - $(MAKE_OPTS) \ - modules -endef - -$(eval $(call KernelPackage,pcie_mhi)) diff --git a/quectel_MHI/src/Makefile b/quectel_MHI/src/Makefile deleted file mode 100644 index 1b2c1f585..000000000 --- a/quectel_MHI/src/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -#ccflags-y += -g -obj-m += pcie_mhi.o -pcie_mhi-objs := core/mhi_init.o core/mhi_main.o core/mhi_pm.o core/mhi_boot.o core/mhi_dtr.o controllers/mhi_qti.o -pcie_mhi-objs += devices/mhi_uci.o - -ifeq (1,1) -pcie_mhi-objs += devices/mhi_netdev_quectel.o -else -pcie_mhi-objs += devices/mhi_netdev.o -pcie_mhi-objs += devices/rmnet_handler.o -endif - -PWD := $(shell pwd) -ifeq ($(ARCH),) -ARCH := $(shell uname -m) -endif -ifeq ($(CROSS_COMPILE),) -CROSS_COMPILE := -endif -ifeq ($(KDIR),) -KDIR := /lib/modules/$(shell uname -r)/build -endif - -pcie_mhi: clean - $(MAKE) ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} -C $(KDIR) M=$(PWD) modules - #cp pcie_mhi.ko /tftpboot/ - -clean: - $(MAKE) ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} -C $(KDIR) M=$(PWD) clean - find . -name *.o.ur-safe | xargs rm -f - -install: pcie_mhi - sudo cp pcie_mhi.ko /lib/modules/${shell uname -r}/kernel/drivers/pci/ - sudo depmod diff --git a/quectel_MHI/src/README b/quectel_MHI/src/README deleted file mode 100644 index 927df0cfa..000000000 --- a/quectel_MHI/src/README +++ /dev/null @@ -1,36 +0,0 @@ -1. porting pcie_mhi driver as next - -$ git diff drivers/Makefile -diff --git a/drivers/Makefile b/drivers/Makefile -index 77fbc52..e45837e 100644 ---- a/drivers/Makefile -+++ b/drivers/Makefile -@@ -184,3 +184,4 @@ obj-$(CONFIG_FPGA) += fpga/ - obj-$(CONFIG_FSI) += fsi/ - obj-$(CONFIG_TEE) += tee/ - obj-$(CONFIG_MULTIPLEXER) += mux/ -+obj-y += pcie_mhi/ - -$ tree drivers/pcie_mhi/ -L 1 -drivers/pcie_mhi/ - controllers - core - devices - Makefile - -2. check RG500 attach pcie_mhi driver successful - -root@OpenWrt:/# lspci -00:00.0 Class 0604: 17cb:0302 -01:00.0 Class ff00: 17cb:0306 - -root@OpenWrt:~# dmesg | grep mhi -[ 138.483252] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.6 -[ 138.492350] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306 - -3. how to use, see next logs - -log/QXDM_OVER_PCIE.txt -log/AT_OVER_PCIE.txt -log/MBIM_OVER_PCIE.txt -log/QMI_OVER_PCIE.txt diff --git a/quectel_MHI/src/ReleaseNote.txt b/quectel_MHI/src/ReleaseNote.txt deleted file mode 100644 index 8a55788d1..000000000 --- a/quectel_MHI/src/ReleaseNote.txt +++ /dev/null @@ -1,132 +0,0 @@ -Release Notes -[V1.3.8] -Date: 27/06/2024 -enhancement: - 1. support SDX7x's PID/VID - -Release Notes -[V1.3.7] -Date: 27/03/2024 -enhancement: - 1. support SDX35's PID/VID - 2. support IPQ QSDK MHI used with rmnetdata driver - -Release Notes -[V1.3.6] -Date: 01/08/2023 -enhancement: - 1. support Linux Kernel V6.4 - 2. support change mtu -fix: - 1. fix compile error on ipq's spf12.x - -Release Notes -[V1.3.5] -Date: 25/02/2023 -enhancement: - 1. support efuse SDX sleep - 2. support IPQ9574 SFE -fix: - 1. fix cannot find the node when dialing. Nodes in the /sys/bus/mhi_q/devices directory named hex - -[V1.3.4] -Date: 12/8/2022 -enhancement: - 1. only allow to enable autosuspend when module is in MHI_EE_AMSS - 2. show pcie link speed and width when driver probe - 3. check pcie link status by read pcie vid and pid when driver probe, - if pcie link is down, return -EIO - 4. support RM520 (1eac:1004) - 5. support qmap command packet -fix: - 1. fix tx queue is wrong stop when do uplink TPUT - 2. fix after QFirehose, module fail to bootup at very small probability - 3. mhi uci add mutex lock for concurrent reads/writes - -[V1.3.3] -Date: 30/6/2022 -enhancement: - 1. remove one un-necessary kmalloc when do qfirehose - 2. support mhi monitor (like usbmon), usage: cat /sys/kernel/debug/mhi_q/0306_00\:01.00/mhimon - 3. set ring size of event 0 to 256 (from 1024), required by x6x - 4. support PCIE local network card mhi_swip0 (chan 46/47), default disabled - 5. porting IPQ5018 mhi rate controll code from spf11.5 - 6. set pcie rmnet download max qmap packet size to 15KB (same to IPQ MHI Driver) - 7. support set different mac address for rmnet net card - 8. when mhi netdev fail to malloc, use delay_work instead work - 9. optimize code for 'when driver load, modem is still in MHI_EE_PTHRU' -fix: - 1. Fix not synchronize access rp/wp when mhi_queue_xxx and mhi_process_xxx_ring run on different CPU - 2. set dma mask when driver probe, some SOC like rpi_4 need it - -[V1.3.2] -Date: 12/16/2021 -enhancement: - 1. support Linux Kernel V5.14 - 2. mhi_netdev_quectel.c do not print log in softirq context - -[V1.3.1] -Date: 9/26/2021 -enhancement: -fix: - -[V1.3.0.19] -Date: 9/18/2021 -enhancement: - 1. support sdx62 (17cb:0308) - 2. support IPQ5018's NSS - 3. use 'qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c' instead myself rmnet_nss.c - and pcie_mhi.ko must load after then rmnet_nss.ko - 4. allow bhi irq is not 0 (for ipq5018) -fix: - -[V1.3.0.18] -Date: 4/14/2021 -enhancement: - 1. support mbim multiple call, usage: - # insmod pcie_mhi.ko mhi_mbim_enabeld=1 qmap_mode=4 - # quectel-mbim-proxy -d /dev/mhi_MBIM & - # quectel-CM -n X -fix: - -[V1.3.0.17] -Date: 3/11/2021 -enhancement: -fix: - 1. fix CPU loading very high when TPUT test when only one MSI interrupt - 2. fix error on latest X24 modem - -[V1.3.0.16] -Date: 11/18/2020 -enhancement: -fix: - 1. add ring size to 32, for in-bound chan, if one ring is full, modem will not generate MSI interrupt for all chan - -[V1.3.0.15] -Date: 10/30/2020 -enhancement: - 1. support multi-modems, named as /dev/mhi_X -fix: - 1. fix compile error on kernel v5.8 - -[V1.3.0.14] -Date: 10/9/2020 -enhancement: - 1. suppport EM120&EM160 -fix: - 1. fix compile error on kernel v5.6 - 2. support runtime suspend - -[V1.3.0.13] -Date: 9/7/2020 -enhancement: - 1. suppport EM120&EM160 -fix: - 1. fix error on X55 + PCIE2.0(e.g IPQ4019) - 2. support runtime suspend - -[V1.3.0.12] -Date: 7/7/2020 -enhancement: - 1. suppport create only none netcard (enabled by marco MHI_NETDEV_ONE_CARD_MODE), -fix: \ No newline at end of file diff --git a/quectel_MHI/src/controllers/Kconfig b/quectel_MHI/src/controllers/Kconfig deleted file mode 100644 index e18b38b25..000000000 --- a/quectel_MHI/src/controllers/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -menu "MHI controllers" - -config MHI_QTI - tristate "MHI QTI" - depends on MHI_BUS - help - If you say yes to this option, MHI bus support for QTI modem chipsets - will be enabled. QTI PCIe based modems uses MHI as the communication - protocol. MHI control driver is the bus master for such modems. As the - bus master driver, it oversees power management operations such as - suspend, resume, powering on and off the device. - -endmenu diff --git a/quectel_MHI/src/controllers/Makefile b/quectel_MHI/src/controllers/Makefile deleted file mode 100644 index ab9ec55fb..000000000 --- a/quectel_MHI/src/controllers/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_MHI_QTI) += mhi_qti.o mhi_arch_qti.o diff --git a/quectel_MHI/src/controllers/mhi_arch_qti.c b/quectel_MHI/src/controllers/mhi_arch_qti.c deleted file mode 100644 index de19d94c0..000000000 --- a/quectel_MHI/src/controllers/mhi_arch_qti.c +++ /dev/null @@ -1,275 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../core/mhi.h" -#include "mhi_qti.h" - -struct arch_info { - struct mhi_dev *mhi_dev; - struct msm_bus_scale_pdata *msm_bus_pdata; - u32 bus_client; - struct pci_saved_state *pcie_state; - struct pci_saved_state *ref_pcie_state; - struct dma_iommu_mapping *mapping; -}; - -struct mhi_bl_info { - struct mhi_device *mhi_device; - async_cookie_t cookie; - void *ipc_log; -}; - -/* ipc log markings */ -#define DLOG "Dev->Host: " -#define HLOG "Host: " - -#ifdef CONFIG_MHI_DEBUG - -#define MHI_IPC_LOG_PAGES (100) -enum MHI_DEBUG_LEVEL mhi_ipc_log_lvl = MHI_MSG_LVL_VERBOSE; - -#else - -#define MHI_IPC_LOG_PAGES (10) -enum MHI_DEBUG_LEVEL mhi_ipc_log_lvl = MHI_MSG_LVL_ERROR; - -#endif - -static int mhi_arch_set_bus_request(struct mhi_controller *mhi_cntrl, int index) -{ - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - struct arch_info *arch_info = mhi_dev->arch_info; - - MHI_LOG("Setting bus request to index %d\n", index); - - if (arch_info->bus_client) - return msm_bus_scale_client_update_request( - arch_info->bus_client, - index); - - /* default return success */ - return 0; -} - -static void mhi_bl_dl_cb(struct mhi_device *mhi_dev, - struct mhi_result *mhi_result) -{ - struct mhi_bl_info *mhi_bl_info = mhi_device_get_devdata(mhi_dev); - char *buf = mhi_result->buf_addr; - - /* force a null at last character */ - buf[mhi_result->bytes_xferd - 1] = 0; - - ipc_log_string(mhi_bl_info->ipc_log, "%s %s", DLOG, buf); -} - -static void mhi_bl_dummy_cb(struct mhi_device *mhi_dev, - struct mhi_result *mhi_result) -{ -} - -static void mhi_bl_remove(struct mhi_device *mhi_dev) -{ - struct mhi_bl_info *mhi_bl_info = mhi_device_get_devdata(mhi_dev); - - ipc_log_string(mhi_bl_info->ipc_log, HLOG "Received Remove notif.\n"); - - /* wait for boot monitor to exit */ - async_synchronize_cookie(mhi_bl_info->cookie + 1); -} - -static void mhi_bl_boot_monitor(void *data, async_cookie_t cookie) -{ - struct mhi_bl_info *mhi_bl_info = data; - struct mhi_device *mhi_device = mhi_bl_info->mhi_device; - struct mhi_controller *mhi_cntrl = mhi_device->mhi_cntrl; - /* 15 sec timeout for booting device */ - const u32 timeout = msecs_to_jiffies(15000); - - /* wait for device to enter boot stage */ - wait_event_timeout(mhi_cntrl->state_event, mhi_cntrl->ee == MHI_EE_AMSS - || mhi_cntrl->ee == MHI_EE_DISABLE_TRANSITION, - timeout); - - if (mhi_cntrl->ee == MHI_EE_AMSS) { - ipc_log_string(mhi_bl_info->ipc_log, HLOG - "Device successfully booted to mission mode\n"); - - mhi_unprepare_from_transfer(mhi_device); - } else { - ipc_log_string(mhi_bl_info->ipc_log, HLOG - "Device failed to boot to mission mode, ee = %s\n", - TO_MHI_EXEC_STR(mhi_cntrl->ee)); - } -} - -static int mhi_bl_probe(struct mhi_device *mhi_dev, - const struct mhi_device_id *id) -{ - char node_name[32]; - struct mhi_bl_info *mhi_bl_info; - - mhi_bl_info = devm_kzalloc(&mhi_dev->dev, sizeof(*mhi_bl_info), - GFP_KERNEL); - if (!mhi_bl_info) - return -ENOMEM; - - snprintf(node_name, sizeof(node_name), "mhi_bl_%04x_%02u.%02u.%02u", - mhi_dev->dev_id, mhi_dev->domain, mhi_dev->bus, mhi_dev->slot); - - mhi_bl_info->ipc_log = ipc_log_context_create(MHI_IPC_LOG_PAGES, - node_name, 0); - if (!mhi_bl_info->ipc_log) - return -EINVAL; - - mhi_bl_info->mhi_device = mhi_dev; - mhi_device_set_devdata(mhi_dev, mhi_bl_info); - - ipc_log_string(mhi_bl_info->ipc_log, HLOG - "Entered SBL, Session ID:0x%x\n", - mhi_dev->mhi_cntrl->session_id); - - /* start a thread to monitor entering mission mode */ - mhi_bl_info->cookie = async_schedule(mhi_bl_boot_monitor, mhi_bl_info); - - return 0; -} - -static const struct mhi_device_id mhi_bl_match_table[] = { - { .chan = "BL" }, - {}, -}; - -static struct mhi_driver mhi_bl_driver = { - .id_table = mhi_bl_match_table, - .remove = mhi_bl_remove, - .probe = mhi_bl_probe, - .ul_xfer_cb = mhi_bl_dummy_cb, - .dl_xfer_cb = mhi_bl_dl_cb, - .driver = { - .name = "MHI_BL", - .owner = THIS_MODULE, - }, -}; - -int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl) -{ - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - struct arch_info *arch_info = mhi_dev->arch_info; - char node[32]; - - if (!arch_info) { - arch_info = devm_kzalloc(&mhi_dev->pci_dev->dev, - sizeof(*arch_info), GFP_KERNEL); - if (!arch_info) - return -ENOMEM; - - mhi_dev->arch_info = arch_info; - - snprintf(node, sizeof(node), "mhi_%04x_%02u.%02u.%02u", - mhi_cntrl->dev_id, mhi_cntrl->domain, mhi_cntrl->bus, - mhi_cntrl->slot); - mhi_cntrl->log_buf = ipc_log_context_create(MHI_IPC_LOG_PAGES, - node, 0); - mhi_cntrl->log_lvl = mhi_ipc_log_lvl; - - /* save reference state for pcie config space */ - arch_info->ref_pcie_state = pci_store_saved_state( - mhi_dev->pci_dev); - - mhi_driver_register(&mhi_bl_driver); - } - - return mhi_arch_set_bus_request(mhi_cntrl, 1); -} - -void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl) -{ - mhi_arch_set_bus_request(mhi_cntrl, 0); -} - -int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, bool graceful) -{ - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - struct arch_info *arch_info = mhi_dev->arch_info; - struct pci_dev *pci_dev = mhi_dev->pci_dev; - int ret; - - MHI_LOG("Entered\n"); - - if (graceful) { - pci_clear_master(pci_dev); - ret = pci_save_state(mhi_dev->pci_dev); - if (ret) { - MHI_ERR("Failed with pci_save_state, ret:%d\n", ret); - return ret; - } - - arch_info->pcie_state = pci_store_saved_state(pci_dev); - pci_disable_device(pci_dev); - } - - /* - * We will always attempt to put link into D3hot, however - * link down may have happened due to error fatal, so - * ignoring the return code - */ - pci_set_power_state(pci_dev, PCI_D3hot); - - /* release the resources */ - mhi_arch_set_bus_request(mhi_cntrl, 0); - - MHI_LOG("Exited\n"); - - return 0; -} - -int mhi_arch_link_on(struct mhi_controller *mhi_cntrl) -{ - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - struct arch_info *arch_info = mhi_dev->arch_info; - struct pci_dev *pci_dev = mhi_dev->pci_dev; - int ret; - - MHI_LOG("Entered\n"); - - /* request resources and establish link trainning */ - ret = mhi_arch_set_bus_request(mhi_cntrl, 1); - if (ret) - MHI_LOG("Could not set bus frequency, ret:%d\n", ret); - - ret = pci_set_power_state(pci_dev, PCI_D0); - if (ret) { - MHI_ERR("Failed to set PCI_D0 state, ret:%d\n", ret); - return ret; - } - - ret = pci_enable_device(pci_dev); - if (ret) { - MHI_ERR("Failed to enable device, ret:%d\n", ret); - return ret; - } - - ret = pci_load_and_free_saved_state(pci_dev, &arch_info->pcie_state); - if (ret) - MHI_LOG("Failed to load saved cfg state\n"); - - pci_restore_state(pci_dev); - pci_set_master(pci_dev); - - MHI_LOG("Exited\n"); - - return 0; -} diff --git a/quectel_MHI/src/controllers/mhi_qcom.c b/quectel_MHI/src/controllers/mhi_qcom.c deleted file mode 100644 index 70dd9172b..000000000 --- a/quectel_MHI/src/controllers/mhi_qcom.c +++ /dev/null @@ -1,715 +0,0 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../core/mhi.h" -#include "mhi_qcom.h" - -#if 1 -#ifndef PCI_IRQ_MSI -#define PCI_IRQ_MSI (1 << 1) /* Allow MSI interrupts */ - -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,53 )) -int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec) -{ - int nvec = maxvec; - int rc; - - if (maxvec < minvec) - return -ERANGE; - - do { - rc = pci_enable_msi_block(dev, nvec); - if (rc < 0) { - return rc; - } else if (rc > 0) { - if (rc < minvec) - return -ENOSPC; - nvec = rc; - } - } while (rc); - - return nvec; -} -#endif - -static int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs, - unsigned int max_vecs, unsigned int flags) -{ - return pci_enable_msi_range(dev, min_vecs, max_vecs); -} - -static void pci_free_irq_vectors(struct pci_dev *dev) -{ - pci_disable_msi(dev); -} - -static int pci_irq_vector(struct pci_dev *dev, unsigned int nr) -{ - return dev->irq + nr; -} -#endif -#endif - -static struct pci_device_id mhi_pcie_device_id[] = { - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0303)}, //SDX20 - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0304)}, //SDX24 - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0305)}, - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0306)}, //SDX55 - {PCI_DEVICE(0x2C7C, 0x0512)}, - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, MHI_PCIE_DEBUG_ID)}, - {0}, -}; - -MODULE_DEVICE_TABLE(pci, mhi_pcie_device_id); - -static struct pci_driver mhi_pcie_driver; - -void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl) -{ - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - struct pci_dev *pci_dev = mhi_dev->pci_dev; - - pci_free_irq_vectors(pci_dev); - iounmap(mhi_cntrl->regs); - mhi_cntrl->regs = NULL; - pci_clear_master(pci_dev); - pci_release_region(pci_dev, mhi_dev->resn); - pci_disable_device(pci_dev); -} - -static int mhi_init_pci_dev(struct mhi_controller *mhi_cntrl) -{ - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - struct pci_dev *pci_dev = mhi_dev->pci_dev; - int ret; - resource_size_t start, len; - int i; - - mhi_dev->resn = MHI_PCI_BAR_NUM; - ret = pci_assign_resource(pci_dev, mhi_dev->resn); - if (ret) { - MHI_ERR("Error assign pci resources, ret:%d\n", ret); - return ret; - } - - ret = pci_enable_device(pci_dev); - if (ret) { - MHI_ERR("Error enabling device, ret:%d\n", ret); - goto error_enable_device; - } - - ret = pci_request_region(pci_dev, mhi_dev->resn, "mhi"); - if (ret) { - MHI_ERR("Error pci_request_region, ret:%d\n", ret); - goto error_request_region; - } - - pci_set_master(pci_dev); - - start = pci_resource_start(pci_dev, mhi_dev->resn); - len = pci_resource_len(pci_dev, mhi_dev->resn); - mhi_cntrl->regs = ioremap_nocache(start, len); - MHI_LOG("mhi_cntrl->regs = %p\n", mhi_cntrl->regs); - if (!mhi_cntrl->regs) { - MHI_ERR("Error ioremap region\n"); - goto error_ioremap; - } - - ret = pci_alloc_irq_vectors(pci_dev, 1, mhi_cntrl->msi_required, PCI_IRQ_MSI); - if (IS_ERR_VALUE((ulong)ret) || ret < mhi_cntrl->msi_required) { - if (ret == -ENOSPC) { - /* imx_3.14.52_1.1.0_ga - diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c - index f06e8f0..6a9614f 100644 - --- a/drivers/pci/host/pcie-designware.c - +++ b/drivers/pci/host/pcie-designware.c - @@ -376,6 +376,13 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, - if (msgvec > 5) - msgvec = 0; - - +#if 1 //Add by Quectel 20190419 - + if (msgvec > 0 && pdev->vendor == 0x17cb) { - + dev_info(&pdev->dev, "%s quectel fixup pos=%d, msg_ctr=%04x, msgvec=%d\n", __func__, desc->msi_attrib.pos, msg_ctr, msgvec); - + msgvec = 0; - + } - +#endif - + - irq = assign_irq((1 << msgvec), desc, &pos); - if (irq < 0) - return irq; - */ - } - //imx_4.1.15_2.0.0_ga & DELL_OPTIPLEX_7010 only alloc one msi interrupt for one pcie device - if (ret != 1) { - MHI_ERR("Failed to enable MSI, ret=%d, msi_required=%d\n", ret, mhi_cntrl->msi_required); - goto error_req_msi; - } - } - - mhi_cntrl->msi_allocated = ret; - MHI_LOG("msi_required = %d, msi_allocated = %d, msi_irq = %u\n", mhi_cntrl->msi_required, mhi_cntrl->msi_allocated, pci_dev->irq); - - for (i = 0; i < mhi_cntrl->msi_allocated; i++) { - mhi_cntrl->irq[i] = pci_irq_vector(pci_dev, i); - if (mhi_cntrl->irq[i] < 0) { - ret = mhi_cntrl->irq[i]; - goto error_get_irq_vec; - } - } - -#if 0 - /* configure runtime pm */ - pm_runtime_set_autosuspend_delay(&pci_dev->dev, MHI_RPM_SUSPEND_TMR_MS); - pm_runtime_dont_use_autosuspend(&pci_dev->dev); - pm_suspend_ignore_children(&pci_dev->dev, true); - - /* - * pci framework will increment usage count (twice) before - * calling local device driver probe function. - * 1st pci.c pci_pm_init() calls pm_runtime_forbid - * 2nd pci-driver.c local_pci_probe calls pm_runtime_get_sync - * Framework expect pci device driver to call - * pm_runtime_put_noidle to decrement usage count after - * successful probe and and call pm_runtime_allow to enable - * runtime suspend. - */ - pm_runtime_mark_last_busy(&pci_dev->dev); - pm_runtime_put_noidle(&pci_dev->dev); -#endif - - return 0; - -error_get_irq_vec: - pci_free_irq_vectors(pci_dev); - -error_req_msi: - iounmap(mhi_cntrl->regs); - -error_ioremap: - pci_clear_master(pci_dev); - -error_request_region: - pci_disable_device(pci_dev); - -error_enable_device: - pci_release_region(pci_dev, mhi_dev->resn); - - return ret; -} - -#ifdef CONFIG_PM -static int mhi_runtime_idle(struct device *dev) -{ - struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); - - MHI_LOG("Entered returning -EBUSY\n"); - - /* - * RPM framework during runtime resume always calls - * rpm_idle to see if device ready to suspend. - * If dev.power usage_count count is 0, rpm fw will call - * rpm_idle cb to see if device is ready to suspend. - * if cb return 0, or cb not defined the framework will - * assume device driver is ready to suspend; - * therefore, fw will schedule runtime suspend. - * In MHI power management, MHI host shall go to - * runtime suspend only after entering MHI State M2, even if - * usage count is 0. Return -EBUSY to disable automatic suspend. - */ - return -EBUSY; -} - -static int mhi_runtime_suspend(struct device *dev) -{ - int ret = 0; - struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); - - MHI_LOG("Enter\n"); - - mutex_lock(&mhi_cntrl->pm_mutex); - - ret = mhi_pm_suspend(mhi_cntrl); - if (ret) { - MHI_LOG("Abort due to ret:%d\n", ret); - goto exit_runtime_suspend; - } - - ret = mhi_arch_link_off(mhi_cntrl, true); - if (ret) - MHI_ERR("Failed to Turn off link ret:%d\n", ret); - -exit_runtime_suspend: - mutex_unlock(&mhi_cntrl->pm_mutex); - MHI_LOG("Exited with ret:%d\n", ret); - - return ret; -} - -static int mhi_runtime_resume(struct device *dev) -{ - int ret = 0; - struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - MHI_LOG("Enter\n"); - - mutex_lock(&mhi_cntrl->pm_mutex); - - if (!mhi_dev->powered_on) { - MHI_LOG("Not fully powered, return success\n"); - mutex_unlock(&mhi_cntrl->pm_mutex); - return 0; - } - - /* turn on link */ - ret = mhi_arch_link_on(mhi_cntrl); - if (ret) - goto rpm_resume_exit; - - /* enter M0 state */ - ret = mhi_pm_resume(mhi_cntrl); - -rpm_resume_exit: - mutex_unlock(&mhi_cntrl->pm_mutex); - MHI_LOG("Exited with :%d\n", ret); - - return ret; -} - -static int mhi_system_resume(struct device *dev) -{ - int ret = 0; - struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); - - ret = mhi_runtime_resume(dev); - if (ret) { - MHI_ERR("Failed to resume link\n"); - } else { - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - } - - return ret; -} - -int mhi_system_suspend(struct device *dev) -{ - struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); - - MHI_LOG("Entered\n"); - - /* if rpm status still active then force suspend */ - if (!pm_runtime_status_suspended(dev)) - return mhi_runtime_suspend(dev); - - pm_runtime_set_suspended(dev); - pm_runtime_disable(dev); - - MHI_LOG("Exit\n"); - return 0; -} -#endif - -/* checks if link is down */ -static int mhi_link_status(struct mhi_controller *mhi_cntrl, void *priv) -{ - struct mhi_dev *mhi_dev = priv; - u16 dev_id; - int ret; - - /* try reading device id, if dev id don't match, link is down */ - ret = pci_read_config_word(mhi_dev->pci_dev, PCI_DEVICE_ID, &dev_id); - - return (ret || dev_id != mhi_cntrl->dev_id) ? -EIO : 0; -} - -static int mhi_runtime_get(struct mhi_controller *mhi_cntrl, void *priv) -{ - struct mhi_dev *mhi_dev = priv; - struct device *dev = &mhi_dev->pci_dev->dev; - - return pm_runtime_get(dev); -} - -static void mhi_runtime_put(struct mhi_controller *mhi_cntrl, void *priv) -{ - struct mhi_dev *mhi_dev = priv; - struct device *dev = &mhi_dev->pci_dev->dev; - - pm_runtime_put_noidle(dev); -} - -static void mhi_status_cb(struct mhi_controller *mhi_cntrl, - void *priv, - enum MHI_CB reason) -{ - struct mhi_dev *mhi_dev = priv; - struct device *dev = &mhi_dev->pci_dev->dev; - - if (reason == MHI_CB_IDLE) { - MHI_LOG("Schedule runtime suspend 1\n"); - pm_runtime_mark_last_busy(dev); - pm_request_autosuspend(dev); - } -} - -int mhi_debugfs_trigger_m0(void *data, u64 val) -{ - struct mhi_controller *mhi_cntrl = data; - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - MHI_LOG("Trigger M3 Exit\n"); - pm_runtime_get(&mhi_dev->pci_dev->dev); - pm_runtime_put(&mhi_dev->pci_dev->dev); - - return 0; -} - -int mhi_debugfs_trigger_m3(void *data, u64 val) -{ - struct mhi_controller *mhi_cntrl = data; - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - MHI_LOG("Trigger M3 Entry\n"); - pm_runtime_mark_last_busy(&mhi_dev->pci_dev->dev); - pm_request_autosuspend(&mhi_dev->pci_dev->dev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m0_fops, NULL, - mhi_debugfs_trigger_m0, "%llu\n"); - -DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m3_fops, NULL, - mhi_debugfs_trigger_m3, "%llu\n"); - -static int mhi_init_debugfs_trigger_go(void *data, u64 val) -{ - struct mhi_controller *mhi_cntrl = data; - - MHI_LOG("Trigger power up sequence\n"); - - mhi_async_power_up(mhi_cntrl); - - return 0; -} -DEFINE_SIMPLE_ATTRIBUTE(mhi_init_debugfs_trigger_go_fops, NULL, - mhi_init_debugfs_trigger_go, "%llu\n"); - - -int mhi_init_debugfs_debug_show(struct seq_file *m, void *d) -{ - seq_puts(m, "Enable debug mode to debug external soc\n"); - seq_puts(m, - "Usage: echo 'devid,timeout,domain,smmu_cfg' > debug_mode\n"); - seq_puts(m, "No spaces between parameters\n"); - seq_puts(m, "\t1. devid : 0 or pci device id to register\n"); - seq_puts(m, "\t2. timeout: mhi cmd/state transition timeout\n"); - seq_puts(m, "\t3. domain: Rootcomplex\n"); - seq_puts(m, "\t4. smmu_cfg: smmu configuration mask:\n"); - seq_puts(m, "\t\t- BIT0: ATTACH\n"); - seq_puts(m, "\t\t- BIT1: S1 BYPASS\n"); - seq_puts(m, "\t\t-BIT2: FAST_MAP\n"); - seq_puts(m, "\t\t-BIT3: ATOMIC\n"); - seq_puts(m, "\t\t-BIT4: FORCE_COHERENT\n"); - seq_puts(m, "\t\t-BIT5: GEOMETRY\n"); - seq_puts(m, "\tAll timeout are in ms, enter 0 to keep default\n"); - seq_puts(m, "Examples inputs: '0x307,10000'\n"); - seq_puts(m, "\techo '0,10000,1'\n"); - seq_puts(m, "\techo '0x307,10000,0,0x3d'\n"); - seq_puts(m, "firmware image name will be changed to debug.mbn\n"); - - return 0; -} - -static int mhi_init_debugfs_debug_open(struct inode *node, struct file *file) -{ - return single_open(file, mhi_init_debugfs_debug_show, NULL); -} - -static ssize_t mhi_init_debugfs_debug_write(struct file *fp, - const char __user *ubuf, - size_t count, - loff_t *pos) -{ - char *buf = kmalloc(count + 1, GFP_KERNEL); - /* #,devid,timeout,domain,smmu-cfg */ - int args[5] = {0}; - static char const *dbg_fw = "debug.mbn"; - int ret; - struct mhi_controller *mhi_cntrl = fp->f_inode->i_private; - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - struct pci_device_id *id; - - if (!buf) - return -ENOMEM; - - ret = copy_from_user(buf, ubuf, count); - if (ret) - goto error_read; - buf[count] = 0; - get_options(buf, ARRAY_SIZE(args), args); - kfree(buf); - - /* override default parameters */ - mhi_cntrl->fw_image = dbg_fw; - mhi_cntrl->edl_image = dbg_fw; - - if (args[0] >= 2 && args[2]) - mhi_cntrl->timeout_ms = args[2]; - - if (args[0] >= 3 && args[3]) - mhi_cntrl->domain = args[3]; - - if (args[0] >= 4 && args[4]) - mhi_dev->smmu_cfg = args[4]; - - /* If it's a new device id register it */ - if (args[0] && args[1]) { - /* find the debug_id and overwrite it */ - for (id = mhi_pcie_device_id; id->vendor; id++) - if (id->device == MHI_PCIE_DEBUG_ID) { - id->device = args[1]; - pci_unregister_driver(&mhi_pcie_driver); - ret = pci_register_driver(&mhi_pcie_driver); - } - } - - mhi_dev->debug_mode = true; - debugfs_create_file("go", 0444, mhi_cntrl->parent, mhi_cntrl, - &mhi_init_debugfs_trigger_go_fops); - pr_info( - "%s: ret:%d pcidev:0x%x smm_cfg:%u timeout:%u\n", - __func__, ret, args[1], mhi_dev->smmu_cfg, - mhi_cntrl->timeout_ms); - return count; - -error_read: - kfree(buf); - return ret; -} - -static const struct file_operations debugfs_debug_ops = { - .open = mhi_init_debugfs_debug_open, - .release = single_release, - .read = seq_read, - .write = mhi_init_debugfs_debug_write, -}; - -static struct mhi_controller * mhi_platform_probe(struct pci_dev *pci_dev) -{ - struct mhi_controller *mhi_cntrl; - struct mhi_dev *mhi_dev; - u64 addr_win[2]; - int ret; - - mhi_cntrl = mhi_alloc_controller(sizeof(*mhi_dev)); - if (!mhi_cntrl) { - pr_err("mhi_alloc_controller fail\n"); - return NULL; - } - - mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - mhi_cntrl->dev_id = pci_dev->device; - mhi_cntrl->domain = pci_domain_nr(pci_dev->bus); - mhi_cntrl->bus = pci_dev->bus->number; - mhi_cntrl->slot = PCI_SLOT(pci_dev->devfn); - mhi_dev->smmu_cfg = 0; - #if 0 //def CONFIG_HAVE_MEMBLOCK - addr_win[0] = memblock_start_of_DRAM(); - addr_win[1] = memblock_end_of_DRAM(); - #else -#define MHI_MEM_BASE_DEFAULT 0x000000000 -#define MHI_MEM_SIZE_DEFAULT 0x2000000000 - addr_win[0] = MHI_MEM_BASE_DEFAULT; - addr_win[1] = MHI_MEM_SIZE_DEFAULT; - if (sizeof(dma_addr_t) == 4) { - addr_win[1] = 0xFFFFFFFF; - } - #endif - - mhi_cntrl->iova_start = addr_win[0]; - mhi_cntrl->iova_stop = addr_win[1]; - - mhi_dev->pci_dev = pci_dev; - mhi_cntrl->pci_dev = pci_dev; - - /* setup power management apis */ - mhi_cntrl->status_cb = mhi_status_cb; - mhi_cntrl->runtime_get = mhi_runtime_get; - mhi_cntrl->runtime_put = mhi_runtime_put; - mhi_cntrl->link_status = mhi_link_status; - - ret = mhi_arch_platform_init(mhi_dev); - if (ret) - goto error_probe; - - ret = mhi_register_mhi_controller(mhi_cntrl); - if (ret) - goto error_register; - - if (mhi_cntrl->parent) - debugfs_create_file("debug_mode", 0444, mhi_cntrl->parent, - mhi_cntrl, &debugfs_debug_ops); - - return mhi_cntrl; - -error_register: - mhi_arch_platform_deinit(mhi_dev); - -error_probe: - mhi_free_controller(mhi_cntrl); - - return NULL; -} - -int mhi_pci_probe(struct pci_dev *pci_dev, - const struct pci_device_id *device_id) -{ - struct mhi_controller *mhi_cntrl = NULL; - u32 domain = pci_domain_nr(pci_dev->bus); - u32 bus = pci_dev->bus->number; - u32 slot = PCI_SLOT(pci_dev->devfn); - struct mhi_dev *mhi_dev; - int ret; - - pr_info("%s pci_dev->name = %s, domain=%d, bus=%d, slot=%d, vendor=%04X, device=%04X\n", - __func__, dev_name(&pci_dev->dev), domain, bus, slot, pci_dev->vendor, pci_dev->device); - - mhi_cntrl = mhi_platform_probe(pci_dev); - if (!mhi_cntrl) { - pr_err("mhi_platform_probe fail\n"); - return -EPROBE_DEFER; - } - - mhi_cntrl->dev_id = pci_dev->device; - mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - mhi_dev->pci_dev = pci_dev; - mhi_dev->powered_on = true; - - ret = mhi_arch_pcie_init(mhi_cntrl); - if (ret) { - MHI_ERR("Error mhi_arch_pcie_init, ret:%d\n", ret); - return ret; - } - - ret = mhi_arch_iommu_init(mhi_cntrl); - if (ret) { - MHI_ERR("Error mhi_arch_iommu_init, ret:%d\n", ret); - goto error_iommu_init; - } - - ret = mhi_init_pci_dev(mhi_cntrl); - if (ret) { - MHI_ERR("Error mhi_init_pci_dev, ret:%d\n", ret); - goto error_init_pci; - } - - /* start power up sequence if not in debug mode */ - if (!mhi_dev->debug_mode) { - ret = mhi_async_power_up(mhi_cntrl); - if (ret) { - MHI_ERR("Error mhi_async_power_up, ret:%d\n", ret); - goto error_power_up; - } - } - -#if 0 - pm_runtime_mark_last_busy(&pci_dev->dev); - pm_runtime_allow(&pci_dev->dev); - pm_runtime_disable(&pci_dev->dev); -#endif - - if (mhi_cntrl->dentry) { - debugfs_create_file("m0", 0444, mhi_cntrl->dentry, mhi_cntrl, - &debugfs_trigger_m0_fops); - debugfs_create_file("m3", 0444, mhi_cntrl->dentry, mhi_cntrl, - &debugfs_trigger_m3_fops); - } - - dev_set_drvdata(&pci_dev->dev, mhi_cntrl); - MHI_LOG("Return successful\n"); - - return 0; - -error_power_up: - mhi_deinit_pci_dev(mhi_cntrl); - -error_init_pci: - mhi_arch_iommu_deinit(mhi_cntrl); - -error_iommu_init: - mhi_arch_pcie_deinit(mhi_cntrl); - - return ret; -} - -static void mhi_pci_remove(struct pci_dev *pci_dev) -{ - struct mhi_controller *mhi_cntrl = (struct mhi_controller *)dev_get_drvdata(&pci_dev->dev); - - if (mhi_cntrl && mhi_cntrl->pci_dev == pci_dev) { - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - MHI_LOG("%s\n", dev_name(&pci_dev->dev)); - if (!mhi_dev->debug_mode) { - mhi_power_down(mhi_cntrl, 1); - } - mhi_deinit_pci_dev(mhi_cntrl); - mhi_arch_iommu_deinit(mhi_cntrl); - mhi_arch_pcie_deinit(mhi_cntrl); - mhi_unregister_mhi_controller(mhi_cntrl); - } -} - -static const struct dev_pm_ops pm_ops = { - SET_RUNTIME_PM_OPS(mhi_runtime_suspend, - mhi_runtime_resume, - mhi_runtime_idle) - SET_SYSTEM_SLEEP_PM_OPS(mhi_system_suspend, mhi_system_resume) -}; - -static struct pci_driver mhi_pcie_driver = { - .name = "mhi", - .id_table = mhi_pcie_device_id, - .probe = mhi_pci_probe, - .remove = mhi_pci_remove, - .driver = { - .pm = &pm_ops - } -}; - -int __init mhi_controller_qcom_init(void) -{ - return pci_register_driver(&mhi_pcie_driver); -}; - -void mhi_controller_qcom_exit(void) -{ - pr_info("%s enter\n", __func__); - pci_unregister_driver(&mhi_pcie_driver); - pr_info("%s exit\n", __func__); -} diff --git a/quectel_MHI/src/controllers/mhi_qcom.h b/quectel_MHI/src/controllers/mhi_qcom.h deleted file mode 100644 index 65ec5b8d2..000000000 --- a/quectel_MHI/src/controllers/mhi_qcom.h +++ /dev/null @@ -1,92 +0,0 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#ifndef _MHI_QCOM_ -#define _MHI_QCOM_ - -/* iova cfg bitmask */ -#define MHI_SMMU_ATTACH BIT(0) -#define MHI_SMMU_S1_BYPASS BIT(1) -#define MHI_SMMU_FAST BIT(2) -#define MHI_SMMU_ATOMIC BIT(3) -#define MHI_SMMU_FORCE_COHERENT BIT(4) - -#define MHI_PCIE_VENDOR_ID (0x17cb) -#define MHI_PCIE_DEBUG_ID (0xffff) -#define MHI_RPM_SUSPEND_TMR_MS (3000) -#define MHI_PCI_BAR_NUM (0) - -struct mhi_dev { - struct pci_dev *pci_dev; - u32 smmu_cfg; - int resn; - void *arch_info; - bool powered_on; - bool debug_mode; -}; - -void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl); -int mhi_pci_probe(struct pci_dev *pci_dev, - const struct pci_device_id *device_id); - -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,65 )) -static inline int dma_set_mask_and_coherent(struct device *dev, u64 mask) -{ - int rc = dma_set_mask(dev, mask); - if (rc == 0) - dma_set_coherent_mask(dev, mask); - return rc; -} -#endif - -static inline int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl) -{ - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - mhi_cntrl->dev = &mhi_dev->pci_dev->dev; - - return dma_set_mask_and_coherent(mhi_cntrl->dev, DMA_BIT_MASK(64)); -} - -static inline void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl) -{ -} - -static inline int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl) -{ - return 0; -} - -static inline void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl) -{ -} - -static inline int mhi_arch_platform_init(struct mhi_dev *mhi_dev) -{ - return 0; -} - -static inline void mhi_arch_platform_deinit(struct mhi_dev *mhi_dev) -{ -} - -static inline int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, - bool graceful) -{ - return 0; -} - -static inline int mhi_arch_link_on(struct mhi_controller *mhi_cntrl) -{ - return 0; -} - -#endif /* _MHI_QCOM_ */ diff --git a/quectel_MHI/src/controllers/mhi_qti.c b/quectel_MHI/src/controllers/mhi_qti.c deleted file mode 100644 index e31db9829..000000000 --- a/quectel_MHI/src/controllers/mhi_qti.c +++ /dev/null @@ -1,1309 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#define MAX_MHI 8 -#ifdef CONFIG_PCI_MSM -#define QCOM_AP_AND_EFUSE_PCIE_SLEEP -#ifdef QCOM_AP_AND_EFUSE_PCIE_SLEEP -#include -#include -#endif -#endif -//#define QCOM_AP_SDM845_IOMMU_MAP -#ifdef QCOM_AP_SDM845_IOMMU_MAP -#include -#include -#include -#endif -#include "../core/mhi.h" -#include "../core/mhi_internal.h" -#include "mhi_qti.h" - -#ifdef QCOM_AP_AND_EFUSE_PCIE_SLEEP -extern int pci_write_config_byte(const struct pci_dev *dev, int where, u8 val); -struct arch_info { - struct mhi_dev *mhi_dev; - struct msm_bus_scale_pdata *msm_bus_pdata; - u32 bus_client; - struct pci_saved_state *pcie_state; - struct pci_saved_state *ref_pcie_state; - struct dma_iommu_mapping *mapping; -}; -#endif - -#if 1 -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,65 )) -static inline int dma_set_mask_and_coherent(struct device *dev, u64 mask) -{ - int rc = dma_set_mask(dev, mask); - if (rc == 0) - dma_set_coherent_mask(dev, mask); - return rc; -} -#endif - -#ifdef PCI_IRQ_NOMSIX -#define PCI_IRQ_MSI PCI_IRQ_NOMSIX -#endif - -#ifndef PCI_IRQ_MSI -#define PCI_IRQ_MSI (1 << 1) /* Allow MSI interrupts */ - -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,53 )) -int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec) -{ - int nvec = maxvec; - int rc; - - if (maxvec < minvec) - return -ERANGE; - - do { - rc = pci_enable_msi_block(dev, nvec); - if (rc < 0) { - return rc; - } else if (rc > 0) { - if (rc < minvec) - return -ENOSPC; - nvec = rc; - } - } while (rc); - - return nvec; -} -#endif - -static int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs, - unsigned int max_vecs, unsigned int flags) -{ - return pci_enable_msi_range(dev, min_vecs, max_vecs); -} - -static void pci_free_irq_vectors(struct pci_dev *dev) -{ - pci_disable_msi(dev); -} - -static int pci_irq_vector(struct pci_dev *dev, unsigned int nr) -{ -#if 0//defined(CONFIG_PINCTRL_IPQ5018) - struct pcie_port *pp = dev->bus->sysdata; - pp->msi[nr]; //msi maybe not continuous -#endif - return dev->irq + nr; -} -#endif -#endif - -struct firmware_info { - unsigned int dev_id; - const char *fw_image; - const char *edl_image; -}; - -static const struct firmware_info firmware_table[] = { - {.dev_id = 0x306, .fw_image = "sdx55m/sbl1.mbn"}, - {.dev_id = 0x305, .fw_image = "sdx50m/sbl1.mbn"}, - {.dev_id = 0x304, .fw_image = "sbl.mbn", .edl_image = "edl.mbn"}, - /* default, set to debug.mbn */ - {.fw_image = "debug.mbn"}, -}; - -static int debug_mode; -module_param_named(debug_mode, debug_mode, int, 0644); - -int mhi_debugfs_trigger_m0(void *data, u64 val) -{ - struct mhi_controller *mhi_cntrl = data; - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - MHI_LOG("Trigger M3 Exit\n"); - pm_runtime_get(&mhi_dev->pci_dev->dev); - pm_runtime_put(&mhi_dev->pci_dev->dev); - - return 0; -} -DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m0_fops, NULL, - mhi_debugfs_trigger_m0, "%llu\n"); - -int mhi_debugfs_trigger_m3(void *data, u64 val) -{ - struct mhi_controller *mhi_cntrl = data; - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - MHI_LOG("Trigger M3 Entry\n"); - pm_runtime_mark_last_busy(&mhi_dev->pci_dev->dev); - pm_request_autosuspend(&mhi_dev->pci_dev->dev); - - return 0; -} -DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m3_fops, NULL, - mhi_debugfs_trigger_m3, "%llu\n"); - -void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl) -{ - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - struct pci_dev *pci_dev = mhi_dev->pci_dev; - - pm_runtime_mark_last_busy(&pci_dev->dev); - pm_runtime_dont_use_autosuspend(&pci_dev->dev); - pm_runtime_disable(&pci_dev->dev); - pci_free_irq_vectors(pci_dev); - kfree(mhi_cntrl->irq); - mhi_cntrl->irq = NULL; - iounmap(mhi_cntrl->regs); - mhi_cntrl->regs = NULL; - pci_clear_master(pci_dev); - pci_release_region(pci_dev, mhi_dev->resn); - pci_disable_device(pci_dev); -} - -static int mhi_init_pci_dev(struct mhi_controller *mhi_cntrl) -{ - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - struct pci_dev *pci_dev = mhi_dev->pci_dev; - int ret; - resource_size_t len; - int i; - - mhi_dev->resn = MHI_PCI_BAR_NUM; - ret = pci_assign_resource(pci_dev, mhi_dev->resn); - if (ret) { - MHI_ERR("Error assign pci resources, ret:%d\n", ret); - return ret; - } - - ret = pci_enable_device(pci_dev); - if (ret) { - MHI_ERR("Error enabling device, ret:%d\n", ret); - goto error_enable_device; - } - - ret = pci_request_region(pci_dev, mhi_dev->resn, "mhi"); - if (ret) { - MHI_ERR("Error pci_request_region, ret:%d\n", ret); - goto error_request_region; - } - - pci_set_master(pci_dev); - -#if 1 //some SOC like rpi_4b need next codes - ret = -EIO; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)) - if (!dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(64))) { - ret = dma_set_coherent_mask(&pci_dev->dev, DMA_BIT_MASK(64)); - } else if (!dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) { - ret = dma_set_coherent_mask(&pci_dev->dev, DMA_BIT_MASK(32)); - } -#else - if (!pci_set_dma_mask(pci_dev, DMA_BIT_MASK(64))) { - ret = pci_set_consistent_dma_mask(pci_dev, DMA_BIT_MASK(64)); - } else if (!pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) { - ret = pci_set_consistent_dma_mask(pci_dev, DMA_BIT_MASK(32)); - } -#endif - if (ret) { - MHI_ERR("Error dma mask\n"); - } -#endif - - mhi_cntrl->base_addr = pci_resource_start(pci_dev, mhi_dev->resn); - len = pci_resource_len(pci_dev, mhi_dev->resn); -#ifndef ioremap_nocache //4bdc0d676a643140bdf17dbf7eafedee3d496a3c -#define ioremap_nocache ioremap -#endif - mhi_cntrl->regs = ioremap_nocache(mhi_cntrl->base_addr, len); - if (!mhi_cntrl->regs) { - MHI_ERR("Error ioremap region\n"); - goto error_ioremap; - } - -#if 0 - ret = pci_alloc_irq_vectors(pci_dev, mhi_cntrl->msi_required, - mhi_cntrl->msi_required, PCI_IRQ_NOMSIX); - if (IS_ERR_VALUE((ulong)ret) || ret < mhi_cntrl->msi_required) { - MHI_ERR("Failed to enable MSI, ret:%d\n", ret); - goto error_req_msi; - } -#else - ret = pci_alloc_irq_vectors(pci_dev, 1, mhi_cntrl->msi_required, PCI_IRQ_MSI); - if (IS_ERR_VALUE((ulong)ret) || ret < mhi_cntrl->msi_required) { - if (ret == -ENOSPC) { - /* imx_3.14.52_1.1.0_ga - diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c - index f06e8f0..6a9614f 100644 - --- a/drivers/pci/host/pcie-designware.c - +++ b/drivers/pci/host/pcie-designware.c - @@ -376,6 +376,13 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, - if (msgvec > 5) - msgvec = 0; - - +#if 1 //Add by Quectel 20190419 - + if (msgvec > 0 && pdev->vendor == 0x17cb) { - + dev_info(&pdev->dev, "%s quectel fixup pos=%d, msg_ctr=%04x, msgvec=%d\n", __func__, desc->msi_attrib.pos, msg_ctr, msgvec); - + msgvec = 0; - + } - +#endif - + - irq = assign_irq((1 << msgvec), desc, &pos); - if (irq < 0) - return irq; - */ - } - //imx_4.1.15_2.0.0_ga & DELL_OPTIPLEX_7010 only alloc one msi interrupt for one pcie device - if (ret != 1) { - MHI_ERR("Failed to enable MSI, ret=%d, msi_required=%d\n", ret, mhi_cntrl->msi_required); - goto error_req_msi; - } - } - MHI_LOG("msi_required = %d, msi_allocated = %d, msi_irq = %u\n", mhi_cntrl->msi_required, ret, pci_dev->irq); -#endif - - mhi_cntrl->msi_allocated = ret; - mhi_cntrl->irq = kmalloc_array(mhi_cntrl->msi_allocated, - sizeof(*mhi_cntrl->irq), GFP_KERNEL); - if (!mhi_cntrl->irq) { - ret = -ENOMEM; - goto error_alloc_msi_vec; - } - - for (i = 0; i < mhi_cntrl->msi_allocated; i++) { - mhi_cntrl->irq[i] = pci_irq_vector(pci_dev, i); - if (mhi_cntrl->irq[i] < 0) { - ret = mhi_cntrl->irq[i]; - goto error_get_irq_vec; - } - } - - dev_set_drvdata(&pci_dev->dev, mhi_cntrl); - - /* configure runtime pm */ - pm_runtime_set_autosuspend_delay(&pci_dev->dev, MHI_RPM_SUSPEND_TMR_MS); - pm_runtime_use_autosuspend(&pci_dev->dev); - pm_suspend_ignore_children(&pci_dev->dev, true); - - /* - * pci framework will increment usage count (twice) before - * calling local device driver probe function. - * 1st pci.c pci_pm_init() calls pm_runtime_forbid - * 2nd pci-driver.c local_pci_probe calls pm_runtime_get_sync - * Framework expect pci device driver to call - * pm_runtime_put_noidle to decrement usage count after - * successful probe and and call pm_runtime_allow to enable - * runtime suspend. - */ - pm_runtime_mark_last_busy(&pci_dev->dev); - pm_runtime_put_noidle(&pci_dev->dev); - - return 0; - -error_get_irq_vec: - kfree(mhi_cntrl->irq); - mhi_cntrl->irq = NULL; - -error_alloc_msi_vec: - pci_free_irq_vectors(pci_dev); - -error_req_msi: - iounmap(mhi_cntrl->regs); - -error_ioremap: - pci_clear_master(pci_dev); - -error_request_region: - pci_disable_device(pci_dev); - -error_enable_device: - pci_release_region(pci_dev, mhi_dev->resn); - - return ret; -} - -#ifdef CONFIG_PM -static int mhi_runtime_suspend(struct device *dev) -{ - int ret = 0; - struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - MHI_LOG("Enter\n"); - - mutex_lock(&mhi_cntrl->pm_mutex); - - if (!mhi_dev->powered_on) { - MHI_LOG("Not fully powered, return success\n"); - mutex_unlock(&mhi_cntrl->pm_mutex); - return 0; - } - - if (mhi_cntrl->ee != MHI_EE_AMSS) { - MHI_LOG("Not AMSS, return busy\n"); - mutex_unlock(&mhi_cntrl->pm_mutex); - return -EBUSY; - } - - ret = mhi_pm_suspend(mhi_cntrl); - if (ret) { - MHI_LOG("Abort due to ret:%d\n", ret); - goto exit_runtime_suspend; - } - - ret = mhi_arch_link_off(mhi_cntrl, true); - if (ret) - MHI_ERR("Failed to Turn off link ret:%d\n", ret); - -exit_runtime_suspend: - mutex_unlock(&mhi_cntrl->pm_mutex); - MHI_LOG("Exited with ret:%d\n", ret); - - return ret; -} - -static int mhi_runtime_idle(struct device *dev) -{ - struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); - - if ((mhi_cntrl->dev_state == MHI_STATE_M0 || mhi_cntrl->dev_state == MHI_STATE_M3) - && mhi_cntrl->ee == MHI_EE_AMSS) { - return 0; - } - MHI_LOG("Entered returning -EBUSY, mhi_state:%s exec_env:%s\n", - TO_MHI_STATE_STR(mhi_get_mhi_state(mhi_cntrl)), TO_MHI_EXEC_STR(mhi_get_exec_env(mhi_cntrl))); - - /* - * RPM framework during runtime resume always calls - * rpm_idle to see if device ready to suspend. - * If dev.power usage_count count is 0, rpm fw will call - * rpm_idle cb to see if device is ready to suspend. - * if cb return 0, or cb not defined the framework will - * assume device driver is ready to suspend; - * therefore, fw will schedule runtime suspend. - * In MHI power management, MHI host shall go to - * runtime suspend only after entering MHI State M2, even if - * usage count is 0. Return -EBUSY to disable automatic suspend. - */ - return -EBUSY; -} - -static int mhi_runtime_resume(struct device *dev) -{ - int ret = 0; - struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - MHI_LOG("Enter\n"); - - mutex_lock(&mhi_cntrl->pm_mutex); - - if (!mhi_dev->powered_on) { - MHI_LOG("Not fully powered, return success\n"); - mutex_unlock(&mhi_cntrl->pm_mutex); - return 0; - } - - /* turn on link */ - ret = mhi_arch_link_on(mhi_cntrl); - if (ret) - goto rpm_resume_exit; - - /* enter M0 state */ - ret = mhi_pm_resume(mhi_cntrl); - -rpm_resume_exit: - mutex_unlock(&mhi_cntrl->pm_mutex); - MHI_LOG("Exited with :%d\n", ret); - - return ret; -} - -static int __maybe_unused mhi_system_resume(struct device *dev) -{ - int ret = 0; - struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); - - ret = mhi_runtime_resume(dev); - if (ret) { - MHI_ERR("Failed to resume link\n"); - } else { - //pm_runtime_set_active(dev); - //pm_runtime_enable(dev); - } - - return ret; -} - -int mhi_system_suspend(struct device *dev) -{ - struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); - int ret; - - MHI_LOG("Entered\n"); - - if (atomic_read(&mhi_cntrl->pending_pkts)) { - MHI_LOG("Abort due to pending_pkts:%d\n", atomic_read(&mhi_cntrl->pending_pkts)); - return -EBUSY; - } - - /* if rpm status still active then force suspend */ - if (!pm_runtime_status_suspended(dev)) { - ret = mhi_runtime_suspend(dev); - if (ret) { - MHI_LOG("suspend failed ret:%d\n", ret); - return ret; - } - } - - //pm_runtime_set_suspended(dev); - //pm_runtime_disable(dev); - - MHI_LOG("Exit\n"); - return 0; -} -#endif - -/* checks if link is down */ -static int mhi_link_status(struct mhi_controller *mhi_cntrl, void *priv) -{ - struct mhi_dev *mhi_dev = priv; - u16 dev_id; - int ret; - - /* try reading device id, if dev id don't match, link is down */ - ret = pci_read_config_word(mhi_dev->pci_dev, PCI_DEVICE_ID, &dev_id); - - return (ret || dev_id != mhi_cntrl->dev_id) ? -EIO : 0; -} - -/* disable PCIe L1 */ -static int mhi_lpm_disable(struct mhi_controller *mhi_cntrl, void *priv) -{ - struct mhi_dev *mhi_dev = priv; - struct pci_dev *pci_dev = mhi_dev->pci_dev; - int lnkctl = pci_dev->pcie_cap + PCI_EXP_LNKCTL; - u8 val; - int ret; - - ret = pci_read_config_byte(pci_dev, lnkctl, &val); - if (ret) { - MHI_ERR("Error reading LNKCTL, ret:%d\n", ret); - return ret; - } - - /* L1 is not supported or already disabled */ - if (!(val & PCI_EXP_LNKCTL_ASPM_L1)) - return 0; - - val &= ~PCI_EXP_LNKCTL_ASPM_L1; - ret = pci_write_config_byte(pci_dev, lnkctl, val); - if (ret) { - MHI_ERR("Error writing LNKCTL to disable LPM, ret:%d\n", ret); - return ret; - } - - mhi_dev->lpm_disabled = true; - - return ret; -} - -/* enable PCIe L1 */ -static int mhi_lpm_enable(struct mhi_controller *mhi_cntrl, void *priv) -{ - struct mhi_dev *mhi_dev = priv; - struct pci_dev *pci_dev = mhi_dev->pci_dev; - int lnkctl = pci_dev->pcie_cap + PCI_EXP_LNKCTL; - u8 val; - int ret; - - /* L1 is not supported or already disabled */ - if (!mhi_dev->lpm_disabled) - return 0; - - ret = pci_read_config_byte(pci_dev, lnkctl, &val); - if (ret) { - MHI_ERR("Error reading LNKCTL, ret:%d\n", ret); - return ret; - } - - val |= PCI_EXP_LNKCTL_ASPM_L1; - ret = pci_write_config_byte(pci_dev, lnkctl, val); - if (ret) { - MHI_ERR("Error writing LNKCTL to enable LPM, ret:%d\n", ret); - return ret; - } - - mhi_dev->lpm_disabled = false; - - return ret; -} - -static int mhi_power_up(struct mhi_controller *mhi_cntrl) -{ - enum mhi_dev_state dev_state = mhi_get_mhi_state(mhi_cntrl); - const u32 delayus = 10; - int itr = DIV_ROUND_UP(mhi_cntrl->timeout_ms * 1000, delayus); - int ret; - - MHI_LOG("dev_state:%s\n", TO_MHI_STATE_STR(mhi_get_mhi_state(mhi_cntrl))); - - /* - * It's possible device did not go thru a cold reset before - * power up and still in error state. If device in error state, - * we need to trigger a soft reset before continue with power - * up - */ - if (dev_state == MHI_STATE_SYS_ERR) { - mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET); - while (itr--) { - dev_state = mhi_get_mhi_state(mhi_cntrl); - if (dev_state != MHI_STATE_SYS_ERR) - break; - usleep_range(delayus, delayus << 1); - } - MHI_LOG("dev_state:%s\n", TO_MHI_STATE_STR(mhi_get_mhi_state(mhi_cntrl))); - - /* device still in error state, abort power up */ - if (dev_state == MHI_STATE_SYS_ERR) - return -EIO; - } - - ret = mhi_async_power_up(mhi_cntrl); - - /* power up create the dentry */ - if (mhi_cntrl->dentry) { - debugfs_create_file("m0", 0444, mhi_cntrl->dentry, mhi_cntrl, - &debugfs_trigger_m0_fops); - debugfs_create_file("m3", 0444, mhi_cntrl->dentry, mhi_cntrl, - &debugfs_trigger_m3_fops); - } - - return ret; -} - -static int mhi_runtime_get(struct mhi_controller *mhi_cntrl, void *priv) -{ - struct mhi_dev *mhi_dev = priv; - struct device *dev = &mhi_dev->pci_dev->dev; - - return pm_runtime_get(dev); -} - -static void mhi_runtime_put(struct mhi_controller *mhi_cntrl, void *priv) -{ - struct mhi_dev *mhi_dev = priv; - struct device *dev = &mhi_dev->pci_dev->dev; - - pm_runtime_mark_last_busy(dev); - pm_runtime_put(dev); -} - -static void mhi_runtime_mark_last_busy(struct mhi_controller *mhi_cntrl, void *priv) -{ - struct mhi_dev *mhi_dev = priv; - struct device *dev = &mhi_dev->pci_dev->dev; - - pm_runtime_mark_last_busy(dev); -} - -#ifdef QCOM_AP_AND_EFUSE_PCIE_SLEEP -static void mhi_pci_event_cb(struct msm_pcie_notify *notify) -{ - struct pci_dev *pci_dev = notify->user; - struct device *dev = &pci_dev->dev; - - dev_info(&pci_dev->dev, "Received PCIe event %d", notify->event); - switch (notify->event) { - case MSM_PCIE_EVENT_WAKEUP: - if (dev && pm_runtime_status_suspended(dev)) { - pm_request_resume(dev); - pm_runtime_mark_last_busy(dev); - } - break; - default: - break; - } -} - -static struct msm_pcie_register_event mhi_pcie_events[MAX_MHI]; -#endif - -static void mhi_status_cb(struct mhi_controller *mhi_cntrl, - void *priv, - enum MHI_CB reason) -{ - struct mhi_dev *mhi_dev = priv; - struct device *dev = &mhi_dev->pci_dev->dev; - - switch (reason) { - case MHI_CB_FATAL_ERROR: - case MHI_CB_SYS_ERROR: - pm_runtime_forbid(dev); - break; - case MHI_CB_EE_MISSION_MODE: - //pm_runtime_allow(dev); - break; - default: - break; - } -} - -/* capture host SoC XO time in ticks */ -static u64 mhi_time_get(struct mhi_controller *mhi_cntrl, void *priv) -{ - return 0; -} - -static ssize_t timeout_ms_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct mhi_device *mhi_dev = to_mhi_device(dev); - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - - /* buffer provided by sysfs has a minimum size of PAGE_SIZE */ - return snprintf(buf, PAGE_SIZE, "%u\n", mhi_cntrl->timeout_ms); -} - -static ssize_t timeout_ms_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) -{ - struct mhi_device *mhi_dev = to_mhi_device(dev); - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - u32 timeout_ms; - - if (kstrtou32(buf, 0, &timeout_ms) < 0) - return -EINVAL; - - mhi_cntrl->timeout_ms = timeout_ms; - - return count; -} -static DEVICE_ATTR_RW(timeout_ms); - -static ssize_t power_up_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) -{ - int ret; - struct mhi_device *mhi_dev = to_mhi_device(dev); - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - - ret = mhi_power_up(mhi_cntrl); - if (ret) - return ret; - - return count; -} -static DEVICE_ATTR_WO(power_up); - -static struct attribute *mhi_attrs[] = { - &dev_attr_timeout_ms.attr, - &dev_attr_power_up.attr, - NULL -}; - -static const struct attribute_group mhi_group = { - .attrs = mhi_attrs, -}; - -static struct mhi_controller *mhi_register_controller(struct pci_dev *pci_dev) -{ - struct mhi_controller *mhi_cntrl; - struct mhi_dev *mhi_dev; - struct device_node *of_node = pci_dev->dev.of_node; - const struct firmware_info *firmware_info; - bool use_bb; - u64 addr_win[2]; - int ret, i; - - //if (!of_node) - // return ERR_PTR(-ENODEV); - - mhi_cntrl = mhi_alloc_controller(sizeof(*mhi_dev)); - if (!mhi_cntrl) - return ERR_PTR(-ENOMEM); - - mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - mhi_cntrl->dev = &pci_dev->dev; - mhi_cntrl->domain = pci_domain_nr(pci_dev->bus); - mhi_cntrl->vendor = pci_dev->vendor; - mhi_cntrl->dev_id = pci_dev->device; - mhi_cntrl->bus = pci_dev->bus->number; - mhi_cntrl->slot = PCI_SLOT(pci_dev->devfn); - -#if 0 - use_bb = of_property_read_bool(of_node, "mhi,use-bb"); - - /* - * if s1 translation enabled or using bounce buffer pull iova addr - * from dt - */ - if (use_bb || (mhi_dev->smmu_cfg & MHI_SMMU_ATTACH && - !(mhi_dev->smmu_cfg & MHI_SMMU_S1_BYPASS))) { - ret = of_property_count_elems_of_size(of_node, "qti,addr-win", - sizeof(addr_win)); - if (ret != 1) - goto error_register; - ret = of_property_read_u64_array(of_node, "qti,addr-win", - addr_win, 2); - if (ret) - goto error_register; - } else { - addr_win[0] = memblock_start_of_DRAM(); - addr_win[1] = memblock_end_of_DRAM(); - } -#else - use_bb = false; - (void)use_bb; - addr_win[0] = 0x000000000; - addr_win[1] = 0x2000000000; //MHI_MEM_SIZE_DEFAULT - if (sizeof(dma_addr_t) == 4) { - addr_win[1] = 0xFFFFFFFF; - } -#endif - - mhi_dev->iova_start = addr_win[0]; - mhi_dev->iova_stop = addr_win[1]; - - /* - * If S1 is enabled, set MHI_CTRL start address to 0 so we can use low - * level mapping api to map buffers outside of smmu domain - */ - if (mhi_dev->smmu_cfg & MHI_SMMU_ATTACH && - !(mhi_dev->smmu_cfg & MHI_SMMU_S1_BYPASS)) - mhi_cntrl->iova_start = 0; - else - mhi_cntrl->iova_start = addr_win[0]; - - mhi_cntrl->iova_stop = mhi_dev->iova_stop; - mhi_cntrl->of_node = of_node; - - mhi_dev->pci_dev = pci_dev; - - /* setup power management apis */ - mhi_cntrl->status_cb = mhi_status_cb; - mhi_cntrl->runtime_get = mhi_runtime_get; - mhi_cntrl->runtime_put = mhi_runtime_put; - mhi_cntrl->runtime_mark_last_busy = mhi_runtime_mark_last_busy; - mhi_cntrl->link_status = mhi_link_status; - - mhi_cntrl->lpm_disable = mhi_lpm_disable; - mhi_cntrl->lpm_enable = mhi_lpm_enable; - mhi_cntrl->time_get = mhi_time_get; - - ret = of_register_mhi_controller(mhi_cntrl); - if (ret) - goto error_register; - - for (i = 0; i < ARRAY_SIZE(firmware_table); i++) { - firmware_info = firmware_table + i; - - /* debug mode always use default */ - if (!debug_mode && mhi_cntrl->dev_id == firmware_info->dev_id) - break; - } - -#if 0 - mhi_cntrl->fw_image = firmware_info->fw_image; - mhi_cntrl->edl_image = firmware_info->edl_image; -#endif - - if (sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj, &mhi_group)) - MHI_ERR("Error while creating the sysfs group\n"); - - return mhi_cntrl; - -error_register: - mhi_free_controller(mhi_cntrl); - - return ERR_PTR(-EINVAL); -} - -static bool mhi_pci_is_alive(struct pci_dev *pdev) -{ - u16 vendor = 0; - - if (pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor)) - return false; - - if (vendor == (u16) ~0 || vendor == 0) - return false; - - return true; -} - -static void mhi_pci_show_link(struct mhi_controller *mhi_cntrl, struct pci_dev *pci_dev) -{ - int pcie_cap_reg; - u16 stat; - u32 caps; - const char *speed; - - pcie_cap_reg = pci_find_capability(pci_dev, PCI_CAP_ID_EXP); - - if (!pcie_cap_reg) - return; - - pci_read_config_word(pci_dev, - pcie_cap_reg + PCI_EXP_LNKSTA, - &stat); - pci_read_config_dword(pci_dev, - pcie_cap_reg + PCI_EXP_LNKCAP, - &caps); - - switch (caps & PCI_EXP_LNKCAP_SLS) { - case PCI_EXP_LNKCAP_SLS_2_5GB: speed = "2.5"; break; - case PCI_EXP_LNKCAP_SLS_5_0GB: speed = "5"; break; - case 3: speed = "8"; break; - case 4: speed = "16"; break; - case 5: speed = "32"; break; - case 6: speed = "64"; break; - default: speed = "0"; break; - } - - MHI_LOG("LnkCap: Speed %sGT/s, Width x%d\n", speed, - (caps & PCI_EXP_LNKCAP_MLW) >> 4); - - switch (stat & PCI_EXP_LNKSTA_CLS) { - case PCI_EXP_LNKSTA_CLS_2_5GB: speed = "2.5"; break; - case PCI_EXP_LNKSTA_CLS_5_0GB: speed = "5"; break; - case 3: speed = "8"; break; - case 4: speed = "16"; break; - case 5: speed = "32"; break; - case 6: speed = "64"; break; - default: speed = "0"; break; - } - - MHI_LOG("LnkSta: Speed %sGT/s, Width x%d\n", speed, - (stat & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT); - -} - -int mhi_pci_probe(struct pci_dev *pci_dev, - const struct pci_device_id *device_id) -{ - struct mhi_controller *mhi_cntrl; - u32 domain = pci_domain_nr(pci_dev->bus); - u32 bus = pci_dev->bus->number; - u32 dev_id = pci_dev->device; - u32 slot = PCI_SLOT(pci_dev->devfn); - struct mhi_dev *mhi_dev; - int ret; - - pr_info("%s pci_dev->name = %s, domain=%d, bus=%d, slot=%d, vendor=%04X, device=%04X\n", - __func__, dev_name(&pci_dev->dev), domain, bus, slot, pci_dev->vendor, pci_dev->device); - -#if !defined(CONFIG_PCI_MSI) - /* MT7621 RTL8198D EcoNet-EN7565 */ - #error "pcie msi is not support by this soc! and i donot support INTx (SW1SDX55-2688)" -#endif - - if (!mhi_pci_is_alive(pci_dev)) { - /* - root@OpenWrt:~# hexdump /sys/bus/pci/devices/0000:01:00.0/config - 0000000 ffff ffff ffff ffff ffff ffff ffff ffff - * - 0001000 - */ - pr_err("mhi_pci is not alive! pcie link is down\n"); - pr_err("double check by 'hexdump /sys/bus/pci/devices/%s/config'\n", dev_name(&pci_dev->dev)); - return -EIO; - } - - /* see if we already registered */ - mhi_cntrl = mhi_bdf_to_controller(domain, bus, slot, dev_id); - if (!mhi_cntrl) - mhi_cntrl = mhi_register_controller(pci_dev); - - if (IS_ERR(mhi_cntrl)) - return PTR_ERR(mhi_cntrl); - - mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - mhi_dev->powered_on = true; - - mhi_arch_iommu_init(mhi_cntrl); - - ret = mhi_arch_pcie_init(mhi_cntrl); - if (ret) - goto error_init_pci_arch; - - mhi_cntrl->dev = &pci_dev->dev; - ret = mhi_init_pci_dev(mhi_cntrl); - if (ret) - goto error_init_pci; - - /* start power up sequence */ - if (!debug_mode) { - ret = mhi_power_up(mhi_cntrl); - if (ret) - goto error_power_up; - } - - pm_runtime_mark_last_busy(&pci_dev->dev); - - mhi_pci_show_link(mhi_cntrl, pci_dev); - -#ifdef QCOM_AP_AND_EFUSE_PCIE_SLEEP - { - struct msm_pcie_register_event *pcie_event = &mhi_pcie_events[mhi_cntrl->cntrl_idx]; - - pcie_event->events = MSM_PCIE_EVENT_WAKEUP; -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 4,14,117 )) - pcie_event->pcie_event.user = pci_dev; - pcie_event->pcie_event.mode = MSM_PCIE_TRIGGER_CALLBACK; - pcie_event->pcie_event.callback = mhi_pci_event_cb; -#else - pcie_event->user = pci_dev; - pcie_event->mode = MSM_PCIE_TRIGGER_CALLBACK; - pcie_event->callback = mhi_pci_event_cb; -#endif - - ret = msm_pcie_register_event(pcie_event); - if (ret) { - MHI_LOG("Failed to register for PCIe event"); - } - } -#endif - - MHI_LOG("Return successful\n"); - - return 0; - - mhi_unregister_mhi_controller(mhi_cntrl); -error_power_up: - mhi_deinit_pci_dev(mhi_cntrl); - -error_init_pci: - mhi_arch_pcie_deinit(mhi_cntrl); -error_init_pci_arch: - mhi_arch_iommu_deinit(mhi_cntrl); - - return ret; -} - -void mhi_pci_device_removed(struct pci_dev *pci_dev) -{ - struct mhi_controller *mhi_cntrl; - u32 domain = pci_domain_nr(pci_dev->bus); - u32 bus = pci_dev->bus->number; - u32 dev_id = pci_dev->device; - u32 slot = PCI_SLOT(pci_dev->devfn); - - mhi_cntrl = mhi_bdf_to_controller(domain, bus, slot, dev_id); - - if (mhi_cntrl) { - - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - -#ifdef QCOM_AP_AND_EFUSE_PCIE_SLEEP - { - struct msm_pcie_register_event *pcie_event = &mhi_pcie_events[mhi_cntrl->cntrl_idx]; - - msm_pcie_deregister_event(pcie_event); - } -#endif - - pm_stay_awake(&mhi_cntrl->mhi_dev->dev); - - /* if link is in drv suspend, wake it up */ - pm_runtime_get_sync(&pci_dev->dev); - - mutex_lock(&mhi_cntrl->pm_mutex); - if (!mhi_dev->powered_on) { - MHI_LOG("Not in active state\n"); - mutex_unlock(&mhi_cntrl->pm_mutex); - pm_runtime_put_noidle(&pci_dev->dev); - return; - } - mhi_dev->powered_on = false; - mutex_unlock(&mhi_cntrl->pm_mutex); - - pm_runtime_put_noidle(&pci_dev->dev); - - MHI_LOG("Triggering shutdown process\n"); - mhi_power_down(mhi_cntrl, false); - - /* turn the link off */ - mhi_deinit_pci_dev(mhi_cntrl); - mhi_arch_link_off(mhi_cntrl, false); - - mhi_arch_pcie_deinit(mhi_cntrl); - mhi_arch_iommu_deinit(mhi_cntrl); - - pm_relax(&mhi_cntrl->mhi_dev->dev); - - mhi_unregister_mhi_controller(mhi_cntrl); - } -} - -static const struct dev_pm_ops pm_ops = { - SET_RUNTIME_PM_OPS(mhi_runtime_suspend, - mhi_runtime_resume, - mhi_runtime_idle) - SET_SYSTEM_SLEEP_PM_OPS(mhi_system_suspend, mhi_system_resume) -}; - -static struct pci_device_id mhi_pcie_device_id[] = { - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0303)}, - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0304)}, //SDX20 - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0305)}, //SDX24 - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0306)}, //SDX55 - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0308)}, //SDX62 - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x011a)}, //SDX35 - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0309)}, //SDX7X - {PCI_DEVICE(0x1eac, 0x1001)}, //EM120 - {PCI_DEVICE(0x1eac, 0x1002)}, //EM160 - {PCI_DEVICE(0x1eac, 0x1004)}, //RM520 - {PCI_DEVICE(0x1eac, 0x100b)}, //RM255 - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, MHI_PCIE_DEBUG_ID)}, - {0}, -}; - -MODULE_DEVICE_TABLE(pci, mhi_pcie_device_id); - -static struct pci_driver mhi_pcie_driver = { - .name = "mhi_q", - .id_table = mhi_pcie_device_id, - .probe = mhi_pci_probe, - .remove = mhi_pci_device_removed, - .driver = { - .pm = &pm_ops - } -}; - -#if 0 -module_pci_driver(mhi_pcie_driver); -#else -int __init mhi_controller_qcom_init(void) -{ - return pci_register_driver(&mhi_pcie_driver); -}; - -void mhi_controller_qcom_exit(void) -{ - pr_info("%s enter\n", __func__); - pci_unregister_driver(&mhi_pcie_driver); - pr_info("%s exit\n", __func__); -} - -#ifdef QCOM_AP_SDM845_IOMMU_MAP -struct dma_iommu_mapping *mhi_smmu_mapping[MAX_MHI]; - -#define SMMU_BASE 0x10000000 -#define SMMU_SIZE 0x40000000 -static struct dma_iommu_mapping * sdm845_smmu_init(struct pci_dev *pdev) { - int ret = 0; - int atomic_ctx = 1; - int s1_bypass = 1; - struct dma_iommu_mapping *mapping; - - mapping = arm_iommu_create_mapping(&platform_bus_type, SMMU_BASE, SMMU_SIZE); - if (IS_ERR(mapping)) { - ret = PTR_ERR(mapping); - dev_err(&pdev->dev, "Create mapping failed, err = %d\n", ret); - return NULL; - } - - ret = iommu_domain_set_attr(mapping->domain, DOMAIN_ATTR_ATOMIC, &atomic_ctx); - if (ret < 0) { - dev_err(&pdev->dev, "Set atomic_ctx attribute failed, err = %d\n", ret); - goto set_attr_fail; - } - - ret = iommu_domain_set_attr(mapping->domain, DOMAIN_ATTR_S1_BYPASS, &s1_bypass); - if (ret < 0) { - dev_err(&pdev->dev, "Set s1_bypass attribute failed, err = %d\n", ret); - arm_iommu_release_mapping(mapping); - goto set_attr_fail; - } - - ret = arm_iommu_attach_device(&pdev->dev, mapping); - if (ret < 0) { - dev_err(&pdev->dev, "Attach device failed, err = %d\n", ret); - goto attach_fail; - } - - return mapping; - -attach_fail: -set_attr_fail: - arm_iommu_release_mapping(mapping); - return NULL; -} -#endif - -int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl) -{ -#ifdef QCOM_AP_SDM845_IOMMU_MAP - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - mhi_smmu_mapping[mhi_cntrl->cntrl_idx] = sdm845_smmu_init(mhi_dev->pci_dev); -#endif - - return 0; -} - -void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl) -{ -#ifdef QCOM_AP_SDM845_IOMMU_MAP - if (mhi_smmu_mapping[mhi_cntrl->cntrl_idx]) { - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - arm_iommu_detach_device(&mhi_dev->pci_dev->dev); - arm_iommu_release_mapping(mhi_smmu_mapping[mhi_cntrl->cntrl_idx]); - mhi_smmu_mapping[mhi_cntrl->cntrl_idx] = NULL; - } -#endif -} - -static int mhi_arch_set_bus_request(struct mhi_controller *mhi_cntrl, int index) -{ - MHI_LOG("Setting bus request to index %d\n", index); - return 0; -} - -int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl) -{ -#ifdef QCOM_AP_AND_EFUSE_PCIE_SLEEP - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - struct arch_info *arch_info = mhi_dev->arch_info; - - if (!arch_info) { - arch_info = devm_kzalloc(&mhi_dev->pci_dev->dev, - sizeof(*arch_info), GFP_KERNEL); - if (!arch_info) - return -ENOMEM; - - mhi_dev->arch_info = arch_info; - - /* save reference state for pcie config space */ - arch_info->ref_pcie_state = pci_store_saved_state( - mhi_dev->pci_dev); - } -#endif - - return mhi_arch_set_bus_request(mhi_cntrl, 1); -} - -void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl) -{ - mhi_arch_set_bus_request(mhi_cntrl, 0); -} - -int mhi_arch_platform_init(struct mhi_dev *mhi_dev) -{ - return 0; -} - -void mhi_arch_platform_deinit(struct mhi_dev *mhi_dev) -{ -} - -int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, - bool graceful) -{ -#ifdef QCOM_AP_AND_EFUSE_PCIE_SLEEP - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - struct arch_info *arch_info = mhi_dev->arch_info; - struct pci_dev *pci_dev = mhi_dev->pci_dev; - int ret; - - MHI_LOG("Entered\n"); - - if (graceful) { - pci_clear_master(pci_dev); - ret = pci_save_state(mhi_dev->pci_dev); - if (ret) { - MHI_ERR("Failed with pci_save_state, ret:%d\n", ret); - return ret; - } - - arch_info->pcie_state = pci_store_saved_state(pci_dev); - pci_disable_device(pci_dev); - } - - /* - * We will always attempt to put link into D3hot, however - * link down may have happened due to error fatal, so - * ignoring the return code - */ - pci_set_power_state(pci_dev, PCI_D3hot); - - ret = msm_pcie_pm_control(MSM_PCIE_SUSPEND, mhi_cntrl->bus, pci_dev, - NULL, 0); - MHI_ERR("msm_pcie_pm_control(MSM_PCIE_SUSPEND), ret:%d\n", ret); - - /* release the resources */ - mhi_arch_set_bus_request(mhi_cntrl, 0); - - MHI_LOG("Exited\n"); -#endif - - return 0; -} - -int mhi_arch_link_on(struct mhi_controller *mhi_cntrl) -{ -#ifdef QCOM_AP_AND_EFUSE_PCIE_SLEEP - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - struct arch_info *arch_info = mhi_dev->arch_info; - struct pci_dev *pci_dev = mhi_dev->pci_dev; - int ret; - - MHI_LOG("Entered\n"); - - /* request resources and establish link trainning */ - ret = mhi_arch_set_bus_request(mhi_cntrl, 1); - if (ret) - MHI_LOG("Could not set bus frequency, ret:%d\n", ret); - - ret = msm_pcie_pm_control(MSM_PCIE_RESUME, mhi_cntrl->bus, pci_dev, - NULL, 0); - MHI_LOG("msm_pcie_pm_control(MSM_PCIE_RESUME), ret:%d\n", ret); - if (ret) { - MHI_ERR("Link training failed, ret:%d\n", ret); - return ret; - } - - ret = pci_set_power_state(pci_dev, PCI_D0); - if (ret) { - MHI_ERR("Failed to set PCI_D0 state, ret:%d\n", ret); - return ret; - } - - ret = pci_enable_device(pci_dev); - if (ret) { - MHI_ERR("Failed to enable device, ret:%d\n", ret); - return ret; - } - - ret = pci_load_and_free_saved_state(pci_dev, &arch_info->pcie_state); - if (ret) - MHI_LOG("Failed to load saved cfg state\n"); - - pci_restore_state(pci_dev); - pci_set_master(pci_dev); - - MHI_LOG("Exited\n"); -#endif - - return 0; -} -#endif diff --git a/quectel_MHI/src/controllers/mhi_qti.h b/quectel_MHI/src/controllers/mhi_qti.h deleted file mode 100644 index 7ac021a3c..000000000 --- a/quectel_MHI/src/controllers/mhi_qti.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/ - -#ifndef _MHI_QTI_ -#define _MHI_QTI_ - -/* iova cfg bitmask */ -#define MHI_SMMU_ATTACH BIT(0) -#define MHI_SMMU_S1_BYPASS BIT(1) -#define MHI_SMMU_FAST BIT(2) -#define MHI_SMMU_ATOMIC BIT(3) -#define MHI_SMMU_FORCE_COHERENT BIT(4) - -#define MHI_PCIE_VENDOR_ID (0x17cb) -#define MHI_PCIE_DEBUG_ID (0xffff) - -/* runtime suspend timer */ -#define MHI_RPM_SUSPEND_TMR_MS (2000) -#define MHI_PCI_BAR_NUM (0) - -struct mhi_dev { - struct pci_dev *pci_dev; - u32 smmu_cfg; - int resn; - void *arch_info; - bool powered_on; - dma_addr_t iova_start; - dma_addr_t iova_stop; - bool lpm_disabled; -}; - -void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl); -int mhi_pci_probe(struct pci_dev *pci_dev, - const struct pci_device_id *device_id); - -void mhi_pci_device_removed(struct pci_dev *pci_dev); -int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl); -void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl); -int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl); -void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl); -int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, bool graceful); -int mhi_arch_link_on(struct mhi_controller *mhi_cntrl); - -#endif /* _MHI_QTI_ */ diff --git a/quectel_MHI/src/core/Makefile b/quectel_MHI/src/core/Makefile deleted file mode 100644 index a743fbfa4..000000000 --- a/quectel_MHI/src/core/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_MHI_BUS) +=mhi_init.o mhi_main.o mhi_pm.o mhi_boot.o mhi_dtr.o diff --git a/quectel_MHI/src/core/mhi.h b/quectel_MHI/src/core/mhi.h deleted file mode 100644 index f9671af4a..000000000 --- a/quectel_MHI/src/core/mhi.h +++ /dev/null @@ -1,908 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */ - -#ifndef _MHI_H_ -#define _MHI_H_ - -#define PCIE_MHI_DRIVER_VERSION "V1.3.8" -#define ENABLE_MHI_MON -//#define ENABLE_IP_SW0 - -// #define ENABLE_ADPL - -// #define ENABLE_QDSS - -#include -typedef enum -{ - MHI_CLIENT_LOOPBACK_OUT = 0, - MHI_CLIENT_LOOPBACK_IN = 1, - MHI_CLIENT_SAHARA_OUT = 2, - MHI_CLIENT_SAHARA_IN = 3, - MHI_CLIENT_DIAG_OUT = 4, - MHI_CLIENT_DIAG_IN = 5, - MHI_CLIENT_SSR_OUT = 6, - MHI_CLIENT_SSR_IN = 7, - MHI_CLIENT_QDSS_OUT = 8, - MHI_CLIENT_QDSS_IN = 9, - MHI_CLIENT_EFS_OUT = 10, - MHI_CLIENT_EFS_IN = 11, - MHI_CLIENT_MBIM_OUT = 12, - MHI_CLIENT_MBIM_IN = 13, - MHI_CLIENT_QMI_OUT = 14, - MHI_CLIENT_QMI_IN = 15, - MHI_CLIENT_QMI_2_OUT = 16, - MHI_CLIENT_QMI_2_IN = 17, - MHI_CLIENT_IP_CTRL_1_OUT = 18, - MHI_CLIENT_IP_CTRL_1_IN = 19, - MHI_CLIENT_IPCR_OUT = 20, - MHI_CLIENT_IPCR_IN = 21, - MHI_CLIENT_TEST_FW_OUT = 22, - MHI_CLIENT_TEST_FW_IN = 23, - MHI_CLIENT_RESERVED_0 = 24, - MHI_CLIENT_BOOT_LOG_IN = 25, - MHI_CLIENT_DCI_OUT = 26, - MHI_CLIENT_DCI_IN = 27, - MHI_CLIENT_QBI_OUT = 28, - MHI_CLIENT_QBI_IN = 29, - MHI_CLIENT_RESERVED_1_LOWER = 30, - MHI_CLIENT_RESERVED_1_UPPER = 31, - MHI_CLIENT_DUN_OUT = 32, - MHI_CLIENT_DUN_IN = 33, - MHI_CLIENT_EDL_OUT = 34, - MHI_CLIENT_EDL_IN = 35, - MHI_CLIENT_ADB_FB_OUT = 36, - MHI_CLIENT_ADB_FB_IN = 37, - MHI_CLIENT_RESERVED_2_LOWER = 38, - MHI_CLIENT_RESERVED_2_UPPER = 41, - MHI_CLIENT_CSVT_OUT = 42, - MHI_CLIENT_CSVT_IN = 43, - MHI_CLIENT_SMCT_OUT = 44, - MHI_CLIENT_SMCT_IN = 45, - MHI_CLIENT_IP_SW_0_OUT = 46, - MHI_CLIENT_IP_SW_0_IN = 47, - MHI_CLIENT_IP_SW_1_OUT = 48, - MHI_CLIENT_IP_SW_1_IN = 49, - MHI_CLIENT_RESERVED_3_LOWER = 50, - MHI_CLIENT_RESERVED_3_UPPER = 59, - MHI_CLIENT_TEST_0_OUT = 60, - MHI_CLIENT_TEST_0_IN = 61, - MHI_CLIENT_TEST_1_OUT = 62, - MHI_CLIENT_TEST_1_IN = 63, - MHI_CLIENT_TEST_2_OUT = 64, - MHI_CLIENT_TEST_2_IN = 65, - MHI_CLIENT_TEST_3_OUT = 66, - MHI_CLIENT_TEST_3_IN = 67, - MHI_CLIENT_RESERVED_4_LOWER = 68, - MHI_CLIENT_RESERVED_4_UPPER = 91, - MHI_CLIENT_OEM_0_OUT = 92, - MHI_CLIENT_OEM_0_IN = 93, - MHI_CLIENT_OEM_1_OUT = 94, - MHI_CLIENT_OEM_1_IN = 95, - MHI_CLIENT_OEM_2_OUT = 96, - MHI_CLIENT_OEM_2_IN = 97, - MHI_CLIENT_OEM_3_OUT = 98, - MHI_CLIENT_OEM_3_IN = 99, - MHI_CLIENT_IP_HW_0_OUT = 100, - MHI_CLIENT_IP_HW_0_IN = 101, - MHI_CLIENT_ADPL = 102, - MHI_CLIENT_IP_HW_QDSS = 103, - // MHI_CLIENT_RESERVED_5_LOWER = 103, - MHI_CLIENT_RESERVED_5_UPPER = 127, - MHI_MAX_CHANNELS = 128 -}MHI_CLIENT_CHANNEL_TYPE; - -/* Event Ring Index */ -typedef enum -{ - SW_EVT_RING = 0, - PRIMARY_EVENT_RING = SW_EVT_RING, -#ifdef ENABLE_IP_SW0 - SW_0_OUT_EVT_RING, - SW_0_IN_EVT_RING, -#endif - IPA_OUT_EVENT_RING, - IPA_IN_EVENT_RING, -#ifdef ENABLE_ADPL - ADPL_EVT_RING, -#endif -#ifdef ENABLE_QDSS - QDSS_EVT_RING, -#endif - - MAX_EVT_RING_IDX -}MHI_EVT_RING_IDX; - -#define MHI_VERSION 0x01000000 -#define MHIREGLEN_VALUE 0x100 /* **** WRONG VALUE *** */ -#define MHI_MSI_INDEX 1 -#define MAX_NUM_MHI_DEVICES 1 -#define NUM_MHI_XFER_RINGS 128 -#define NUM_MHI_EVT_RINGS MAX_EVT_RING_IDX -#define NUM_MHI_HW_EVT_RINGS 4 -#define NUM_MHI_XFER_RING_ELEMENTS 16 -#define NUM_MHI_EVT_RING_ELEMENTS (NUM_MHI_IPA_IN_RING_ELEMENTS*2) //must *2, event ring full will make x55 dump -#define NUM_MHI_IPA_IN_RING_ELEMENTS 512 -#define NUM_MHI_IPA_OUT_RING_ELEMENTS 512 //donot use ul agg, so increase -#define NUM_MHI_DIAG_IN_RING_ELEMENTS 128 -#define NUM_MHI_SW_IP_RING_ELEMENTS 512 - -#ifdef ENABLE_ADPL -#define NUM_MHI_ADPL_RING_ELEMENTS 256 -#endif - -#ifdef ENABLE_QDSS -#define NUM_MHI_QDSS_RING_ELEMENTS 256 -#endif - -/* -* for if set Interrupt moderation time as 1ms, -and transfer more than NUM_MHI_CHAN_RING_ELEMENTS data are sent to the modem in 1ms. -e.g. firehose upgrade. -modem will not trigger irq for these transfer. -*/ -#define NUM_MHI_CHAN_RING_ELEMENTS 32 //8 -#define MHI_EVT_CMD_QUEUE_SIZE 160 -#define MHI_EVT_STATE_QUEUE_SIZE 128 -#define MHI_EVT_XFER_QUEUE_SIZE 1024 - -#define CHAN_INBOUND(_x) ((_x)%2) - -#define CHAN_SBL(_x) (((_x) == MHI_CLIENT_SAHARA_OUT) || \ - ((_x) == MHI_CLIENT_SAHARA_IN) || \ - ((_x) == MHI_CLIENT_BOOT_LOG_IN)) - -#define CHAN_EDL(_x) (((_x) == MHI_CLIENT_EDL_OUT) || \ - ((_x) == MHI_CLIENT_EDL_IN)) - -struct mhi_chan; -struct mhi_event; -struct mhi_ctxt; -struct mhi_cmd; -struct image_info; -struct bhi_vec_entry; -struct mhi_timesync; -struct mhi_buf_info; - -/** - * enum MHI_CB - MHI callback - * @MHI_CB_IDLE: MHI entered idle state - * @MHI_CB_PENDING_DATA: New data available for client to process - * @MHI_CB_LPM_ENTER: MHI host entered low power mode - * @MHI_CB_LPM_EXIT: MHI host about to exit low power mode - * @MHI_CB_EE_RDDM: MHI device entered RDDM execution enviornment - * @MHI_CB_EE_MISSION_MODE: MHI device entered Mission Mode exec env - * @MHI_CB_SYS_ERROR: MHI device enter error state (may recover) - * @MHI_CB_FATAL_ERROR: MHI device entered fatal error - */ -enum MHI_CB { - MHI_CB_IDLE, - MHI_CB_PENDING_DATA, - MHI_CB_LPM_ENTER, - MHI_CB_LPM_EXIT, - MHI_CB_EE_RDDM, - MHI_CB_EE_MISSION_MODE, - MHI_CB_SYS_ERROR, - MHI_CB_FATAL_ERROR, -}; - -/** - * enum MHI_DEBUG_LEVL - various debugging level - */ -enum MHI_DEBUG_LEVEL { - MHI_MSG_LVL_VERBOSE, - MHI_MSG_LVL_INFO, - MHI_MSG_LVL_ERROR, - MHI_MSG_LVL_CRITICAL, - MHI_MSG_LVL_MASK_ALL, -}; - -/* -GSI_XFER_FLAG_BEI: Block event interrupt -1: Event generated by this ring element must not assert an interrupt to the host -0: Event generated by this ring element must assert an interrupt to the host - -GSI_XFER_FLAG_EOT: Interrupt on end of transfer -1: If an EOT condition is encountered when processing this ring element, an event is generated by the device with its completion code set to EOT. -0: If an EOT condition is encountered for this ring element, a completion event is not be generated by the device, unless IEOB is 1 - -GSI_XFER_FLAG_EOB: Interrupt on end of block -1: Device notifies host after processing this ring element by sending a completion event -0: Completion event is not required after processing this ring element - -GSI_XFER_FLAG_CHAIN: Chain bit that identifies the ring elements in a TD -*/ - -/** - * enum MHI_FLAGS - Transfer flags - * @MHI_EOB: End of buffer for bulk transfer - * @MHI_EOT: End of transfer - * @MHI_CHAIN: Linked transfer - */ -enum MHI_FLAGS { - MHI_EOB, - MHI_EOT, - MHI_CHAIN, -}; - -/** - * enum mhi_device_type - Device types - * @MHI_XFER_TYPE: Handles data transfer - * @MHI_TIMESYNC_TYPE: Use for timesync feature - * @MHI_CONTROLLER_TYPE: Control device - */ -enum mhi_device_type { - MHI_XFER_TYPE, - MHI_TIMESYNC_TYPE, - MHI_CONTROLLER_TYPE, -}; - -/** - * enum mhi_ee - device current execution enviornment - * @MHI_EE_PBL - device in PBL - * @MHI_EE_SBL - device in SBL - * @MHI_EE_AMSS - device in mission mode (firmware fully loaded) - * @MHI_EE_RDDM - device in ram dump collection mode - * @MHI_EE_WFW - device in WLAN firmware mode - * @MHI_EE_PTHRU - device in PBL but configured in pass thru mode - * @MHI_EE_EDL - device in emergency download mode - */ -enum mhi_ee { - MHI_EE_PBL = 0x0, - MHI_EE_SBL = 0x1, - MHI_EE_AMSS = 0x2, - MHI_EE_RDDM = 0x3, - MHI_EE_WFW = 0x4, - MHI_EE_PTHRU = 0x5, - MHI_EE_EDL = 0x6, - MHI_EE_FP = 0x7, /* FlashProg, Flash Programmer Environment */ - MHI_EE_MAX_SUPPORTED = MHI_EE_FP, - MHI_EE_DISABLE_TRANSITION, /* local EE, not related to mhi spec */ - MHI_EE_MAX, -}; - -/** - * enum mhi_dev_state - device current MHI state - */ -enum mhi_dev_state { - MHI_STATE_RESET = 0x0, - MHI_STATE_READY = 0x1, - MHI_STATE_M0 = 0x2, - MHI_STATE_M1 = 0x3, - MHI_STATE_M2 = 0x4, - MHI_STATE_M3 = 0x5, - MHI_STATE_BHI = 0x7, - MHI_STATE_SYS_ERR = 0xFF, - MHI_STATE_MAX, -}; - -extern const char * const mhi_ee_str[MHI_EE_MAX]; -#define TO_MHI_EXEC_STR(ee) (((ee) >= MHI_EE_MAX) ? \ - "INVALID_EE" : mhi_ee_str[ee]) - -/** - * struct image_info - firmware and rddm table table - * @mhi_buf - Contain device firmware and rddm table - * @entries - # of entries in table - */ -struct image_info { - struct mhi_buf *mhi_buf; - struct bhi_vec_entry *bhi_vec; - u32 entries; -}; - -/** - * struct mhi_controller - Master controller structure for external modem - * @dev: Device associated with this controller - * @of_node: DT that has MHI configuration information - * @regs: Points to base of MHI MMIO register space - * @bhi: Points to base of MHI BHI register space - * @bhie: Points to base of MHI BHIe register space - * @wake_db: MHI WAKE doorbell register address - * @dev_id: PCIe device id of the external device - * @domain: PCIe domain the device connected to - * @bus: PCIe bus the device assigned to - * @slot: PCIe slot for the modem - * @iova_start: IOMMU starting address for data - * @iova_stop: IOMMU stop address for data - * @fw_image: Firmware image name for normal booting - * @edl_image: Firmware image name for emergency download mode - * @fbc_download: MHI host needs to do complete image transfer - * @rddm_size: RAM dump size that host should allocate for debugging purpose - * @sbl_size: SBL image size - * @seg_len: BHIe vector size - * @fbc_image: Points to firmware image buffer - * @rddm_image: Points to RAM dump buffer - * @max_chan: Maximum number of channels controller support - * @mhi_chan: Points to channel configuration table - * @lpm_chans: List of channels that require LPM notifications - * @total_ev_rings: Total # of event rings allocated - * @hw_ev_rings: Number of hardware event rings - * @sw_ev_rings: Number of software event rings - * @msi_required: Number of msi required to operate - * @msi_allocated: Number of msi allocated by bus master - * @irq: base irq # to request - * @mhi_event: MHI event ring configurations table - * @mhi_cmd: MHI command ring configurations table - * @mhi_ctxt: MHI device context, shared memory between host and device - * @timeout_ms: Timeout in ms for state transitions - * @pm_state: Power management state - * @ee: MHI device execution environment - * @dev_state: MHI STATE - * @status_cb: CB function to notify various power states to but master - * @link_status: Query link status in case of abnormal value read from device - * @runtime_get: Async runtime resume function - * @runtimet_put: Release votes - * @time_get: Return host time in us - * @lpm_disable: Request controller to disable link level low power modes - * @lpm_enable: Controller may enable link level low power modes again - * @priv_data: Points to bus master's private data - */ -struct mhi_controller { - struct list_head node; - struct mhi_device *mhi_dev; - - /* device node for iommu ops */ - struct device *dev; - struct device_node *of_node; - - /* mmio base */ - phys_addr_t base_addr; - void __iomem *regs; - void __iomem *bhi; - void __iomem *bhie; - void __iomem *wake_db; - - /* device topology */ - u32 vendor; - u32 dev_id; - u32 domain; - u32 bus; - u32 slot; - u32 cntrl_idx; - struct device *cntrl_dev; - - /* addressing window */ - dma_addr_t iova_start; - dma_addr_t iova_stop; - - /* fw images */ - const char *fw_image; - const char *edl_image; - - /* mhi host manages downloading entire fbc images */ - bool fbc_download; - size_t rddm_size; - size_t sbl_size; - size_t seg_len; - u32 session_id; - u32 sequence_id; - struct image_info *fbc_image; - struct image_info *rddm_image; - - /* physical channel config data */ - u32 max_chan; - struct mhi_chan *mhi_chan; - struct list_head lpm_chans; /* these chan require lpm notification */ - - /* physical event config data */ - u32 total_ev_rings; - u32 hw_ev_rings; - u32 sw_ev_rings; - u32 msi_required; - u32 msi_allocated; - u32 msi_irq_base; - int *irq; /* interrupt table */ - struct mhi_event *mhi_event; - - /* cmd rings */ - struct mhi_cmd *mhi_cmd; - - /* mhi context (shared with device) */ - struct mhi_ctxt *mhi_ctxt; - - u32 timeout_ms; - - /* caller should grab pm_mutex for suspend/resume operations */ - struct mutex pm_mutex; - bool pre_init; - rwlock_t pm_lock; - u32 pm_state; - enum mhi_ee ee; - enum mhi_dev_state dev_state; - bool wake_set; - atomic_t dev_wake; - atomic_t alloc_size; - atomic_t pending_pkts; - struct list_head transition_list; - spinlock_t transition_lock; - spinlock_t wlock; - - /* debug counters */ - u32 M0, M2, M3; - - /* worker for different state transitions */ - struct work_struct st_worker; - struct work_struct fw_worker; - struct work_struct syserr_worker; - struct delayed_work ready_worker; - wait_queue_head_t state_event; - - /* shadow functions */ - void (*status_cb)(struct mhi_controller *mhi_cntrl, void *priv, - enum MHI_CB reason); - int (*link_status)(struct mhi_controller *mhi_cntrl, void *priv); - void (*wake_get)(struct mhi_controller *mhi_cntrl, bool override); - void (*wake_put)(struct mhi_controller *mhi_cntrl, bool override); - int (*runtime_get)(struct mhi_controller *mhi_cntrl, void *priv); - void (*runtime_put)(struct mhi_controller *mhi_cntrl, void *priv); - void (*runtime_mark_last_busy)(struct mhi_controller *mhi_cntrl, void *priv); - u64 (*time_get)(struct mhi_controller *mhi_cntrl, void *priv); - int (*lpm_disable)(struct mhi_controller *mhi_cntrl, void *priv); - int (*lpm_enable)(struct mhi_controller *mhi_cntrl, void *priv); - int (*map_single)(struct mhi_controller *mhi_cntrl, - struct mhi_buf_info *buf); - void (*unmap_single)(struct mhi_controller *mhi_cntrl, - struct mhi_buf_info *buf); - - /* channel to control DTR messaging */ - struct mhi_device *dtr_dev; - - /* bounce buffer settings */ - bool bounce_buf; - size_t buffer_len; - - /* supports time sync feature */ - struct mhi_timesync *mhi_tsync; - struct mhi_device *tsync_dev; - - /* kernel log level */ - enum MHI_DEBUG_LEVEL klog_lvl; - int klog_slient; - - /* private log level controller driver to set */ - enum MHI_DEBUG_LEVEL log_lvl; - - /* controller specific data */ - void *priv_data; - void *log_buf; - struct dentry *dentry; - struct dentry *parent; - - struct miscdevice miscdev; - -#ifdef ENABLE_MHI_MON - spinlock_t lock; - - /* Ref */ - int nreaders; /* Under mon_lock AND mbus->lock */ - struct list_head r_list; /* Chain of readers (usually one) */ - struct kref ref; /* Under mon_lock */ - - /* Stats */ - unsigned int cnt_events; - unsigned int cnt_text_lost; -#endif -}; - -#ifdef ENABLE_MHI_MON -struct mhi_tre; -struct mon_reader { - struct list_head r_link; - struct mhi_controller *m_bus; - void *r_data; /* Use container_of instead? */ - - void (*rnf_submit)(void *data, u32 chan, dma_addr_t wp, struct mhi_tre *mhi_tre, void *buf, size_t len); - void (*rnf_receive)(void *data, u32 chan, dma_addr_t wp, struct mhi_tre *mhi_tre, void *buf, size_t len); - void (*rnf_complete)(void *data, u32 chan, dma_addr_t wp, struct mhi_tre *mhi_tre); -}; -#endif - -/** - * struct mhi_device - mhi device structure associated bind to channel - * @dev: Device associated with the channels - * @mtu: Maximum # of bytes controller support - * @ul_chan_id: MHI channel id for UL transfer - * @dl_chan_id: MHI channel id for DL transfer - * @tiocm: Device current terminal settings - * @priv: Driver private data - */ -struct mhi_device { - struct device dev; - u32 vendor; - u32 dev_id; - u32 domain; - u32 bus; - u32 slot; - size_t mtu; - int ul_chan_id; - int dl_chan_id; - int ul_event_id; - int dl_event_id; - u32 tiocm; - const struct mhi_device_id *id; - const char *chan_name; - struct mhi_controller *mhi_cntrl; - struct mhi_chan *ul_chan; - struct mhi_chan *dl_chan; - atomic_t dev_wake; - enum mhi_device_type dev_type; - void *priv_data; - int (*ul_xfer)(struct mhi_device *mhi_dev, struct mhi_chan *mhi_chan, - void *buf, size_t len, enum MHI_FLAGS flags); - int (*dl_xfer)(struct mhi_device *mhi_dev, struct mhi_chan *mhi_chan, - void *buf, size_t size, enum MHI_FLAGS flags); - void (*status_cb)(struct mhi_device *mhi_dev, enum MHI_CB reason); -}; - -/** - * struct mhi_result - Completed buffer information - * @buf_addr: Address of data buffer - * @dir: Channel direction - * @bytes_xfer: # of bytes transferred - * @transaction_status: Status of last trasnferred - */ -struct mhi_result { - void *buf_addr; - enum dma_data_direction dir; - size_t bytes_xferd; - int transaction_status; -}; - -/** - * struct mhi_buf - Describes the buffer - * @page: buffer as a page - * @buf: cpu address for the buffer - * @phys_addr: physical address of the buffer - * @dma_addr: iommu address for the buffer - * @skb: skb of ip packet - * @len: # of bytes - * @name: Buffer label, for offload channel configurations name must be: - * ECA - Event context array data - * CCA - Channel context array data - */ -struct mhi_buf { - struct list_head node; - struct page *page; - void *buf; - phys_addr_t phys_addr; - dma_addr_t dma_addr; - struct sk_buff *skb; - size_t len; - const char *name; /* ECA, CCA */ -}; - -/** - * struct mhi_driver - mhi driver information - * @id_table: NULL terminated channel ID names - * @ul_xfer_cb: UL data transfer callback - * @dl_xfer_cb: DL data transfer callback - * @status_cb: Asynchronous status callback - */ -struct mhi_driver { - const struct mhi_device_id *id_table; - int (*probe)(struct mhi_device *mhi_dev, - const struct mhi_device_id *id); - void (*remove)(struct mhi_device *mhi_dev); - void (*ul_xfer_cb)(struct mhi_device *mhi_dev, struct mhi_result *res); - void (*dl_xfer_cb)(struct mhi_device *mhi_dev, struct mhi_result *res); - void (*status_cb)(struct mhi_device *mhi_dev, enum MHI_CB mhi_cb); - struct device_driver driver; -}; - -#define to_mhi_driver(drv) container_of(drv, struct mhi_driver, driver) -#define to_mhi_device(dev) container_of(dev, struct mhi_device, dev) - -static inline void mhi_device_set_devdata(struct mhi_device *mhi_dev, - void *priv) -{ - mhi_dev->priv_data = priv; -} - -static inline void *mhi_device_get_devdata(struct mhi_device *mhi_dev) -{ - return mhi_dev->priv_data; -} - -/** - * mhi_queue_transfer - Queue a buffer to hardware - * All transfers are asyncronous transfers - * @mhi_dev: Device associated with the channels - * @dir: Data direction - * @buf: Data buffer (skb for hardware channels) - * @len: Size in bytes - * @mflags: Interrupt flags for the device - */ -static inline int mhi_queue_transfer(struct mhi_device *mhi_dev, - enum dma_data_direction dir, - void *buf, - size_t len, - enum MHI_FLAGS mflags) -{ - if (dir == DMA_TO_DEVICE) - return mhi_dev->ul_xfer(mhi_dev, mhi_dev->ul_chan, buf, len, - mflags); - else - return mhi_dev->dl_xfer(mhi_dev, mhi_dev->dl_chan, buf, len, - mflags); -} - -static inline void *mhi_controller_get_devdata(struct mhi_controller *mhi_cntrl) -{ - return mhi_cntrl->priv_data; -} - -static inline void mhi_free_controller(struct mhi_controller *mhi_cntrl) -{ - kfree(mhi_cntrl); -} - -/** - * mhi_driver_register - Register driver with MHI framework - * @mhi_drv: mhi_driver structure - */ -int mhi_driver_register(struct mhi_driver *mhi_drv); - -/** - * mhi_driver_unregister - Unregister a driver for mhi_devices - * @mhi_drv: mhi_driver structure - */ -void mhi_driver_unregister(struct mhi_driver *mhi_drv); - -/** - * mhi_device_configure - configure ECA or CCA context - * For offload channels that client manage, call this - * function to configure channel context or event context - * array associated with the channel - * @mhi_div: Device associated with the channels - * @dir: Direction of the channel - * @mhi_buf: Configuration data - * @elements: # of configuration elements - */ -int mhi_device_configure(struct mhi_device *mhi_div, - enum dma_data_direction dir, - struct mhi_buf *mhi_buf, - int elements); - -/** - * mhi_device_get - disable all low power modes - * Only disables lpm, does not immediately exit low power mode - * if controller already in a low power mode - * @mhi_dev: Device associated with the channels - */ -void mhi_device_get(struct mhi_device *mhi_dev); - -/** - * mhi_device_get_sync - disable all low power modes - * Synchronously disable all low power, exit low power mode if - * controller already in a low power state - * @mhi_dev: Device associated with the channels - */ -int mhi_device_get_sync(struct mhi_device *mhi_dev); - -/** - * mhi_device_put - re-enable low power modes - * @mhi_dev: Device associated with the channels - */ -void mhi_device_put(struct mhi_device *mhi_dev); - -/** - * mhi_prepare_for_transfer - setup channel for data transfer - * Moves both UL and DL channel from RESET to START state - * @mhi_dev: Device associated with the channels - */ -int mhi_prepare_for_transfer(struct mhi_device *mhi_dev); - -/** - * mhi_unprepare_from_transfer -unprepare the channels - * Moves both UL and DL channels to RESET state - * @mhi_dev: Device associated with the channels - */ -void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev); - -/** - * mhi_get_no_free_descriptors - Get transfer ring length - * Get # of TD available to queue buffers - * @mhi_dev: Device associated with the channels - * @dir: Direction of the channel - */ -int mhi_get_no_free_descriptors(struct mhi_device *mhi_dev, - enum dma_data_direction dir); - -/** - * mhi_poll - poll for any available data to consume - * This is only applicable for DL direction - * @mhi_dev: Device associated with the channels - * @budget: In descriptors to service before returning - */ -int mhi_poll(struct mhi_device *mhi_dev, u32 budget); - -/** - * mhi_ioctl - user space IOCTL support for MHI channels - * Native support for setting TIOCM - * @mhi_dev: Device associated with the channels - * @cmd: IOCTL cmd - * @arg: Optional parameter, iotcl cmd specific - */ -long mhi_ioctl(struct mhi_device *mhi_dev, unsigned int cmd, unsigned long arg); - -/** - * mhi_alloc_controller - Allocate mhi_controller structure - * Allocate controller structure and additional data for controller - * private data. You may get the private data pointer by calling - * mhi_controller_get_devdata - * @size: # of additional bytes to allocate - */ -struct mhi_controller *mhi_alloc_controller(size_t size); - -/** - * of_register_mhi_controller - Register MHI controller - * Registers MHI controller with MHI bus framework. DT must be supported - * @mhi_cntrl: MHI controller to register - */ -int of_register_mhi_controller(struct mhi_controller *mhi_cntrl); - -void mhi_unregister_mhi_controller(struct mhi_controller *mhi_cntrl); - -/** - * mhi_bdf_to_controller - Look up a registered controller - * Search for controller based on device identification - * @domain: RC domain of the device - * @bus: Bus device connected to - * @slot: Slot device assigned to - * @dev_id: Device Identification - */ -struct mhi_controller *mhi_bdf_to_controller(u32 domain, u32 bus, u32 slot, - u32 dev_id); - -/** - * mhi_prepare_for_power_up - Do pre-initialization before power up - * This is optional, call this before power up if controller do not - * want bus framework to automatically free any allocated memory during shutdown - * process. - * @mhi_cntrl: MHI controller - */ -int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl); - -/** - * mhi_async_power_up - Starts MHI power up sequence - * @mhi_cntrl: MHI controller - */ -int mhi_async_power_up(struct mhi_controller *mhi_cntrl); -int mhi_sync_power_up(struct mhi_controller *mhi_cntrl); - -/** - * mhi_power_down - Start MHI power down sequence - * @mhi_cntrl: MHI controller - * @graceful: link is still accessible, do a graceful shutdown process otherwise - * we will shutdown host w/o putting device into RESET state - */ -void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful); - -/** - * mhi_unprepare_after_powre_down - free any allocated memory for power up - * @mhi_cntrl: MHI controller - */ -void mhi_unprepare_after_power_down(struct mhi_controller *mhi_cntrl); - -/** - * mhi_pm_suspend - Move MHI into a suspended state - * Transition to MHI state M3 state from M0||M1||M2 state - * @mhi_cntrl: MHI controller - */ -int mhi_pm_suspend(struct mhi_controller *mhi_cntrl); - -/** - * mhi_pm_resume - Resume MHI from suspended state - * Transition to MHI state M0 state from M3 state - * @mhi_cntrl: MHI controller - */ -int mhi_pm_resume(struct mhi_controller *mhi_cntrl); - -/** - * mhi_download_rddm_img - Download ramdump image from device for - * debugging purpose. - * @mhi_cntrl: MHI controller - * @in_panic: If we trying to capture image while in kernel panic - */ -int mhi_download_rddm_img(struct mhi_controller *mhi_cntrl, bool in_panic); - -/** - * mhi_force_rddm_mode - Force external device into rddm mode - * to collect device ramdump. This is useful if host driver assert - * and we need to see device state as well. - * @mhi_cntrl: MHI controller - */ -int mhi_force_rddm_mode(struct mhi_controller *mhi_cntrl); - -/** - * mhi_get_remote_time_sync - Get external soc time relative to local soc time - * using MMIO method. - * @mhi_dev: Device associated with the channels - * @t_host: Pointer to output local soc time - * @t_dev: Pointer to output remote soc time - */ -int mhi_get_remote_time_sync(struct mhi_device *mhi_dev, - u64 *t_host, - u64 *t_dev); - -/** - * mhi_get_mhi_state - Return MHI state of device - * @mhi_cntrl: MHI controller - */ -enum mhi_dev_state mhi_get_mhi_state(struct mhi_controller *mhi_cntrl); - -/** - * mhi_set_mhi_state - Set device state - * @mhi_cntrl: MHI controller - * @state: state to set - */ -void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl, - enum mhi_dev_state state); - - -/** - * mhi_is_active - helper function to determine if MHI in active state - * @mhi_dev: client device - */ -static inline bool mhi_is_active(struct mhi_device *mhi_dev) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - - return (mhi_cntrl->dev_state >= MHI_STATE_M0 && - mhi_cntrl->dev_state <= MHI_STATE_M3); -} - -/** - * mhi_debug_reg_dump - dump MHI registers for debug purpose - * @mhi_cntrl: MHI controller - */ -void mhi_debug_reg_dump(struct mhi_controller *mhi_cntrl); - -#ifdef CONFIG_MHI_DEBUG - -#define MHI_VERB(fmt, ...) do { \ - if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_VERBOSE) \ - pr_debug("[D][mhi%d][%s] " fmt, mhi_cntrl->cntrl_idx, __func__, ##__VA_ARGS__);\ -} while (0) - -#else - -#define MHI_VERB(fmt, ...) - -#endif - -#define MHI_LOG(fmt, ...) do { \ - if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_INFO) \ - pr_info("[I][mhi%d][%s] " fmt, mhi_cntrl->cntrl_idx, __func__, ##__VA_ARGS__);\ - else if (!mhi_cntrl->klog_slient) \ - printk(KERN_DEBUG "[I][mhi%d][%s] " fmt, mhi_cntrl->cntrl_idx, __func__, ##__VA_ARGS__);\ -} while (0) - -#define MHI_ERR(fmt, ...) do { \ - if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_ERROR) \ - pr_err("[E][mhi%d][%s] " fmt, mhi_cntrl->cntrl_idx, __func__, ##__VA_ARGS__); \ -} while (0) - -#define MHI_CRITICAL(fmt, ...) do { \ - if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_CRITICAL) \ - pr_alert("[C][mhi%d][%s] " fmt, mhi_cntrl->cntrl_idx, __func__, ##__VA_ARGS__); \ -} while (0) - -int mhi_register_mhi_controller(struct mhi_controller *mhi_cntrl); -void mhi_unregister_mhi_controller(struct mhi_controller *mhi_cntrl); - -#ifndef MHI_NAME_SIZE -#define MHI_NAME_SIZE 32 -/** - * * struct mhi_device_id - MHI device identification - * * @chan: MHI channel name - * * @driver_data: driver data; - * */ -struct mhi_device_id { - const char chan[MHI_NAME_SIZE]; - unsigned long driver_data; -}; -#endif - -#endif /* _MHI_H_ */ diff --git a/quectel_MHI/src/core/mhi_boot.c b/quectel_MHI/src/core/mhi_boot.c deleted file mode 100644 index 8f1924f9d..000000000 --- a/quectel_MHI/src/core/mhi_boot.c +++ /dev/null @@ -1,860 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mhi.h" -#include "mhi_internal.h" - -/* Software defines */ -/* BHI Version */ -#define BHI_MAJOR_VERSION 0x1 -#define BHI_MINOR_VERSION 0x1 - -#define MSMHWID_NUMDWORDS 6 /* Number of dwords that make the MSMHWID */ -#define OEMPKHASH_NUMDWORDS 48 /* Number of dwords that make the OEM PK HASH */ - -#define IsPBLExecEnv(ExecEnv) ((ExecEnv == MHI_EE_PBL) || (ExecEnv == MHI_EE_EDL) ) - -typedef u32 ULONG; - -typedef struct _bhi_info_type -{ - ULONG bhi_ver_minor; - ULONG bhi_ver_major; - ULONG bhi_image_address_low; - ULONG bhi_image_address_high; - ULONG bhi_image_size; - ULONG bhi_rsvd1; - ULONG bhi_imgtxdb; - ULONG bhi_rsvd2; - ULONG bhi_msivec; - ULONG bhi_rsvd3; - ULONG bhi_ee; - ULONG bhi_status; - ULONG bhi_errorcode; - ULONG bhi_errdbg1; - ULONG bhi_errdbg2; - ULONG bhi_errdbg3; - ULONG bhi_sernum; - ULONG bhi_sblantirollbackver; - ULONG bhi_numsegs; - ULONG bhi_msmhwid[6]; - ULONG bhi_oempkhash[48]; - ULONG bhi_rsvd5; -}BHI_INFO_TYPE, *PBHI_INFO_TYPE; - -static void PrintBhiInfo(struct mhi_controller *mhi_cntrl, BHI_INFO_TYPE *bhi_info) -{ - ULONG index; - char str[128]; - - MHI_LOG("BHI Device Info...\n"); - MHI_LOG("BHI Version = { Major = 0x%X Minor = 0x%X}\n", bhi_info->bhi_ver_major, bhi_info->bhi_ver_minor); - MHI_LOG("BHI Execution Environment = 0x%X\n", bhi_info->bhi_ee); - MHI_LOG("BHI Status = 0x%X\n", bhi_info->bhi_status); - MHI_LOG("BHI Error code = 0x%X { Dbg1 = 0x%X Dbg2 = 0x%X Dbg3 = 0x%X }\n", bhi_info->bhi_errorcode, bhi_info->bhi_errdbg1, bhi_info->bhi_errdbg2, bhi_info->bhi_errdbg3); - MHI_LOG("BHI Serial Number = 0x%X\n", bhi_info->bhi_sernum); - MHI_LOG("BHI SBL Anti-Rollback Ver = 0x%X\n", bhi_info->bhi_sblantirollbackver); - MHI_LOG("BHI Number of Segments = 0x%X\n", bhi_info->bhi_numsegs); - for (index = 0; index < 6; index++) - { - snprintf(str+3*index, sizeof(str)-3*index, "%02x ", bhi_info->bhi_msmhwid[index]); - } - MHI_LOG("BHI MSM HW-Id = %s\n", str); - - for (index = 0; index < 24; index++) - { - snprintf(str+3*index, sizeof(str)-3*index, "%02x ", bhi_info->bhi_oempkhash[index]); - } - MHI_LOG("BHI OEM PK Hash = %s\n", str); -} - -static u32 bhi_read_reg(struct mhi_controller *mhi_cntrl, u32 offset) -{ - u32 out = 0; - int ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_EXECENV, &out); - - return (ret) ? 0 : out; -} - -static int BhiRead(struct mhi_controller *mhi_cntrl, BHI_INFO_TYPE *bhi_info) -{ - ULONG index; - - memset(bhi_info, 0x00, sizeof(BHI_INFO_TYPE)); - - /* bhi_ver */ - bhi_info->bhi_ver_minor = bhi_read_reg(mhi_cntrl, BHI_BHIVERSION_MINOR); - bhi_info->bhi_ver_major = bhi_read_reg(mhi_cntrl, BHI_BHIVERSION_MINOR); - bhi_info->bhi_image_address_low = bhi_read_reg(mhi_cntrl, BHI_IMGADDR_LOW); - bhi_info->bhi_image_address_high = bhi_read_reg(mhi_cntrl, BHI_IMGADDR_HIGH); - bhi_info->bhi_image_size = bhi_read_reg(mhi_cntrl, BHI_IMGSIZE); - bhi_info->bhi_rsvd1 = bhi_read_reg(mhi_cntrl, BHI_RSVD1); - bhi_info->bhi_imgtxdb = bhi_read_reg(mhi_cntrl, BHI_IMGTXDB); - bhi_info->bhi_rsvd2 = bhi_read_reg(mhi_cntrl, BHI_RSVD2); - bhi_info->bhi_msivec = bhi_read_reg(mhi_cntrl, BHI_INTVEC); - bhi_info->bhi_rsvd3 = bhi_read_reg(mhi_cntrl, BHI_RSVD3); - bhi_info->bhi_ee = bhi_read_reg(mhi_cntrl, BHI_EXECENV); - bhi_info->bhi_status = bhi_read_reg(mhi_cntrl, BHI_STATUS); - bhi_info->bhi_errorcode = bhi_read_reg(mhi_cntrl, BHI_ERRCODE); - bhi_info->bhi_errdbg1 = bhi_read_reg(mhi_cntrl, BHI_ERRDBG1); - bhi_info->bhi_errdbg2 = bhi_read_reg(mhi_cntrl, BHI_ERRDBG2); - bhi_info->bhi_errdbg3 = bhi_read_reg(mhi_cntrl, BHI_ERRDBG3); - bhi_info->bhi_sernum = bhi_read_reg(mhi_cntrl, BHI_SERIALNU); - bhi_info->bhi_sblantirollbackver = bhi_read_reg(mhi_cntrl, BHI_SBLANTIROLLVER); - bhi_info->bhi_numsegs = bhi_read_reg(mhi_cntrl, BHI_NUMSEG); - for (index = 0; index < MSMHWID_NUMDWORDS; index++) - { - bhi_info->bhi_msmhwid[index] = bhi_read_reg(mhi_cntrl, BHI_MSMHWID(index)); - } - for (index = 0; index < OEMPKHASH_NUMDWORDS; index++) - { - bhi_info->bhi_oempkhash[index] = bhi_read_reg(mhi_cntrl, BHI_OEMPKHASH(index)); - } - bhi_info->bhi_rsvd5 = bhi_read_reg(mhi_cntrl, BHI_RSVD5); - PrintBhiInfo(mhi_cntrl, bhi_info); - /* Check the Execution Environment */ - if (!IsPBLExecEnv(bhi_info->bhi_ee)) - { - MHI_LOG("E - EE: 0x%X Expected PBL/EDL\n", bhi_info->bhi_ee); - } - - /* Return the number of bytes read */ - return 0; -} - -/* setup rddm vector table for rddm transfer */ -static void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl, - struct image_info *img_info) -{ - struct mhi_buf *mhi_buf = img_info->mhi_buf; - struct bhi_vec_entry *bhi_vec = img_info->bhi_vec; - int i = 0; - - for (i = 0; i < img_info->entries - 1; i++, mhi_buf++, bhi_vec++) { - MHI_VERB("Setting vector:%pad size:%zu\n", - &mhi_buf->dma_addr, mhi_buf->len); - bhi_vec->dma_addr = mhi_buf->dma_addr; - bhi_vec->size = mhi_buf->len; - } -} - -/* collect rddm during kernel panic */ -static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl) -{ - int ret; - struct mhi_buf *mhi_buf; - u32 sequence_id; - u32 rx_status; - enum mhi_ee ee; - struct image_info *rddm_image = mhi_cntrl->rddm_image; - const u32 delayus = 2000; - u32 retry = (mhi_cntrl->timeout_ms * 1000) / delayus; - const u32 rddm_timeout_us = 200000; - int rddm_retry = rddm_timeout_us / delayus; /* time to enter rddm */ - void __iomem *base = mhi_cntrl->bhie; - - MHI_LOG("Entered with pm_state:%s dev_state:%s ee:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_STATE_STR(mhi_cntrl->dev_state), - TO_MHI_EXEC_STR(mhi_cntrl->ee)); - - /* - * This should only be executing during a kernel panic, we expect all - * other cores to shutdown while we're collecting rddm buffer. After - * returning from this function, we expect device to reset. - * - * Normaly, we would read/write pm_state only after grabbing - * pm_lock, since we're in a panic, skipping it. Also there is no - * gurantee this state change would take effect since - * we're setting it w/o grabbing pmlock, it's best effort - */ - mhi_cntrl->pm_state = MHI_PM_LD_ERR_FATAL_DETECT; - /* update should take the effect immediately */ - smp_wmb(); - - /* setup the RX vector table */ - mhi_rddm_prepare(mhi_cntrl, rddm_image); - mhi_buf = &rddm_image->mhi_buf[rddm_image->entries - 1]; - - MHI_LOG("Starting BHIe programming for RDDM\n"); - - mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_HIGH_OFFS, - upper_32_bits(mhi_buf->dma_addr)); - - mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_LOW_OFFS, - lower_32_bits(mhi_buf->dma_addr)); - - mhi_write_reg(mhi_cntrl, base, BHIE_RXVECSIZE_OFFS, mhi_buf->len); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)) - sequence_id = get_random_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK; -#else - sequence_id = prandom_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK; -#endif - if (unlikely(!sequence_id)) - sequence_id = 1; - - - mhi_write_reg_field(mhi_cntrl, base, BHIE_RXVECDB_OFFS, - BHIE_RXVECDB_SEQNUM_BMSK, BHIE_RXVECDB_SEQNUM_SHFT, - sequence_id); - - MHI_LOG("Trigger device into RDDM mode\n"); - mhi_set_mhi_state(mhi_cntrl, MHI_STATE_SYS_ERR); - - MHI_LOG("Waiting for device to enter RDDM\n"); - while (rddm_retry--) { - ee = mhi_get_exec_env(mhi_cntrl); - if (ee == MHI_EE_RDDM) - break; - - udelay(delayus); - } - - if (rddm_retry <= 0) { - /* This is a hardware reset, will force device to enter rddm */ - MHI_LOG( - "Did not enter RDDM triggering host req. reset to force rddm\n"); - mhi_write_reg(mhi_cntrl, mhi_cntrl->regs, - MHI_SOC_RESET_REQ_OFFSET, MHI_SOC_RESET_REQ); - udelay(delayus); - } - - ee = mhi_get_exec_env(mhi_cntrl); - MHI_LOG("Waiting for image download completion, current EE:%s\n", - TO_MHI_EXEC_STR(ee)); - while (retry--) { - ret = mhi_read_reg_field(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS, - BHIE_RXVECSTATUS_STATUS_BMSK, - BHIE_RXVECSTATUS_STATUS_SHFT, - &rx_status); - if (ret) - return -EIO; - - if (rx_status == BHIE_RXVECSTATUS_STATUS_XFER_COMPL) { - MHI_LOG("RDDM successfully collected\n"); - return 0; - } - - udelay(delayus); - } - - ee = mhi_get_exec_env(mhi_cntrl); - ret = mhi_read_reg(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS, &rx_status); - - MHI_ERR("Did not complete RDDM transfer\n"); - MHI_ERR("Current EE:%s\n", TO_MHI_EXEC_STR(ee)); - MHI_ERR("RXVEC_STATUS:0x%x, ret:%d\n", rx_status, ret); - - return -EIO; -} - -/* download ramdump image from device */ -int mhi_download_rddm_img(struct mhi_controller *mhi_cntrl, bool in_panic) -{ - void __iomem *base = mhi_cntrl->bhie; - rwlock_t *pm_lock = &mhi_cntrl->pm_lock; - struct image_info *rddm_image = mhi_cntrl->rddm_image; - struct mhi_buf *mhi_buf; - int ret; - u32 rx_status; - u32 sequence_id; - - if (!rddm_image) - return -ENOMEM; - - if (in_panic) - return __mhi_download_rddm_in_panic(mhi_cntrl); - - MHI_LOG("Waiting for device to enter RDDM state from EE:%s\n", - TO_MHI_EXEC_STR(mhi_cntrl->ee)); - - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_cntrl->ee == MHI_EE_RDDM || - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - - if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - MHI_ERR("MHI is not in valid state, pm_state:%s ee:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_EXEC_STR(mhi_cntrl->ee)); - return -EIO; - } - - mhi_rddm_prepare(mhi_cntrl, mhi_cntrl->rddm_image); - - /* vector table is the last entry */ - mhi_buf = &rddm_image->mhi_buf[rddm_image->entries - 1]; - - read_lock_bh(pm_lock); - if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) { - read_unlock_bh(pm_lock); - return -EIO; - } - - MHI_LOG("Starting BHIe Programming for RDDM\n"); - - mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_HIGH_OFFS, - upper_32_bits(mhi_buf->dma_addr)); - - mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_LOW_OFFS, - lower_32_bits(mhi_buf->dma_addr)); - - mhi_write_reg(mhi_cntrl, base, BHIE_RXVECSIZE_OFFS, mhi_buf->len); - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)) - sequence_id = get_random_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK; -#else - sequence_id = prandom_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK; -#endif - mhi_write_reg_field(mhi_cntrl, base, BHIE_RXVECDB_OFFS, - BHIE_RXVECDB_SEQNUM_BMSK, BHIE_RXVECDB_SEQNUM_SHFT, - sequence_id); - read_unlock_bh(pm_lock); - - MHI_LOG("Upper:0x%x Lower:0x%x len:0x%zx sequence:%u\n", - upper_32_bits(mhi_buf->dma_addr), - lower_32_bits(mhi_buf->dma_addr), - mhi_buf->len, sequence_id); - MHI_LOG("Waiting for image download completion\n"); - - /* waiting for image download completion */ - wait_event_timeout(mhi_cntrl->state_event, - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) || - mhi_read_reg_field(mhi_cntrl, base, - BHIE_RXVECSTATUS_OFFS, - BHIE_RXVECSTATUS_STATUS_BMSK, - BHIE_RXVECSTATUS_STATUS_SHFT, - &rx_status) || rx_status, - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - - if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) - return -EIO; - - return (rx_status == BHIE_RXVECSTATUS_STATUS_XFER_COMPL) ? 0 : -EIO; -} -EXPORT_SYMBOL(mhi_download_rddm_img); - -static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl, - const struct mhi_buf *mhi_buf) -{ - void __iomem *base = mhi_cntrl->bhie; - rwlock_t *pm_lock = &mhi_cntrl->pm_lock; - u32 tx_status; - - read_lock_bh(pm_lock); - if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) { - read_unlock_bh(pm_lock); - return -EIO; - } - - MHI_LOG("Starting BHIe Programming\n"); - - mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_HIGH_OFFS, - upper_32_bits(mhi_buf->dma_addr)); - - mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_LOW_OFFS, - lower_32_bits(mhi_buf->dma_addr)); - - mhi_write_reg(mhi_cntrl, base, BHIE_TXVECSIZE_OFFS, mhi_buf->len); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)) - mhi_cntrl->sequence_id = get_random_u32() & BHIE_TXVECSTATUS_SEQNUM_BMSK; -#else - mhi_cntrl->sequence_id = prandom_u32() & BHIE_TXVECSTATUS_SEQNUM_BMSK; -#endif - mhi_write_reg_field(mhi_cntrl, base, BHIE_TXVECDB_OFFS, - BHIE_TXVECDB_SEQNUM_BMSK, BHIE_TXVECDB_SEQNUM_SHFT, - mhi_cntrl->sequence_id); - read_unlock_bh(pm_lock); - - MHI_LOG("Upper:0x%x Lower:0x%x len:0x%zx sequence:%u\n", - upper_32_bits(mhi_buf->dma_addr), - lower_32_bits(mhi_buf->dma_addr), - mhi_buf->len, mhi_cntrl->sequence_id); - MHI_LOG("Waiting for image transfer completion\n"); - - /* waiting for image download completion */ - wait_event_timeout(mhi_cntrl->state_event, - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) || - mhi_read_reg_field(mhi_cntrl, base, - BHIE_TXVECSTATUS_OFFS, - BHIE_TXVECSTATUS_STATUS_BMSK, - BHIE_TXVECSTATUS_STATUS_SHFT, - &tx_status) || tx_status, - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - - if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) - return -EIO; - - return (tx_status == BHIE_TXVECSTATUS_STATUS_XFER_COMPL) ? 0 : -EIO; -} - -static int mhi_fw_load_sbl(struct mhi_controller *mhi_cntrl, - dma_addr_t dma_addr, - size_t size) -{ - u32 tx_status, val; - u32 ImgTxDb = 0x1; - int i, ret; - void __iomem *base = mhi_cntrl->bhi; - rwlock_t *pm_lock = &mhi_cntrl->pm_lock; - struct { - char *name; - u32 offset; - } error_reg[] = { - { "ERROR_CODE", BHI_ERRCODE }, - { "ERROR_DBG1", BHI_ERRDBG1 }, - { "ERROR_DBG2", BHI_ERRDBG2 }, - { "ERROR_DBG3", BHI_ERRDBG3 }, - { NULL }, - }; - - MHI_LOG("Starting BHI programming\n"); - - /* program start sbl download via bhi protocol */ - read_lock_bh(pm_lock); - if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) { - read_unlock_bh(pm_lock); - goto invalid_pm_state; - } - - mhi_write_reg(mhi_cntrl, base, BHI_STATUS, 0); - mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_HIGH, - upper_32_bits(dma_addr)); - mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_LOW, - lower_32_bits(dma_addr)); - mhi_write_reg(mhi_cntrl, base, BHI_IMGSIZE, size); - mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICFG, MHICFG_NER_MASK, MHICFG_NER_SHIFT, NUM_MHI_EVT_RINGS); - mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICFG, MHICFG_NHWER_MASK, MHICFG_NHWER_SHIFT, NUM_MHI_HW_EVT_RINGS); - mhi_write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, mhi_cntrl->msi_irq_base); - mhi_write_reg(mhi_cntrl, base, BHI_IMGTXDB, ImgTxDb); - read_unlock_bh(pm_lock); - - MHI_LOG("Waiting for image transfer completion\n"); - - /* waiting for image download completion */ - ret = wait_event_timeout(mhi_cntrl->state_event, - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) || - mhi_read_reg_field(mhi_cntrl, base, BHI_STATUS, - BHI_STATUS_MASK, BHI_STATUS_SHIFT, - &tx_status) || tx_status, - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) - goto invalid_pm_state; - - if (tx_status == BHI_STATUS_ERROR) { - MHI_ERR("Image transfer failed\n"); - read_lock_bh(pm_lock); - if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) { - for (i = 0; error_reg[i].name; i++) { - ret = mhi_read_reg(mhi_cntrl, base, - error_reg[i].offset, &val); - if (ret) - break; - MHI_ERR("reg:%s value:0x%x\n", - error_reg[i].name, val); - } - } - read_unlock_bh(pm_lock); - goto invalid_pm_state; - } - - return (tx_status == BHI_STATUS_SUCCESS) ? 0 : -ETIMEDOUT; - -invalid_pm_state: - - return -EIO; -} - -void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl, - struct image_info *image_info) -{ - int i; - struct mhi_buf *mhi_buf = image_info->mhi_buf; - - for (i = 0; i < image_info->entries; i++, mhi_buf++) - mhi_free_coherent(mhi_cntrl, mhi_buf->len, mhi_buf->buf, - mhi_buf->dma_addr); - - kfree(image_info->mhi_buf); - kfree(image_info); -} - -int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl, - struct image_info **image_info, - size_t alloc_size) -{ - size_t seg_size = mhi_cntrl->seg_len; - /* requier additional entry for vec table */ - int segments = DIV_ROUND_UP(alloc_size, seg_size) + 1; - int i; - struct image_info *img_info; - struct mhi_buf *mhi_buf; - - MHI_LOG("Allocating bytes:%zu seg_size:%zu total_seg:%u\n", - alloc_size, seg_size, segments); - - img_info = kzalloc(sizeof(*img_info), GFP_KERNEL); - if (!img_info) - return -ENOMEM; - - /* allocate memory for entries */ - img_info->mhi_buf = kcalloc(segments, sizeof(*img_info->mhi_buf), - GFP_KERNEL); - if (!img_info->mhi_buf) - goto error_alloc_mhi_buf; - - /* allocate and populate vector table */ - mhi_buf = img_info->mhi_buf; - for (i = 0; i < segments; i++, mhi_buf++) { - size_t vec_size = seg_size; - - /* last entry is for vector table */ - if (i == segments - 1) - vec_size = sizeof(struct bhi_vec_entry) * i; - - mhi_buf->len = vec_size; - mhi_buf->buf = mhi_alloc_coherent(mhi_cntrl, vec_size, - &mhi_buf->dma_addr, GFP_KERNEL); - if (!mhi_buf->buf) - goto error_alloc_segment; - - MHI_LOG("Entry:%d Address:0x%llx size:%zu\n", i, - (unsigned long long)mhi_buf->dma_addr, - mhi_buf->len); - } - - img_info->bhi_vec = img_info->mhi_buf[segments - 1].buf; - img_info->entries = segments; - *image_info = img_info; - - MHI_LOG("Successfully allocated bhi vec table\n"); - - return 0; - -error_alloc_segment: - for (--i, --mhi_buf; i >= 0; i--, mhi_buf--) - mhi_free_coherent(mhi_cntrl, mhi_buf->len, mhi_buf->buf, - mhi_buf->dma_addr); - -error_alloc_mhi_buf: - kfree(img_info); - - return -ENOMEM; -} - -static void mhi_firmware_copy(struct mhi_controller *mhi_cntrl, - const struct firmware *firmware, - struct image_info *img_info) -{ - size_t remainder = firmware->size; - size_t to_cpy; - const u8 *buf = firmware->data; - int i = 0; - struct mhi_buf *mhi_buf = img_info->mhi_buf; - struct bhi_vec_entry *bhi_vec = img_info->bhi_vec; - - while (remainder) { - MHI_ASSERT(i >= img_info->entries, "malformed vector table"); - - to_cpy = min(remainder, mhi_buf->len); - memcpy(mhi_buf->buf, buf, to_cpy); - bhi_vec->dma_addr = mhi_buf->dma_addr; - bhi_vec->size = to_cpy; - - MHI_VERB("Setting Vector:0x%llx size: %llu\n", - bhi_vec->dma_addr, bhi_vec->size); - buf += to_cpy; - remainder -= to_cpy; - i++; - bhi_vec++; - mhi_buf++; - } -} - -void mhi_fw_load_worker(struct work_struct *work) -{ - int ret; - struct mhi_controller *mhi_cntrl; - const char *fw_name; - const struct firmware *firmware; - struct image_info *image_info; - void *buf; - dma_addr_t dma_addr; - size_t size; - - mhi_cntrl = container_of(work, struct mhi_controller, fw_worker); - - MHI_LOG("Waiting for device to enter PBL from EE:%s\n", - TO_MHI_EXEC_STR(mhi_cntrl->ee)); - - ret = wait_event_timeout(mhi_cntrl->state_event, - MHI_IN_PBL(mhi_cntrl->ee) || - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - - if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - MHI_ERR("MHI is not in valid state\n"); - return; - } - - MHI_LOG("Device current EE:%s\n", TO_MHI_EXEC_STR(mhi_cntrl->ee)); - - /* if device in pthru, we do not have to load firmware */ - if (mhi_cntrl->ee == MHI_EE_PTHRU) - return; - - fw_name = (mhi_cntrl->ee == MHI_EE_EDL) ? - mhi_cntrl->edl_image : mhi_cntrl->fw_image; - - if (!fw_name || (mhi_cntrl->fbc_download && (!mhi_cntrl->sbl_size || - !mhi_cntrl->seg_len))) { - MHI_ERR("No firmware image defined or !sbl_size || !seg_len\n"); - return; - } - - ret = request_firmware(&firmware, fw_name, mhi_cntrl->dev); - if (ret) { - MHI_ERR("Error loading firmware, ret:%d\n", ret); - return; - } - - size = (mhi_cntrl->fbc_download) ? mhi_cntrl->sbl_size : firmware->size; - - /* the sbl size provided is maximum size, not necessarily image size */ - if (size > firmware->size) - size = firmware->size; - - buf = mhi_alloc_coherent(mhi_cntrl, size, &dma_addr, GFP_KERNEL); - if (!buf) { - MHI_ERR("Could not allocate memory for image\n"); - release_firmware(firmware); - return; - } - - /* load sbl image */ - memcpy(buf, firmware->data, size); - ret = mhi_fw_load_sbl(mhi_cntrl, dma_addr, size); - mhi_free_coherent(mhi_cntrl, size, buf, dma_addr); - - /* error or in edl, we're done */ - if (ret || mhi_cntrl->ee == MHI_EE_EDL) { - release_firmware(firmware); - return; - } - - write_lock_irq(&mhi_cntrl->pm_lock); - mhi_cntrl->dev_state = MHI_STATE_RESET; - write_unlock_irq(&mhi_cntrl->pm_lock); - - /* - * if we're doing fbc, populate vector tables while - * device transitioning into MHI READY state - */ - if (mhi_cntrl->fbc_download) { - ret = mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image, - firmware->size); - if (ret) { - MHI_ERR("Error alloc size of %zu\n", firmware->size); - goto error_alloc_fw_table; - } - - MHI_LOG("Copying firmware image into vector table\n"); - - /* load the firmware into BHIE vec table */ - mhi_firmware_copy(mhi_cntrl, firmware, mhi_cntrl->fbc_image); - } - - /* transitioning into MHI RESET->READY state */ - ret = mhi_ready_state_transition(mhi_cntrl); - - MHI_LOG("To Reset->Ready PM_STATE:%s MHI_STATE:%s EE:%s, ret:%d\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_STATE_STR(mhi_cntrl->dev_state), - TO_MHI_EXEC_STR(mhi_cntrl->ee), ret); - - if (!mhi_cntrl->fbc_download) { - release_firmware(firmware); - return; - } - - if (ret) { - MHI_ERR("Did not transition to READY state\n"); - goto error_read; - } - - /* wait for SBL event */ - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_cntrl->ee == MHI_EE_SBL || - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - - if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - MHI_ERR("MHI did not enter BHIE\n"); - goto error_read; - } - - /* start full firmware image download */ - image_info = mhi_cntrl->fbc_image; - ret = mhi_fw_load_amss(mhi_cntrl, - /* last entry is vec table */ - &image_info->mhi_buf[image_info->entries - 1]); - - MHI_LOG("amss fw_load, ret:%d\n", ret); - - release_firmware(firmware); - - return; - -error_read: - mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image); - mhi_cntrl->fbc_image = NULL; - -error_alloc_fw_table: - release_firmware(firmware); -} - -int BhiWrite(struct mhi_controller *mhi_cntrl, void __user *ubuf, size_t size) -{ - int ret; - dma_addr_t dma_addr; - void *dma_buf; - - MHI_LOG("Device current EE:%s, M:%s, PM:%s\n", - TO_MHI_EXEC_STR(mhi_get_exec_env(mhi_cntrl)), - TO_MHI_STATE_STR(mhi_get_mhi_state(mhi_cntrl)), - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - -#if 0 - if (mhi_get_exec_env(mhi_cntrl) == MHI_EE_EDL && mhi_cntrl->ee != MHI_EE_EDL) { - mhi_cntrl->ee = MHI_EE_EDL; - wait_event_timeout(mhi_cntrl->state_event, - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), - msecs_to_jiffies(mhi_cntrl->timeout_ms + 500)); - } -#endif - -#if 0 - if (!MHI_IN_PBL(mhi_cntrl->ee) || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - MHI_ERR("MHI is not in valid BHI state\n"); - return -EINVAL; - } -#endif - - if (mhi_cntrl->ee != MHI_EE_EDL) { - MHI_ERR("MHI is not in EDL state\n"); - return -EINVAL; - } - - dma_buf = mhi_alloc_coherent(mhi_cntrl, size, &dma_addr, GFP_KERNEL); - if (!dma_buf) { - MHI_ERR("Could not allocate memory for image\n"); - return -ENOMEM; - } - - ret = copy_from_user(dma_buf, ubuf, size); - if (ret) { - MHI_ERR("IOCTL_BHI_WRITEIMAGE copy buf error, ret = %d\n", ret); - mhi_free_coherent(mhi_cntrl, size, dma_buf, dma_addr);; - return ret; - } - - ret = mhi_fw_load_sbl(mhi_cntrl, dma_addr, size); - mhi_free_coherent(mhi_cntrl, size, dma_buf, dma_addr); - - if (ret) { - MHI_ERR("ret = %d, ee=%d\n", ret, mhi_cntrl->ee); - goto error_state; - } - - write_lock_irq(&mhi_cntrl->pm_lock); - mhi_cntrl->dev_state = MHI_STATE_RESET; - write_unlock_irq(&mhi_cntrl->pm_lock); - - /* transitioning into MHI RESET->READY state */ - ret = mhi_ready_state_transition(mhi_cntrl); - if (ret) { - MHI_ERR("Did not transition to READY state\n"); - goto error_state; - } - - MHI_LOG("To Reset->Ready PM_STATE:%s MHI_STATE:%s EE:%s, ret:%d\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_STATE_STR(mhi_cntrl->dev_state), - TO_MHI_EXEC_STR(mhi_cntrl->ee), ret); - - /* wait for BHIE event */ - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_cntrl->ee == MHI_EE_FP || - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - MHI_ERR("MHI did not enter Flash Programmer Environment\n"); - goto error_state; - } - - MHI_LOG("MHI enter Flash Programmer Environment\n"); - return 0; - -error_state: - MHI_LOG("Device current EE:%s, M:%s\n", - TO_MHI_EXEC_STR(mhi_get_exec_env(mhi_cntrl)), - TO_MHI_STATE_STR(mhi_get_mhi_state(mhi_cntrl))); - - return ret; -} - -long bhi_get_dev_info(struct mhi_controller *mhi_cntrl, void __user *ubuf) -{ - long ret = -EINVAL; - BHI_INFO_TYPE bhi_info; - - ret = BhiRead(mhi_cntrl, &bhi_info); - if (ret) { - MHI_ERR("IOCTL_BHI_GETDEVINFO BhiRead error, ret = %ld\n", ret); - return ret; - } - - ret = copy_to_user(ubuf, &bhi_info, sizeof(bhi_info)); - if (ret) { - MHI_ERR("IOCTL_BHI_GETDEVINFO copy error, ret = %ld\n", ret); - } - - return ret; -} - -long bhi_write_image(struct mhi_controller *mhi_cntrl, void __user *ubuf) -{ - long ret = -EINVAL; - size_t size; - - ret = copy_from_user(&size, ubuf, sizeof(size)); - if (ret) { - MHI_ERR("IOCTL_BHI_WRITEIMAGE copy size error, ret = %ld\n", ret); - return ret; - } - if (size <= 0) { - MHI_ERR("IOCTL_BHI_WRITEIMAGE copy size error, size\n"); - return -EINVAL; - } - ret = BhiWrite(mhi_cntrl, ubuf+sizeof(size), size); - if (ret) { - MHI_ERR("IOCTL_BHI_WRITEIMAGE BhiWrite error, ret = %ld\n", ret); - } - - return ret; -} diff --git a/quectel_MHI/src/core/mhi_dtr.c b/quectel_MHI/src/core/mhi_dtr.c deleted file mode 100644 index 7ce44b363..000000000 --- a/quectel_MHI/src/core/mhi_dtr.c +++ /dev/null @@ -1,274 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mhi.h" -#include "mhi_internal.h" - -struct __packed dtr_ctrl_msg { - u32 preamble; - u32 msg_id; - u32 dest_id; - u32 size; - u32 msg; -}; - -#define CTRL_MAGIC (0x4C525443) -#define CTRL_MSG_DTR BIT(0) -#define CTRL_MSG_RTS BIT(1) -#define CTRL_MSG_DCD BIT(0) -#define CTRL_MSG_DSR BIT(1) -#define CTRL_MSG_RI BIT(3) -#define CTRL_HOST_STATE (0x10) -#define CTRL_DEVICE_STATE (0x11) -#define CTRL_GET_CHID(dtr) (dtr->dest_id & 0xFF) - -static int mhi_dtr_tiocmset(struct mhi_controller *mhi_cntrl, - struct mhi_device *mhi_dev, - u32 tiocm) -{ - struct dtr_ctrl_msg *dtr_msg = NULL; - struct mhi_chan *dtr_chan = mhi_cntrl->dtr_dev->ul_chan; - spinlock_t *res_lock = &mhi_dev->dev.devres_lock; - u32 cur_tiocm; - int ret = 0; - - cur_tiocm = mhi_dev->tiocm & ~(TIOCM_CD | TIOCM_DSR | TIOCM_RI); - - tiocm &= (TIOCM_DTR | TIOCM_RTS); - - /* state did not changed */ - if (cur_tiocm == tiocm) - return 0; - - mutex_lock(&dtr_chan->mutex); - - dtr_msg = kzalloc(sizeof(*dtr_msg), GFP_KERNEL); - if (!dtr_msg) { - ret = -ENOMEM; - goto tiocm_exit; - } - - dtr_msg->preamble = CTRL_MAGIC; - dtr_msg->msg_id = CTRL_HOST_STATE; - dtr_msg->dest_id = mhi_dev->ul_chan_id; - dtr_msg->size = sizeof(u32); - if (tiocm & TIOCM_DTR) - dtr_msg->msg |= CTRL_MSG_DTR; - if (tiocm & TIOCM_RTS) - dtr_msg->msg |= CTRL_MSG_RTS; - -/* -* 'minicom -D /dev/mhi_DUN' will send RTS:1 when open, and RTS:0 when exit. -* RTS:0 will prevent modem output AT response. -* But 'busybox microcom' do not send any RTS to modem. -* [75094.969783] mhi_uci_q 0306_00.03.00_DUN: mhi_dtr_tiocmset DTR:0 RTS:1 -* [75100.210994] mhi_uci_q 0306_00.03.00_DUN: mhi_dtr_tiocmset DTR:0 RTS:0 -*/ - dev_dbg(&mhi_dev->dev, "%s DTR:%d RTS:%d\n", __func__, - !!(tiocm & TIOCM_DTR), !!(tiocm & TIOCM_RTS)); - - reinit_completion(&dtr_chan->completion); - ret = mhi_queue_transfer(mhi_cntrl->dtr_dev, DMA_TO_DEVICE, dtr_msg, - sizeof(*dtr_msg), MHI_EOT); - if (ret) - goto tiocm_exit; - - ret = wait_for_completion_timeout(&dtr_chan->completion, - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - if (!ret) { - MHI_ERR("Failed to receive transfer callback\n"); - ret = -EIO; - goto tiocm_exit; - } - - ret = 0; - spin_lock_irq(res_lock); - mhi_dev->tiocm &= ~(TIOCM_DTR | TIOCM_RTS); - mhi_dev->tiocm |= tiocm; - spin_unlock_irq(res_lock); - -tiocm_exit: - kfree(dtr_msg); - mutex_unlock(&dtr_chan->mutex); - - return ret; -} - -long mhi_ioctl(struct mhi_device *mhi_dev, unsigned int cmd, unsigned long arg) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - int ret; - - /* ioctl not supported by this controller */ - if (!mhi_cntrl->dtr_dev) - return -EIO; - - switch (cmd) { - case TIOCMGET: - return mhi_dev->tiocm; - case TIOCMSET: - { - u32 tiocm; - - ret = get_user(tiocm, (u32 *)arg); - if (ret) - return ret; - - return mhi_dtr_tiocmset(mhi_cntrl, mhi_dev, tiocm); - } - default: - break; - } - - return -EINVAL; -} -EXPORT_SYMBOL(mhi_ioctl); - -static int mhi_dtr_queue_inbound(struct mhi_controller *mhi_cntrl) -{ - struct mhi_device *mhi_dev = mhi_cntrl->dtr_dev; - int nr_trbs = mhi_get_no_free_descriptors(mhi_dev, DMA_FROM_DEVICE); - size_t mtu = mhi_dev->mtu; - void *buf; - int ret = -EIO, i; - - for (i = 0; i < nr_trbs; i++) { - buf = kmalloc(mtu, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, buf, mtu, - MHI_EOT); - if (ret) { - kfree(buf); - return ret; - } - } - - return ret; -} - -static void mhi_dtr_dl_xfer_cb(struct mhi_device *mhi_dev, - struct mhi_result *mhi_result) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - struct dtr_ctrl_msg *dtr_msg = mhi_result->buf_addr; - u32 chan; - spinlock_t *res_lock; - - if (mhi_result->transaction_status == -ENOTCONN) { - kfree(mhi_result->buf_addr); - return; - } - - if (mhi_result->bytes_xferd != sizeof(*dtr_msg)) { - MHI_ERR("Unexpected length %zu received\n", - mhi_result->bytes_xferd); - return; - } - - MHI_LOG("preamble:0x%x msg_id:%u dest_id:%u msg:0x%x\n", - dtr_msg->preamble, dtr_msg->msg_id, dtr_msg->dest_id, - dtr_msg->msg); - - chan = CTRL_GET_CHID(dtr_msg); - if (chan >= mhi_cntrl->max_chan) - goto auto_queue; - - mhi_dev = mhi_cntrl->mhi_chan[chan].mhi_dev; - if (!mhi_dev) - goto auto_queue; - - res_lock = &mhi_dev->dev.devres_lock; - spin_lock_irq(res_lock); - mhi_dev->tiocm &= ~(TIOCM_CD | TIOCM_DSR | TIOCM_RI); - - if (dtr_msg->msg & CTRL_MSG_DCD) - mhi_dev->tiocm |= TIOCM_CD; - - if (dtr_msg->msg & CTRL_MSG_DSR) - mhi_dev->tiocm |= TIOCM_DSR; - - if (dtr_msg->msg & CTRL_MSG_RI) - mhi_dev->tiocm |= TIOCM_RI; - spin_unlock_irq(res_lock); - -auto_queue: - mhi_queue_transfer(mhi_cntrl->dtr_dev, DMA_FROM_DEVICE, mhi_result->buf_addr, - mhi_cntrl->dtr_dev->mtu, MHI_EOT); -} - -static void mhi_dtr_ul_xfer_cb(struct mhi_device *mhi_dev, - struct mhi_result *mhi_result) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - struct mhi_chan *dtr_chan = mhi_cntrl->dtr_dev->ul_chan; - - MHI_VERB("Received with status:%d\n", mhi_result->transaction_status); - if (!mhi_result->transaction_status) - complete(&dtr_chan->completion); -} - -static void mhi_dtr_remove(struct mhi_device *mhi_dev) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - - mhi_cntrl->dtr_dev = NULL; -} - -static int mhi_dtr_probe(struct mhi_device *mhi_dev, - const struct mhi_device_id *id) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - int ret; - - MHI_LOG("Enter for DTR control channel\n"); - - mhi_dev->mtu = min_t(size_t, id->driver_data, mhi_dev->mtu); - ret = mhi_prepare_for_transfer(mhi_dev); - if (!ret) - mhi_cntrl->dtr_dev = mhi_dev; - - if (!ret) - ret = mhi_dtr_queue_inbound(mhi_cntrl); - - MHI_LOG("Exit with ret:%d\n", ret); - - return ret; -} - -static const struct mhi_device_id mhi_dtr_table[] = { - { .chan = "IP_CTRL", .driver_data = sizeof(struct dtr_ctrl_msg) }, - {}, -}; - -static struct mhi_driver mhi_dtr_driver = { - .id_table = mhi_dtr_table, - .remove = mhi_dtr_remove, - .probe = mhi_dtr_probe, - .ul_xfer_cb = mhi_dtr_ul_xfer_cb, - .dl_xfer_cb = mhi_dtr_dl_xfer_cb, - .driver = { - .name = "MHI_DTR", - .owner = THIS_MODULE, - } -}; - -int __init mhi_dtr_init(void) -{ - return mhi_driver_register(&mhi_dtr_driver); -} -void mhi_dtr_exit(void) { - mhi_driver_unregister(&mhi_dtr_driver); -} diff --git a/quectel_MHI/src/core/mhi_init.c b/quectel_MHI/src/core/mhi_init.c deleted file mode 100644 index 6d8eec502..000000000 --- a/quectel_MHI/src/core/mhi_init.c +++ /dev/null @@ -1,2776 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 4,11,0 )) -#include -#else -#include -#endif -#include "mhi.h" -#include "mhi_internal.h" - -struct mhi_controller_map { - u32 dev_id; - u32 domain; - u32 bus; - u32 slot; - struct mhi_controller *mhi_cntrl; -}; - -#define MAX_MHI_CONTROLLER 16 -struct mhi_controller_map mhi_controller_minors[MAX_MHI_CONTROLLER]; - -#define MHI_CNTRL_DRIVER_NAME "mhi_cntrl_q" -struct mhi_cntrl_drv { - struct list_head head; - struct mutex lock; - struct class *class; - int major; -}; -static struct mhi_cntrl_drv mhi_cntrl_drv; - -const char * const mhi_ee_str[MHI_EE_MAX] = { - [MHI_EE_PBL] = "PBL", - [MHI_EE_SBL] = "SBL", - [MHI_EE_AMSS] = "AMSS", - [MHI_EE_RDDM] = "RDDM", - [MHI_EE_WFW] = "WFW", - [MHI_EE_PTHRU] = "PASS THRU", - [MHI_EE_EDL] = "EDL", - [MHI_EE_FP] = "FlashProg", - [MHI_EE_DISABLE_TRANSITION] = "DISABLE", -}; - -const char * const mhi_state_tran_str[MHI_ST_TRANSITION_MAX] = { - [MHI_ST_TRANSITION_PBL] = "PBL", - [MHI_ST_TRANSITION_READY] = "READY", - [MHI_ST_TRANSITION_SBL] = "SBL", - [MHI_ST_TRANSITION_MISSION_MODE] = "MISSION MODE", - [MHI_ST_TRANSITION_FP] = "FlashProg", -}; - -const char * const mhi_state_str[MHI_STATE_MAX] = { - [MHI_STATE_RESET] = "RESET", - [MHI_STATE_READY] = "READY", - [MHI_STATE_M0] = "M0", - [MHI_STATE_M1] = "M1", - [MHI_STATE_M2] = "M2", - [MHI_STATE_M3] = "M3", - [MHI_STATE_BHI] = "BHI", - [MHI_STATE_SYS_ERR] = "SYS_ERR", -}; - -static const char * const mhi_pm_state_str[] = { - [MHI_PM_BIT_DISABLE] = "DISABLE", - [MHI_PM_BIT_POR] = "POR", - [MHI_PM_BIT_M0] = "M0", - [MHI_PM_BIT_M2] = "M2", - [MHI_PM_BIT_M3_ENTER] = "M?->M3", - [MHI_PM_BIT_M3] = "M3", - [MHI_PM_BIT_M3_EXIT] = "M3->M0", - [MHI_PM_BIT_FW_DL_ERR] = "FW DL Error", - [MHI_PM_BIT_SYS_ERR_DETECT] = "SYS_ERR Detect", - [MHI_PM_BIT_SYS_ERR_PROCESS] = "SYS_ERR Process", - [MHI_PM_BIT_SHUTDOWN_PROCESS] = "SHUTDOWN Process", - [MHI_PM_BIT_LD_ERR_FATAL_DETECT] = "LD or Error Fatal Detect", -}; - -struct mhi_bus mhi_bus; - -const char *to_mhi_pm_state_str(enum MHI_PM_STATE state) -{ - int index = find_last_bit((unsigned long *)&state, 32); - - if (index >= ARRAY_SIZE(mhi_pm_state_str)) - return "Invalid State"; - - return mhi_pm_state_str[index]; -} - -#if 0 -/* MHI protocol require transfer ring to be aligned to ring length */ -static int mhi_alloc_aligned_ring(struct mhi_controller *mhi_cntrl, - struct mhi_ring *ring, - u64 len) -{ - ring->alloc_size = len + (len - 1); - ring->pre_aligned = mhi_alloc_coherent(mhi_cntrl, ring->alloc_size, - &ring->dma_handle, GFP_KERNEL); - if (!ring->pre_aligned) - return -ENOMEM; - - ring->iommu_base = (ring->dma_handle + (len - 1)) & ~(len - 1); - ring->base = ring->pre_aligned + (ring->iommu_base - ring->dma_handle); - return 0; -} -#endif - -static void mhi_ring_aligned_check(struct mhi_controller *mhi_cntrl, u64 rbase, u64 rlen) { - uint64_t ra; - - ra = rbase; - do_div(ra, roundup_pow_of_two(rlen)); - - if (rbase != ra * roundup_pow_of_two(rlen)) { - MHI_ERR("bad params ring base not aligned 0x%llx align 0x%lx\n", rbase, roundup_pow_of_two(rlen)); - } -} - -void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl) -{ - int i; - struct mhi_event *mhi_event = mhi_cntrl->mhi_event; - - - if (mhi_cntrl->msi_allocated == 1) { - free_irq(mhi_cntrl->irq[mhi_cntrl->msi_irq_base], mhi_cntrl); - return; - } - - for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) { - if (mhi_event->offload_ev) - continue; - - free_irq(mhi_cntrl->irq[mhi_event->msi], mhi_event); - } - - free_irq(mhi_cntrl->irq[mhi_cntrl->msi_irq_base], mhi_cntrl); -} - -int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl) -{ - int i; - int ret; - struct mhi_event *mhi_event = mhi_cntrl->mhi_event; - - if (mhi_cntrl->msi_allocated == 1) { - for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) { - mhi_event->msi = 0; - } - - ret = request_threaded_irq(mhi_cntrl->irq[0], NULL, - mhi_one_msi_handlr, IRQF_ONESHOT, "mhi", mhi_cntrl); - if (ret) { - MHI_ERR("Error requesting irq:%d, ret=%d\n", mhi_cntrl->irq[0], ret); - } - return ret; - } - - /* for BHI INTVEC msi */ - ret = request_threaded_irq(mhi_cntrl->irq[mhi_cntrl->msi_irq_base], mhi_intvec_handlr, - mhi_intvec_threaded_handlr, IRQF_ONESHOT, - "mhi", mhi_cntrl); - if (ret) - return ret; - - for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) { - if (mhi_event->offload_ev) - continue; - - ret = request_irq(mhi_cntrl->irq[mhi_event->msi], - mhi_msi_handlr, IRQF_SHARED, "mhi", - mhi_event); - if (ret) { - MHI_ERR("Error requesting irq:%d for ev:%d\n", - mhi_cntrl->irq[mhi_event->msi], i); - goto error_request; - } - } - - return 0; - -error_request: - for (--i, --mhi_event; i >= 0; i--, mhi_event--) { - if (mhi_event->offload_ev) - continue; - - free_irq(mhi_cntrl->irq[mhi_event->msi], mhi_event); - } - free_irq(mhi_cntrl->irq[0], mhi_cntrl); - - return ret; -} - -void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl) -{ - int i; - struct mhi_ctxt *mhi_ctxt = mhi_cntrl->mhi_ctxt; - struct mhi_cmd *mhi_cmd; - struct mhi_event *mhi_event; - struct mhi_ring *ring; - - mhi_cmd = mhi_cntrl->mhi_cmd; - for (i = 0; i < NR_OF_CMD_RINGS; i++, mhi_cmd++) { - ring = &mhi_cmd->ring; -#if 0 - mhi_free_coherent(mhi_cntrl, ring->alloc_size, - ring->pre_aligned, ring->dma_handle); -#endif - ring->base = NULL; - ring->iommu_base = 0; - } - -#if 0 - mhi_free_coherent(mhi_cntrl, - sizeof(*mhi_ctxt->cmd_ctxt) * NR_OF_CMD_RINGS, - mhi_ctxt->cmd_ctxt, mhi_ctxt->cmd_ctxt_addr); -#endif - - mhi_event = mhi_cntrl->mhi_event; - for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) { - if (mhi_event->offload_ev) - continue; - - ring = &mhi_event->ring; -#if 0 - mhi_free_coherent(mhi_cntrl, ring->alloc_size, - ring->pre_aligned, ring->dma_handle); -#endif - ring->base = NULL; - ring->iommu_base = 0; - } - -#if 0 - mhi_free_coherent(mhi_cntrl, sizeof(*mhi_ctxt->er_ctxt) * - mhi_cntrl->total_ev_rings, mhi_ctxt->er_ctxt, - mhi_ctxt->er_ctxt_addr); - - mhi_free_coherent(mhi_cntrl, sizeof(*mhi_ctxt->chan_ctxt) * - mhi_cntrl->max_chan, mhi_ctxt->chan_ctxt, - mhi_ctxt->chan_ctxt_addr); -#endif - - mhi_free_coherent(mhi_cntrl, sizeof(*mhi_ctxt->ctrl_seg), mhi_ctxt->ctrl_seg, mhi_ctxt->ctrl_seg_addr); - kfree(mhi_ctxt); - mhi_cntrl->mhi_ctxt = NULL; -} - -static int mhi_init_debugfs_mhi_states_open(struct inode *inode, - struct file *fp) -{ - return single_open(fp, mhi_debugfs_mhi_states_show, inode->i_private); -} - -static int mhi_init_debugfs_mhi_event_open(struct inode *inode, struct file *fp) -{ - return single_open(fp, mhi_debugfs_mhi_event_show, inode->i_private); -} - -static int mhi_init_debugfs_mhi_chan_open(struct inode *inode, struct file *fp) -{ - return single_open(fp, mhi_debugfs_mhi_chan_show, inode->i_private); -} - -static const struct file_operations debugfs_state_ops = { - .open = mhi_init_debugfs_mhi_states_open, - .release = single_release, - .read = seq_read, -}; - -static const struct file_operations debugfs_ev_ops = { - .open = mhi_init_debugfs_mhi_event_open, - .release = single_release, - .read = seq_read, -}; - -static const struct file_operations debugfs_chan_ops = { - .open = mhi_init_debugfs_mhi_chan_open, - .release = single_release, - .read = seq_read, -}; - -DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_reset_fops, NULL, - mhi_debugfs_trigger_reset, "%llu\n"); - -#ifdef ENABLE_MHI_MON -struct mon_event_text { - struct list_head e_link; - int type; /* submit, complete, etc. */ - unsigned int tstamp; - u32 chan; - dma_addr_t wp; - struct mhi_tre mhi_tre; - u8 data[32]; - size_t len; -}; - -#define EVENT_MAX (16*PAGE_SIZE / sizeof(struct mon_event_text)) -#define PRINTF_DFL 250 -#define SLAB_NAME_SZ 30 - -struct mon_reader_text { - struct kmem_cache *e_slab; - int nevents; - struct list_head e_list; - struct mon_reader r; /* In C, parent class can be placed anywhere */ - - wait_queue_head_t wait; - int printf_size; - char *printf_buf; - int left_size; - int left_pos; - struct mutex printf_lock; - - char slab_name[SLAB_NAME_SZ]; -}; - -struct mon_text_ptr { - int cnt, limit; - char *pbuf; -}; - -static DEFINE_MUTEX(mon_lock); - -static inline unsigned int mon_get_timestamp(void) -{ - struct timespec64 now; - unsigned int stamp; - - ktime_get_ts64(&now); - stamp = now.tv_sec & 0xFFF; /* 2^32 = 4294967296. Limit to 4096s. */ - stamp = stamp * USEC_PER_SEC + now.tv_nsec / NSEC_PER_USEC; - return stamp; -} - -static void mon_text_event(struct mon_reader_text *rp, - u32 chan, dma_addr_t wp, struct mhi_tre *mhi_tre, void *buf, size_t len, - char ev_type) -{ - struct mon_event_text *ep; - - if (rp->nevents >= EVENT_MAX || - (ep = kmem_cache_alloc(rp->e_slab, GFP_ATOMIC)) == NULL) { - rp->r.m_bus->cnt_text_lost++; - return; - } - - ep->type = ev_type; - ep->tstamp = mon_get_timestamp(); - ep->chan = chan; - ep->wp = wp; - ep->mhi_tre = *mhi_tre; - if (len > sizeof(ep->data)) - len = sizeof(ep->data); - memcpy(ep->data, buf, len); - ep->len = len; - rp->nevents++; - list_add_tail(&ep->e_link, &rp->e_list); - wake_up(&rp->wait); -} - -static void mon_text_submit(void *data, u32 chan, dma_addr_t wp, struct mhi_tre *mhi_tre, void *buf, size_t len) -{ - struct mon_reader_text *rp = data; - mon_text_event(rp, chan, wp, mhi_tre, buf, len, 'W'); -} - -static void mon_text_receive(void *data, u32 chan, dma_addr_t wp, struct mhi_tre *mhi_tre, void *buf, size_t len) -{ - struct mon_reader_text *rp = data; - mon_text_event(rp, chan, wp, mhi_tre, buf, len, 'R'); -} - -static void mon_text_complete(void *data, u32 chan, dma_addr_t wp, struct mhi_tre *mhi_tre) -{ - struct mon_reader_text *rp = data; - mon_text_event(rp, chan, wp, mhi_tre, NULL, 0, 'E'); -} - -static void mon_reader_add(struct mhi_controller *mbus, struct mon_reader *r) -{ - unsigned long flags; - - spin_lock_irqsave(&mbus->lock, flags); - mbus->nreaders++; - list_add_tail(&r->r_link, &mbus->r_list); - spin_unlock_irqrestore(&mbus->lock, flags); - - kref_get(&mbus->ref); -} - -static void mon_bus_drop(struct kref *r) -{ - struct mhi_controller *mbus = container_of(r, struct mhi_controller, ref); - kfree(mbus); -} - -static void mon_reader_del(struct mhi_controller *mbus, struct mon_reader *r) -{ - unsigned long flags; - - spin_lock_irqsave(&mbus->lock, flags); - list_del(&r->r_link); - --mbus->nreaders; - spin_unlock_irqrestore(&mbus->lock, flags); - - kref_put(&mbus->ref, mon_bus_drop); -} - -static void mon_text_ctor(void *mem) -{ - /* - * Nothing to initialize. No, really! - * So, we fill it with garbage to emulate a reused object. - */ - memset(mem, 0xe5, sizeof(struct mon_event_text)); -} - -static int mon_text_open(struct inode *inode, struct file *file) -{ - struct mhi_controller *mbus; - struct mon_reader_text *rp; - int rc; - - mutex_lock(&mon_lock); - mbus = inode->i_private; - - rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL); - if (rp == NULL) { - rc = -ENOMEM; - goto err_alloc; - } - INIT_LIST_HEAD(&rp->e_list); - init_waitqueue_head(&rp->wait); - mutex_init(&rp->printf_lock); - - rp->printf_size = PRINTF_DFL; - rp->printf_buf = kmalloc(rp->printf_size, GFP_KERNEL); - if (rp->printf_buf == NULL) { - rc = -ENOMEM; - goto err_alloc_pr; - } - - rp->r.m_bus = mbus; - rp->r.r_data = rp; - rp->r.rnf_submit = mon_text_submit; - rp->r.rnf_receive = mon_text_receive; - rp->r.rnf_complete = mon_text_complete; - - snprintf(rp->slab_name, SLAB_NAME_SZ, "mon_text_%p", rp); - rp->e_slab = kmem_cache_create(rp->slab_name, - sizeof(struct mon_event_text), sizeof(long), 0, - mon_text_ctor); - if (rp->e_slab == NULL) { - rc = -ENOMEM; - goto err_slab; - } - - mon_reader_add(mbus, &rp->r); - - file->private_data = rp; - mutex_unlock(&mon_lock); - return 0; - -// err_busy: -// kmem_cache_destroy(rp->e_slab); -err_slab: - kfree(rp->printf_buf); -err_alloc_pr: - kfree(rp); -err_alloc: - mutex_unlock(&mon_lock); - return rc; -} - -static struct mon_event_text *mon_text_fetch(struct mon_reader_text *rp, - struct mhi_controller *mbus) -{ - struct list_head *p; - unsigned long flags; - - spin_lock_irqsave(&mbus->lock, flags); - if (list_empty(&rp->e_list)) { - spin_unlock_irqrestore(&mbus->lock, flags); - return NULL; - } - p = rp->e_list.next; - list_del(p); - --rp->nevents; - spin_unlock_irqrestore(&mbus->lock, flags); - return list_entry(p, struct mon_event_text, e_link); -} - -static struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp, - struct file *file) -{ - struct mhi_controller *mbus = rp->r.m_bus; - DECLARE_WAITQUEUE(waita, current); - struct mon_event_text *ep; - - add_wait_queue(&rp->wait, &waita); - set_current_state(TASK_INTERRUPTIBLE); - while ((ep = mon_text_fetch(rp, mbus)) == NULL) { - if (file->f_flags & O_NONBLOCK) { - set_current_state(TASK_RUNNING); - remove_wait_queue(&rp->wait, &waita); - return ERR_PTR(-EWOULDBLOCK); - } - /* - * We do not count nwaiters, because ->release is supposed - * to be called when all openers are gone only. - */ - schedule(); - if (signal_pending(current)) { - remove_wait_queue(&rp->wait, &waita); - return ERR_PTR(-EINTR); - } - set_current_state(TASK_INTERRUPTIBLE); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&rp->wait, &waita); - return ep; -} - -static ssize_t mon_text_read_u(struct file *file, char __user *buf, - size_t nbytes, loff_t *ppos) -{ - struct mon_reader_text *rp = file->private_data; - struct mon_event_text *ep; - struct mon_text_ptr ptr; - - if (rp->left_size) { - int cnt = rp->left_size; - - if (cnt > nbytes) - cnt = nbytes; - if (copy_to_user(buf, rp->printf_buf + rp->left_pos, cnt)) - return -EFAULT; - rp->left_pos += cnt; - rp->left_size -= cnt; - return cnt; - } - - if (IS_ERR(ep = mon_text_read_wait(rp, file))) - return PTR_ERR(ep); - mutex_lock(&rp->printf_lock); - ptr.cnt = 0; - ptr.pbuf = rp->printf_buf; - ptr.limit = rp->printf_size; - - ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt, - "%u %c %03d WP:%llx TRE: %llx %08x %08x", - ep->tstamp, ep->type, ep->chan, (long long unsigned int)ep->wp, - ep->mhi_tre.ptr, ep->mhi_tre.dword[0], ep->mhi_tre.dword[1]); - - if (ep->len) { - struct mon_text_ptr *p = &ptr; - size_t i = 0; - - for (i = 0; i < ep->len; i++) { - if (i % 4 == 0) { - p->cnt += snprintf(p->pbuf + p->cnt, - p->limit - p->cnt, - " "); - } - p->cnt += snprintf(p->pbuf + p->cnt, - p->limit - p->cnt, - "%02x", ep->data[i]); - } - - } - - ptr.cnt += snprintf(ptr.pbuf +ptr.cnt, ptr.limit - ptr.cnt, "\n"); - - if (ptr.cnt > nbytes) { - rp->left_pos = nbytes; - rp->left_size = ptr.cnt - nbytes; - ptr.cnt = nbytes; - } - - if (copy_to_user(buf, rp->printf_buf, ptr.cnt)) - ptr.cnt = -EFAULT; - mutex_unlock(&rp->printf_lock); - kmem_cache_free(rp->e_slab, ep); - return ptr.cnt; -} - -static int mon_text_release(struct inode *inode, struct file *file) -{ - struct mon_reader_text *rp = file->private_data; - struct mhi_controller *mbus; - /* unsigned long flags; */ - struct list_head *p; - struct mon_event_text *ep; - - mutex_lock(&mon_lock); - mbus = inode->i_private; - - if (mbus->nreaders <= 0) { - mutex_unlock(&mon_lock); - return 0; - } - mon_reader_del(mbus, &rp->r); - - /* - * In theory, e_list is protected by mbus->lock. However, - * after mon_reader_del has finished, the following is the case: - * - we are not on reader list anymore, so new events won't be added; - * - whole mbus may be dropped if it was orphaned. - * So, we better not touch mbus. - */ - /* spin_lock_irqsave(&mbus->lock, flags); */ - while (!list_empty(&rp->e_list)) { - p = rp->e_list.next; - ep = list_entry(p, struct mon_event_text, e_link); - list_del(p); - --rp->nevents; - kmem_cache_free(rp->e_slab, ep); - } - /* spin_unlock_irqrestore(&mbus->lock, flags); */ - - kmem_cache_destroy(rp->e_slab); - kfree(rp->printf_buf); - kfree(rp); - - mutex_unlock(&mon_lock); - return 0; -} - - -static const struct file_operations mon_fops_text_u = { - .owner = THIS_MODULE, - .open = mon_text_open, - .llseek = no_llseek, - .read = mon_text_read_u, - .release = mon_text_release, -}; -#endif - -void mhi_init_debugfs(struct mhi_controller *mhi_cntrl) -{ - struct dentry *dentry; - char node[64]; - -#ifdef ENABLE_MHI_MON - struct mhi_controller *mbus = mhi_cntrl; - - mbus->nreaders = 0; - kref_init(&mbus->ref); - spin_lock_init(&mbus->lock); - INIT_LIST_HEAD(&mbus->r_list); -#endif - - if (!mhi_cntrl->parent) - snprintf(node, sizeof(node), "mhi_%04x_%02x:%02x.%02x", - mhi_cntrl->dev_id, mhi_cntrl->domain, mhi_cntrl->bus, - mhi_cntrl->slot); - else - snprintf(node, sizeof(node), "%04x_%02x:%02x.%02x", - mhi_cntrl->dev_id, mhi_cntrl->domain, mhi_cntrl->bus, - mhi_cntrl->slot); - - dentry = debugfs_create_dir(node, mhi_cntrl->parent); - if (IS_ERR_OR_NULL(dentry)) - return; - - debugfs_create_file("states", 0444, dentry, mhi_cntrl, - &debugfs_state_ops); - debugfs_create_file("events", 0444, dentry, mhi_cntrl, - &debugfs_ev_ops); - debugfs_create_file("chan", 0444, dentry, mhi_cntrl, - &debugfs_chan_ops); - debugfs_create_file("reset", 0444, dentry, mhi_cntrl, - &debugfs_trigger_reset_fops); -#ifdef ENABLE_MHI_MON - debugfs_create_file("mhimon", 0444, dentry, mhi_cntrl, - &mon_fops_text_u); -#endif - mhi_cntrl->dentry = dentry; -} - -void mhi_deinit_debugfs(struct mhi_controller *mhi_cntrl) -{ - debugfs_remove_recursive(mhi_cntrl->dentry); - mhi_cntrl->dentry = NULL; -} - -int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl) -{ - struct mhi_ctxt *mhi_ctxt; - struct mhi_chan_ctxt *chan_ctxt; - struct mhi_event_ctxt *er_ctxt; - struct mhi_cmd_ctxt *cmd_ctxt; - struct mhi_chan *mhi_chan; - struct mhi_event *mhi_event; - struct mhi_cmd *mhi_cmd; - int ret = -ENOMEM, i; - - atomic_set(&mhi_cntrl->dev_wake, 0); - atomic_set(&mhi_cntrl->alloc_size, 0); - atomic_set(&mhi_cntrl->pending_pkts, 0); - - mhi_ctxt = kzalloc(sizeof(*mhi_ctxt), GFP_KERNEL); - if (!mhi_ctxt) - return -ENOMEM; - - mhi_ctxt->ctrl_seg = mhi_alloc_coherent(mhi_cntrl, sizeof(*mhi_ctxt->ctrl_seg), - &mhi_ctxt->ctrl_seg_addr, GFP_KERNEL); - MHI_LOG("mhi_ctxt->ctrl_seg = %p\n", mhi_ctxt->ctrl_seg); - if (!mhi_ctxt->ctrl_seg) - goto error_alloc_chan_ctxt; - - if ((unsigned long)mhi_ctxt->ctrl_seg & (4096-1)) { - mhi_free_coherent(mhi_cntrl, sizeof(*mhi_ctxt->ctrl_seg), mhi_ctxt->ctrl_seg, mhi_ctxt->ctrl_seg_addr); - goto error_alloc_chan_ctxt; - } - - /* setup channel ctxt */ -#if 1 - mhi_ctxt->chan_ctxt = mhi_ctxt->ctrl_seg->chan_ctxt; - mhi_ctxt->chan_ctxt_addr = mhi_ctxt->ctrl_seg_addr + offsetof(struct mhi_ctrl_seg, chan_ctxt); -#else - mhi_ctxt->chan_ctxt = mhi_alloc_coherent(mhi_cntrl, - sizeof(*mhi_ctxt->chan_ctxt) * mhi_cntrl->max_chan, - &mhi_ctxt->chan_ctxt_addr, GFP_KERNEL); - if (!mhi_ctxt->chan_ctxt) - goto error_alloc_chan_ctxt; -#endif - - mhi_chan = mhi_cntrl->mhi_chan; - chan_ctxt = mhi_ctxt->chan_ctxt; - for (i = 0; i < mhi_cntrl->max_chan; i++, chan_ctxt++, mhi_chan++) { - /* If it's offload channel skip this step */ - if (mhi_chan->offload_ch) - continue; - - chan_ctxt->chstate = MHI_CH_STATE_DISABLED; - chan_ctxt->brstmode = mhi_chan->db_cfg.brstmode; - chan_ctxt->pollcfg = mhi_chan->db_cfg.pollcfg; - chan_ctxt->chtype = mhi_chan->type; - chan_ctxt->erindex = mhi_chan->er_index; - - mhi_chan->ch_state = MHI_CH_STATE_DISABLED; - mhi_chan->tre_ring.db_addr = &chan_ctxt->wp; - } - - /* setup event context */ -#if 1 - mhi_ctxt->er_ctxt = mhi_ctxt->ctrl_seg->er_ctxt; - mhi_ctxt->er_ctxt_addr = mhi_ctxt->ctrl_seg_addr + offsetof(struct mhi_ctrl_seg, er_ctxt); -#else - mhi_ctxt->er_ctxt = mhi_alloc_coherent(mhi_cntrl, - sizeof(*mhi_ctxt->er_ctxt) * mhi_cntrl->total_ev_rings, - &mhi_ctxt->er_ctxt_addr, GFP_KERNEL); - if (!mhi_ctxt->er_ctxt) - goto error_alloc_er_ctxt; -#endif - - er_ctxt = mhi_ctxt->er_ctxt; - mhi_event = mhi_cntrl->mhi_event; - for (i = 0; i < mhi_cntrl->total_ev_rings; i++, er_ctxt++, - mhi_event++) { - struct mhi_ring *ring = &mhi_event->ring; - - /* it's a satellite ev, we do not touch it */ - if (mhi_event->offload_ev) - continue; - - er_ctxt->intmodc = 0; - er_ctxt->intmodt = mhi_event->intmod; - er_ctxt->ertype = MHI_ER_TYPE_VALID; - if (mhi_cntrl->msi_allocated == 1) { - mhi_event->msi = 0; - } - er_ctxt->msivec = mhi_event->msi; - mhi_event->db_cfg.db_mode = true; - - ring->el_size = sizeof(struct mhi_tre); - ring->len = ring->el_size * ring->elements; -#if 1 - ring->alloc_size = ring->len; - ring->pre_aligned = mhi_ctxt->ctrl_seg->event_ring[i]; - ring->dma_handle = mhi_ctxt->ctrl_seg_addr + offsetof(struct mhi_ctrl_seg, event_ring[i]); - ring->iommu_base = ring->dma_handle; - ring->base = ring->pre_aligned + (ring->iommu_base - ring->dma_handle); -#else - ret = mhi_alloc_aligned_ring(mhi_cntrl, ring, ring->len); - if (ret) - goto error_alloc_er; -#endif - - ring->rp = ring->wp = ring->base; - er_ctxt->rbase = ring->iommu_base; - er_ctxt->rp = er_ctxt->wp = er_ctxt->rbase; - er_ctxt->rlen = ring->len; - ring->ctxt_wp = &er_ctxt->wp; - - mhi_ring_aligned_check(mhi_cntrl, er_ctxt->rbase, er_ctxt->rlen); - memset(ring->base, 0xCC, ring->len); - } - - /* setup cmd context */ -#if 1 - mhi_ctxt->cmd_ctxt = mhi_ctxt->ctrl_seg->cmd_ctxt; - mhi_ctxt->cmd_ctxt_addr = mhi_ctxt->ctrl_seg_addr + offsetof(struct mhi_ctrl_seg, cmd_ctxt); -#else - mhi_ctxt->cmd_ctxt = mhi_alloc_coherent(mhi_cntrl, - sizeof(*mhi_ctxt->cmd_ctxt) * NR_OF_CMD_RINGS, - &mhi_ctxt->cmd_ctxt_addr, GFP_KERNEL); - if (!mhi_ctxt->cmd_ctxt) - goto error_alloc_er; -#endif - - mhi_cmd = mhi_cntrl->mhi_cmd; - cmd_ctxt = mhi_ctxt->cmd_ctxt; - for (i = 0; i < NR_OF_CMD_RINGS; i++, mhi_cmd++, cmd_ctxt++) { - struct mhi_ring *ring = &mhi_cmd->ring; - - ring->el_size = sizeof(struct mhi_tre); - ring->elements = CMD_EL_PER_RING; - ring->len = ring->el_size * ring->elements; -#if 1 - ring->alloc_size = ring->len; - ring->pre_aligned = mhi_ctxt->ctrl_seg->cmd_ring[i]; - ring->dma_handle = mhi_ctxt->ctrl_seg_addr + offsetof(struct mhi_ctrl_seg, cmd_ring[i]); - ring->iommu_base = ring->dma_handle; - ring->base = ring->pre_aligned + (ring->iommu_base - ring->dma_handle); -#else - ret = mhi_alloc_aligned_ring(mhi_cntrl, ring, ring->len); - if (ret) - goto error_alloc_cmd; -#endif - - ring->rp = ring->wp = ring->base; - cmd_ctxt->rbase = ring->iommu_base; - cmd_ctxt->rp = cmd_ctxt->wp = cmd_ctxt->rbase; - cmd_ctxt->rlen = ring->len; - ring->ctxt_wp = &cmd_ctxt->wp; - - mhi_ring_aligned_check(mhi_cntrl, cmd_ctxt->rbase, cmd_ctxt->rlen); - } - - mhi_cntrl->mhi_ctxt = mhi_ctxt; - - return 0; - -#if 0 -error_alloc_cmd: - for (--i, --mhi_cmd; i >= 0; i--, mhi_cmd--) { - struct mhi_ring *ring = &mhi_cmd->ring; - - mhi_free_coherent(mhi_cntrl, ring->alloc_size, - ring->pre_aligned, ring->dma_handle); - } - mhi_free_coherent(mhi_cntrl, - sizeof(*mhi_ctxt->cmd_ctxt) * NR_OF_CMD_RINGS, - mhi_ctxt->cmd_ctxt, mhi_ctxt->cmd_ctxt_addr); - i = mhi_cntrl->total_ev_rings; - mhi_event = mhi_cntrl->mhi_event + i; - -error_alloc_er: - for (--i, --mhi_event; i >= 0; i--, mhi_event--) { - struct mhi_ring *ring = &mhi_event->ring; - - if (mhi_event->offload_ev) - continue; - - mhi_free_coherent(mhi_cntrl, ring->alloc_size, - ring->pre_aligned, ring->dma_handle); - } - mhi_free_coherent(mhi_cntrl, sizeof(*mhi_ctxt->er_ctxt) * - mhi_cntrl->total_ev_rings, mhi_ctxt->er_ctxt, - mhi_ctxt->er_ctxt_addr); - -error_alloc_er_ctxt: - mhi_free_coherent(mhi_cntrl, sizeof(*mhi_ctxt->chan_ctxt) * - mhi_cntrl->max_chan, mhi_ctxt->chan_ctxt, - mhi_ctxt->chan_ctxt_addr); -#endif - -error_alloc_chan_ctxt: - kfree(mhi_ctxt); - - return ret; -} - -static int mhi_get_tsync_er_cfg(struct mhi_controller *mhi_cntrl) -{ - int i; - struct mhi_event *mhi_event = mhi_cntrl->mhi_event; - - /* find event ring with timesync support */ - for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) - if (mhi_event->data_type == MHI_ER_TSYNC_ELEMENT_TYPE) - return mhi_event->er_index; - - return -ENOENT; -} - -int mhi_init_timesync(struct mhi_controller *mhi_cntrl) -{ - struct mhi_timesync *mhi_tsync; - u32 time_offset, db_offset; - int ret; - - read_lock_bh(&mhi_cntrl->pm_lock); - - if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) { - ret = -EIO; - goto exit_timesync; - } - - ret = mhi_get_capability_offset(mhi_cntrl, TIMESYNC_CAP_ID, - &time_offset); - if (ret) { - MHI_LOG("No timesync capability found\n"); - goto exit_timesync; - } - - read_unlock_bh(&mhi_cntrl->pm_lock); - - if (!mhi_cntrl->time_get || !mhi_cntrl->lpm_disable || - !mhi_cntrl->lpm_enable) - return -EINVAL; - - /* register method supported */ - mhi_tsync = kzalloc(sizeof(*mhi_tsync), GFP_KERNEL); - if (!mhi_tsync) - return -ENOMEM; - - spin_lock_init(&mhi_tsync->lock); - INIT_LIST_HEAD(&mhi_tsync->head); - init_completion(&mhi_tsync->completion); - - /* save time_offset for obtaining time */ - MHI_LOG("TIME OFFS:0x%x\n", time_offset); - mhi_tsync->time_reg = mhi_cntrl->regs + time_offset - + TIMESYNC_TIME_LOW_OFFSET; - - mhi_cntrl->mhi_tsync = mhi_tsync; - - ret = mhi_create_timesync_sysfs(mhi_cntrl); - if (unlikely(ret)) { - /* kernel method still work */ - MHI_ERR("Failed to create timesync sysfs nodes\n"); - } - - read_lock_bh(&mhi_cntrl->pm_lock); - - if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) { - ret = -EIO; - goto exit_timesync; - } - - /* get DB offset if supported, else return */ - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, - time_offset + TIMESYNC_DB_OFFSET, &db_offset); - if (ret || !db_offset) { - ret = 0; - goto exit_timesync; - } - - MHI_LOG("TIMESYNC_DB OFFS:0x%x\n", db_offset); - mhi_tsync->db = mhi_cntrl->regs + db_offset; - - read_unlock_bh(&mhi_cntrl->pm_lock); - - /* get time-sync event ring configuration */ - ret = mhi_get_tsync_er_cfg(mhi_cntrl); - if (ret < 0) { - MHI_LOG("Could not find timesync event ring\n"); - return ret; - } - - mhi_tsync->er_index = ret; - - ret = mhi_send_cmd(mhi_cntrl, NULL, MHI_CMD_TIMSYNC_CFG); - if (ret) { - MHI_ERR("Failed to send time sync cfg cmd\n"); - return ret; - } - - ret = wait_for_completion_timeout(&mhi_tsync->completion, - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - - if (!ret || mhi_tsync->ccs != MHI_EV_CC_SUCCESS) { - MHI_ERR("Failed to get time cfg cmd completion\n"); - return -EIO; - } - - return 0; - -exit_timesync: - read_unlock_bh(&mhi_cntrl->pm_lock); - - return ret; -} - -int mhi_init_mmio(struct mhi_controller *mhi_cntrl) -{ - u32 val = 0; - int i, ret; - struct mhi_chan *mhi_chan; - struct mhi_event *mhi_event; - void __iomem *base = mhi_cntrl->regs; - struct { - u32 offset; - u32 mask; - u32 shift; - u32 val; - } reg_info[] = { - { - CCABAP_HIGHER, U32_MAX, 0, - upper_32_bits(mhi_cntrl->mhi_ctxt->chan_ctxt_addr), - }, - { - CCABAP_LOWER, U32_MAX, 0, - lower_32_bits(mhi_cntrl->mhi_ctxt->chan_ctxt_addr), - }, - { - ECABAP_HIGHER, U32_MAX, 0, - upper_32_bits(mhi_cntrl->mhi_ctxt->er_ctxt_addr), - }, - { - ECABAP_LOWER, U32_MAX, 0, - lower_32_bits(mhi_cntrl->mhi_ctxt->er_ctxt_addr), - }, - { - CRCBAP_HIGHER, U32_MAX, 0, - upper_32_bits(mhi_cntrl->mhi_ctxt->cmd_ctxt_addr), - }, - { - CRCBAP_LOWER, U32_MAX, 0, - lower_32_bits(mhi_cntrl->mhi_ctxt->cmd_ctxt_addr), - }, -#if 0 //carl.yin 20190527 UDE-WIN-InitMmio - { - MHICFG, MHICFG_NER_MASK, MHICFG_NER_SHIFT, - mhi_cntrl->total_ev_rings, - }, - { - MHICFG, MHICFG_NHWER_MASK, MHICFG_NHWER_SHIFT, - mhi_cntrl->hw_ev_rings, - }, -#endif - { - MHICTRLBASE_HIGHER, U32_MAX, 0, - upper_32_bits(mhi_cntrl->mhi_ctxt->ctrl_seg_addr), - }, - { - MHICTRLBASE_LOWER, U32_MAX, 0, - lower_32_bits(mhi_cntrl->mhi_ctxt->ctrl_seg_addr), - }, - { - MHIDATABASE_HIGHER, U32_MAX, 0, - upper_32_bits(mhi_cntrl->iova_start), - }, - { - MHIDATABASE_LOWER, U32_MAX, 0, - lower_32_bits(mhi_cntrl->iova_start), - }, - { - MHICTRLLIMIT_HIGHER, U32_MAX, 0, - upper_32_bits(mhi_cntrl->mhi_ctxt->ctrl_seg_addr+sizeof(struct mhi_ctrl_seg)), - }, - { - MHICTRLLIMIT_LOWER, U32_MAX, 0, - lower_32_bits(mhi_cntrl->mhi_ctxt->ctrl_seg_addr+sizeof(struct mhi_ctrl_seg)), - }, - { - MHIDATALIMIT_HIGHER, U32_MAX, 0, - upper_32_bits(mhi_cntrl->iova_stop), - }, - { - MHIDATALIMIT_LOWER, U32_MAX, 0, - lower_32_bits(mhi_cntrl->iova_stop), - }, - { 0, 0, 0, 0 } - }; - - MHI_LOG("Initializing MMIO\n"); - - /* set up DB register for all the chan rings */ - ret = mhi_read_reg_field(mhi_cntrl, base, CHDBOFF, CHDBOFF_CHDBOFF_MASK, - CHDBOFF_CHDBOFF_SHIFT, &val); - if (ret) - return -EIO; - - MHI_LOG("CHDBOFF:0x%x\n", val); - - /* setup wake db */ - mhi_cntrl->wake_db = base + val + (8 * MHI_DEV_WAKE_DB); -#if 0 //'EM120RGLAPR02A07M4G_11' will treate as chan 127's interrput, and report complete event over cmd ring, but cmd ring is not set by now - mhi_write_reg(mhi_cntrl, mhi_cntrl->wake_db, 4, 0); - mhi_write_reg(mhi_cntrl, mhi_cntrl->wake_db, 0, 0); - mhi_cntrl->wake_set = false; -#endif - - /* setup channel db addresses */ - mhi_chan = mhi_cntrl->mhi_chan; - for (i = 0; i < mhi_cntrl->max_chan; i++, val += 8, mhi_chan++) - mhi_chan->tre_ring.db_addr = base + val; - - /* setup event ring db addresses */ - ret = mhi_read_reg_field(mhi_cntrl, base, ERDBOFF, ERDBOFF_ERDBOFF_MASK, - ERDBOFF_ERDBOFF_SHIFT, &val); - if (ret) - return -EIO; - - MHI_LOG("ERDBOFF:0x%x\n", val); - - mhi_event = mhi_cntrl->mhi_event; - for (i = 0; i < mhi_cntrl->total_ev_rings; i++, val += 8, mhi_event++) { - if (mhi_event->offload_ev) - continue; - - mhi_event->ring.db_addr = base + val; - } - - /* set up DB register for primary CMD rings */ - mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING].ring.db_addr = base + CRDB_LOWER; - - MHI_LOG("Programming all MMIO values.\n"); - for (i = 0; reg_info[i].offset; i++) - mhi_write_reg_field(mhi_cntrl, base, reg_info[i].offset, - reg_info[i].mask, reg_info[i].shift, - reg_info[i].val); - - return 0; -} - -void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan) -{ - struct mhi_ring *buf_ring; - struct mhi_ring *tre_ring; - struct mhi_chan_ctxt *chan_ctxt; - - buf_ring = &mhi_chan->buf_ring; - tre_ring = &mhi_chan->tre_ring; - chan_ctxt = &mhi_cntrl->mhi_ctxt->chan_ctxt[mhi_chan->chan]; - -#if 0 - mhi_free_coherent(mhi_cntrl, tre_ring->alloc_size, - tre_ring->pre_aligned, tre_ring->dma_handle); -#endif - kfree(buf_ring->base); - - buf_ring->base = tre_ring->base = NULL; - chan_ctxt->rbase = 0; -} - -int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan) -{ - struct mhi_ring *buf_ring; - struct mhi_ring *tre_ring; - struct mhi_chan_ctxt *chan_ctxt; - int ret; - - buf_ring = &mhi_chan->buf_ring; - tre_ring = &mhi_chan->tre_ring; - tre_ring->el_size = sizeof(struct mhi_tre); - tre_ring->len = tre_ring->el_size * tre_ring->elements; - chan_ctxt = &mhi_cntrl->mhi_ctxt->chan_ctxt[mhi_chan->chan]; -#if 1 - tre_ring->alloc_size = tre_ring->len; - if (MHI_CLIENT_IP_HW_0_IN == mhi_chan->chan) { - tre_ring->pre_aligned = &mhi_cntrl->mhi_ctxt->ctrl_seg->hw_in_chan_ring[mhi_chan->ring]; - tre_ring->dma_handle = mhi_cntrl->mhi_ctxt->ctrl_seg_addr + offsetof(struct mhi_ctrl_seg, hw_in_chan_ring[mhi_chan->ring]); - } - else if (MHI_CLIENT_IP_HW_0_OUT == mhi_chan->chan) { - tre_ring->pre_aligned = &mhi_cntrl->mhi_ctxt->ctrl_seg->hw_out_chan_ring[mhi_chan->ring]; - tre_ring->dma_handle = mhi_cntrl->mhi_ctxt->ctrl_seg_addr + offsetof(struct mhi_ctrl_seg, hw_out_chan_ring[mhi_chan->ring]); - } -#ifdef ENABLE_IP_SW0 - else if (MHI_CLIENT_IP_SW_0_IN == mhi_chan->chan) { - tre_ring->pre_aligned = &mhi_cntrl->mhi_ctxt->ctrl_seg->sw_in_chan_ring[mhi_chan->ring]; - tre_ring->dma_handle = mhi_cntrl->mhi_ctxt->ctrl_seg_addr + offsetof(struct mhi_ctrl_seg, sw_in_chan_ring[mhi_chan->ring]); - } - else if (MHI_CLIENT_IP_SW_0_OUT == mhi_chan->chan) { - tre_ring->pre_aligned = &mhi_cntrl->mhi_ctxt->ctrl_seg->sw_out_chan_ring[mhi_chan->ring]; - tre_ring->dma_handle = mhi_cntrl->mhi_ctxt->ctrl_seg_addr + offsetof(struct mhi_ctrl_seg, sw_out_chan_ring[mhi_chan->ring]); - } -#endif - -#ifdef ENABLE_ADPL - else if (MHI_CLIENT_ADPL == mhi_chan->chan) { - tre_ring->pre_aligned = &mhi_cntrl->mhi_ctxt->ctrl_seg->adpl_in_chan_ring[mhi_chan->ring]; - tre_ring->dma_handle = mhi_cntrl->mhi_ctxt->ctrl_seg_addr + offsetof(struct mhi_ctrl_seg, adpl_in_chan_ring[mhi_chan->ring]); - } -#endif - -#ifdef ENABLE_QDSS - else if (MHI_CLIENT_IP_HW_QDSS == mhi_chan->chan) { - tre_ring->pre_aligned = &mhi_cntrl->mhi_ctxt->ctrl_seg->qdss_in_chan_ring[mhi_chan->ring]; - tre_ring->dma_handle = mhi_cntrl->mhi_ctxt->ctrl_seg_addr + offsetof(struct mhi_ctrl_seg, qdss_in_chan_ring[mhi_chan->ring]); - } -#endif - - else if (MHI_CLIENT_DIAG_IN == mhi_chan->chan) { - tre_ring->pre_aligned = &mhi_cntrl->mhi_ctxt->ctrl_seg->diag_in_chan_ring[mhi_chan->ring]; - tre_ring->dma_handle = mhi_cntrl->mhi_ctxt->ctrl_seg_addr + offsetof(struct mhi_ctrl_seg, diag_in_chan_ring[mhi_chan->ring]); - } - else { - tre_ring->pre_aligned = &mhi_cntrl->mhi_ctxt->ctrl_seg->chan_ring[mhi_chan->ring]; - tre_ring->dma_handle = mhi_cntrl->mhi_ctxt->ctrl_seg_addr + offsetof(struct mhi_ctrl_seg, chan_ring[mhi_chan->ring]); - } - tre_ring->iommu_base = tre_ring->dma_handle; - tre_ring->base = tre_ring->pre_aligned + (tre_ring->iommu_base - tre_ring->dma_handle); - ret = 0; -#else - ret = mhi_alloc_aligned_ring(mhi_cntrl, tre_ring, tre_ring->len); -#endif - if (ret) - return -ENOMEM; - - buf_ring->el_size = sizeof(struct mhi_buf_info); - buf_ring->len = buf_ring->el_size * buf_ring->elements; - buf_ring->base = kzalloc(buf_ring->len, GFP_KERNEL); - - if (!buf_ring->base) { -#if 0 - mhi_free_coherent(mhi_cntrl, tre_ring->alloc_size, - tre_ring->pre_aligned, tre_ring->dma_handle); -#endif - return -ENOMEM; - } - - chan_ctxt->chstate = MHI_CH_STATE_ENABLED; - chan_ctxt->rbase = tre_ring->iommu_base; - chan_ctxt->rp = chan_ctxt->wp = chan_ctxt->rbase; - chan_ctxt->rlen = tre_ring->len; - tre_ring->ctxt_wp = &chan_ctxt->wp; - - tre_ring->rp = tre_ring->wp = tre_ring->base; - buf_ring->rp = buf_ring->wp = buf_ring->base; - mhi_chan->db_cfg.db_mode = true; - - mhi_ring_aligned_check(mhi_cntrl, chan_ctxt->rbase, chan_ctxt->rlen); - /* update to all cores */ - smp_wmb(); - - return 0; -} - -int mhi_device_configure(struct mhi_device *mhi_dev, - enum dma_data_direction dir, - struct mhi_buf *cfg_tbl, - int elements) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - struct mhi_chan *mhi_chan; - struct mhi_event_ctxt *er_ctxt; - struct mhi_chan_ctxt *ch_ctxt; - int er_index, chan; - - switch (dir) { - case DMA_TO_DEVICE: - mhi_chan = mhi_dev->ul_chan; - break; - case DMA_BIDIRECTIONAL: - case DMA_FROM_DEVICE: - case DMA_NONE: - mhi_chan = mhi_dev->dl_chan; - break; - default: - return -EINVAL; - } - - er_index = mhi_chan->er_index; - chan = mhi_chan->chan; - - for (; elements > 0; elements--, cfg_tbl++) { - /* update event context array */ - if (!strcmp(cfg_tbl->name, "ECA")) { - er_ctxt = &mhi_cntrl->mhi_ctxt->er_ctxt[er_index]; - if (sizeof(*er_ctxt) != cfg_tbl->len) { - MHI_ERR( - "Invalid ECA size, expected:%zu actual%zu\n", - sizeof(*er_ctxt), cfg_tbl->len); - return -EINVAL; - } - memcpy((void *)er_ctxt, cfg_tbl->buf, sizeof(*er_ctxt)); - continue; - } - - /* update channel context array */ - if (!strcmp(cfg_tbl->name, "CCA")) { - ch_ctxt = &mhi_cntrl->mhi_ctxt->chan_ctxt[chan]; - if (cfg_tbl->len != sizeof(*ch_ctxt)) { - MHI_ERR( - "Invalid CCA size, expected:%zu actual:%zu\n", - sizeof(*ch_ctxt), cfg_tbl->len); - return -EINVAL; - } - memcpy((void *)ch_ctxt, cfg_tbl->buf, sizeof(*ch_ctxt)); - continue; - } - - return -EINVAL; - } - - return 0; -} - -#if 0 -static int of_parse_ev_cfg(struct mhi_controller *mhi_cntrl, - struct device_node *of_node) -{ - int i, ret, num = 0; - struct mhi_event *mhi_event; - struct device_node *child; - - of_node = of_find_node_by_name(of_node, "mhi_events"); - if (!of_node) - return -EINVAL; - - for_each_available_child_of_node(of_node, child) { - if (!strcmp(child->name, "mhi_event")) - num++; - } - - if (!num) - return -EINVAL; - - mhi_cntrl->total_ev_rings = num; - mhi_cntrl->mhi_event = kcalloc(num, sizeof(*mhi_cntrl->mhi_event), - GFP_KERNEL); - if (!mhi_cntrl->mhi_event) - return -ENOMEM; - - /* populate ev ring */ - mhi_event = mhi_cntrl->mhi_event; - i = 0; - for_each_available_child_of_node(of_node, child) { - if (strcmp(child->name, "mhi_event")) - continue; - - mhi_event->er_index = i++; - ret = of_property_read_u32(child, "mhi,num-elements", - (u32 *)&mhi_event->ring.elements); - if (ret) - goto error_ev_cfg; - - ret = of_property_read_u32(child, "mhi,intmod", - &mhi_event->intmod); - if (ret) - goto error_ev_cfg; - - ret = of_property_read_u32(child, "mhi,msi", - &mhi_event->msi); - if (ret) - goto error_ev_cfg; - - ret = of_property_read_u32(child, "mhi,chan", - &mhi_event->chan); - if (!ret) { - if (mhi_event->chan >= mhi_cntrl->max_chan) - goto error_ev_cfg; - /* this event ring has a dedicated channel */ - mhi_event->mhi_chan = - &mhi_cntrl->mhi_chan[mhi_event->chan]; - } - - ret = of_property_read_u32(child, "mhi,priority", - &mhi_event->priority); - if (ret) - goto error_ev_cfg; - - ret = of_property_read_u32(child, "mhi,brstmode", - &mhi_event->db_cfg.brstmode); - if (ret || MHI_INVALID_BRSTMODE(mhi_event->db_cfg.brstmode)) - goto error_ev_cfg; - - mhi_event->db_cfg.process_db = - (mhi_event->db_cfg.brstmode == MHI_BRSTMODE_ENABLE) ? - mhi_db_brstmode : mhi_db_brstmode_disable; - - ret = of_property_read_u32(child, "mhi,data-type", - &mhi_event->data_type); - if (ret) - mhi_event->data_type = MHI_ER_DATA_ELEMENT_TYPE; - - if (mhi_event->data_type > MHI_ER_DATA_TYPE_MAX) - goto error_ev_cfg; - - switch (mhi_event->data_type) { - case MHI_ER_DATA_ELEMENT_TYPE: - mhi_event->process_event = mhi_process_data_event_ring; - break; - case MHI_ER_CTRL_ELEMENT_TYPE: - mhi_event->process_event = mhi_process_ctrl_ev_ring; - break; - case MHI_ER_TSYNC_ELEMENT_TYPE: - mhi_event->process_event = mhi_process_tsync_event_ring; - break; - } - - mhi_event->hw_ring = of_property_read_bool(child, "mhi,hw-ev"); - if (mhi_event->hw_ring) - mhi_cntrl->hw_ev_rings++; - else - mhi_cntrl->sw_ev_rings++; - mhi_event->cl_manage = of_property_read_bool(child, - "mhi,client-manage"); - mhi_event->offload_ev = of_property_read_bool(child, - "mhi,offload"); - mhi_event++; - } - - /* we need msi for each event ring + additional one for BHI */ - mhi_cntrl->msi_required = mhi_cntrl->total_ev_rings + 1; - - return 0; - -error_ev_cfg: - - kfree(mhi_cntrl->mhi_event); - return -EINVAL; -} -static int of_parse_ch_cfg(struct mhi_controller *mhi_cntrl, - struct device_node *of_node) -{ - int ret; - struct device_node *child; - u32 chan; - - ret = of_property_read_u32(of_node, "mhi,max-channels", - &mhi_cntrl->max_chan); - if (ret) - return ret; - - of_node = of_find_node_by_name(of_node, "mhi_channels"); - if (!of_node) - return -EINVAL; - - mhi_cntrl->mhi_chan = kcalloc(mhi_cntrl->max_chan, - sizeof(*mhi_cntrl->mhi_chan), GFP_KERNEL); - if (!mhi_cntrl->mhi_chan) - return -ENOMEM; - - INIT_LIST_HEAD(&mhi_cntrl->lpm_chans); - - /* populate channel configurations */ - for_each_available_child_of_node(of_node, child) { - struct mhi_chan *mhi_chan; - - if (strcmp(child->name, "mhi_chan")) - continue; - - ret = of_property_read_u32(child, "reg", &chan); - if (ret || chan >= mhi_cntrl->max_chan) - goto error_chan_cfg; - - mhi_chan = &mhi_cntrl->mhi_chan[chan]; - - ret = of_property_read_string(child, "label", - &mhi_chan->name); - if (ret) - goto error_chan_cfg; - - mhi_chan->chan = chan; - - ret = of_property_read_u32(child, "mhi,num-elements", - (u32 *)&mhi_chan->tre_ring.elements); - if (!ret && !mhi_chan->tre_ring.elements) - goto error_chan_cfg; - - /* - * For some channels, local ring len should be bigger than - * transfer ring len due to internal logical channels in device. - * So host can queue much more buffers than transfer ring len. - * Example, RSC channels should have a larger local channel - * than transfer ring length. - */ - ret = of_property_read_u32(child, "mhi,local-elements", - (u32 *)&mhi_chan->buf_ring.elements); - if (ret) - mhi_chan->buf_ring.elements = - mhi_chan->tre_ring.elements; - - ret = of_property_read_u32(child, "mhi,event-ring", - &mhi_chan->er_index); - if (ret) - goto error_chan_cfg; - - ret = of_property_read_u32(child, "mhi,chan-dir", - &mhi_chan->dir); - if (ret) - goto error_chan_cfg; - - /* - * For most channels, chtype is identical to channel directions, - * if not defined, assign ch direction to chtype - */ - ret = of_property_read_u32(child, "mhi,chan-type", - &mhi_chan->type); - if (ret) - mhi_chan->type = (enum mhi_ch_type)mhi_chan->dir; - - ret = of_property_read_u32(child, "mhi,ee", &mhi_chan->ee_mask); - if (ret) - goto error_chan_cfg; - - of_property_read_u32(child, "mhi,pollcfg", - &mhi_chan->db_cfg.pollcfg); - - ret = of_property_read_u32(child, "mhi,data-type", - &mhi_chan->xfer_type); - if (ret) - goto error_chan_cfg; - - switch (mhi_chan->xfer_type) { - case MHI_XFER_BUFFER: - mhi_chan->gen_tre = mhi_gen_tre; - mhi_chan->queue_xfer = mhi_queue_buf; - break; - case MHI_XFER_SKB: - mhi_chan->queue_xfer = mhi_queue_skb; - break; - case MHI_XFER_SCLIST: - mhi_chan->gen_tre = mhi_gen_tre; - mhi_chan->queue_xfer = mhi_queue_sclist; - break; - case MHI_XFER_NOP: - mhi_chan->queue_xfer = mhi_queue_nop; - break; - case MHI_XFER_DMA: - case MHI_XFER_RSC_DMA: - mhi_chan->queue_xfer = mhi_queue_dma; - break; - default: - goto error_chan_cfg; - } - - mhi_chan->lpm_notify = of_property_read_bool(child, - "mhi,lpm-notify"); - mhi_chan->offload_ch = of_property_read_bool(child, - "mhi,offload-chan"); - mhi_chan->db_cfg.reset_req = of_property_read_bool(child, - "mhi,db-mode-switch"); - mhi_chan->pre_alloc = of_property_read_bool(child, - "mhi,auto-queue"); - mhi_chan->auto_start = of_property_read_bool(child, - "mhi,auto-start"); - mhi_chan->wake_capable = of_property_read_bool(child, - "mhi,wake-capable"); - - if (mhi_chan->pre_alloc && - (mhi_chan->dir != DMA_FROM_DEVICE || - mhi_chan->xfer_type != MHI_XFER_BUFFER)) - goto error_chan_cfg; - - /* bi-dir and dirctionless channels must be a offload chan */ - if ((mhi_chan->dir == DMA_BIDIRECTIONAL || - mhi_chan->dir == DMA_NONE) && !mhi_chan->offload_ch) - goto error_chan_cfg; - - /* if mhi host allocate the buffers then client cannot queue */ - if (mhi_chan->pre_alloc) - mhi_chan->queue_xfer = mhi_queue_nop; - - if (!mhi_chan->offload_ch) { - ret = of_property_read_u32(child, "mhi,doorbell-mode", - &mhi_chan->db_cfg.brstmode); - if (ret || - MHI_INVALID_BRSTMODE(mhi_chan->db_cfg.brstmode)) - goto error_chan_cfg; - - mhi_chan->db_cfg.process_db = - (mhi_chan->db_cfg.brstmode == - MHI_BRSTMODE_ENABLE) ? - mhi_db_brstmode : mhi_db_brstmode_disable; - } - - mhi_chan->configured = true; - - if (mhi_chan->lpm_notify) - list_add_tail(&mhi_chan->node, &mhi_cntrl->lpm_chans); - } - - return 0; - -error_chan_cfg: - kfree(mhi_cntrl->mhi_chan); - - return -EINVAL; -} -#else -static int of_parse_ev_cfg(struct mhi_controller *mhi_cntrl, - struct device_node *of_node) -{ - int i, num = 0; - struct mhi_event *mhi_event; - - num = NUM_MHI_EVT_RINGS; - mhi_cntrl->total_ev_rings = num; - mhi_cntrl->mhi_event = kcalloc(num, sizeof(*mhi_cntrl->mhi_event), - GFP_KERNEL); - if (!mhi_cntrl->mhi_event) - return -ENOMEM; - - mhi_cntrl->msi_irq_base = 0; - /* populate ev ring */ - mhi_event = mhi_cntrl->mhi_event; - i = 0; - - for (i = 0; i < mhi_cntrl->total_ev_rings; i++) { - mhi_event->er_index = i; - - mhi_event->ring.elements = NUM_MHI_EVT_RING_ELEMENTS; //Event ring length in elements - if (i == PRIMARY_EVENT_RING) - mhi_event->ring.elements = 256; //256 is enough, and 1024 some times make driver fail to open channel (reason is x6x fail to malloc) - - mhi_event->intmod = 1; //Interrupt moderation time in ms - -#ifdef ENABLE_ADPL - if (i == ADPL_EVT_RING) - mhi_event->ring.elements = 256; -#endif - -#ifdef ENABLE_QDSS - if (i == QDSS_EVT_RING) - mhi_event->ring.elements = 512; -#endif - - /* see mhi_netdev_status_cb(), when interrupt come, the napi_poll maybe scheduled, so can reduce interrupts - root@OpenWrt:/# cat /proc/interrupts | grep mhi - root@OpenWrt:/# cat /sys/kernel/debug/mhi_q/mhi_netdev/pcie_mhi_0306_00.01.00_0/rx_int - */ - if (i == IPA_IN_EVENT_RING || i == IPA_OUT_EVENT_RING) - mhi_event->intmod = 5; - -#ifdef ENABLE_IP_SW0 - if (i == SW_0_IN_EVT_RING) - mhi_event->intmod = 5; -#endif - -#ifdef ENABLE_ADPL - if (i == ADPL_EVT_RING) - mhi_event->intmod = 0; -#endif - -#ifdef ENABLE_QDSS - if (i == QDSS_EVT_RING) - mhi_event->intmod = 0; -#endif - - mhi_event->msi = 1 + i + mhi_cntrl->msi_irq_base; //MSI associated with this event ring - - if (i == IPA_OUT_EVENT_RING) - mhi_event->chan = MHI_CLIENT_IP_HW_0_OUT; //Dedicated channel number, if it's a dedicated event ring - else if (i == IPA_IN_EVENT_RING) - mhi_event->chan = MHI_CLIENT_IP_HW_0_IN; //Dedicated channel number, if it's a dedicated event ring -#ifdef ENABLE_IP_SW0 - else if (i == SW_0_OUT_EVT_RING) - mhi_event->chan = MHI_CLIENT_IP_SW_0_OUT; - else if (i == SW_0_IN_EVT_RING) - mhi_event->chan = MHI_CLIENT_IP_SW_0_IN; -#endif - -#ifdef ENABLE_ADPL - else if (i == ADPL_EVT_RING) - mhi_event->chan = MHI_CLIENT_ADPL; -#endif - -#ifdef ENABLE_QDSS - else if (i == QDSS_EVT_RING) - mhi_event->chan = MHI_CLIENT_IP_HW_QDSS; -#endif - else - mhi_event->chan = 0; - - /* this event ring has a dedicated channel */ - mhi_event->mhi_chan = - &mhi_cntrl->mhi_chan[mhi_event->chan]; - - mhi_event->priority = 1; //Event ring priority, set to 1 for now - - if (mhi_event->chan && mhi_event->mhi_chan->db_cfg.brstmode == MHI_BRSTMODE_ENABLE) - mhi_event->db_cfg.brstmode = MHI_BRSTMODE_ENABLE; - else - mhi_event->db_cfg.brstmode = MHI_BRSTMODE_DISABLE; - - mhi_event->db_cfg.process_db = - (mhi_event->db_cfg.brstmode == MHI_BRSTMODE_ENABLE) ? - mhi_db_brstmode : mhi_db_brstmode_disable; - - if (i == IPA_OUT_EVENT_RING || i == IPA_IN_EVENT_RING) - mhi_event->data_type = MHI_ER_DATA_ELEMENT_TYPE; -#ifdef ENABLE_IP_SW0 - else if (i == SW_0_OUT_EVT_RING || i == SW_0_IN_EVT_RING) - mhi_event->data_type = MHI_ER_DATA_ELEMENT_TYPE; -#endif - -#ifdef ENABLE_ADPL - else if (i == ADPL_EVT_RING) - mhi_event->data_type = MHI_ER_DATA_ELEMENT_TYPE; -#endif - -#ifdef ENABLE_QDSS - else if (i == QDSS_EVT_RING) - mhi_event->data_type = MHI_ER_DATA_ELEMENT_TYPE; -#endif - else - mhi_event->data_type = MHI_ER_CTRL_ELEMENT_TYPE; - - switch (mhi_event->data_type) { - case MHI_ER_DATA_ELEMENT_TYPE: - mhi_event->process_event = mhi_process_data_event_ring; - break; - case MHI_ER_CTRL_ELEMENT_TYPE: - mhi_event->process_event = mhi_process_ctrl_ev_ring; - break; - case MHI_ER_TSYNC_ELEMENT_TYPE: - mhi_event->process_event = mhi_process_tsync_event_ring; - break; - } - - if (i == IPA_OUT_EVENT_RING || i == IPA_IN_EVENT_RING -#ifdef ENABLE_ADPL - || i == ADPL_EVT_RING -#endif -#ifdef ENABLE_QDSS - || i == QDSS_EVT_RING -#endif - ) - mhi_event->hw_ring = true; - else - mhi_event->hw_ring = false; - - if (mhi_event->hw_ring) - mhi_cntrl->hw_ev_rings++; - else - mhi_cntrl->sw_ev_rings++; - - mhi_event->cl_manage = false; - if (mhi_event->chan == MHI_CLIENT_IP_HW_0_IN || mhi_event->chan == MHI_CLIENT_IP_SW_0_IN) - mhi_event->cl_manage = true; - mhi_event->offload_ev = false; - mhi_event++; - } - - /* we need msi for each event ring + additional one for BHI */ - mhi_cntrl->msi_required = mhi_cntrl->total_ev_rings + 1 + mhi_cntrl->msi_irq_base; - - return 0; -} - -struct chan_cfg_t { - const char *chan_name; - u32 chan_id; - u32 elements; -}; - -static struct chan_cfg_t chan_cfg[] = { -//"Qualcomm PCIe Loopback" - {"LOOPBACK", MHI_CLIENT_LOOPBACK_OUT, NUM_MHI_CHAN_RING_ELEMENTS}, - {"LOOPBACK", MHI_CLIENT_LOOPBACK_IN, NUM_MHI_CHAN_RING_ELEMENTS}, -//"Qualcomm PCIe Sahara" - {"SAHARA", MHI_CLIENT_SAHARA_OUT, NUM_MHI_CHAN_RING_ELEMENTS}, - {"SAHARA", MHI_CLIENT_SAHARA_IN, NUM_MHI_CHAN_RING_ELEMENTS}, -//"Qualcomm PCIe Diagnostics" - {"DIAG", MHI_CLIENT_DIAG_OUT, NUM_MHI_CHAN_RING_ELEMENTS}, - {"DIAG", MHI_CLIENT_DIAG_IN, NUM_MHI_DIAG_IN_RING_ELEMENTS}, -//"Qualcomm PCIe QDSS Data" -//"Do not use this QDSS. xingduo.du 2023-02-16" -// {"QDSS", MHI_CLIENT_QDSS_OUT, NUM_MHI_CHAN_RING_ELEMENTS}, -// {"QDSS", MHI_CLIENT_QDSS_IN, NUM_MHI_CHAN_RING_ELEMENTS}, -//"Qualcomm PCIe EFS" - {"EFS", MHI_CLIENT_EFS_OUT, NUM_MHI_CHAN_RING_ELEMENTS}, - {"EFS", MHI_CLIENT_EFS_IN, NUM_MHI_CHAN_RING_ELEMENTS}, -//"Qualcomm PCIe MBIM" - {"MBIM", MHI_CLIENT_MBIM_OUT, NUM_MHI_CHAN_RING_ELEMENTS}, - {"MBIM", MHI_CLIENT_MBIM_IN, NUM_MHI_CHAN_RING_ELEMENTS}, -//"Qualcomm PCIe QMI" - {"QMI0", MHI_CLIENT_QMI_OUT, NUM_MHI_CHAN_RING_ELEMENTS}, - {"QMI0", MHI_CLIENT_QMI_IN, NUM_MHI_CHAN_RING_ELEMENTS}, -//"Qualcomm PCIe QMI" - //{"QMI1", MHI_CLIENT_QMI_2_OUT, NUM_MHI_CHAN_RING_ELEMENTS}, - //{"QMI1", MHI_CLIENT_QMI_2_IN, NUM_MHI_CHAN_RING_ELEMENTS}, -//"Qualcomm PCIe IP CTRL" - {"IP_CTRL", MHI_CLIENT_IP_CTRL_1_OUT, NUM_MHI_CHAN_RING_ELEMENTS}, - {"IP_CTRL", MHI_CLIENT_IP_CTRL_1_IN, NUM_MHI_CHAN_RING_ELEMENTS}, -#if 0 //AG15 -//"Qualcomm PCIe IPCR" - {"IPCR", MHI_CLIENT_DIAG_CONS_IF_OUT, NUM_MHI_CHAN_RING_ELEMENTS}, - {"IPCR", MHI_CLIENT_DIAG_CONS_IF_IN, NUM_MHI_CHAN_RING_ELEMENTS}, -#endif -//"Qualcomm PCIe Boot Logging" - //{"BL", MHI_CLIENT_BOOT_LOG_OUT, NUM_MHI_CHAN_RING_ELEMENTS}, - //{"BL", MHI_CLIENT_BOOT_LOG_IN, NUM_MHI_CHAN_RING_ELEMENTS}, -//"Qualcomm PCIe Modem" - {"DUN", MHI_CLIENT_DUN_OUT, NUM_MHI_CHAN_RING_ELEMENTS}, - {"DUN", MHI_CLIENT_DUN_IN, NUM_MHI_CHAN_RING_ELEMENTS}, -//"Qualcomm EDL " - {"EDL", MHI_CLIENT_EDL_OUT, NUM_MHI_CHAN_RING_ELEMENTS}, - {"EDL", MHI_CLIENT_EDL_IN, NUM_MHI_CHAN_RING_ELEMENTS}, -#ifdef ENABLE_IP_SW0 -//"Qualcomm PCIe LOCAL Adapter" - {"IP_SW0", MHI_CLIENT_IP_SW_0_OUT, NUM_MHI_SW_IP_RING_ELEMENTS}, - {"IP_SW0", MHI_CLIENT_IP_SW_0_IN, NUM_MHI_SW_IP_RING_ELEMENTS}, -#endif -//"Qualcomm PCIe WWAN Adapter" - {"IP_HW0", MHI_CLIENT_IP_HW_0_OUT, NUM_MHI_IPA_OUT_RING_ELEMENTS}, - {"IP_HW0", MHI_CLIENT_IP_HW_0_IN, NUM_MHI_IPA_IN_RING_ELEMENTS}, -#ifdef ENABLE_ADPL - {"ADPL", MHI_CLIENT_ADPL, NUM_MHI_ADPL_RING_ELEMENTS}, -#endif - -#ifdef ENABLE_QDSS - {"QDSS", MHI_CLIENT_IP_HW_QDSS, NUM_MHI_QDSS_RING_ELEMENTS}, -#endif -}; - -extern int mhi_netdev_mbin_enabled(void); -extern int mhi_netdev_use_xfer_type_dma(unsigned chan); -static int of_parse_ch_cfg(struct mhi_controller *mhi_cntrl, - struct device_node *of_node) -{ - u32 chan; - u32 i, num; - u32 ring = 0; - - mhi_cntrl->max_chan = MHI_MAX_CHANNELS; - num = sizeof(chan_cfg)/sizeof(chan_cfg[0]); - - mhi_cntrl->mhi_chan = kcalloc(mhi_cntrl->max_chan, - sizeof(*mhi_cntrl->mhi_chan), GFP_KERNEL); - if (!mhi_cntrl->mhi_chan) - return -ENOMEM; - - INIT_LIST_HEAD(&mhi_cntrl->lpm_chans); - - /* populate channel configurations */ - for (i = 0; i < num; i++) { - struct mhi_chan *mhi_chan; - - if (!strncmp( chan_cfg[i].chan_name, "MBIM", 4)) { - if (!mhi_netdev_mbin_enabled()) - continue; - } - else if (!strncmp( chan_cfg[i].chan_name, "QMI", 3)) { - if (mhi_netdev_mbin_enabled()) - continue; - } - - chan = chan_cfg[i].chan_id; - - mhi_chan = &mhi_cntrl->mhi_chan[chan]; - - mhi_chan->name = chan_cfg[i].chan_name; - - mhi_chan->chan = chan; - - mhi_chan->tre_ring.elements = chan_cfg[i].elements; - - /* - * For some channels, local ring len should be bigger than - * transfer ring len due to internal logical channels in device. - * So host can queue much more buffers than transfer ring len. - * Example, RSC channels should have a larger local channel - * than transfer ring length. - */ - mhi_chan->buf_ring.elements = mhi_chan->tre_ring.elements; - - if (chan == MHI_CLIENT_IP_HW_0_OUT || chan == MHI_CLIENT_IP_HW_0_IN || chan == MHI_CLIENT_DIAG_IN - || chan == MHI_CLIENT_IP_SW_0_OUT || chan == MHI_CLIENT_IP_SW_0_IN -#ifdef ENABLE_ADPL - || chan == MHI_CLIENT_ADPL -#endif -#ifdef ENABLE_QDSS - || chan == MHI_CLIENT_IP_HW_QDSS -#endif - ) { - mhi_chan->ring = 0; - } - else { - mhi_chan->ring = ring; - ring += mhi_chan->buf_ring.elements; - } - - if (chan == MHI_CLIENT_IP_HW_0_OUT) - mhi_chan->er_index = IPA_OUT_EVENT_RING; - else if (chan == MHI_CLIENT_IP_HW_0_IN) - mhi_chan->er_index = IPA_IN_EVENT_RING; -#ifdef ENABLE_IP_SW0 - else if (chan == MHI_CLIENT_IP_SW_0_OUT) - mhi_chan->er_index = SW_0_OUT_EVT_RING; - else if (chan == MHI_CLIENT_IP_SW_0_IN) - mhi_chan->er_index = SW_0_IN_EVT_RING; -#endif - -#ifdef ENABLE_ADPL - else if (chan == MHI_CLIENT_ADPL) - mhi_chan->er_index = ADPL_EVT_RING; -#endif -#ifdef ENABLE_QDSS - else if (chan == MHI_CLIENT_IP_HW_QDSS) - mhi_chan->er_index = QDSS_EVT_RING; -#endif - else - mhi_chan->er_index = PRIMARY_EVENT_RING; - - mhi_chan->dir = CHAN_INBOUND(chan) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - -#ifdef ENABLE_ADPL - if (chan == MHI_CLIENT_ADPL) - mhi_chan->dir = DMA_FROM_DEVICE; -#endif - -#ifdef ENABLE_QDSS - if (chan == MHI_CLIENT_IP_HW_QDSS) - mhi_chan->dir = DMA_FROM_DEVICE; -#endif - /* - * For most channels, chtype is identical to channel directions, - * if not defined, assign ch direction to chtype - */ - mhi_chan->type = (enum mhi_ch_type)mhi_chan->dir; - - mhi_chan->ee_mask = BIT(MHI_EE_AMSS); - if (CHAN_SBL(chan)) - mhi_chan->ee_mask = BIT(MHI_EE_SBL); - else if (CHAN_EDL(chan)) - mhi_chan->ee_mask = BIT(MHI_EE_FP); - - mhi_chan->db_cfg.pollcfg = 0; - - if (chan == MHI_CLIENT_IP_HW_0_OUT || chan == MHI_CLIENT_IP_SW_0_OUT) - mhi_chan->xfer_type = MHI_XFER_SKB; - else if (chan == MHI_CLIENT_IP_HW_0_IN) - mhi_chan->xfer_type = mhi_netdev_use_xfer_type_dma(chan) ? MHI_XFER_DMA: MHI_XFER_SKB; - else if (chan == MHI_CLIENT_IP_SW_0_IN) - mhi_chan->xfer_type = MHI_XFER_SKB; //MHI_XFER_DMA; -#ifdef ENABLE_ADPL - else if (chan == MHI_CLIENT_ADPL) - mhi_chan->xfer_type = MHI_XFER_BUFFER; -#endif -#ifdef ENABLE_QDSS - else if (chan == MHI_CLIENT_IP_HW_QDSS) - mhi_chan->xfer_type = MHI_XFER_BUFFER; -#endif - else - mhi_chan->xfer_type = MHI_XFER_BUFFER; - - if (chan_cfg[i].elements == 0) { - mhi_chan->dir = DMA_BIDIRECTIONAL; - mhi_chan->xfer_type = MHI_XFER_NOP; - } - - switch (mhi_chan->xfer_type) { - case MHI_XFER_BUFFER: - mhi_chan->gen_tre = mhi_gen_tre; - mhi_chan->queue_xfer = mhi_queue_buf; - break; - case MHI_XFER_SKB: - mhi_chan->queue_xfer = mhi_queue_skb; - break; - case MHI_XFER_SCLIST: - mhi_chan->gen_tre = mhi_gen_tre; - mhi_chan->queue_xfer = mhi_queue_sclist; - break; - case MHI_XFER_NOP: - mhi_chan->queue_xfer = mhi_queue_nop; - break; - case MHI_XFER_DMA: - case MHI_XFER_RSC_DMA: - mhi_chan->queue_xfer = mhi_queue_dma; - break; - default: - goto error_chan_cfg; - } - - mhi_chan->lpm_notify = false; - mhi_chan->offload_ch = (chan_cfg[i].elements == 0); - mhi_chan->db_cfg.reset_req = false; - mhi_chan->pre_alloc = false; - mhi_chan->auto_start = false; - mhi_chan->wake_capable = false; - - if (mhi_chan->pre_alloc && - (mhi_chan->dir != DMA_FROM_DEVICE || - mhi_chan->xfer_type != MHI_XFER_BUFFER)) - goto error_chan_cfg; - - /* bi-dir and dirctionless channels must be a offload chan */ - if ((mhi_chan->dir == DMA_BIDIRECTIONAL || - mhi_chan->dir == DMA_NONE) && !mhi_chan->offload_ch) - goto error_chan_cfg; - - /* if mhi host allocate the buffers then client cannot queue */ - if (mhi_chan->pre_alloc) - mhi_chan->queue_xfer = mhi_queue_nop; - - if (!mhi_chan->offload_ch) { - mhi_chan->db_cfg.brstmode = MHI_BRSTMODE_DISABLE; - if (chan == MHI_CLIENT_IP_HW_0_OUT || chan == MHI_CLIENT_IP_HW_0_IN) - mhi_chan->db_cfg.brstmode = MHI_BRSTMODE_ENABLE; - -#ifdef ENABLE_ADPL - if (chan == MHI_CLIENT_ADPL) - mhi_chan->db_cfg.brstmode = MHI_BRSTMODE_DISABLE; -#endif -#ifdef ENABLE_QDSS - if (chan == MHI_CLIENT_IP_HW_QDSS) - mhi_chan->db_cfg.brstmode = MHI_BRSTMODE_DISABLE; -#endif - if (MHI_INVALID_BRSTMODE(mhi_chan->db_cfg.brstmode)) - goto error_chan_cfg; - - mhi_chan->db_cfg.process_db = - (mhi_chan->db_cfg.brstmode == - MHI_BRSTMODE_ENABLE) ? - mhi_db_brstmode : mhi_db_brstmode_disable; - } - - mhi_chan->configured = true; - - if (mhi_chan->lpm_notify) - list_add_tail(&mhi_chan->node, &mhi_cntrl->lpm_chans); - } - - return 0; - -error_chan_cfg: - kfree(mhi_cntrl->mhi_chan); - - return -EINVAL; -} -#endif - -static int of_parse_dt(struct mhi_controller *mhi_cntrl, - struct device_node *of_node) -{ - int ret; - - /* parse MHI channel configuration */ - ret = of_parse_ch_cfg(mhi_cntrl, of_node); - if (ret) - return ret; - - /* parse MHI event configuration */ - ret = of_parse_ev_cfg(mhi_cntrl, of_node); - if (ret) - goto error_ev_cfg; -#if defined(QCOM_AP_QCA6490_DMA_IOMMU) - /* for QCS6490 iommu-dma is fastmap - for SG845 iommu-dma is set in driver - for ipq iommu-dma is disabled - */ - const char *str; - ret = of_property_read_string(of_node, "qcom,iommu-dma", &str); - if (ret) - MHI_ERR("mhi qcom,iommu-dma need set"); - -#endif -#if 0 - ret = of_property_read_u32(of_node, "mhi,timeout", - &mhi_cntrl->timeout_ms); - if (ret) - mhi_cntrl->timeout_ms = MHI_TIMEOUT_MS; - - mhi_cntrl->bounce_buf = of_property_read_bool(of_node, "mhi,use-bb"); - ret = of_property_read_u32(of_node, "mhi,buffer-len", - (u32 *)&mhi_cntrl->buffer_len); - if (ret) - mhi_cntrl->buffer_len = MHI_MAX_MTU; -#else - mhi_cntrl->timeout_ms = MHI_TIMEOUT_MS; - mhi_cntrl->bounce_buf = false; - mhi_cntrl->buffer_len = MHI_MAX_MTU; -#endif - - return 0; - -error_ev_cfg: - kfree(mhi_cntrl->mhi_chan); - - return ret; -} - -int of_register_mhi_controller(struct mhi_controller *mhi_cntrl) -{ - int ret; - int i; - struct mhi_event *mhi_event; - struct mhi_chan *mhi_chan; - struct mhi_cmd *mhi_cmd; - struct mhi_device *mhi_dev; - - //if (!mhi_cntrl->of_node) - // return -EINVAL; - - for (i = 0; i < MAX_MHI_CONTROLLER; i++) { - if (mhi_controller_minors[i].dev_id == mhi_cntrl->dev_id - && mhi_controller_minors[i].domain == mhi_cntrl->domain - && mhi_controller_minors[i].bus == mhi_cntrl->bus - && mhi_controller_minors[i].slot == mhi_cntrl->slot) { - mhi_cntrl->cntrl_idx = i; - break; - } - else if (mhi_controller_minors[i].dev_id == 0 - && mhi_controller_minors[i].domain == 0 - && mhi_controller_minors[i].bus == 0 - && mhi_controller_minors[i].slot == 0) { - mhi_controller_minors[i].dev_id = mhi_cntrl->dev_id; - mhi_controller_minors[i].domain = mhi_cntrl->domain; - mhi_controller_minors[i].bus = mhi_cntrl->bus; - mhi_controller_minors[i].slot = mhi_cntrl->slot; - mhi_controller_minors[i].mhi_cntrl = mhi_cntrl; - mhi_cntrl->cntrl_idx = i; - break; - } - } - - if (i == MAX_MHI_CONTROLLER) - return -EINVAL; - - if (!mhi_cntrl->runtime_get || !mhi_cntrl->runtime_put) - return -EINVAL; - - if (!mhi_cntrl->status_cb || !mhi_cntrl->link_status) - return -EINVAL; - - ret = of_parse_dt(mhi_cntrl, mhi_cntrl->of_node); - if (ret) - return -EINVAL; - - mhi_cntrl->mhi_cmd = kcalloc(NR_OF_CMD_RINGS, - sizeof(*mhi_cntrl->mhi_cmd), GFP_KERNEL); - if (!mhi_cntrl->mhi_cmd) { - ret = -ENOMEM; - goto error_alloc_cmd; - } - - INIT_LIST_HEAD(&mhi_cntrl->transition_list); - mutex_init(&mhi_cntrl->pm_mutex); - rwlock_init(&mhi_cntrl->pm_lock); - spin_lock_init(&mhi_cntrl->transition_lock); - spin_lock_init(&mhi_cntrl->wlock); - INIT_WORK(&mhi_cntrl->st_worker, mhi_pm_st_worker); - INIT_WORK(&mhi_cntrl->fw_worker, mhi_fw_load_worker); - INIT_WORK(&mhi_cntrl->syserr_worker, mhi_pm_sys_err_worker); - INIT_DELAYED_WORK(&mhi_cntrl->ready_worker, mhi_pm_ready_worker); - init_waitqueue_head(&mhi_cntrl->state_event); - - mhi_cmd = mhi_cntrl->mhi_cmd; - for (i = 0; i < NR_OF_CMD_RINGS; i++, mhi_cmd++) - spin_lock_init(&mhi_cmd->lock); - - mhi_event = mhi_cntrl->mhi_event; - for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) { - if (mhi_event->offload_ev) - continue; - - mhi_event->mhi_cntrl = mhi_cntrl; - spin_lock_init(&mhi_event->lock); - if (mhi_event->data_type == MHI_ER_CTRL_ELEMENT_TYPE) - tasklet_init(&mhi_event->task, mhi_ctrl_ev_task, - (ulong)mhi_event); - else - tasklet_init(&mhi_event->task, mhi_ev_task, - (ulong)mhi_event); - } - - mhi_chan = mhi_cntrl->mhi_chan; - for (i = 0; i < mhi_cntrl->max_chan; i++, mhi_chan++) { - mutex_init(&mhi_chan->mutex); - init_completion(&mhi_chan->completion); - rwlock_init(&mhi_chan->lock); - } - - if (mhi_cntrl->bounce_buf) { - mhi_cntrl->map_single = mhi_map_single_use_bb; - mhi_cntrl->unmap_single = mhi_unmap_single_use_bb; - } else { - mhi_cntrl->map_single = mhi_map_single_no_bb; - mhi_cntrl->unmap_single = mhi_unmap_single_no_bb; - } - - /* register controller with mhi_bus */ - mhi_dev = mhi_alloc_device(mhi_cntrl); - if (!mhi_dev) { - ret = -ENOMEM; - goto error_alloc_dev; - } - - mhi_dev->dev_type = MHI_CONTROLLER_TYPE; - mhi_dev->mhi_cntrl = mhi_cntrl; - dev_set_name(&mhi_dev->dev, "%04x_%02u.%02u.%02u", mhi_dev->dev_id, - mhi_dev->domain, mhi_dev->bus, mhi_dev->slot); - - ret = device_add(&mhi_dev->dev); - if (ret) - goto error_add_dev; - - if (mhi_cntrl->cntrl_idx) - mhi_cntrl->cntrl_dev = device_create(mhi_cntrl_drv.class, mhi_cntrl->dev, - MKDEV(mhi_cntrl_drv.major, mhi_cntrl->cntrl_idx), NULL, - "mhi_BHI%d", mhi_cntrl->cntrl_idx); - else - mhi_cntrl->cntrl_dev = device_create(mhi_cntrl_drv.class, mhi_cntrl->dev, - MKDEV(mhi_cntrl_drv.major, mhi_cntrl->cntrl_idx), NULL, - "mhi_BHI"); - - mhi_cntrl->mhi_dev = mhi_dev; - - mhi_cntrl->parent = mhi_bus.dentry; - mhi_cntrl->klog_lvl = MHI_MSG_LVL_ERROR; - - /* adding it to this list only for debug purpose */ - mutex_lock(&mhi_bus.lock); - list_add_tail(&mhi_cntrl->node, &mhi_bus.controller_list); - mutex_unlock(&mhi_bus.lock); - - return 0; - -error_add_dev: - mhi_dealloc_device(mhi_cntrl, mhi_dev); - -error_alloc_dev: - kfree(mhi_cntrl->mhi_cmd); - -error_alloc_cmd: - kfree(mhi_cntrl->mhi_chan); - kfree(mhi_cntrl->mhi_event); - - return ret; -}; -EXPORT_SYMBOL(of_register_mhi_controller); - -void mhi_unregister_mhi_controller(struct mhi_controller *mhi_cntrl) -{ - struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev; - - kfree(mhi_cntrl->mhi_cmd); - kfree(mhi_cntrl->mhi_event); - kfree(mhi_cntrl->mhi_chan); - kfree(mhi_cntrl->mhi_tsync); - - if (mhi_cntrl->cntrl_dev) - device_destroy(mhi_cntrl_drv.class, MKDEV(mhi_cntrl_drv.major, mhi_cntrl->cntrl_idx)); - device_del(&mhi_dev->dev); - put_device(&mhi_dev->dev); - - mutex_lock(&mhi_bus.lock); - list_del(&mhi_cntrl->node); - mutex_unlock(&mhi_bus.lock); -} - -/* set ptr to control private data */ -static inline void mhi_controller_set_devdata(struct mhi_controller *mhi_cntrl, - void *priv) -{ - mhi_cntrl->priv_data = priv; -} - - -/* allocate mhi controller to register */ -struct mhi_controller *mhi_alloc_controller(size_t size) -{ - struct mhi_controller *mhi_cntrl; - - mhi_cntrl = kzalloc(size + sizeof(*mhi_cntrl), GFP_KERNEL); - - if (mhi_cntrl && size) - mhi_controller_set_devdata(mhi_cntrl, mhi_cntrl + 1); - - return mhi_cntrl; -} -EXPORT_SYMBOL(mhi_alloc_controller); - -int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl) -{ - int ret; - u32 bhie_off; - - mutex_lock(&mhi_cntrl->pm_mutex); - - ret = mhi_init_dev_ctxt(mhi_cntrl); - if (ret) { - MHI_ERR("Error with init dev_ctxt\n"); - goto error_dev_ctxt; - } - - ret = mhi_init_irq_setup(mhi_cntrl); - if (ret) { - MHI_ERR("Error setting up irq\n"); - goto error_setup_irq; - } - - /* - * allocate rddm table if specified, this table is for debug purpose - * so we'll ignore erros - */ - if (mhi_cntrl->rddm_size) { - mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->rddm_image, - mhi_cntrl->rddm_size); - - /* - * This controller supports rddm, we need to manually clear - * BHIE RX registers since por values are undefined. - */ - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, BHIEOFF, - &bhie_off); - if (ret) { - MHI_ERR("Error getting bhie offset\n"); - goto bhie_error; - } - - memset_io(mhi_cntrl->regs + bhie_off + BHIE_RXVECADDR_LOW_OFFS, - 0, BHIE_RXVECSTATUS_OFFS - BHIE_RXVECADDR_LOW_OFFS + - 4); - } - - mhi_cntrl->pre_init = true; - - mutex_unlock(&mhi_cntrl->pm_mutex); - - return 0; - -bhie_error: - if (mhi_cntrl->rddm_image) { - mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->rddm_image); - mhi_cntrl->rddm_image = NULL; - } - mhi_deinit_free_irq(mhi_cntrl); - -error_setup_irq: - mhi_deinit_dev_ctxt(mhi_cntrl); - -error_dev_ctxt: - mutex_unlock(&mhi_cntrl->pm_mutex); - - return ret; -} -EXPORT_SYMBOL(mhi_prepare_for_power_up); - -void mhi_unprepare_after_power_down(struct mhi_controller *mhi_cntrl) -{ - if (mhi_cntrl->fbc_image) { - mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image); - mhi_cntrl->fbc_image = NULL; - } - - if (mhi_cntrl->rddm_image) { - mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->rddm_image); - mhi_cntrl->rddm_image = NULL; - } - - mhi_deinit_free_irq(mhi_cntrl); - mhi_deinit_dev_ctxt(mhi_cntrl); - mhi_cntrl->pre_init = false; -} - -/* match dev to drv */ -static int mhi_match(struct device *dev, struct device_driver *drv) -{ - struct mhi_device *mhi_dev = to_mhi_device(dev); - struct mhi_driver *mhi_drv = to_mhi_driver(drv); - const struct mhi_device_id *id; - - /* if controller type there is no client driver associated with it */ - if (mhi_dev->dev_type == MHI_CONTROLLER_TYPE) - return 0; - - for (id = mhi_drv->id_table; id->chan[0]; id++) - if (!strcmp(mhi_dev->chan_name, id->chan)) { - mhi_dev->id = id; - return 1; - } - - return 0; -}; - -static void mhi_release_device(struct device *dev) -{ - struct mhi_device *mhi_dev = to_mhi_device(dev); - - if (mhi_dev->ul_chan) - mhi_dev->ul_chan->mhi_dev = NULL; - - if (mhi_dev->dl_chan) - mhi_dev->dl_chan->mhi_dev = NULL; - - kfree(mhi_dev); -} - -struct bus_type mhi_bus_type = { - .name = "mhi_q", - .dev_name = "mhi_q", - .match = mhi_match, -}; - -static int mhi_driver_probe(struct device *dev) -{ - struct mhi_device *mhi_dev = to_mhi_device(dev); - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - struct device_driver *drv = dev->driver; - struct mhi_driver *mhi_drv = to_mhi_driver(drv); - struct mhi_event *mhi_event; - struct mhi_chan *ul_chan = mhi_dev->ul_chan; - struct mhi_chan *dl_chan = mhi_dev->dl_chan; - bool auto_start = false; - int ret; - - /* bring device out of lpm */ - ret = mhi_device_get_sync(mhi_dev); - if (ret) - return ret; - - ret = -EINVAL; - if (ul_chan) { - /* lpm notification require status_cb */ - if (ul_chan->lpm_notify && !mhi_drv->status_cb) - goto exit_probe; - - if (!ul_chan->offload_ch && !mhi_drv->ul_xfer_cb) - goto exit_probe; - - ul_chan->xfer_cb = mhi_drv->ul_xfer_cb; - mhi_dev->status_cb = mhi_drv->status_cb; - auto_start = ul_chan->auto_start; - } - - if (dl_chan) { - if (dl_chan->lpm_notify && !mhi_drv->status_cb) - goto exit_probe; - - if (!dl_chan->offload_ch && !mhi_drv->dl_xfer_cb) - goto exit_probe; - - mhi_event = &mhi_cntrl->mhi_event[dl_chan->er_index]; - - /* - * if this channal event ring manage by client, then - * status_cb must be defined so we can send the async - * cb whenever there are pending data - */ - if (mhi_event->cl_manage && !mhi_drv->status_cb) - goto exit_probe; - - dl_chan->xfer_cb = mhi_drv->dl_xfer_cb; - - /* ul & dl uses same status cb */ - mhi_dev->status_cb = mhi_drv->status_cb; - auto_start = (auto_start || dl_chan->auto_start); - } - - ret = mhi_drv->probe(mhi_dev, mhi_dev->id); - - if (!ret && auto_start) - mhi_prepare_for_transfer(mhi_dev); - -exit_probe: - mhi_device_put(mhi_dev); - - return ret; -} - -static int mhi_driver_remove(struct device *dev) -{ - struct mhi_device *mhi_dev = to_mhi_device(dev); - struct mhi_driver *mhi_drv = to_mhi_driver(dev->driver); - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - struct mhi_chan *mhi_chan; - enum MHI_CH_STATE ch_state[] = { - MHI_CH_STATE_DISABLED, - MHI_CH_STATE_DISABLED - }; - int dir; - - /* control device has no work to do */ - if (mhi_dev->dev_type == MHI_CONTROLLER_TYPE) - return 0; - - MHI_LOG("Removing device for chan:%s\n", mhi_dev->chan_name); - - /* reset both channels */ - for (dir = 0; dir < 2; dir++) { - mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; - - if (!mhi_chan) - continue; - - /* wake all threads waiting for completion */ - write_lock_irq(&mhi_chan->lock); - mhi_chan->ccs = MHI_EV_CC_INVALID; - complete_all(&mhi_chan->completion); - write_unlock_irq(&mhi_chan->lock); - - /* move channel state to disable, no more processing */ - mutex_lock(&mhi_chan->mutex); - write_lock_irq(&mhi_chan->lock); - ch_state[dir] = mhi_chan->ch_state; - mhi_chan->ch_state = MHI_CH_STATE_SUSPENDED; - write_unlock_irq(&mhi_chan->lock); - - /* reset the channel */ - if (!mhi_chan->offload_ch) - mhi_reset_chan(mhi_cntrl, mhi_chan); - - mutex_unlock(&mhi_chan->mutex); - } - - /* destroy the device */ - mhi_drv->remove(mhi_dev); - - /* de_init channel if it was enabled */ - for (dir = 0; dir < 2; dir++) { - mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; - - if (!mhi_chan) - continue; - - mutex_lock(&mhi_chan->mutex); - - if (ch_state[dir] == MHI_CH_STATE_ENABLED && - !mhi_chan->offload_ch) - mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan); - - mhi_chan->ch_state = MHI_CH_STATE_DISABLED; - - mutex_unlock(&mhi_chan->mutex); - } - - - if (mhi_cntrl->tsync_dev == mhi_dev) - mhi_cntrl->tsync_dev = NULL; - - /* relinquish any pending votes */ - read_lock_bh(&mhi_cntrl->pm_lock); - while (atomic_read(&mhi_dev->dev_wake)) - mhi_device_put(mhi_dev); - read_unlock_bh(&mhi_cntrl->pm_lock); - - return 0; -} - -int mhi_driver_register(struct mhi_driver *mhi_drv) -{ - struct device_driver *driver = &mhi_drv->driver; - - if (!mhi_drv->probe || !mhi_drv->remove) - return -EINVAL; - - driver->bus = &mhi_bus_type; - driver->probe = mhi_driver_probe; - driver->remove = mhi_driver_remove; - return driver_register(driver); -} -EXPORT_SYMBOL(mhi_driver_register); - -void mhi_driver_unregister(struct mhi_driver *mhi_drv) -{ - driver_unregister(&mhi_drv->driver); -} -EXPORT_SYMBOL(mhi_driver_unregister); - -struct mhi_device *mhi_alloc_device(struct mhi_controller *mhi_cntrl) -{ - struct mhi_device *mhi_dev = kzalloc(sizeof(*mhi_dev), GFP_KERNEL); - struct device *dev; - - if (!mhi_dev) - return NULL; - - dev = &mhi_dev->dev; - device_initialize(dev); - dev->bus = &mhi_bus_type; - dev->release = mhi_release_device; - dev->parent = mhi_cntrl->dev; - mhi_dev->mhi_cntrl = mhi_cntrl; - mhi_dev->vendor = mhi_cntrl->vendor; - mhi_dev->dev_id = mhi_cntrl->dev_id; - mhi_dev->domain = mhi_cntrl->domain; - mhi_dev->bus = mhi_cntrl->bus; - mhi_dev->slot = mhi_cntrl->slot; - mhi_dev->mtu = MHI_MAX_MTU; - atomic_set(&mhi_dev->dev_wake, 0); - - return mhi_dev; -} - -static int mhi_cntrl_open(struct inode *inode, struct file *f) -{ - int ret = -ENODEV; - struct mhi_controller *mhi_cntrl; - - mutex_lock(&mhi_bus.lock); - list_for_each_entry(mhi_cntrl, &mhi_bus.controller_list, node) { - if (MINOR(inode->i_rdev) == mhi_cntrl->cntrl_idx) { - ret = 0; - f->private_data = mhi_cntrl; - break; - } - } - mutex_unlock(&mhi_bus.lock); - - return ret; -} - -static int mhi_cntrl_release(struct inode *inode, struct file *f) -{ - f->private_data = NULL; - return 0; -} - -#define IOCTL_BHI_GETDEVINFO 0x8BE0 + 1 -#define IOCTL_BHI_WRITEIMAGE 0x8BE0 + 2 -long bhi_get_dev_info(struct mhi_controller *mhi_cntrl, void __user *to); -long bhi_write_image(struct mhi_controller *mhi_cntrl, void __user *from); - -static long mhi_cntrl_ioctl(struct file *f, unsigned int cmd, unsigned long __arg) -{ - long ret = -ENODEV; - struct mhi_controller *mhi_cntrl; - - mutex_lock(&mhi_bus.lock); - list_for_each_entry(mhi_cntrl, &mhi_bus.controller_list, node) { - if (mhi_cntrl == (struct mhi_controller *)f->private_data) { - ret = 0; - break; - } - } - mutex_unlock(&mhi_bus.lock); - - if (ret) - return ret; - - switch (cmd) { - case IOCTL_BHI_GETDEVINFO: - ret = bhi_get_dev_info(mhi_cntrl, (void __user *)__arg); - break; - - case IOCTL_BHI_WRITEIMAGE: - ret = bhi_write_image(mhi_cntrl, (void __user *)__arg); - break; - - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static const struct file_operations mhi_cntrl_fops = { - .unlocked_ioctl = mhi_cntrl_ioctl, - .open = mhi_cntrl_open, - .release = mhi_cntrl_release, -}; - -static int __init mhi_cntrl_init(void) -{ - int ret; - - ret = register_chrdev(0, MHI_CNTRL_DRIVER_NAME, &mhi_cntrl_fops); - if (ret < 0) - return ret; - - mhi_cntrl_drv.major = ret; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)) - mhi_cntrl_drv.class = class_create(MHI_CNTRL_DRIVER_NAME); -#else - mhi_cntrl_drv.class = class_create(THIS_MODULE, MHI_CNTRL_DRIVER_NAME); -#endif - if (IS_ERR(mhi_cntrl_drv.class)) { - unregister_chrdev(mhi_cntrl_drv.major, MHI_CNTRL_DRIVER_NAME); - return -ENODEV; - } - - mutex_init(&mhi_cntrl_drv.lock); - INIT_LIST_HEAD(&mhi_cntrl_drv.head); - - return 0; -} - -void mhi_cntrl_exit(void) -{ - class_destroy(mhi_cntrl_drv.class); - unregister_chrdev(mhi_cntrl_drv.major, MHI_CNTRL_DRIVER_NAME); -} - -extern int mhi_dtr_init(void); -extern void mhi_dtr_exit(void); -extern int mhi_device_netdev_init(struct dentry *parent); -extern void mhi_device_netdev_exit(void); -extern int mhi_device_uci_init(void); -extern void mhi_device_uci_exit(void); -extern int mhi_controller_qcom_init(void); -extern void mhi_controller_qcom_exit(void); - -static char mhi_version[] = "Quectel_Linux_PCIE_MHI_Driver_"PCIE_MHI_DRIVER_VERSION; -module_param_string(mhi_version, mhi_version, sizeof(mhi_version), S_IRUGO); - -static int __init mhi_init(void) -{ - int ret; - - pr_info("%s %s\n", __func__, mhi_version); - - mutex_init(&mhi_bus.lock); - INIT_LIST_HEAD(&mhi_bus.controller_list); - - /* parent directory */ - mhi_bus.dentry = debugfs_create_dir(mhi_bus_type.name, NULL); - - ret = bus_register(&mhi_bus_type); - if (ret) { - pr_err("Error bus_register ret:%d\n", ret); - return ret; - } - - ret = mhi_dtr_init(); - if (ret) { - pr_err("Error mhi_dtr_init ret:%d\n", ret); - bus_unregister(&mhi_bus_type); - return ret; - } - - ret = mhi_device_netdev_init(mhi_bus.dentry); - if (ret) { - pr_err("Error mhi_device_netdev_init ret:%d\n", ret); - } - - ret = mhi_device_uci_init(); - if (ret) { - pr_err("Error mhi_device_uci_init ret:%d\n", ret); - } - - ret = mhi_cntrl_init(); - if (ret) { - pr_err("Error mhi_cntrl_init ret:%d\n", ret); - } - - ret = mhi_controller_qcom_init(); - if (ret) { - pr_err("Error mhi_controller_qcom_init ret:%d\n", ret); - } - - return ret; -} - -static void mhi_exit(void) -{ - mhi_controller_qcom_exit(); - mhi_cntrl_exit(); - mhi_device_uci_exit(); - mhi_device_netdev_exit(); - mhi_dtr_exit(); - bus_unregister(&mhi_bus_type); - debugfs_remove_recursive(mhi_bus.dentry); -} - -module_init(mhi_init); -module_exit(mhi_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("MHI_CORE"); -MODULE_DESCRIPTION("MHI Host Interface"); diff --git a/quectel_MHI/src/core/mhi_internal.h b/quectel_MHI/src/core/mhi_internal.h deleted file mode 100644 index 09f3aac7e..000000000 --- a/quectel_MHI/src/core/mhi_internal.h +++ /dev/null @@ -1,1190 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */ - -#ifndef _MHI_INT_H -#define _MHI_INT_H - -#include -#ifndef writel_relaxed -#define writel_relaxed writel -#endif - -#ifndef writel_relaxed_no_log -#define writel_relaxed_no_log writel_relaxed -#endif - -#ifndef readq -static inline u64 readq(void __iomem *reg) -{ - return ((u64) readl(reg)) | (((u64) readl(reg + 4UL)) << 32); -} -#endif - -#ifndef readq_relaxed -#define readq_relaxed readq -#endif - -#ifndef readq_relaxed_no_log -#define readq_relaxed_no_log readq_relaxed -#endif - -#ifndef U32_MAX -#define U32_MAX ((u32)~0U) -#endif - -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,53 )) -static inline void reinit_completion(struct completion *x) -{ - x->done = 0; -} -#endif - -#ifndef __ATTR_RO -#define __ATTR_RO(_name) { \ - .attr = { .name = __stringify(_name), .mode = S_IRUGO }, \ - .show = _name##_show, \ -} -#endif -#ifndef __ATTR_WO -#define __ATTR_WO(_name) { \ - .attr = { .name = __stringify(_name), .mode = S_IWUSR }, \ - .store = _name##_store, \ -} -#endif -#ifndef __ATTR_RW -#define __ATTR_RW(_name) __ATTR(_name, (S_IWUSR | S_IRUGO), \ - _name##_show, _name##_store) -#endif -#ifndef DEVICE_ATTR_RO -#define DEVICE_ATTR_RO(_name) \ - struct device_attribute dev_attr_##_name = __ATTR_RO(_name) -#endif -#ifndef DEVICE_ATTR_WO -#define DEVICE_ATTR_WO(_name) \ - struct device_attribute dev_attr_##_name = __ATTR_WO(_name) -#endif -#ifndef DEVICE_ATTR_RW -#define DRIVER_ATTR_RW(_name) \ - struct driver_attribute driver_attr_##_name = __ATTR_RW(_name) -#endif - -#ifdef EXPORT_SYMBOL -#undef EXPORT_SYMBOL -#define EXPORT_SYMBOL(sym) -#endif - -extern struct bus_type mhi_bus_type; - -/* MHI mmio register mapping */ -#define PCI_INVALID_READ(val) (val == U32_MAX) - -#define MHIREGLEN (0x0) -#define MHIREGLEN_MHIREGLEN_MASK (0xFFFFFFFF) -#define MHIREGLEN_MHIREGLEN_SHIFT (0) - -#define MHIVER (0x8) -#define MHIVER_MHIVER_MASK (0xFFFFFFFF) -#define MHIVER_MHIVER_SHIFT (0) - -#define MHICFG (0x10) -#define MHICFG_NHWER_MASK (0xFF000000) -#define MHICFG_NHWER_SHIFT (24) -#define MHICFG_NER_MASK (0xFF0000) -#define MHICFG_NER_SHIFT (16) -#define MHICFG_NHWCH_MASK (0xFF00) -#define MHICFG_NHWCH_SHIFT (8) -#define MHICFG_NCH_MASK (0xFF) -#define MHICFG_NCH_SHIFT (0) - -#define CHDBOFF (0x18) -#define CHDBOFF_CHDBOFF_MASK (0xFFFFFFFF) -#define CHDBOFF_CHDBOFF_SHIFT (0) - -#define ERDBOFF (0x20) -#define ERDBOFF_ERDBOFF_MASK (0xFFFFFFFF) -#define ERDBOFF_ERDBOFF_SHIFT (0) - -#define BHIOFF (0x28) -#define BHIOFF_BHIOFF_MASK (0xFFFFFFFF) -#define BHIOFF_BHIOFF_SHIFT (0) - -#define BHIEOFF (0x2C) -#define BHIEOFF_BHIEOFF_MASK (0xFFFFFFFF) -#define BHIEOFF_BHIEOFF_SHIFT (0) - -#define DEBUGOFF (0x30) -#define DEBUGOFF_DEBUGOFF_MASK (0xFFFFFFFF) -#define DEBUGOFF_DEBUGOFF_SHIFT (0) - -#define MHICTRL (0x38) -#define MHICTRL_MHISTATE_MASK (0x0000FF00) -#define MHICTRL_MHISTATE_SHIFT (8) -#define MHICTRL_RESET_MASK (0x2) -#define MHICTRL_RESET_SHIFT (1) - -#define MHISTATUS (0x48) -#define MHISTATUS_MHISTATE_MASK (0x0000FF00) -#define MHISTATUS_MHISTATE_SHIFT (8) -#define MHISTATUS_SYSERR_MASK (0x4) -#define MHISTATUS_SYSERR_SHIFT (2) -#define MHISTATUS_READY_MASK (0x1) -#define MHISTATUS_READY_SHIFT (0) - -#define CCABAP_LOWER (0x58) -#define CCABAP_LOWER_CCABAP_LOWER_MASK (0xFFFFFFFF) -#define CCABAP_LOWER_CCABAP_LOWER_SHIFT (0) - -#define CCABAP_HIGHER (0x5C) -#define CCABAP_HIGHER_CCABAP_HIGHER_MASK (0xFFFFFFFF) -#define CCABAP_HIGHER_CCABAP_HIGHER_SHIFT (0) - -#define ECABAP_LOWER (0x60) -#define ECABAP_LOWER_ECABAP_LOWER_MASK (0xFFFFFFFF) -#define ECABAP_LOWER_ECABAP_LOWER_SHIFT (0) - -#define ECABAP_HIGHER (0x64) -#define ECABAP_HIGHER_ECABAP_HIGHER_MASK (0xFFFFFFFF) -#define ECABAP_HIGHER_ECABAP_HIGHER_SHIFT (0) - -#define CRCBAP_LOWER (0x68) -#define CRCBAP_LOWER_CRCBAP_LOWER_MASK (0xFFFFFFFF) -#define CRCBAP_LOWER_CRCBAP_LOWER_SHIFT (0) - -#define CRCBAP_HIGHER (0x6C) -#define CRCBAP_HIGHER_CRCBAP_HIGHER_MASK (0xFFFFFFFF) -#define CRCBAP_HIGHER_CRCBAP_HIGHER_SHIFT (0) - -#define CRDB_LOWER (0x70) -#define CRDB_LOWER_CRDB_LOWER_MASK (0xFFFFFFFF) -#define CRDB_LOWER_CRDB_LOWER_SHIFT (0) - -#define CRDB_HIGHER (0x74) -#define CRDB_HIGHER_CRDB_HIGHER_MASK (0xFFFFFFFF) -#define CRDB_HIGHER_CRDB_HIGHER_SHIFT (0) - -#define MHICTRLBASE_LOWER (0x80) -#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_MASK (0xFFFFFFFF) -#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_SHIFT (0) - -#define MHICTRLBASE_HIGHER (0x84) -#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_MASK (0xFFFFFFFF) -#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_SHIFT (0) - -#define MHICTRLLIMIT_LOWER (0x88) -#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_MASK (0xFFFFFFFF) -#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_SHIFT (0) - -#define MHICTRLLIMIT_HIGHER (0x8C) -#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_MASK (0xFFFFFFFF) -#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_SHIFT (0) - -#define MHIDATABASE_LOWER (0x98) -#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_MASK (0xFFFFFFFF) -#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_SHIFT (0) - -#define MHIDATABASE_HIGHER (0x9C) -#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_MASK (0xFFFFFFFF) -#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_SHIFT (0) - -#define MHIDATALIMIT_LOWER (0xA0) -#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_MASK (0xFFFFFFFF) -#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_SHIFT (0) - -#define MHIDATALIMIT_HIGHER (0xA4) -#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_MASK (0xFFFFFFFF) -#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_SHIFT (0) - -#define MHI_READ_REG_FIELD(_VAR,_REG,_FIELD) \ - ((_VAR & _REG ## _ ## _FIELD ## _MASK) >> _REG ## _ ## _FIELD ## _SHIFT) - -#define MHI_WRITE_REG_FIELD(_VAR,_REG,_FIELD,_VAL) \ - do { \ - _VAR &= ~_REG ## _ ## _FIELD ## _MASK; \ - _VAR |= (_VAL << _REG ## _ ## _FIELD ## _SHIFT); \ - } while(0) - -/* Host request register */ -#define MHI_SOC_RESET_REQ_OFFSET (0xB0) -#define MHI_SOC_RESET_REQ BIT(0) - -/* MHI misc capability registers */ -#define MISC_OFFSET (0x24) -#define MISC_CAP_MASK (0xFFFFFFFF) -#define MISC_CAP_SHIFT (0) - -#define CAP_CAPID_MASK (0xFF000000) -#define CAP_CAPID_SHIFT (24) -#define CAP_NEXT_CAP_MASK (0x00FFF000) -#define CAP_NEXT_CAP_SHIFT (12) - -/* MHI Timesync offsets */ -#define TIMESYNC_CFG_OFFSET (0x00) -#define TIMESYNC_CFG_CAPID_MASK (CAP_CAPID_MASK) -#define TIMESYNC_CFG_CAPID_SHIFT (CAP_CAPID_SHIFT) -#define TIMESYNC_CFG_NEXT_OFF_MASK (CAP_NEXT_CAP_MASK) -#define TIMESYNC_CFG_NEXT_OFF_SHIFT (CAP_NEXT_CAP_SHIFT) -#define TIMESYNC_CFG_NUMCMD_MASK (0xFF) -#define TIMESYNC_CFG_NUMCMD_SHIFT (0) -#define TIMESYNC_DB_OFFSET (0x4) -#define TIMESYNC_TIME_LOW_OFFSET (0x8) -#define TIMESYNC_TIME_HIGH_OFFSET (0xC) - -#define TIMESYNC_CAP_ID (2) - -/* MHI BHI offfsets */ -#define BHI_BHIVERSION_MINOR (0x00) -#define BHI_BHIVERSION_MAJOR (0x04) -#define BHI_IMGADDR_LOW (0x08) -#define BHI_IMGADDR_HIGH (0x0C) -#define BHI_IMGSIZE (0x10) -#define BHI_RSVD1 (0x14) -#define BHI_IMGTXDB (0x18) -#define BHI_TXDB_SEQNUM_BMSK (0x3FFFFFFF) -#define BHI_TXDB_SEQNUM_SHFT (0) -#define BHI_RSVD2 (0x1C) -#define BHI_INTVEC (0x20) -#define BHI_RSVD3 (0x24) -#define BHI_EXECENV (0x28) -#define BHI_STATUS (0x2C) -#define BHI_ERRCODE (0x30) -#define BHI_ERRDBG1 (0x34) -#define BHI_ERRDBG2 (0x38) -#define BHI_ERRDBG3 (0x3C) -#define BHI_SERIALNU (0x40) -#define BHI_SBLANTIROLLVER (0x44) -#define BHI_NUMSEG (0x48) -#define BHI_MSMHWID(n) (0x4C + (0x4 * n)) -#define BHI_OEMPKHASH(n) (0x64 + (0x4 * n)) -#define BHI_RSVD5 (0xC4) -#define BHI_STATUS_MASK (0xC0000000) -#define BHI_STATUS_SHIFT (30) -#define BHI_STATUS_ERROR (3) -#define BHI_STATUS_SUCCESS (2) -#define BHI_STATUS_RESET (0) - -/* MHI BHIE offsets */ -#define BHIE_MSMSOCID_OFFS (0x0000) -#define BHIE_TXVECADDR_LOW_OFFS (0x002C) -#define BHIE_TXVECADDR_HIGH_OFFS (0x0030) -#define BHIE_TXVECSIZE_OFFS (0x0034) -#define BHIE_TXVECDB_OFFS (0x003C) -#define BHIE_TXVECDB_SEQNUM_BMSK (0x3FFFFFFF) -#define BHIE_TXVECDB_SEQNUM_SHFT (0) -#define BHIE_TXVECSTATUS_OFFS (0x0044) -#define BHIE_TXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF) -#define BHIE_TXVECSTATUS_SEQNUM_SHFT (0) -#define BHIE_TXVECSTATUS_STATUS_BMSK (0xC0000000) -#define BHIE_TXVECSTATUS_STATUS_SHFT (30) -#define BHIE_TXVECSTATUS_STATUS_RESET (0x00) -#define BHIE_TXVECSTATUS_STATUS_XFER_COMPL (0x02) -#define BHIE_TXVECSTATUS_STATUS_ERROR (0x03) -#define BHIE_RXVECADDR_LOW_OFFS (0x0060) -#define BHIE_RXVECADDR_HIGH_OFFS (0x0064) -#define BHIE_RXVECSIZE_OFFS (0x0068) -#define BHIE_RXVECDB_OFFS (0x0070) -#define BHIE_RXVECDB_SEQNUM_BMSK (0x3FFFFFFF) -#define BHIE_RXVECDB_SEQNUM_SHFT (0) -#define BHIE_RXVECSTATUS_OFFS (0x0078) -#define BHIE_RXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF) -#define BHIE_RXVECSTATUS_SEQNUM_SHFT (0) -#define BHIE_RXVECSTATUS_STATUS_BMSK (0xC0000000) -#define BHIE_RXVECSTATUS_STATUS_SHFT (30) -#define BHIE_RXVECSTATUS_STATUS_RESET (0x00) -#define BHIE_RXVECSTATUS_STATUS_XFER_COMPL (0x02) -#define BHIE_RXVECSTATUS_STATUS_ERROR (0x03) - -/* convert ticks to micro seconds by dividing by 19.2 */ -#define TIME_TICKS_TO_US(x) (div_u64((x) * 10, 192)) - -struct mhi_event_ctxt { - u32 reserved : 8; - u32 intmodc : 8; - u32 intmodt : 16; - u32 ertype; - u32 msivec; - - u64 rbase __packed __aligned(4); - u64 rlen __packed __aligned(4); - u64 rp __packed __aligned(4); - u64 wp __packed __aligned(4); -}; - -struct mhi_chan_ctxt { - u32 chstate : 8; - u32 brstmode : 2; - u32 pollcfg : 6; - u32 reserved : 16; - u32 chtype; - u32 erindex; - - u64 rbase __packed __aligned(4); - u64 rlen __packed __aligned(4); - u64 rp __packed __aligned(4); - u64 wp __packed __aligned(4); -}; - -struct mhi_cmd_ctxt { - u32 reserved0; - u32 reserved1; - u32 reserved2; - - u64 rbase __packed __aligned(4); - u64 rlen __packed __aligned(4); - u64 rp __packed __aligned(4); - u64 wp __packed __aligned(4); -}; - -struct mhi_tre { - u64 ptr; - u32 dword[2]; -}; - -/* Channel context state */ -enum mhi_dev_ch_ctx_state { - MHI_DEV_CH_STATE_DISABLED, - MHI_DEV_CH_STATE_ENABLED, - MHI_DEV_CH_STATE_RUNNING, - MHI_DEV_CH_STATE_SUSPENDED, - MHI_DEV_CH_STATE_STOP, - MHI_DEV_CH_STATE_ERROR, - MHI_DEV_CH_STATE_RESERVED, - MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF -}; - -/* Channel type */ -enum mhi_dev_ch_ctx_type { - MHI_DEV_CH_TYPE_NONE, - MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL, - MHI_DEV_CH_TYPE_INBOUND_CHANNEL, - MHI_DEV_CH_RESERVED -}; - -/* Channel context type */ -struct mhi_dev_ch_ctx { - enum mhi_dev_ch_ctx_state ch_state; - enum mhi_dev_ch_ctx_type ch_type; - uint32_t err_indx; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -enum mhi_dev_ring_element_type_id { - MHI_DEV_RING_EL_INVALID = 0, - MHI_DEV_RING_EL_NOOP = 1, - MHI_DEV_RING_EL_TRANSFER = 2, - MHI_DEV_RING_EL_RESET = 16, - MHI_DEV_RING_EL_STOP = 17, - MHI_DEV_RING_EL_START = 18, - MHI_DEV_RING_EL_MHI_STATE_CHG = 32, - MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33, - MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34, - MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64, - MHI_DEV_RING_EL_UNDEF -}; - -enum mhi_dev_ring_state { - RING_STATE_UINT = 0, - RING_STATE_IDLE, - RING_STATE_PENDING, -}; - -enum mhi_dev_ring_type { - RING_TYPE_CMD = 0, - RING_TYPE_ER, - RING_TYPE_CH, - RING_TYPE_INVAL -}; - -/* Event context interrupt moderation */ -enum mhi_dev_evt_ctx_int_mod_timer { - MHI_DEV_EVT_INT_MODERATION_DISABLED -}; - -/* Event ring type */ -enum mhi_dev_evt_ctx_event_ring_type { - MHI_DEV_EVT_TYPE_DEFAULT, - MHI_DEV_EVT_TYPE_VALID, - MHI_DEV_EVT_RESERVED -}; - -/* Event ring context type */ -struct mhi_dev_ev_ctx { - uint32_t res1:16; - enum mhi_dev_evt_ctx_int_mod_timer intmodt:16; - enum mhi_dev_evt_ctx_event_ring_type ertype; - uint32_t msivec; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -/* Command context */ -struct mhi_dev_cmd_ctx { - uint32_t res1; - uint32_t res2; - uint32_t res3; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -/* generic context */ -struct mhi_dev_gen_ctx { - uint32_t res1; - uint32_t res2; - uint32_t res3; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -/* Transfer ring element */ -struct mhi_dev_transfer_ring_element { - uint64_t data_buf_ptr; - uint32_t len:16; - uint32_t res1:16; - uint32_t chain:1; - uint32_t res2:7; - uint32_t ieob:1; - uint32_t ieot:1; - uint32_t bei:1; - uint32_t res3:5; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res4:8; -} __packed; - -/* Command ring element */ -/* Command ring No op command */ -struct mhi_dev_cmd_ring_op { - uint64_t res1; - uint32_t res2; - uint32_t res3:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command ring reset channel command */ -struct mhi_dev_cmd_ring_reset_channel_cmd { - uint64_t res1; - uint32_t res2; - uint32_t res3:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command ring stop channel command */ -struct mhi_dev_cmd_ring_stop_channel_cmd { - uint64_t res1; - uint32_t res2; - uint32_t res3:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command ring start channel command */ -struct mhi_dev_cmd_ring_start_channel_cmd { - uint64_t res1; - uint32_t seqnum; - uint32_t reliable:1; - uint32_t res2:15; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -enum mhi_dev_cmd_completion_code { - MHI_CMD_COMPL_CODE_INVALID = 0, - MHI_CMD_COMPL_CODE_SUCCESS = 1, - MHI_CMD_COMPL_CODE_EOT = 2, - MHI_CMD_COMPL_CODE_OVERFLOW = 3, - MHI_CMD_COMPL_CODE_EOB = 4, - MHI_CMD_COMPL_CODE_UNDEFINED = 16, - MHI_CMD_COMPL_CODE_RING_EL = 17, - MHI_CMD_COMPL_CODE_RES -}; - -/* Event ring elements */ -/* Transfer completion event */ -struct mhi_dev_event_ring_transfer_completion { - uint64_t ptr; - uint32_t len:16; - uint32_t res1:8; - enum mhi_dev_cmd_completion_code code:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command completion event */ -struct mhi_dev_event_ring_cmd_completion { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_cmd_completion_code code:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res3:8; -} __packed; - -/* MHI state change event */ -struct mhi_dev_event_ring_state_change { - uint64_t ptr; - uint32_t res1:24; - uint32_t /*enum mhi_dev_state*/ mhistate:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res3:8; -} __packed; - -enum mhi_dev_execenv { - MHI_DEV_SBL_EE = 1, - MHI_DEV_AMSS_EE = 2, - MHI_DEV_UNRESERVED -}; - -/* EE state change event */ -struct mhi_dev_event_ring_ee_state_change { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_execenv execenv:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res3:8; -} __packed; - -/* Generic cmd to parse common details like type and channel id */ -struct mhi_dev_ring_generic { - uint64_t ptr; - uint32_t res1:24; - uint32_t /*enum mhi_dev_state*/ mhistate:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -struct mhi_config { - uint32_t mhi_reg_len; - uint32_t version; - uint32_t event_rings; - uint32_t channels; - uint32_t chdb_offset; - uint32_t erdb_offset; -}; - -/* Possible ring element types */ -union mhi_dev_ring_element_type { - struct mhi_dev_cmd_ring_op cmd_no_op; - struct mhi_dev_cmd_ring_reset_channel_cmd cmd_reset; - struct mhi_dev_cmd_ring_stop_channel_cmd cmd_stop; - struct mhi_dev_cmd_ring_start_channel_cmd cmd_start; - struct mhi_dev_transfer_ring_element cmd_transfer; - struct mhi_dev_event_ring_transfer_completion evt_tr_comp; - struct mhi_dev_event_ring_cmd_completion evt_cmd_comp; - struct mhi_dev_event_ring_state_change evt_state_change; - struct mhi_dev_event_ring_ee_state_change evt_ee_state; - struct mhi_dev_ring_generic generic; -}; - -struct bhi_vec_entry { - u64 dma_addr; - u64 size; -}; - -enum mhi_cmd_type { - MHI_CMD_TYPE_NOP = 1, - MHI_CMD_TYPE_RESET = 16, - MHI_CMD_TYPE_STOP = 17, - MHI_CMD_TYPE_START = 18, - MHI_CMD_TYPE_TSYNC = 24, -}; - -/* no operation command */ -#define MHI_TRE_CMD_NOOP_PTR (0) -#define MHI_TRE_CMD_NOOP_DWORD0 (0) -#define MHI_TRE_CMD_NOOP_DWORD1 (MHI_CMD_TYPE_NOP << 16) - -/* channel reset command */ -#define MHI_TRE_CMD_RESET_PTR (0) -#define MHI_TRE_CMD_RESET_DWORD0 (0) -#define MHI_TRE_CMD_RESET_DWORD1(chid) ((chid << 24) | \ - (MHI_CMD_TYPE_RESET << 16)) - -/* channel stop command */ -#define MHI_TRE_CMD_STOP_PTR (0) -#define MHI_TRE_CMD_STOP_DWORD0 (0) -#define MHI_TRE_CMD_STOP_DWORD1(chid) ((chid << 24) | (MHI_CMD_TYPE_STOP << 16)) - -/* channel start command */ -#define MHI_TRE_CMD_START_PTR (0) -#define MHI_TRE_CMD_START_DWORD0 (0) -#define MHI_TRE_CMD_START_DWORD1(chid) ((chid << 24) | \ - (MHI_CMD_TYPE_START << 16)) - -/* time sync cfg command */ -#define MHI_TRE_CMD_TSYNC_CFG_PTR (0) -#define MHI_TRE_CMD_TSYNC_CFG_DWORD0 (0) -#define MHI_TRE_CMD_TSYNC_CFG_DWORD1(er) ((MHI_CMD_TYPE_TSYNC << 16) | \ - (er << 24)) - -#define MHI_TRE_GET_CMD_CHID(tre) (((tre)->dword[1] >> 24) & 0xFF) -#define MHI_TRE_GET_CMD_TYPE(tre) (((tre)->dword[1] >> 16) & 0xFF) - -/* event descriptor macros */ -#define MHI_TRE_EV_PTR(ptr) (ptr) -#define MHI_TRE_EV_DWORD0(code, len) ((code << 24) | len) -#define MHI_TRE_EV_DWORD1(chid, type) ((chid << 24) | (type << 16)) -#define MHI_TRE_GET_EV_PTR(tre) ((tre)->ptr) -#define MHI_TRE_GET_EV_CODE(tre) (((tre)->dword[0] >> 24) & 0xFF) -#define MHI_TRE_GET_EV_LEN(tre) ((tre)->dword[0] & 0xFFFF) -#define MHI_TRE_GET_EV_CHID(tre) (((tre)->dword[1] >> 24) & 0xFF) -#define MHI_TRE_GET_EV_TYPE(tre) (((tre)->dword[1] >> 16) & 0xFF) -#define MHI_TRE_GET_EV_STATE(tre) (((tre)->dword[0] >> 24) & 0xFF) -#define MHI_TRE_GET_EV_EXECENV(tre) (((tre)->dword[0] >> 24) & 0xFF) -#define MHI_TRE_GET_EV_SEQ(tre) ((tre)->dword[0]) -#define MHI_TRE_GET_EV_TIME(tre) ((tre)->ptr) -#define MHI_TRE_GET_EV_COOKIE(tre) lower_32_bits((tre)->ptr) -#define MHI_TRE_GET_EV_VEID(tre) (((tre)->dword[0] >> 16) & 0xFF) - -/* transfer descriptor macros */ -#define MHI_TRE_DATA_PTR(ptr) (ptr) -#define MHI_TRE_DATA_DWORD0(len) (len & MHI_MAX_MTU) -#define MHI_TRE_DATA_DWORD1(bei, ieot, ieob, chain) ((2 << 16) | (bei << 10) \ - | (ieot << 9) | (ieob << 8) | chain) - -/* rsc transfer descriptor macros */ -#define MHI_RSCTRE_DATA_PTR(ptr, len) (((u64)len << 48) | ptr) -#define MHI_RSCTRE_DATA_DWORD0(cookie) (cookie) -#define MHI_RSCTRE_DATA_DWORD1 (MHI_PKT_TYPE_COALESCING << 16) - -enum MHI_CMD { - MHI_CMD_RESET_CHAN, - MHI_CMD_START_CHAN, - MHI_CMD_TIMSYNC_CFG, -}; - -enum MHI_PKT_TYPE { - MHI_PKT_TYPE_INVALID = 0x0, - MHI_PKT_TYPE_NOOP_CMD = 0x1, - MHI_PKT_TYPE_TRANSFER = 0x2, - MHI_PKT_TYPE_COALESCING = 0x8, - MHI_PKT_TYPE_RESET_CHAN_CMD = 0x10, - MHI_PKT_TYPE_STOP_CHAN_CMD = 0x11, - MHI_PKT_TYPE_START_CHAN_CMD = 0x12, - MHI_PKT_TYPE_STATE_CHANGE_EVENT = 0x20, - MHI_PKT_TYPE_CMD_COMPLETION_EVENT = 0x21, - MHI_PKT_TYPE_TX_EVENT = 0x22, - MHI_PKT_TYPE_RSC_TX_EVENT = 0x28, - MHI_PKT_TYPE_EE_EVENT = 0x40, - MHI_PKT_TYPE_TSYNC_EVENT = 0x48, - MHI_PKT_TYPE_STALE_EVENT, /* internal event */ -}; - -/* MHI transfer completion events */ -enum MHI_EV_CCS { - MHI_EV_CC_INVALID = 0x0, - MHI_EV_CC_SUCCESS = 0x1, - MHI_EV_CC_EOT = 0x2, - MHI_EV_CC_OVERFLOW = 0x3, - MHI_EV_CC_EOB = 0x4, - MHI_EV_CC_OOB = 0x5, - MHI_EV_CC_DB_MODE = 0x6, - MHI_EV_CC_UNDEFINED_ERR = 0x10, - MHI_EV_CC_BAD_TRE = 0x11, -}; - -enum MHI_CH_STATE { - MHI_CH_STATE_DISABLED = 0x0, - MHI_CH_STATE_ENABLED = 0x1, - MHI_CH_STATE_RUNNING = 0x2, - MHI_CH_STATE_SUSPENDED = 0x3, - MHI_CH_STATE_STOP = 0x4, - MHI_CH_STATE_ERROR = 0x5, -}; - -enum MHI_BRSTMODE { - MHI_BRSTMODE_DISABLE = 0x2, - MHI_BRSTMODE_ENABLE = 0x3, -}; - -#define MHI_INVALID_BRSTMODE(mode) (mode != MHI_BRSTMODE_DISABLE && \ - mode != MHI_BRSTMODE_ENABLE) - -#define MHI_IN_PBL(ee) (ee == MHI_EE_PBL || ee == MHI_EE_PTHRU || \ - ee == MHI_EE_EDL) - -#define MHI_IN_MISSION_MODE(ee) (ee == MHI_EE_AMSS || ee == MHI_EE_WFW) - -enum MHI_ST_TRANSITION { - MHI_ST_TRANSITION_PBL, - MHI_ST_TRANSITION_READY, - MHI_ST_TRANSITION_SBL, - MHI_ST_TRANSITION_MISSION_MODE, - MHI_ST_TRANSITION_FP, - MHI_ST_TRANSITION_MAX, -}; - -extern const char * const mhi_state_tran_str[MHI_ST_TRANSITION_MAX]; -#define TO_MHI_STATE_TRANS_STR(state) (((state) >= MHI_ST_TRANSITION_MAX) ? \ - "INVALID_STATE" : mhi_state_tran_str[state]) - -extern const char * const mhi_state_str[MHI_STATE_MAX]; -#define TO_MHI_STATE_STR(state) ((state >= MHI_STATE_MAX || \ - !mhi_state_str[state]) ? \ - "INVALID_STATE" : mhi_state_str[state]) - -enum { - MHI_PM_BIT_DISABLE, - MHI_PM_BIT_POR, - MHI_PM_BIT_M0, - MHI_PM_BIT_M2, - MHI_PM_BIT_M3_ENTER, - MHI_PM_BIT_M3, - MHI_PM_BIT_M3_EXIT, - MHI_PM_BIT_FW_DL_ERR, - MHI_PM_BIT_SYS_ERR_DETECT, - MHI_PM_BIT_SYS_ERR_PROCESS, - MHI_PM_BIT_SHUTDOWN_PROCESS, - MHI_PM_BIT_LD_ERR_FATAL_DETECT, - MHI_PM_BIT_MAX -}; - -/* internal power states */ -enum MHI_PM_STATE { - MHI_PM_DISABLE = BIT(MHI_PM_BIT_DISABLE), /* MHI is not enabled */ - MHI_PM_POR = BIT(MHI_PM_BIT_POR), /* reset state */ - MHI_PM_M0 = BIT(MHI_PM_BIT_M0), - MHI_PM_M2 = BIT(MHI_PM_BIT_M2), - MHI_PM_M3_ENTER = BIT(MHI_PM_BIT_M3_ENTER), - MHI_PM_M3 = BIT(MHI_PM_BIT_M3), - MHI_PM_M3_EXIT = BIT(MHI_PM_BIT_M3_EXIT), - /* firmware download failure state */ - MHI_PM_FW_DL_ERR = BIT(MHI_PM_BIT_FW_DL_ERR), - MHI_PM_SYS_ERR_DETECT = BIT(MHI_PM_BIT_SYS_ERR_DETECT), - MHI_PM_SYS_ERR_PROCESS = BIT(MHI_PM_BIT_SYS_ERR_PROCESS), - MHI_PM_SHUTDOWN_PROCESS = BIT(MHI_PM_BIT_SHUTDOWN_PROCESS), - /* link not accessible */ - MHI_PM_LD_ERR_FATAL_DETECT = BIT(MHI_PM_BIT_LD_ERR_FATAL_DETECT), -}; - -#define MHI_REG_ACCESS_VALID(pm_state) ((pm_state & (MHI_PM_POR | MHI_PM_M0 | \ - MHI_PM_M2 | MHI_PM_M3_ENTER | MHI_PM_M3_EXIT | \ - MHI_PM_SYS_ERR_DETECT | MHI_PM_SYS_ERR_PROCESS | \ - MHI_PM_SHUTDOWN_PROCESS | MHI_PM_FW_DL_ERR))) -#define MHI_PM_IN_ERROR_STATE(pm_state) (pm_state >= MHI_PM_FW_DL_ERR) -#define MHI_PM_IN_FATAL_STATE(pm_state) (pm_state == MHI_PM_LD_ERR_FATAL_DETECT) -#define MHI_DB_ACCESS_VALID(pm_state) (pm_state & MHI_PM_M0) -#define MHI_WAKE_DB_CLEAR_VALID(pm_state) (pm_state & (MHI_PM_M0 | \ - MHI_PM_M2 | MHI_PM_M3_EXIT)) -#define MHI_WAKE_DB_SET_VALID(pm_state) (pm_state & MHI_PM_M2) -#define MHI_WAKE_DB_FORCE_SET_VALID(pm_state) MHI_WAKE_DB_CLEAR_VALID(pm_state) -#define MHI_EVENT_ACCESS_INVALID(pm_state) (pm_state == MHI_PM_DISABLE || \ - MHI_PM_IN_ERROR_STATE(pm_state)) -#define MHI_PM_IN_SUSPEND_STATE(pm_state) (pm_state & \ - (MHI_PM_M3_ENTER | MHI_PM_M3)) - -/* accepted buffer type for the channel */ -enum MHI_XFER_TYPE { - MHI_XFER_BUFFER, - MHI_XFER_SKB, - MHI_XFER_SCLIST, - MHI_XFER_NOP, /* CPU offload channel, host does not accept transfer */ - MHI_XFER_DMA, /* receive dma address, already mapped by client */ - MHI_XFER_RSC_DMA, /* RSC type, accept premapped buffer */ -}; - -#define NR_OF_CMD_RINGS (1) -#define CMD_EL_PER_RING (128) -#define PRIMARY_CMD_RING (0) -#define MHI_DEV_WAKE_DB (127) -#define MHI_MAX_MTU (0xffff) - -enum MHI_ER_TYPE { - MHI_ER_TYPE_INVALID = 0x0, - MHI_ER_TYPE_VALID = 0x1, -}; - -enum mhi_er_data_type { - MHI_ER_DATA_ELEMENT_TYPE, - MHI_ER_CTRL_ELEMENT_TYPE, - MHI_ER_TSYNC_ELEMENT_TYPE, - MHI_ER_DATA_TYPE_MAX = MHI_ER_TSYNC_ELEMENT_TYPE, -}; - -enum mhi_ch_ee_mask { - MHI_CH_EE_PBL = BIT(MHI_EE_PBL), - MHI_CH_EE_SBL = BIT(MHI_EE_SBL), - MHI_CH_EE_AMSS = BIT(MHI_EE_AMSS), - MHI_CH_EE_RDDM = BIT(MHI_EE_RDDM), - MHI_CH_EE_PTHRU = BIT(MHI_EE_PTHRU), - MHI_CH_EE_WFW = BIT(MHI_EE_WFW), - MHI_CH_EE_EDL = BIT(MHI_EE_EDL), -}; - -enum mhi_ch_type { - MHI_CH_TYPE_INVALID = 0, - MHI_CH_TYPE_OUTBOUND = DMA_TO_DEVICE, - MHI_CH_TYPE_INBOUND = DMA_FROM_DEVICE, - MHI_CH_TYPE_INBOUND_COALESCED = 3, -}; - -struct db_cfg { - bool reset_req; - bool db_mode; - u32 pollcfg; - enum MHI_BRSTMODE brstmode; - dma_addr_t db_val; - void (*process_db)(struct mhi_controller *mhi_cntrl, - struct db_cfg *db_cfg, void __iomem *io_addr, - dma_addr_t db_val); -}; - -struct mhi_pm_transitions { - enum MHI_PM_STATE from_state; - u32 to_states; -}; - -struct state_transition { - struct list_head node; - enum MHI_ST_TRANSITION state; -}; - -/* Control Segment */ -struct mhi_ctrl_seg -{ - struct mhi_tre hw_in_chan_ring[NUM_MHI_IPA_IN_RING_ELEMENTS] __packed __aligned(NUM_MHI_IPA_IN_RING_ELEMENTS*16); - struct mhi_tre hw_out_chan_ring[NUM_MHI_IPA_OUT_RING_ELEMENTS] __packed __aligned(NUM_MHI_IPA_OUT_RING_ELEMENTS*16); -#ifdef ENABLE_IP_SW0 - struct mhi_tre sw_in_chan_ring[NUM_MHI_SW_IP_RING_ELEMENTS] __packed __aligned(NUM_MHI_IPA_IN_RING_ELEMENTS*16); - struct mhi_tre sw_out_chan_ring[NUM_MHI_SW_IP_RING_ELEMENTS] __packed __aligned(NUM_MHI_IPA_OUT_RING_ELEMENTS*16); -#endif - -#ifdef ENABLE_ADPL - struct mhi_tre adpl_in_chan_ring[NUM_MHI_ADPL_RING_ELEMENTS] __packed __aligned(NUM_MHI_IPA_IN_RING_ELEMENTS*16); -#endif - -#ifdef ENABLE_QDSS - struct mhi_tre qdss_in_chan_ring[NUM_MHI_QDSS_RING_ELEMENTS] __packed __aligned(NUM_MHI_IPA_IN_RING_ELEMENTS*16); -#endif - - struct mhi_tre diag_in_chan_ring[NUM_MHI_DIAG_IN_RING_ELEMENTS] __packed __aligned(NUM_MHI_IPA_OUT_RING_ELEMENTS*16); - struct mhi_tre chan_ring[NUM_MHI_CHAN_RING_ELEMENTS*2*12] __packed __aligned(NUM_MHI_CHAN_RING_ELEMENTS*16); - struct mhi_tre event_ring[NUM_MHI_EVT_RINGS][NUM_MHI_EVT_RING_ELEMENTS] __packed __aligned(NUM_MHI_EVT_RING_ELEMENTS*16); - struct mhi_tre cmd_ring[NR_OF_CMD_RINGS][CMD_EL_PER_RING] __packed __aligned(CMD_EL_PER_RING*16); - - struct mhi_chan_ctxt chan_ctxt[NUM_MHI_XFER_RINGS] __aligned(128); - struct mhi_event_ctxt er_ctxt[NUM_MHI_EVT_RINGS] __aligned(128); - struct mhi_cmd_ctxt cmd_ctxt[NR_OF_CMD_RINGS] __aligned(128); -} __aligned(4096); - -struct mhi_ctxt { - struct mhi_event_ctxt *er_ctxt; - struct mhi_chan_ctxt *chan_ctxt; - struct mhi_cmd_ctxt *cmd_ctxt; - dma_addr_t er_ctxt_addr; - dma_addr_t chan_ctxt_addr; - dma_addr_t cmd_ctxt_addr; - struct mhi_ctrl_seg *ctrl_seg; - dma_addr_t ctrl_seg_addr; -}; - -struct mhi_ring { - dma_addr_t dma_handle; - dma_addr_t iommu_base; - u64 *ctxt_wp; /* point to ctxt wp */ - void *pre_aligned; - void *base; - void *rp; - void *wp; - size_t el_size; - size_t len; - size_t elements; - size_t alloc_size; - void __iomem *db_addr; -}; - -struct mhi_cmd { - struct mhi_ring ring; - spinlock_t lock; -}; - -struct mhi_buf_info { - dma_addr_t p_addr; - void *v_addr; - void *bb_addr; - void *wp; - size_t len; - void *cb_buf; - bool used; /* indicate element is free to use */ - bool pre_mapped; /* already pre-mapped by client */ - enum dma_data_direction dir; -}; - -struct mhi_event { - u32 er_index; - u32 intmod; - u32 msi; - int chan; /* this event ring is dedicated to a channel */ - u32 priority; - enum mhi_er_data_type data_type; - struct mhi_ring ring; - struct db_cfg db_cfg; - u32 used_elements; - bool hw_ring; - bool cl_manage; - bool offload_ev; /* managed by a device driver */ - spinlock_t lock; - struct mhi_chan *mhi_chan; /* dedicated to channel */ - struct tasklet_struct task; - int (*process_event)(struct mhi_controller *mhi_cntrl, - struct mhi_event *mhi_event, - u32 event_quota); - struct mhi_controller *mhi_cntrl; -}; - -struct mhi_chan { - u32 chan; - const char *name; - /* - * important, when consuming increment tre_ring first, when releasing - * decrement buf_ring first. If tre_ring has space, buf_ring - * guranteed to have space so we do not need to check both rings. - */ - struct mhi_ring buf_ring; - struct mhi_ring tre_ring; - - u32 used_elements; - u32 used_events[MHI_EV_CC_DB_MODE+1]; - - u32 er_index; - u32 intmod; - enum mhi_ch_type type; - enum dma_data_direction dir; - struct db_cfg db_cfg; - u32 ee_mask; - enum MHI_XFER_TYPE xfer_type; - enum MHI_CH_STATE ch_state; - enum MHI_EV_CCS ccs; - bool lpm_notify; - bool configured; - bool offload_ch; - bool pre_alloc; - bool auto_start; - bool wake_capable; /* channel should wake up system */ - /* functions that generate the transfer ring elements */ - int (*gen_tre)(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan, void *buf, void *cb, - size_t len, enum MHI_FLAGS flags); - int (*queue_xfer)(struct mhi_device *mhi_dev, - struct mhi_chan *mhi_chan, void *buf, - size_t len, enum MHI_FLAGS flags); - /* xfer call back */ - struct mhi_device *mhi_dev; - void (*xfer_cb)(struct mhi_device *mhi_dev, struct mhi_result *result); - struct mutex mutex; - struct completion completion; - rwlock_t lock; - u32 ring; - u32 tiocm; - struct list_head node; -}; - -struct tsync_node { - struct list_head node; - u32 sequence; - u64 local_time; - u64 remote_time; - struct mhi_device *mhi_dev; - void (*cb_func)(struct mhi_device *mhi_dev, u32 sequence, - u64 local_time, u64 remote_time); -}; - -struct mhi_timesync { - u32 er_index; - void __iomem *db; - void __iomem *time_reg; - enum MHI_EV_CCS ccs; - struct completion completion; - spinlock_t lock; /* list protection */ - struct mutex lpm_mutex; /* lpm protection */ - struct list_head head; -}; - -struct mhi_bus { - struct list_head controller_list; - struct mutex lock; - struct dentry *dentry; -}; - -/* default MHI timeout */ -#define MHI_TIMEOUT_MS (3000) -extern struct mhi_bus mhi_bus; - -/* debug fs related functions */ -int mhi_debugfs_mhi_chan_show(struct seq_file *m, void *d); -int mhi_debugfs_mhi_event_show(struct seq_file *m, void *d); -int mhi_debugfs_mhi_states_show(struct seq_file *m, void *d); -int mhi_debugfs_trigger_reset(void *data, u64 val); - -void mhi_deinit_debugfs(struct mhi_controller *mhi_cntrl); -void mhi_init_debugfs(struct mhi_controller *mhi_cntrl); - -/* power management apis */ -enum MHI_PM_STATE __must_check mhi_tryset_pm_state( - struct mhi_controller *mhi_cntrl, - enum MHI_PM_STATE state); -const char *to_mhi_pm_state_str(enum MHI_PM_STATE state); -void mhi_reset_chan(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan); -enum mhi_ee mhi_get_exec_env(struct mhi_controller *mhi_cntrl); -int mhi_queue_state_transition(struct mhi_controller *mhi_cntrl, - enum MHI_ST_TRANSITION state); -void mhi_pm_st_worker(struct work_struct *work); -void mhi_fw_load_worker(struct work_struct *work); -void mhi_pm_sys_err_worker(struct work_struct *work); -void mhi_pm_ready_worker(struct work_struct *work); -int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl); -void mhi_ctrl_ev_task(unsigned long data); -int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl); -void mhi_pm_m1_transition(struct mhi_controller *mhi_cntrl); -int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl); -void mhi_notify(struct mhi_device *mhi_dev, enum MHI_CB cb_reason); -int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, - struct mhi_event *mhi_event, u32 event_quota); -int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, - struct mhi_event *mhi_event, u32 event_quota); -int mhi_process_tsync_event_ring(struct mhi_controller *mhi_cntrl, - struct mhi_event *mhi_event, u32 event_quota); -int mhi_send_cmd(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, - enum MHI_CMD cmd); -int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl); - -/* queue transfer buffer */ -int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, - void *buf, void *cb, size_t buf_len, enum MHI_FLAGS flags); -int mhi_queue_buf(struct mhi_device *mhi_dev, struct mhi_chan *mhi_chan, - void *buf, size_t len, enum MHI_FLAGS mflags); -int mhi_queue_skb(struct mhi_device *mhi_dev, struct mhi_chan *mhi_chan, - void *buf, size_t len, enum MHI_FLAGS mflags); -int mhi_queue_sclist(struct mhi_device *mhi_dev, struct mhi_chan *mhi_chan, - void *buf, size_t len, enum MHI_FLAGS mflags); -int mhi_queue_nop(struct mhi_device *mhi_dev, struct mhi_chan *mhi_chan, - void *buf, size_t len, enum MHI_FLAGS mflags); -int mhi_queue_dma(struct mhi_device *mhi_dev, struct mhi_chan *mhi_chan, - void *buf, size_t len, enum MHI_FLAGS mflags); - -/* register access methods */ -void mhi_db_brstmode(struct mhi_controller *mhi_cntrl, struct db_cfg *db_cfg, - void __iomem *db_addr, dma_addr_t wp); -void mhi_db_brstmode_disable(struct mhi_controller *mhi_cntrl, - struct db_cfg *db_mode, void __iomem *db_addr, - dma_addr_t wp); -int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl, - void __iomem *base, u32 offset, u32 *out); -int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl, - void __iomem *base, u32 offset, u32 mask, - u32 shift, u32 *out); -void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base, - u32 offset, u32 val); -void mhi_write_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base, - u32 offset, u32 mask, u32 shift, u32 val); -void mhi_ring_er_db(struct mhi_event *mhi_event); -void mhi_write_db(struct mhi_controller *mhi_cntrl, void __iomem *db_addr, - dma_addr_t wp); -void mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd); -void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan); -int mhi_get_capability_offset(struct mhi_controller *mhi_cntrl, u32 capability, - u32 *offset); -int mhi_init_timesync(struct mhi_controller *mhi_cntrl); -int mhi_create_timesync_sysfs(struct mhi_controller *mhi_cntrl); -void mhi_destroy_timesync(struct mhi_controller *mhi_cntrl); - -/* memory allocation methods */ -static inline void *mhi_alloc_coherent(struct mhi_controller *mhi_cntrl, - size_t size, - dma_addr_t *dma_handle, - gfp_t gfp) -{ -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 5,0,0 )) - void *buf = dma_zalloc_coherent(mhi_cntrl->dev, size, dma_handle, gfp); -#else - void *buf = dma_alloc_coherent(mhi_cntrl->dev, size, dma_handle, gfp | __GFP_ZERO); -#endif - - MHI_LOG("size = %zd, dma_handle = %llx\n", size, (u64)*dma_handle); - if (buf) - atomic_add(size, &mhi_cntrl->alloc_size); - - return buf; -} -static inline void mhi_free_coherent(struct mhi_controller *mhi_cntrl, - size_t size, - void *vaddr, - dma_addr_t dma_handle) -{ - atomic_sub(size, &mhi_cntrl->alloc_size); - dma_free_coherent(mhi_cntrl->dev, size, vaddr, dma_handle); -} -struct mhi_device *mhi_alloc_device(struct mhi_controller *mhi_cntrl); -static inline void mhi_dealloc_device(struct mhi_controller *mhi_cntrl, - struct mhi_device *mhi_dev) -{ - kfree(mhi_dev); -} -int mhi_destroy_device(struct device *dev, void *data); -void mhi_create_devices(struct mhi_controller *mhi_cntrl); -int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl, - struct image_info **image_info, size_t alloc_size); -void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl, - struct image_info *image_info); - -int mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl, - struct mhi_buf_info *buf_info); -int mhi_map_single_use_bb(struct mhi_controller *mhi_cntrl, - struct mhi_buf_info *buf_info); -void mhi_unmap_single_no_bb(struct mhi_controller *mhi_cntrl, - struct mhi_buf_info *buf_info); -void mhi_unmap_single_use_bb(struct mhi_controller *mhi_cntrl, - struct mhi_buf_info *buf_info); - -/* initialization methods */ -int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan); -void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan); -int mhi_init_mmio(struct mhi_controller *mhi_cntrl); -int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl); -void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl); -int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl); -void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl); -int mhi_dtr_init(void); - -/* isr handlers */ -irqreturn_t mhi_one_msi_handlr(int irq_number, void *dev); -irqreturn_t mhi_msi_handlr(int irq_number, void *dev); -irqreturn_t mhi_intvec_threaded_handlr(int irq_number, void *dev); -irqreturn_t mhi_intvec_handlr(int irq_number, void *dev); -void mhi_ev_task(unsigned long data); - -#ifdef CONFIG_MHI_DEBUG - -#define MHI_ASSERT(cond, msg) do { \ - if (cond) \ - panic(msg); \ -} while (0) - -#else - -#define MHI_ASSERT(cond, msg) do { \ - if (cond) { \ - MHI_ERR(msg); \ - WARN_ON(cond); \ - } \ -} while (0) - -#endif - -#endif /* _MHI_INT_H */ diff --git a/quectel_MHI/src/core/mhi_main.c b/quectel_MHI/src/core/mhi_main.c deleted file mode 100644 index 12d44e6b8..000000000 --- a/quectel_MHI/src/core/mhi_main.c +++ /dev/null @@ -1,2722 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mhi.h" -#include "mhi_internal.h" - -static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan); - -int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl, - void __iomem *base, - u32 offset, - u32 *out) -{ - u32 tmp = readl_relaxed(base + offset); - - /* unexpected value, query the link status */ - if (PCI_INVALID_READ(tmp) && - mhi_cntrl->link_status(mhi_cntrl, mhi_cntrl->priv_data)) - return -EIO; - - *out = tmp; - - return 0; -} - -int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl, - void __iomem *base, - u32 offset, - u32 mask, - u32 shift, - u32 *out) -{ - u32 tmp; - int ret; - - ret = mhi_read_reg(mhi_cntrl, base, offset, &tmp); - if (ret) - return ret; - - *out = (tmp & mask) >> shift; - - return 0; -} - -int mhi_get_capability_offset(struct mhi_controller *mhi_cntrl, - u32 capability, - u32 *offset) -{ - u32 cur_cap, next_offset; - int ret; - - /* get the 1st supported capability offset */ - ret = mhi_read_reg_field(mhi_cntrl, mhi_cntrl->regs, MISC_OFFSET, - MISC_CAP_MASK, MISC_CAP_SHIFT, offset); - if (ret) - return ret; - if (*offset >= 0x1000) - return -ENXIO; - do { - ret = mhi_read_reg_field(mhi_cntrl, mhi_cntrl->regs, *offset, - CAP_CAPID_MASK, CAP_CAPID_SHIFT, - &cur_cap); - if (ret) - return ret; - - if (cur_cap == capability) - return 0; - - ret = mhi_read_reg_field(mhi_cntrl, mhi_cntrl->regs, *offset, - CAP_NEXT_CAP_MASK, CAP_NEXT_CAP_SHIFT, - &next_offset); - if (ret) - return ret; - - *offset += next_offset; - } while (next_offset); - - return -ENXIO; -} - -void mhi_write_reg(struct mhi_controller *mhi_cntrl, - void __iomem *base, - u32 offset, - u32 val) -{ - writel_relaxed(val, base + offset); -} - -void mhi_write_reg_field(struct mhi_controller *mhi_cntrl, - void __iomem *base, - u32 offset, - u32 mask, - u32 shift, - u32 val) -{ - int ret; - u32 tmp; - - ret = mhi_read_reg(mhi_cntrl, base, offset, &tmp); - if (ret) - return; - - tmp &= ~mask; - tmp |= (val << shift); - mhi_write_reg(mhi_cntrl, base, offset, tmp); -} - -void mhi_write_db(struct mhi_controller *mhi_cntrl, - void __iomem *db_addr, - dma_addr_t wp) -{ - mhi_write_reg(mhi_cntrl, db_addr, 4, upper_32_bits(wp)); - mhi_write_reg(mhi_cntrl, db_addr, 0, lower_32_bits(wp)); -#if 0 //carl.yin 20190527 for debug - if ((lower_32_bits(db_addr)&0xFFF) != 0x620) - { - u32 out = 0; - int ret = mhi_read_reg(mhi_cntrl, db_addr, 0, &out); - if (out != lower_32_bits(wp)) - MHI_ERR("%s db=%x, wp=w:%x - r:%x, ret=%d\n", __func__, lower_32_bits(db_addr), lower_32_bits(wp), out, ret); - } -#endif -} - -void mhi_db_brstmode(struct mhi_controller *mhi_cntrl, - struct db_cfg *db_cfg, - void __iomem *db_addr, - dma_addr_t wp) -{ - if (db_cfg->db_mode) { - db_cfg->db_val = wp; - mhi_write_db(mhi_cntrl, db_addr, wp); - db_cfg->db_mode = false; - } -} - -void mhi_db_brstmode_disable(struct mhi_controller *mhi_cntrl, - struct db_cfg *db_cfg, - void __iomem *db_addr, - dma_addr_t wp) -{ - db_cfg->db_val = wp; - mhi_write_db(mhi_cntrl, db_addr, wp); -} - -void mhi_ring_er_db(struct mhi_event *mhi_event) -{ - struct mhi_ring *ring = &mhi_event->ring; - - mhi_event->db_cfg.process_db(mhi_event->mhi_cntrl, &mhi_event->db_cfg, - ring->db_addr, *ring->ctxt_wp); -} - -void mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd) -{ - dma_addr_t db; - struct mhi_ring *ring = &mhi_cmd->ring; - - db = ring->iommu_base + (ring->wp - ring->base); - *ring->ctxt_wp = db; - mhi_write_db(mhi_cntrl, ring->db_addr, db); -} - -//#define DEBUG_CHAN100_DB -#ifdef DEBUG_CHAN100_DB -static atomic_t chan100_seq = ATOMIC_INIT(0); -#define CHAN100_SIZE 0x1000 -static unsigned int chan100_t[CHAN100_SIZE]; -#endif - -void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan) -{ - struct mhi_ring *ring = &mhi_chan->tre_ring; - dma_addr_t db; - - db = ring->iommu_base + (ring->wp - ring->base); - /* - * Writes to the new ring element must be visible to the hardware - * before letting h/w know there is new element to fetch. - */ - dma_wmb(); - *ring->ctxt_wp = db; - mhi_chan->db_cfg.process_db(mhi_cntrl, &mhi_chan->db_cfg, ring->db_addr, - db); -} - -enum mhi_ee mhi_get_exec_env(struct mhi_controller *mhi_cntrl) -{ - u32 exec; - int ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_EXECENV, &exec); - - return (ret) ? MHI_EE_MAX : exec; -} - -enum mhi_dev_state mhi_get_mhi_state(struct mhi_controller *mhi_cntrl) -{ - u32 state; - int ret = mhi_read_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS, - MHISTATUS_MHISTATE_MASK, - MHISTATUS_MHISTATE_SHIFT, &state); - return ret ? MHI_STATE_MAX : state; -} - -int mhi_queue_sclist(struct mhi_device *mhi_dev, - struct mhi_chan *mhi_chan, - void *buf, - size_t len, - enum MHI_FLAGS mflags) -{ - return -EINVAL; -} - -int mhi_queue_nop(struct mhi_device *mhi_dev, - struct mhi_chan *mhi_chan, - void *buf, - size_t len, - enum MHI_FLAGS mflags) -{ - return -EINVAL; -} - -static void mhi_add_ring_element(struct mhi_controller *mhi_cntrl, - struct mhi_ring *ring) -{ - void *wp = ring->wp; - wp += ring->el_size; - if (wp >= (ring->base + ring->len)) - wp = ring->base; - ring->wp = wp; - /* smp update */ - smp_wmb(); -} - -static void mhi_del_ring_element(struct mhi_controller *mhi_cntrl, - struct mhi_ring *ring) -{ - void *rp = ring->rp; - rp += ring->el_size; - if (rp >= (ring->base + ring->len)) - rp = ring->base; - ring->rp = rp; - /* smp update */ - smp_wmb(); -} - -static int get_nr_avail_ring_elements(struct mhi_controller *mhi_cntrl, - struct mhi_ring *ring) -{ - int nr_el; - - if (ring->wp < ring->rp) - nr_el = ((ring->rp - ring->wp) / ring->el_size) - 1; - else { - nr_el = (ring->rp - ring->base) / ring->el_size; - nr_el += ((ring->base + ring->len - ring->wp) / - ring->el_size) - 1; - } - return nr_el; -} - -static u32 get_used_ring_elements(void *rp, void *wp, u32 el_num) -{ - u32 nr_el; - - if (wp >= rp) - nr_el = (wp - rp)/sizeof(struct mhi_tre); - else { - nr_el = (rp - wp)/sizeof(struct mhi_tre); - nr_el = el_num - nr_el; - } - return nr_el; -} - -static void *mhi_to_virtual(struct mhi_ring *ring, dma_addr_t addr) -{ - return (addr - ring->iommu_base) + ring->base; -} - -dma_addr_t mhi_to_physical(struct mhi_ring *ring, void *addr) -{ - return (addr - ring->base) + ring->iommu_base; -} - -static void mhi_recycle_ev_ring_element(struct mhi_controller *mhi_cntrl, - struct mhi_ring *ring) -{ - void *rp, *wp; - - /* update the WP */ - wp = ring->wp; - wp += ring->el_size; - if (wp >= (ring->base + ring->len)) { - wp = ring->base; - } - ring->wp = wp; - - *ring->ctxt_wp = ring->iommu_base + (ring->wp - ring->base); - - /* update the RP */ - rp = ring->rp; - rp += ring->el_size; - if (rp >= (ring->base + ring->len)) - rp = ring->base; - ring->rp = rp; - - /* visible to other cores */ - smp_wmb(); -} - -static bool mhi_is_ring_full(struct mhi_controller *mhi_cntrl, - struct mhi_ring *ring) -{ - void *tmp = ring->wp + ring->el_size; - - if (tmp >= (ring->base + ring->len)) - tmp = ring->base; - - return (tmp == ring->rp); -} - -int mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl, - struct mhi_buf_info *buf_info) -{ - buf_info->p_addr = dma_map_single(mhi_cntrl->dev, buf_info->v_addr, - buf_info->len, buf_info->dir); - if (dma_mapping_error(mhi_cntrl->dev, buf_info->p_addr)) - return -ENOMEM; - - return 0; -} - -int mhi_map_single_use_bb(struct mhi_controller *mhi_cntrl, - struct mhi_buf_info *buf_info) -{ - void *buf = mhi_alloc_coherent(mhi_cntrl, buf_info->len, - &buf_info->p_addr, GFP_ATOMIC); - - if (!buf) - return -ENOMEM; - - if (buf_info->dir == DMA_TO_DEVICE) - memcpy(buf, buf_info->v_addr, buf_info->len); - - buf_info->bb_addr = buf; - - return 0; -} - -void mhi_unmap_single_no_bb(struct mhi_controller *mhi_cntrl, - struct mhi_buf_info *buf_info) -{ - dma_unmap_single(mhi_cntrl->dev, buf_info->p_addr, buf_info->len, - buf_info->dir); -} - -void mhi_unmap_single_use_bb(struct mhi_controller *mhi_cntrl, - struct mhi_buf_info *buf_info) -{ - if (buf_info->dir == DMA_FROM_DEVICE) - memcpy(buf_info->v_addr, buf_info->bb_addr, buf_info->len); - - mhi_free_coherent(mhi_cntrl, buf_info->len, buf_info->bb_addr, - buf_info->p_addr); -} - -#ifdef ENABLE_MHI_MON -static void mon_bus_submit(struct mhi_controller *mbus, u32 chan, dma_addr_t wp, struct mhi_tre *mhi_tre, void *buf, size_t len) -{ - unsigned long flags; - struct list_head *pos; - struct mon_reader *r; - - spin_lock_irqsave(&mbus->lock, flags); - mbus->cnt_events++; - list_for_each (pos, &mbus->r_list) { - r = list_entry(pos, struct mon_reader, r_link); - r->rnf_submit(r->r_data, chan, wp, mhi_tre, buf, len); - } - spin_unlock_irqrestore(&mbus->lock, flags); -} - -static void mon_bus_receive(struct mhi_controller *mbus, u32 chan, dma_addr_t wp, struct mhi_tre *mhi_tre, void *buf, size_t len) -{ - unsigned long flags; - struct list_head *pos; - struct mon_reader *r; - - spin_lock_irqsave(&mbus->lock, flags); - mbus->cnt_events++; - list_for_each (pos, &mbus->r_list) { - r = list_entry(pos, struct mon_reader, r_link); - r->rnf_receive(r->r_data, chan, wp, mhi_tre, buf, len); - } - spin_unlock_irqrestore(&mbus->lock, flags); -} - -static void mon_bus_complete(struct mhi_controller *mbus, u32 chan, dma_addr_t wp, struct mhi_tre *mhi_tre) -{ - unsigned long flags; - struct list_head *pos; - struct mon_reader *r; - - spin_lock_irqsave(&mbus->lock, flags); - mbus->cnt_events++; - list_for_each (pos, &mbus->r_list) { - r = list_entry(pos, struct mon_reader, r_link); - r->rnf_complete(r->r_data, chan, wp, mhi_tre); - } - spin_unlock_irqrestore(&mbus->lock, flags); -} -#endif - -int mhi_queue_skb(struct mhi_device *mhi_dev, - struct mhi_chan *mhi_chan, - void *buf, - size_t len, - enum MHI_FLAGS mflags) -{ - struct sk_buff *skb = buf; - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - struct mhi_ring *tre_ring = &mhi_chan->tre_ring; - struct mhi_ring *buf_ring = &mhi_chan->buf_ring; - struct mhi_buf_info *buf_info; - struct mhi_tre *mhi_tre; - bool assert_wake = false; - int ret; - - if (mhi_is_ring_full(mhi_cntrl, tre_ring)) - return -ENOMEM; - - read_lock_bh(&mhi_cntrl->pm_lock); - if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) { - MHI_VERB("MHI is not in activate state, pm_state:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - read_unlock_bh(&mhi_cntrl->pm_lock); - - return -EIO; - } - - /* we're in M3 or transitioning to M3 */ - if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) { - mhi_cntrl->runtime_get(mhi_cntrl, mhi_cntrl->priv_data); - mhi_cntrl->runtime_put(mhi_cntrl, mhi_cntrl->priv_data); - } - - /* - * For UL channels always assert WAKE until work is done, - * For DL channels only assert if MHI is in a LPM - */ - if (mhi_chan->dir == DMA_TO_DEVICE || - (mhi_chan->dir == DMA_FROM_DEVICE && - mhi_cntrl->pm_state != MHI_PM_M0)) { - assert_wake = true; - mhi_cntrl->wake_get(mhi_cntrl, false); - } - - /* generate the tre */ - buf_info = buf_ring->wp; - buf_info->v_addr = skb->data; - buf_info->cb_buf = skb; - buf_info->wp = tre_ring->wp; - buf_info->dir = mhi_chan->dir; - buf_info->len = len; - ret = mhi_cntrl->map_single(mhi_cntrl, buf_info); - if (ret) - goto map_error; - - mhi_tre = tre_ring->wp; - - mhi_tre->ptr = MHI_TRE_DATA_PTR(buf_info->p_addr); - mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(buf_info->len); - mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(1, 1, 0, 0); - -#ifdef ENABLE_MHI_MON - if (mhi_cntrl->nreaders) { - mon_bus_submit(mhi_cntrl, mhi_chan->chan, - mhi_to_physical(tre_ring, mhi_tre), mhi_tre, buf_info->v_addr, mhi_chan->chan&0x1 ? 0 : buf_info->len); - } -#endif - - MHI_VERB("chan:%d WP:0x%llx TRE:0x%llx 0x%08x 0x%08x\n", mhi_chan->chan, - (u64)mhi_to_physical(tre_ring, mhi_tre), mhi_tre->ptr, - mhi_tre->dword[0], mhi_tre->dword[1]); - - if (mhi_chan->dir == DMA_TO_DEVICE) { - if (atomic_inc_return(&mhi_cntrl->pending_pkts) == 1) - mhi_cntrl->runtime_get(mhi_cntrl, mhi_cntrl->priv_data); - } - - read_lock_bh(&mhi_chan->lock); - /* increment WP */ - mhi_add_ring_element(mhi_cntrl, tre_ring); - mhi_add_ring_element(mhi_cntrl, buf_ring); - -#ifdef DEBUG_CHAN100_DB - if (mhi_chan->chan == 100) { - chan100_t[atomic_inc_return(&chan100_seq)&(CHAN100_SIZE-1)] = (((unsigned long)tre_ring->wp)&0xffff) | (mhi_chan->db_cfg.db_mode<<31) | (0<<30); - } -#endif - if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl->pm_state))) { - mhi_ring_chan_db(mhi_cntrl, mhi_chan); - } - read_unlock_bh(&mhi_chan->lock); - - if (mhi_chan->dir == DMA_FROM_DEVICE && assert_wake) - mhi_cntrl->wake_put(mhi_cntrl, true); - - read_unlock_bh(&mhi_cntrl->pm_lock); - - return 0; - -map_error: - if (assert_wake) - mhi_cntrl->wake_put(mhi_cntrl, false); - - read_unlock_bh(&mhi_cntrl->pm_lock); - - return ret; -} - -int mhi_queue_dma(struct mhi_device *mhi_dev, - struct mhi_chan *mhi_chan, - void *buf, - size_t len, - enum MHI_FLAGS mflags) -{ - struct mhi_buf *mhi_buf = buf; - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - struct mhi_ring *tre_ring = &mhi_chan->tre_ring; - struct mhi_ring *buf_ring = &mhi_chan->buf_ring; - struct mhi_buf_info *buf_info; - struct mhi_tre *mhi_tre; - bool assert_wake = false; - - if (mhi_is_ring_full(mhi_cntrl, tre_ring)) - return -ENOMEM; - - read_lock_bh(&mhi_cntrl->pm_lock); - if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) { - MHI_VERB("MHI is not in activate state, pm_state:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - read_unlock_bh(&mhi_cntrl->pm_lock); - - return -EIO; - } - - /* we're in M3 or transitioning to M3 */ - if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) { - mhi_cntrl->runtime_get(mhi_cntrl, mhi_cntrl->priv_data); - mhi_cntrl->runtime_put(mhi_cntrl, mhi_cntrl->priv_data); - } - - /* - * For UL channels always assert WAKE until work is done, - * For DL channels only assert if MHI is in a LPM - */ - if (mhi_chan->dir == DMA_TO_DEVICE || - (mhi_chan->dir == DMA_FROM_DEVICE && - mhi_cntrl->pm_state != MHI_PM_M0)) { - assert_wake = true; - mhi_cntrl->wake_get(mhi_cntrl, false); - } - - /* generate the tre */ - buf_info = buf_ring->wp; - MHI_ASSERT(buf_info->used, "TRE Not Freed\n"); - buf_info->p_addr = mhi_buf->dma_addr; - buf_info->pre_mapped = true; - buf_info->cb_buf = mhi_buf; - buf_info->wp = tre_ring->wp; - buf_info->dir = mhi_chan->dir; - buf_info->len = len; - - mhi_tre = tre_ring->wp; - - if (mhi_chan->xfer_type == MHI_XFER_RSC_DMA) { - buf_info->used = true; - mhi_tre->ptr = - MHI_RSCTRE_DATA_PTR(buf_info->p_addr, buf_info->len); - mhi_tre->dword[0] = - MHI_RSCTRE_DATA_DWORD0(buf_ring->wp - buf_ring->base); - mhi_tre->dword[1] = MHI_RSCTRE_DATA_DWORD1; - } else { - mhi_tre->ptr = MHI_TRE_DATA_PTR(buf_info->p_addr); - mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(buf_info->len); - mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(1, 1, 0, 0); - } - -#ifdef ENABLE_MHI_MON - if (mhi_cntrl->nreaders) { - mon_bus_submit(mhi_cntrl, mhi_chan->chan, - mhi_to_physical(tre_ring, mhi_tre), mhi_tre, buf_info->v_addr, mhi_chan->chan&0x1 ? 0: buf_info->len); - } -#endif - - MHI_VERB("chan:%d WP:0x%llx TRE:0x%llx 0x%08x 0x%08x\n", mhi_chan->chan, - (u64)mhi_to_physical(tre_ring, mhi_tre), mhi_tre->ptr, - mhi_tre->dword[0], mhi_tre->dword[1]); - - if (mhi_chan->dir == DMA_TO_DEVICE) { - if (atomic_inc_return(&mhi_cntrl->pending_pkts) == 1) - mhi_cntrl->runtime_get(mhi_cntrl, mhi_cntrl->priv_data); - } - - read_lock_bh(&mhi_chan->lock); - /* increment WP */ - mhi_add_ring_element(mhi_cntrl, tre_ring); - mhi_add_ring_element(mhi_cntrl, buf_ring); - - if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl->pm_state))) { - mhi_ring_chan_db(mhi_cntrl, mhi_chan); - } - read_unlock_bh(&mhi_chan->lock); - - if (mhi_chan->dir == DMA_FROM_DEVICE && assert_wake) - mhi_cntrl->wake_put(mhi_cntrl, true); - - read_unlock_bh(&mhi_cntrl->pm_lock); - - return 0; -} - -int mhi_gen_tre(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan, - void *buf, - void *cb, - size_t buf_len, - enum MHI_FLAGS flags) -{ - struct mhi_ring *buf_ring, *tre_ring; - struct mhi_tre *mhi_tre; - struct mhi_buf_info *buf_info; - int eot, eob, chain, bei; - int ret; - - buf_ring = &mhi_chan->buf_ring; - tre_ring = &mhi_chan->tre_ring; - - buf_info = buf_ring->wp; - buf_info->v_addr = buf; - buf_info->cb_buf = cb; - buf_info->wp = tre_ring->wp; - buf_info->dir = mhi_chan->dir; - buf_info->len = buf_len; - - ret = mhi_cntrl->map_single(mhi_cntrl, buf_info); - if (ret) - return ret; - - eob = !!(flags & MHI_EOB); - eot = !!(flags & MHI_EOT); - chain = !!(flags & MHI_CHAIN); - bei = !!(mhi_chan->intmod); - - mhi_tre = tre_ring->wp; - mhi_tre->ptr = MHI_TRE_DATA_PTR(buf_info->p_addr); - mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(buf_len); - mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(bei, eot, eob, chain); - -#ifdef ENABLE_MHI_MON - if (mhi_cntrl->nreaders) { - mon_bus_submit(mhi_cntrl, mhi_chan->chan, - mhi_to_physical(tre_ring, mhi_tre), mhi_tre, buf_info->v_addr, mhi_chan->chan&0x1 ? 0 : buf_info->len); - } -#endif - MHI_VERB("chan:%d WP:0x%llx TRE:0x%llx 0x%08x 0x%08x\n", mhi_chan->chan, - (u64)mhi_to_physical(tre_ring, mhi_tre), mhi_tre->ptr, - mhi_tre->dword[0], mhi_tre->dword[1]); - - /* increment WP */ - mhi_add_ring_element(mhi_cntrl, tre_ring); - mhi_add_ring_element(mhi_cntrl, buf_ring); - - return 0; -} - -int mhi_queue_buf(struct mhi_device *mhi_dev, - struct mhi_chan *mhi_chan, - void *buf, - size_t len, - enum MHI_FLAGS mflags) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - struct mhi_ring *tre_ring; - unsigned long flags; - bool assert_wake = false; - int ret; - - /* - * this check here only as a guard, it's always - * possible mhi can enter error while executing rest of function, - * which is not fatal so we do not need to hold pm_lock - */ - if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) { - MHI_VERB("MHI is not in active state, pm_state:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - - return -EIO; - } - - tre_ring = &mhi_chan->tre_ring; - if (mhi_is_ring_full(mhi_cntrl, tre_ring)) - return -ENOMEM; - - ret = mhi_chan->gen_tre(mhi_cntrl, mhi_chan, buf, buf, len, mflags); - if (unlikely(ret)) - return ret; - - read_lock_irqsave(&mhi_cntrl->pm_lock, flags); - - /* we're in M3 or transitioning to M3 */ - if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) { - mhi_cntrl->runtime_get(mhi_cntrl, mhi_cntrl->priv_data); - mhi_cntrl->runtime_put(mhi_cntrl, mhi_cntrl->priv_data); - } - - /* - * For UL channels always assert WAKE until work is done, - * For DL channels only assert if MHI is in a LPM - */ - if (mhi_chan->dir == DMA_TO_DEVICE || - (mhi_chan->dir == DMA_FROM_DEVICE && - mhi_cntrl->pm_state != MHI_PM_M0)) { - assert_wake = true; - mhi_cntrl->wake_get(mhi_cntrl, false); - } - - if (mhi_chan->dir == DMA_TO_DEVICE) { - if (atomic_inc_return(&mhi_cntrl->pending_pkts) == 1) - mhi_cntrl->runtime_get(mhi_cntrl, mhi_cntrl->priv_data); - } - - if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl->pm_state))) { - unsigned long flags; - - read_lock_irqsave(&mhi_chan->lock, flags); - mhi_ring_chan_db(mhi_cntrl, mhi_chan); - read_unlock_irqrestore(&mhi_chan->lock, flags); - } - - if (mhi_chan->dir == DMA_FROM_DEVICE && assert_wake) - mhi_cntrl->wake_put(mhi_cntrl, true); - - read_unlock_irqrestore(&mhi_cntrl->pm_lock, flags); - - return 0; -} - -static ssize_t ul_chan_id_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct mhi_device *mhi_dev = to_mhi_device(dev); - - return snprintf(buf, PAGE_SIZE, "%d\n", mhi_dev->ul_chan_id); -} - -static DEVICE_ATTR_RO(ul_chan_id); - -static struct attribute *mhi_dev_attrs[] = { - &dev_attr_ul_chan_id.attr, - NULL, -}; - -static struct attribute_group mhi_dev_attr_group = { - .attrs = mhi_dev_attrs, -}; - -/* destroy specific device */ -int mhi_destroy_device(struct device *dev, void *data) -{ - struct mhi_device *mhi_dev; - struct mhi_controller *mhi_cntrl; - - if (dev->bus != &mhi_bus_type) - return 0; - - mhi_dev = to_mhi_device(dev); - mhi_cntrl = mhi_dev->mhi_cntrl; - - /* only destroying virtual devices thats attached to bus */ - if (mhi_dev->dev_type == MHI_CONTROLLER_TYPE) - return 0; - - MHI_LOG("destroy device for chan:%s\n", mhi_dev->chan_name); - - sysfs_remove_group(&mhi_dev->dev.kobj, &mhi_dev_attr_group); - /* notify the client and remove the device from mhi bus */ - device_del(dev); - put_device(dev); - - return 0; -} - -void mhi_notify(struct mhi_device *mhi_dev, enum MHI_CB cb_reason) -{ - struct mhi_driver *mhi_drv; - - if (!mhi_dev->dev.driver) - return; - - mhi_drv = to_mhi_driver(mhi_dev->dev.driver); - - if (mhi_drv->status_cb) - mhi_drv->status_cb(mhi_dev, cb_reason); -} - -static void mhi_assign_of_node(struct mhi_controller *mhi_cntrl, - struct mhi_device *mhi_dev) -{ - struct device_node *controller, *node; - const char *dt_name; - int ret; - - controller = of_find_node_by_name(mhi_cntrl->of_node, "mhi_devices"); - if (!controller) - return; - - for_each_available_child_of_node(controller, node) { - ret = of_property_read_string(node, "mhi,chan", &dt_name); - if (ret) - continue; - if (!strcmp(mhi_dev->chan_name, dt_name)) { - mhi_dev->dev.of_node = node; - break; - } - } -} - -static ssize_t time_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct mhi_device *mhi_dev = to_mhi_device(dev); - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - u64 t_host, t_device; - int ret; - - ret = mhi_get_remote_time_sync(mhi_dev, &t_host, &t_device); - if (ret) { - MHI_ERR("Failed to obtain time, ret:%d\n", ret); - return ret; - } - - return scnprintf(buf, PAGE_SIZE, "local: %llu remote: %llu (ticks)\n", - t_host, t_device); -} -static DEVICE_ATTR_RO(time); - -static ssize_t time_us_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct mhi_device *mhi_dev = to_mhi_device(dev); - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - u64 t_host, t_device; - int ret; - - ret = mhi_get_remote_time_sync(mhi_dev, &t_host, &t_device); - if (ret) { - MHI_ERR("Failed to obtain time, ret:%d\n", ret); - return ret; - } - - return scnprintf(buf, PAGE_SIZE, "local: %llu remote: %llu (us)\n", - TIME_TICKS_TO_US(t_host), TIME_TICKS_TO_US(t_device)); -} -static DEVICE_ATTR_RO(time_us); - -static struct attribute *mhi_tsync_attrs[] = { - &dev_attr_time.attr, - &dev_attr_time_us.attr, - NULL, -}; - -static const struct attribute_group mhi_tsync_group = { - .attrs = mhi_tsync_attrs, -}; - -void mhi_destroy_timesync(struct mhi_controller *mhi_cntrl) -{ - if (mhi_cntrl->mhi_tsync) { - sysfs_remove_group(&mhi_cntrl->mhi_dev->dev.kobj, - &mhi_tsync_group); - kfree(mhi_cntrl->mhi_tsync); - mhi_cntrl->mhi_tsync = NULL; - } -} - -int mhi_create_timesync_sysfs(struct mhi_controller *mhi_cntrl) -{ - return sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj, - &mhi_tsync_group); -} - -static void mhi_create_time_sync_dev(struct mhi_controller *mhi_cntrl) -{ - struct mhi_device *mhi_dev; - int ret; - - if (!MHI_IN_MISSION_MODE(mhi_cntrl->ee)) - return; - - mhi_dev = mhi_alloc_device(mhi_cntrl); - if (!mhi_dev) - return; - - mhi_dev->dev_type = MHI_TIMESYNC_TYPE; - mhi_dev->chan_name = "TIME_SYNC"; - dev_set_name(&mhi_dev->dev, "%04x_%02x.%02x.%02x_%s", mhi_dev->dev_id, - mhi_dev->domain, mhi_dev->bus, mhi_dev->slot, - mhi_dev->chan_name); - - /* add if there is a matching DT node */ - mhi_assign_of_node(mhi_cntrl, mhi_dev); - - ret = device_add(&mhi_dev->dev); - if (ret) { - MHI_ERR("Failed to register dev for chan:%s\n", - mhi_dev->chan_name); - mhi_dealloc_device(mhi_cntrl, mhi_dev); - return; - } - - mhi_cntrl->tsync_dev = mhi_dev; -} - -/* bind mhi channels into mhi devices */ -void mhi_create_devices(struct mhi_controller *mhi_cntrl) -{ - int i; - struct mhi_chan *mhi_chan; - struct mhi_device *mhi_dev; - int ret; - - /* - * we need to create time sync device before creating other - * devices, because client may try to capture time during - * clint probe. - */ - mhi_create_time_sync_dev(mhi_cntrl); - - mhi_chan = mhi_cntrl->mhi_chan; - for (i = 0; i < mhi_cntrl->max_chan; i++, mhi_chan++) { - if (!mhi_chan->configured || mhi_chan->mhi_dev || - !(mhi_chan->ee_mask & BIT(mhi_cntrl->ee))) - continue; - mhi_dev = mhi_alloc_device(mhi_cntrl); - if (!mhi_dev) - return; - - mhi_dev->dev_type = MHI_XFER_TYPE; - switch (mhi_chan->dir) { - case DMA_TO_DEVICE: - mhi_dev->ul_chan = mhi_chan; - mhi_dev->ul_chan_id = mhi_chan->chan; - mhi_dev->ul_xfer = mhi_chan->queue_xfer; - mhi_dev->ul_event_id = mhi_chan->er_index; - break; - case DMA_NONE: - case DMA_BIDIRECTIONAL: - mhi_dev->ul_chan_id = mhi_chan->chan; - mhi_dev->ul_event_id = mhi_chan->er_index; - mhi_dev->dl_chan = mhi_chan; - mhi_dev->dl_chan_id = mhi_chan->chan; - mhi_dev->dl_xfer = mhi_chan->queue_xfer; - mhi_dev->dl_event_id = mhi_chan->er_index; - break; - case DMA_FROM_DEVICE: - /* we use dl_chan for offload channels */ - mhi_dev->dl_chan = mhi_chan; - mhi_dev->dl_chan_id = mhi_chan->chan; - mhi_dev->dl_xfer = mhi_chan->queue_xfer; - mhi_dev->dl_event_id = mhi_chan->er_index; - break; - } - - mhi_chan->mhi_dev = mhi_dev; - - /* check next channel if it matches */ - if ((i + 1) < mhi_cntrl->max_chan && mhi_chan[1].configured) { - if (!strcmp(mhi_chan[1].name, mhi_chan->name)) { - i++; - mhi_chan++; - if (mhi_chan->dir == DMA_TO_DEVICE) { - mhi_dev->ul_chan = mhi_chan; - mhi_dev->ul_chan_id = mhi_chan->chan; - mhi_dev->ul_xfer = mhi_chan->queue_xfer; - mhi_dev->ul_event_id = - mhi_chan->er_index; - } else { - mhi_dev->dl_chan = mhi_chan; - mhi_dev->dl_chan_id = mhi_chan->chan; - mhi_dev->dl_xfer = mhi_chan->queue_xfer; - mhi_dev->dl_event_id = - mhi_chan->er_index; - } - mhi_chan->mhi_dev = mhi_dev; - } - } - - mhi_dev->chan_name = mhi_chan->name; - dev_set_name(&mhi_dev->dev, "%04x_%02x.%02x.%02x_%s", - mhi_dev->dev_id, mhi_dev->domain, mhi_dev->bus, - mhi_dev->slot, mhi_dev->chan_name); - - /* add if there is a matching DT node */ - mhi_assign_of_node(mhi_cntrl, mhi_dev); - - /* init wake source */ - if (mhi_dev->dl_chan && mhi_dev->dl_chan->wake_capable) - device_init_wakeup(&mhi_dev->dev, true); - - ret = device_add(&mhi_dev->dev); - if (ret) { - MHI_ERR("Failed to register dev for chan:%s\n", - mhi_dev->chan_name); - mhi_dealloc_device(mhi_cntrl, mhi_dev); - } - ret = sysfs_create_group(&mhi_dev->dev.kobj, &mhi_dev_attr_group); - } -} - -static void mhi_dump_tre(struct mhi_controller *mhi_cntrl, struct mhi_tre *_ev) { - union mhi_dev_ring_element_type *ev = (union mhi_dev_ring_element_type *)_ev; - - switch (ev->generic.type) { - case MHI_DEV_RING_EL_INVALID: { - MHI_ERR("carl_ev cmd_invalid, ptr=%llx, %x, %x\n", _ev->ptr, _ev->dword[0], _ev->dword[1]); - } - break; - case MHI_DEV_RING_EL_NOOP: { - MHI_LOG("carl_ev cmd_no_op chan=%u\n", ev->cmd_no_op.chid); - } - break; - case MHI_DEV_RING_EL_TRANSFER: { - MHI_LOG("carl_ev cmd_transfer data=%llx, len=%u, chan=%u\n", - ev->cmd_transfer.data_buf_ptr, ev->cmd_transfer.len, ev->cmd_transfer.chain); - } - break; - case MHI_DEV_RING_EL_RESET: { - MHI_LOG("carl_ev cmd_reset chan=%u\n", ev->cmd_reset.chid); - } - break; - case MHI_DEV_RING_EL_STOP: { - MHI_LOG("carl_ev cmd_stop chan=%u\n", ev->cmd_stop.chid); - } - break; - case MHI_DEV_RING_EL_START: { - MHI_LOG("carl_ev cmd_start chan=%u\n", ev->cmd_start.chid); - } - break; - case MHI_DEV_RING_EL_MHI_STATE_CHG: { - MHI_LOG("carl_ev evt_state_change mhistate=%u\n", ev->evt_state_change.mhistate); - } - break; - case MHI_DEV_RING_EL_CMD_COMPLETION_EVT:{ - MHI_LOG("carl_ev evt_cmd_comp code=%u, type=%u\n", ev->evt_cmd_comp.code, ev->evt_cmd_comp.type); - } - break; - case MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT:{ - MHI_VERB("carl_ev evt_tr_comp ptr=%llx, len=%u, code=%u, chan=%u\n", - ev->evt_tr_comp.ptr, ev->evt_tr_comp.len, ev->evt_tr_comp.code, ev->evt_tr_comp.chid); - } - break; - case MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY:{ - MHI_LOG("carl_ev evt_ee_state execenv=%u\n", ev->evt_ee_state.execenv); - } - break; - case MHI_DEV_RING_EL_UNDEF: - default: { - MHI_ERR("carl_ev el_undef type=%d\n", ev->generic.type); - }; - break; - } -} - -static int parse_xfer_event(struct mhi_controller *mhi_cntrl, - struct mhi_tre *event, - struct mhi_chan *mhi_chan) -{ - struct mhi_ring *buf_ring, *tre_ring; - u32 ev_code; - struct mhi_result result; - unsigned long flags = 0; - - ev_code = MHI_TRE_GET_EV_CODE(event); - buf_ring = &mhi_chan->buf_ring; - tre_ring = &mhi_chan->tre_ring; - - result.transaction_status = (ev_code == MHI_EV_CC_OVERFLOW) ? - -EOVERFLOW : 0; - - /* - * if it's a DB Event then we need to grab the lock - * with preemption disable and as a write because we - * have to update db register and another thread could - * be doing same. - */ - if (ev_code >= MHI_EV_CC_OOB) - write_lock_irqsave(&mhi_chan->lock, flags); - else - read_lock_bh(&mhi_chan->lock); - - if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED) - goto end_process_tx_event; - - switch (ev_code) { - case MHI_EV_CC_OVERFLOW: - case MHI_EV_CC_EOB: - case MHI_EV_CC_EOT: - { - dma_addr_t ptr = MHI_TRE_GET_EV_PTR(event); - struct mhi_tre *local_rp, *ev_tre; - void *dev_rp; - struct mhi_buf_info *buf_info; - u16 xfer_len; - - /* Get the TRB this event points to */ - ev_tre = mhi_to_virtual(tre_ring, ptr); - - /* device rp after servicing the TREs */ - dev_rp = ev_tre + 1; - if (dev_rp >= (tre_ring->base + tre_ring->len)) - dev_rp = tre_ring->base; - - mhi_chan->used_events[ev_code]++; - - result.dir = mhi_chan->dir; - - /* local rp */ - local_rp = tre_ring->rp; - while (local_rp != dev_rp) { - buf_info = buf_ring->rp; - /* Always get the get len from the event */ - xfer_len = MHI_TRE_GET_EV_LEN(event); - - /* unmap if it's not premapped by client */ - if (likely(!buf_info->pre_mapped)) - mhi_cntrl->unmap_single(mhi_cntrl, buf_info); - - result.buf_addr = buf_info->cb_buf; - result.bytes_xferd = xfer_len; -#ifdef ENABLE_MHI_MON - if (mhi_cntrl->nreaders) { - void *buf = NULL; - size_t len = 0; - - if (mhi_chan->queue_xfer == mhi_queue_skb) { - struct sk_buff *skb = result.buf_addr; - buf = skb->data; - len = result.bytes_xferd; - } - else if (CHAN_INBOUND(mhi_chan->chan)) { - buf = result.buf_addr; - len = result.bytes_xferd; - } - mon_bus_receive(mhi_cntrl, mhi_chan->chan, - mhi_to_physical(tre_ring, local_rp), local_rp, buf, len); - } -#endif - mhi_del_ring_element(mhi_cntrl, buf_ring); - mhi_del_ring_element(mhi_cntrl, tre_ring); - local_rp = tre_ring->rp; - - /* notify client */ - mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); - - if (mhi_chan->dir == DMA_TO_DEVICE) { - if (atomic_dec_return(&mhi_cntrl->pending_pkts) == 0) - mhi_cntrl->runtime_put(mhi_cntrl, mhi_cntrl->priv_data); - } - - if (mhi_chan->dir == DMA_TO_DEVICE) { - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_put(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); - } - - /* - * recycle the buffer if buffer is pre-allocated, - * if there is error, not much we can do apart from - * dropping the packet - */ - if (mhi_chan->pre_alloc) { - if (mhi_queue_buf(mhi_chan->mhi_dev, mhi_chan, - buf_info->cb_buf, - buf_info->len, MHI_EOT)) { - MHI_ERR( - "Error recycling buffer for chan:%d\n", - mhi_chan->chan); - kfree(buf_info->cb_buf); - } - } - } - break; - } /* CC_EOT */ - case MHI_EV_CC_OOB: - case MHI_EV_CC_DB_MODE: - { - unsigned long flags; - - mhi_chan->used_events[ev_code]++; - MHI_VERB("DB_MODE/OOB Detected chan %d.\n", mhi_chan->chan); - mhi_chan->db_cfg.db_mode = true; - read_lock_irqsave(&mhi_cntrl->pm_lock, flags); -#ifdef DEBUG_CHAN100_DB - if (mhi_chan->chan == 100) { - chan100_t[atomic_inc_return(&chan100_seq)&(CHAN100_SIZE-1)] = (((unsigned long)tre_ring->rp)&0xffff) | (0xf0000); - chan100_t[atomic_inc_return(&chan100_seq)&(CHAN100_SIZE-1)] = (((unsigned long)tre_ring->wp)&0xffff) | (mhi_chan->db_cfg.db_mode<<31) | (1<<30); - } -#endif - if (tre_ring->wp != tre_ring->rp && - MHI_DB_ACCESS_VALID(mhi_cntrl->pm_state)) { - mhi_ring_chan_db(mhi_cntrl, mhi_chan); - } - read_unlock_irqrestore(&mhi_cntrl->pm_lock, flags); - break; - } - case MHI_EV_CC_BAD_TRE: - MHI_ASSERT(1, "Received BAD TRE event for ring"); - break; - default: - MHI_CRITICAL("Unknown TX completion.\n"); - - break; - } /* switch(MHI_EV_READ_CODE(EV_TRB_CODE,event)) */ - -end_process_tx_event: - if (ev_code >= MHI_EV_CC_OOB) - write_unlock_irqrestore(&mhi_chan->lock, flags); - else - read_unlock_bh(&mhi_chan->lock); - - return 0; -} - -static int parse_rsc_event(struct mhi_controller *mhi_cntrl, - struct mhi_tre *event, - struct mhi_chan *mhi_chan) -{ - struct mhi_ring *buf_ring, *tre_ring; - struct mhi_buf_info *buf_info; - struct mhi_result result; - int ev_code; - u32 cookie; /* offset to local descriptor */ - u16 xfer_len; - - buf_ring = &mhi_chan->buf_ring; - tre_ring = &mhi_chan->tre_ring; - - ev_code = MHI_TRE_GET_EV_CODE(event); - cookie = MHI_TRE_GET_EV_COOKIE(event); - xfer_len = MHI_TRE_GET_EV_LEN(event); - - /* received out of bound cookie */ - MHI_ASSERT(cookie >= buf_ring->len, "Invalid Cookie\n"); - - buf_info = buf_ring->base + cookie; - - result.transaction_status = (ev_code == MHI_EV_CC_OVERFLOW) ? - -EOVERFLOW : 0; - result.bytes_xferd = xfer_len; - result.buf_addr = buf_info->cb_buf; - result.dir = mhi_chan->dir; - - read_lock_bh(&mhi_chan->lock); - - if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED) - goto end_process_rsc_event; - - MHI_ASSERT(!buf_info->used, "TRE already Freed\n"); - - /* notify the client */ - mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); - - /* - * Note: We're arbitrarily incrementing RP even though, completion - * packet we processed might not be the same one, reason we can do this - * is because device guaranteed to cache descriptors in order it - * receive, so even though completion event is different we can re-use - * all descriptors in between. - * Example: - * Transfer Ring has descriptors: A, B, C, D - * Last descriptor host queue is D (WP) and first descriptor - * host queue is A (RP). - * The completion event we just serviced is descriptor C. - * Then we can safely queue descriptors to replace A, B, and C - * even though host did not receive any completions. - */ - mhi_del_ring_element(mhi_cntrl, tre_ring); - buf_info->used = false; - -end_process_rsc_event: - read_unlock_bh(&mhi_chan->lock); - - return 0; -} - -static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl, - struct mhi_tre *tre) -{ - dma_addr_t ptr = MHI_TRE_GET_EV_PTR(tre); - struct mhi_cmd *cmd_ring = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING]; - struct mhi_ring *mhi_ring = &cmd_ring->ring; - struct mhi_tre *cmd_pkt; - struct mhi_chan *mhi_chan; - struct mhi_timesync *mhi_tsync; - enum mhi_cmd_type type; - u32 chan; - - cmd_pkt = mhi_to_virtual(mhi_ring, ptr); - - /* out of order completion received */ - MHI_ASSERT(cmd_pkt != mhi_ring->rp, "Out of order cmd completion"); - - type = MHI_TRE_GET_CMD_TYPE(cmd_pkt); - - if (type == MHI_CMD_TYPE_TSYNC) { - mhi_tsync = mhi_cntrl->mhi_tsync; - mhi_tsync->ccs = MHI_TRE_GET_EV_CODE(tre); - complete(&mhi_tsync->completion); - } else { - chan = MHI_TRE_GET_CMD_CHID(cmd_pkt); - mhi_chan = &mhi_cntrl->mhi_chan[chan]; - write_lock_bh(&mhi_chan->lock); - mhi_chan->ccs = MHI_TRE_GET_EV_CODE(tre); - complete(&mhi_chan->completion); - write_unlock_bh(&mhi_chan->lock); - } - - mhi_del_ring_element(mhi_cntrl, mhi_ring); -} - -int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, - struct mhi_event *mhi_event, - u32 event_quota) -{ - struct mhi_tre *dev_rp, *local_rp; - struct mhi_ring *ev_ring = &mhi_event->ring; - struct mhi_event_ctxt *er_ctxt = - &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; - int count = 0; - - /* - * this is a quick check to avoid unnecessary event processing - * in case we already in error state, but it's still possible - * to transition to error state while processing events - */ - if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state))) { - MHI_ERR("No EV access, PM_STATE:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - return -EIO; - } - - dev_rp = mhi_to_virtual(ev_ring, er_ctxt->rp); - local_rp = ev_ring->rp; - - while (dev_rp != local_rp) { - enum MHI_PKT_TYPE type = MHI_TRE_GET_EV_TYPE(local_rp); - -//#define QL_READ_EVENT_WA //from Quectel Windows driver -#ifdef QL_READ_EVENT_WA - if (mhi_event->er_index == 0) { - if (local_rp->ptr == 0 && local_rp->dword[0] == 0 && local_rp->dword[1] == 0) { - // event content no sync to memory, just break and wait next event. - MHI_ERR("Zero Event!\n"); - break; - } - } -#endif - - mhi_dump_tre(mhi_cntrl, local_rp); - MHI_VERB("Processing Event:0x%llx 0x%08x 0x%08x\n", - local_rp->ptr, local_rp->dword[0], local_rp->dword[1]); -#ifdef ENABLE_MHI_MON - if (mhi_cntrl->nreaders) { - mon_bus_complete(mhi_cntrl, mhi_event->er_index, mhi_to_physical(ev_ring, local_rp), local_rp); - } -#endif - - switch (type) { - case MHI_PKT_TYPE_STATE_CHANGE_EVENT: - { - enum mhi_dev_state new_state; - - new_state = MHI_TRE_GET_EV_STATE(local_rp); - - MHI_LOG("MHI state change event to state:%s\n", - TO_MHI_STATE_STR(new_state)); - - switch (new_state) { - case MHI_STATE_M0: - mhi_pm_m0_transition(mhi_cntrl); - break; - case MHI_STATE_M1: - mhi_pm_m1_transition(mhi_cntrl); - break; - case MHI_STATE_M3: - mhi_pm_m3_transition(mhi_cntrl); - break; - case MHI_STATE_SYS_ERR: - { - enum MHI_PM_STATE new_state; - - MHI_ERR("MHI system error detected\n"); - write_lock_irq(&mhi_cntrl->pm_lock); - new_state = mhi_tryset_pm_state(mhi_cntrl, - MHI_PM_SYS_ERR_DETECT); - write_unlock_irq(&mhi_cntrl->pm_lock); - if (new_state == MHI_PM_SYS_ERR_DETECT) - schedule_work( - &mhi_cntrl->syserr_worker); - break; - } - default: - MHI_ERR("Unsupported STE:%s\n", - TO_MHI_STATE_STR(new_state)); - } - - break; - } - case MHI_PKT_TYPE_CMD_COMPLETION_EVENT: - mhi_process_cmd_completion(mhi_cntrl, local_rp); - break; - case MHI_PKT_TYPE_EE_EVENT: - { - enum MHI_ST_TRANSITION st = MHI_ST_TRANSITION_MAX; - enum mhi_ee event = MHI_TRE_GET_EV_EXECENV(local_rp); - - MHI_LOG("MHI EE received event:%s\n", - TO_MHI_EXEC_STR(event)); - switch (event) { - case MHI_EE_SBL: - st = MHI_ST_TRANSITION_SBL; - break; - case MHI_EE_FP: - st = MHI_ST_TRANSITION_FP; - break; - case MHI_EE_WFW: - case MHI_EE_AMSS: - st = MHI_ST_TRANSITION_MISSION_MODE; - break; - case MHI_EE_RDDM: - mhi_cntrl->status_cb(mhi_cntrl, - mhi_cntrl->priv_data, - MHI_CB_EE_RDDM); - write_lock_irq(&mhi_cntrl->pm_lock); - mhi_cntrl->ee = event; - write_unlock_irq(&mhi_cntrl->pm_lock); - wake_up_all(&mhi_cntrl->state_event); - break; - default: - MHI_ERR("Unhandled EE event:%s\n", - TO_MHI_EXEC_STR(event)); - } - if (st != MHI_ST_TRANSITION_MAX) - mhi_queue_state_transition(mhi_cntrl, st); - break; - } -#if 1 //Add by Quectel - case MHI_PKT_TYPE_TX_EVENT: - case MHI_PKT_TYPE_RSC_TX_EVENT: - { - u32 chan = MHI_TRE_GET_EV_CHID(local_rp); - struct mhi_chan *mhi_chan = &mhi_cntrl->mhi_chan[chan]; - - if (likely(type == MHI_PKT_TYPE_TX_EVENT)) { - parse_xfer_event(mhi_cntrl, local_rp, mhi_chan); - } else if (type == MHI_PKT_TYPE_RSC_TX_EVENT) { - parse_rsc_event(mhi_cntrl, local_rp, mhi_chan); - } - break; - } -#endif - default: - MHI_ASSERT(1, "Unsupported ev type"); - break; - } - -#ifdef QL_READ_EVENT_WA - if (mhi_event->er_index == 0) { - local_rp->ptr = 0; - local_rp->dword[0] = local_rp->dword[1] = 0; - } -#endif - - mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring); - local_rp = ev_ring->rp; - dev_rp = mhi_to_virtual(ev_ring, er_ctxt->rp); - count++; - } - - if (count > mhi_event->used_elements) { - mhi_event->used_elements = count; - } - - read_lock_bh(&mhi_cntrl->pm_lock); - if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl->pm_state))) - mhi_ring_er_db(mhi_event); - read_unlock_bh(&mhi_cntrl->pm_lock); - - MHI_VERB("exit er_index:%u\n", mhi_event->er_index); - - return count; -} - -int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, - struct mhi_event *mhi_event, - u32 event_quota) -{ - struct mhi_tre *dev_rp, *local_rp; - struct mhi_ring *ev_ring = &mhi_event->ring; - struct mhi_event_ctxt *er_ctxt = - &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; - int count = 0; - u32 chan; - struct mhi_chan *mhi_chan = NULL; - u32 chan_count = 0; - void *chan_local_rp = NULL; - - if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state))) { - MHI_ERR("No EV access, PM_STATE:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - return -EIO; - } - - dev_rp = mhi_to_virtual(ev_ring, er_ctxt->rp); - local_rp = ev_ring->rp; - - while (dev_rp != local_rp && event_quota > 0) { - enum MHI_PKT_TYPE type = MHI_TRE_GET_EV_TYPE(local_rp); - - mhi_dump_tre(mhi_cntrl, local_rp); - MHI_VERB("Processing Event:0x%llx 0x%08x 0x%08x\n", - local_rp->ptr, local_rp->dword[0], local_rp->dword[1]); - - chan = MHI_TRE_GET_EV_CHID(local_rp); - mhi_chan = &mhi_cntrl->mhi_chan[chan]; - chan_local_rp = mhi_chan->tre_ring.rp; - -#ifdef ENABLE_MHI_MON - if (mhi_cntrl->nreaders) { - mon_bus_complete(mhi_cntrl, mhi_event->er_index, mhi_to_physical(ev_ring, local_rp), local_rp); - } -#endif - if (likely(type == MHI_PKT_TYPE_TX_EVENT)) { - parse_xfer_event(mhi_cntrl, local_rp, mhi_chan); - event_quota--; - } else if (type == MHI_PKT_TYPE_RSC_TX_EVENT) { - parse_rsc_event(mhi_cntrl, local_rp, mhi_chan); - event_quota--; - } - - chan_count += get_used_ring_elements(chan_local_rp, mhi_chan->tre_ring.rp, mhi_chan->tre_ring.elements); - mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring); - local_rp = ev_ring->rp; - if (local_rp == dev_rp || event_quota == 0) { - if (chan_count > mhi_chan->used_elements) - mhi_chan->used_elements = chan_count; - chan_count = 0; - dev_rp = mhi_to_virtual(ev_ring, er_ctxt->rp); - } - count++; - } - - if (count > mhi_event->used_elements) { - mhi_event->used_elements = count; - } - - read_lock_bh(&mhi_cntrl->pm_lock); - if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl->pm_state))) - mhi_ring_er_db(mhi_event); - read_unlock_bh(&mhi_cntrl->pm_lock); - - MHI_VERB("exit er_index:%u\n", mhi_event->er_index); - - return count; -} - -int mhi_process_tsync_event_ring(struct mhi_controller *mhi_cntrl, - struct mhi_event *mhi_event, - u32 event_quota) -{ - struct mhi_tre *dev_rp, *local_rp; - struct mhi_ring *ev_ring = &mhi_event->ring; - struct mhi_event_ctxt *er_ctxt = - &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; - struct mhi_timesync *mhi_tsync = mhi_cntrl->mhi_tsync; - int count = 0; - u32 sequence; - u64 remote_time; - - if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state))) { - MHI_ERR("No EV access, PM_STATE:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - read_unlock_bh(&mhi_cntrl->pm_lock); - return -EIO; - } - - dev_rp = mhi_to_virtual(ev_ring, er_ctxt->rp); - local_rp = ev_ring->rp; - - while (dev_rp != local_rp) { - enum MHI_PKT_TYPE type = MHI_TRE_GET_EV_TYPE(local_rp); - struct tsync_node *tsync_node; - - MHI_VERB("Processing Event:0x%llx 0x%08x 0x%08x\n", - local_rp->ptr, local_rp->dword[0], local_rp->dword[1]); - - MHI_ASSERT(type != MHI_PKT_TYPE_TSYNC_EVENT, "!TSYNC event"); - - sequence = MHI_TRE_GET_EV_SEQ(local_rp); - remote_time = MHI_TRE_GET_EV_TIME(local_rp); - - do { - spin_lock_irq(&mhi_tsync->lock); - tsync_node = list_first_entry_or_null(&mhi_tsync->head, - struct tsync_node, node); - MHI_ASSERT(!tsync_node, "Unexpected Event"); - - if (unlikely(!tsync_node)) - break; - - list_del(&tsync_node->node); - spin_unlock_irq(&mhi_tsync->lock); - - /* - * device may not able to process all time sync commands - * host issue and only process last command it receive - */ - if (tsync_node->sequence == sequence) { - tsync_node->cb_func(tsync_node->mhi_dev, - sequence, - tsync_node->local_time, - remote_time); - kfree(tsync_node); - } else { - kfree(tsync_node); - } - } while (true); - - mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring); - local_rp = ev_ring->rp; - dev_rp = mhi_to_virtual(ev_ring, er_ctxt->rp); - count++; - } - - read_lock_bh(&mhi_cntrl->pm_lock); - if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl->pm_state))) - mhi_ring_er_db(mhi_event); - read_unlock_bh(&mhi_cntrl->pm_lock); - - MHI_VERB("exit er_index:%u\n", mhi_event->er_index); - - return count; -} - -void mhi_ev_task(unsigned long data) -{ - struct mhi_event *mhi_event = (struct mhi_event *)data; - struct mhi_controller *mhi_cntrl = mhi_event->mhi_cntrl; - - MHI_VERB("Enter for ev_index:%d\n", mhi_event->er_index); - - mhi_cntrl->runtime_mark_last_busy(mhi_cntrl, mhi_cntrl->priv_data); - /* process all pending events */ - spin_lock_bh(&mhi_event->lock); - mhi_event->process_event(mhi_cntrl, mhi_event, U32_MAX); - spin_unlock_bh(&mhi_event->lock); -} - -void mhi_ctrl_ev_task(unsigned long data) -{ - struct mhi_event *mhi_event = (struct mhi_event *)data; - struct mhi_controller *mhi_cntrl = mhi_event->mhi_cntrl; - enum mhi_dev_state state; - enum MHI_PM_STATE pm_state = 0; - int ret; - - MHI_VERB("Enter for ev_index:%d\n", mhi_event->er_index); - - /* - * we can check pm_state w/o a lock here because there is no way - * pm_state can change from reg access valid to no access while this - * therad being executed. - */ - if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) - return; - - mhi_cntrl->runtime_mark_last_busy(mhi_cntrl, mhi_cntrl->priv_data); - /* process ctrl events events */ - ret = mhi_event->process_event(mhi_cntrl, mhi_event, U32_MAX); - - /* - * we received a MSI but no events to process maybe device went to - * SYS_ERR state, check the state - */ - if (!ret) { - write_lock_irq(&mhi_cntrl->pm_lock); - state = mhi_get_mhi_state(mhi_cntrl); - if (state == MHI_STATE_SYS_ERR) { - MHI_ERR("MHI system error detected\n"); - pm_state = mhi_tryset_pm_state(mhi_cntrl, - MHI_PM_SYS_ERR_DETECT); - } - write_unlock_irq(&mhi_cntrl->pm_lock); - if (pm_state == MHI_PM_SYS_ERR_DETECT) - schedule_work(&mhi_cntrl->syserr_worker); - } -} - -irqreturn_t mhi_msi_handlr(int irq_number, void *dev) -{ - struct mhi_event *mhi_event = dev; - struct mhi_controller *mhi_cntrl = mhi_event->mhi_cntrl; - struct mhi_event_ctxt *er_ctxt = - &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; - struct mhi_ring *ev_ring = &mhi_event->ring; - void *dev_rp = mhi_to_virtual(ev_ring, er_ctxt->rp); - - /* confirm ER has pending events to process before scheduling work */ - if (ev_ring->rp == dev_rp) - return IRQ_HANDLED; - - /* client managed event ring, notify pending data */ - if (mhi_event->cl_manage) { - struct mhi_chan *mhi_chan = mhi_event->mhi_chan; - struct mhi_device *mhi_dev = mhi_chan->mhi_dev; - - if (mhi_dev) - mhi_dev->status_cb(mhi_dev, MHI_CB_PENDING_DATA); - } else - tasklet_schedule(&mhi_event->task); - - return IRQ_HANDLED; -} - -/* this is the threaded fn */ -irqreturn_t mhi_intvec_threaded_handlr(int irq_number, void *dev) -{ - struct mhi_controller *mhi_cntrl = dev; - enum mhi_dev_state state = MHI_STATE_MAX; - enum MHI_PM_STATE pm_state = 0; - enum mhi_ee ee = MHI_EE_MAX; - unsigned long flags; - - MHI_VERB("Enter\n"); - - write_lock_irqsave(&mhi_cntrl->pm_lock, flags); - if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) { - state = mhi_get_mhi_state(mhi_cntrl); - ee = mhi_get_exec_env(mhi_cntrl); - if (mhi_cntrl->msi_allocated >= 5 ||(mhi_cntrl->msi_allocated == 1 && (mhi_cntrl->dev_state != state || mhi_cntrl->ee != ee))) - MHI_LOG("device ee:%s dev_state:%s, pm_state:%s\n", TO_MHI_EXEC_STR(ee), - TO_MHI_STATE_STR(state), to_mhi_pm_state_str(mhi_cntrl->pm_state)); - } - - if (state == MHI_STATE_SYS_ERR) { - MHI_ERR("MHI system error detected\n"); - if (mhi_cntrl->pm_state != MHI_PM_SYS_ERR_DETECT) - pm_state = mhi_tryset_pm_state(mhi_cntrl, - MHI_PM_SYS_ERR_DETECT); - } - write_unlock_irqrestore(&mhi_cntrl->pm_lock, flags); - if (pm_state == MHI_PM_SYS_ERR_DETECT) { - wake_up_all(&mhi_cntrl->state_event); - - if (mhi_cntrl->ee != ee) { - MHI_LOG("device ee:%s -> %s\n", TO_MHI_EXEC_STR(mhi_cntrl->ee), TO_MHI_EXEC_STR(ee)); - schedule_work(&mhi_cntrl->syserr_worker); - } - /* for fatal errors, we let controller decide next step */ - else if (MHI_IN_PBL(ee)) - mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data, - MHI_CB_FATAL_ERROR); - else - schedule_work(&mhi_cntrl->syserr_worker); - } - if (mhi_cntrl->msi_allocated >= 5||(mhi_cntrl->msi_allocated == 1 && (mhi_cntrl->dev_state != state || mhi_cntrl->ee != ee))) - MHI_LOG("device ee:%s dev_state:%s, %s\n", TO_MHI_EXEC_STR(ee), - TO_MHI_STATE_STR(state), TO_MHI_EXEC_STR(mhi_cntrl->ee)); - - if (pm_state == MHI_PM_POR) { - wake_up_all(&mhi_cntrl->state_event); - } - - MHI_VERB("Exit\n"); - - return IRQ_HANDLED; -} - -irqreturn_t mhi_intvec_handlr(int irq_number, void *dev) -{ - - struct mhi_controller *mhi_cntrl = dev; - - /* wake up any events waiting for state change */ - MHI_VERB("Enter\n"); - wake_up_all(&mhi_cntrl->state_event); - MHI_VERB("Exit\n"); - - return IRQ_WAKE_THREAD; -} - -irqreturn_t mhi_one_msi_handlr(int irq_number, void *dev) -{ - struct mhi_controller *mhi_cntrl = dev; - struct mhi_event_ctxt *er_ctxt = mhi_cntrl->mhi_ctxt->er_ctxt; - struct mhi_event *mhi_event = mhi_cntrl->mhi_event; - u32 i; - u32 handle = 0; - - for (i = 0; i < NUM_MHI_EVT_RINGS; i++, er_ctxt++, mhi_event++) { - struct mhi_ring *ev_ring = &mhi_event->ring; - void *dev_rp = mhi_to_virtual(ev_ring, er_ctxt->rp); - if (ev_ring->rp != dev_rp) { - handle++; - mhi_msi_handlr(irq_number, mhi_event); - } - } - - if (handle ==0) { - mhi_intvec_threaded_handlr(irq_number, dev); - } - - return IRQ_HANDLED; -} - -int mhi_send_cmd(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan, - enum MHI_CMD cmd) -{ - struct mhi_tre *cmd_tre = NULL; - struct mhi_cmd *mhi_cmd = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING]; - struct mhi_ring *ring = &mhi_cmd->ring; - int chan = 0; - - MHI_VERB("Entered, MHI pm_state:%s dev_state:%s ee:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_STATE_STR(mhi_cntrl->dev_state), - TO_MHI_EXEC_STR(mhi_cntrl->ee)); - - if (mhi_chan) - chan = mhi_chan->chan; - - spin_lock_bh(&mhi_cmd->lock); - if (!get_nr_avail_ring_elements(mhi_cntrl, ring)) { - spin_unlock_bh(&mhi_cmd->lock); - return -ENOMEM; - } - - /* prepare the cmd tre */ - cmd_tre = ring->wp; - switch (cmd) { - case MHI_CMD_RESET_CHAN: - cmd_tre->ptr = MHI_TRE_CMD_RESET_PTR; - cmd_tre->dword[0] = MHI_TRE_CMD_RESET_DWORD0; - cmd_tre->dword[1] = MHI_TRE_CMD_RESET_DWORD1(chan); - break; - case MHI_CMD_START_CHAN: - cmd_tre->ptr = MHI_TRE_CMD_START_PTR; - cmd_tre->dword[0] = MHI_TRE_CMD_START_DWORD0; - cmd_tre->dword[1] = MHI_TRE_CMD_START_DWORD1(chan); - break; - case MHI_CMD_TIMSYNC_CFG: - cmd_tre->ptr = MHI_TRE_CMD_TSYNC_CFG_PTR; - cmd_tre->dword[0] = MHI_TRE_CMD_TSYNC_CFG_DWORD0; - cmd_tre->dword[1] = MHI_TRE_CMD_TSYNC_CFG_DWORD1 - (mhi_cntrl->mhi_tsync->er_index); - break; - } - -#ifdef ENABLE_MHI_MON - if (mhi_cntrl->nreaders) { - mon_bus_submit(mhi_cntrl, 128, mhi_to_physical(ring, cmd_tre), cmd_tre, NULL, 0); - } -#endif - MHI_VERB("WP:0x%llx TRE: 0x%llx 0x%08x 0x%08x\n", - (u64)mhi_to_physical(ring, cmd_tre), cmd_tre->ptr, - cmd_tre->dword[0], cmd_tre->dword[1]); - - /* queue to hardware */ - mhi_add_ring_element(mhi_cntrl, ring); - read_lock_bh(&mhi_cntrl->pm_lock); - if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl->pm_state))) - mhi_ring_cmd_db(mhi_cntrl, mhi_cmd); - read_unlock_bh(&mhi_cntrl->pm_lock); - spin_unlock_bh(&mhi_cmd->lock); - - return 0; -} - -static int __mhi_prepare_channel(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan) -{ - int ret = 0; - - MHI_LOG("Entered: preparing channel:%d\n", mhi_chan->chan); - - if (!(BIT(mhi_cntrl->ee) & mhi_chan->ee_mask)) { - MHI_ERR("Current EE:%s Required EE Mask:0x%x for chan:%s\n", - TO_MHI_EXEC_STR(mhi_cntrl->ee), mhi_chan->ee_mask, - mhi_chan->name); - return -ENOTCONN; - } - - mutex_lock(&mhi_chan->mutex); - - /* if channel is not disable state do not allow to start */ - if (mhi_chan->ch_state != MHI_CH_STATE_DISABLED) { - ret = -EIO; - MHI_LOG("channel:%d is not in disabled state, ch_state%d\n", - mhi_chan->chan, mhi_chan->ch_state); - goto error_init_chan; - } - - /* client manages channel context for offload channels */ - if (!mhi_chan->offload_ch) { - ret = mhi_init_chan_ctxt(mhi_cntrl, mhi_chan); - if (ret) { - MHI_ERR("Error with init chan\n"); - goto error_init_chan; - } - } - - reinit_completion(&mhi_chan->completion); - read_lock_bh(&mhi_cntrl->pm_lock); - if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - MHI_ERR("MHI host is not in active state\n"); - read_unlock_bh(&mhi_cntrl->pm_lock); - ret = -EIO; - goto error_pm_state; - } - - mhi_cntrl->wake_get(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->runtime_get(mhi_cntrl, mhi_cntrl->priv_data); - - ret = mhi_send_cmd(mhi_cntrl, mhi_chan, MHI_CMD_START_CHAN); - if (ret) { - MHI_ERR("Failed to send start chan cmd\n"); - goto error_send_cmd; - } - - ret = wait_for_completion_timeout(&mhi_chan->completion, - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - if (!ret || mhi_chan->ccs != MHI_EV_CC_SUCCESS) { - MHI_ERR("Failed to receive cmd completion for chan:%d\n", - mhi_chan->chan); - ret = -EIO; - goto error_send_cmd; - } - - mhi_cntrl->runtime_put(mhi_cntrl, mhi_cntrl->priv_data); - - write_lock_irq(&mhi_chan->lock); - mhi_chan->ch_state = MHI_CH_STATE_ENABLED; - write_unlock_irq(&mhi_chan->lock); - - /* pre allocate buffer for xfer ring */ - if (mhi_chan->pre_alloc) { - int nr_el = get_nr_avail_ring_elements(mhi_cntrl, - &mhi_chan->tre_ring); - size_t len = mhi_cntrl->buffer_len; - - while (nr_el--) { - void *buf; - - buf = kmalloc(len, GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - goto error_pre_alloc; - } - - /* prepare transfer descriptors */ - ret = mhi_chan->gen_tre(mhi_cntrl, mhi_chan, buf, buf, - len, MHI_EOT); - if (ret) { - MHI_ERR("Chan:%d error prepare buffer\n", - mhi_chan->chan); - kfree(buf); - goto error_pre_alloc; - } - } - - read_lock_bh(&mhi_cntrl->pm_lock); - if (MHI_DB_ACCESS_VALID(mhi_cntrl->pm_state)) { - read_lock_irq(&mhi_chan->lock); - mhi_ring_chan_db(mhi_cntrl, mhi_chan); - read_unlock_irq(&mhi_chan->lock); - } - read_unlock_bh(&mhi_cntrl->pm_lock); - } - - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_put(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); - - mutex_unlock(&mhi_chan->mutex); - - MHI_LOG("Chan:%d successfully moved to start state\n", mhi_chan->chan); - - return 0; - -error_send_cmd: - mhi_cntrl->runtime_put(mhi_cntrl, mhi_cntrl->priv_data); - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_put(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); - -error_pm_state: - if (!mhi_chan->offload_ch) - mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan); - -error_init_chan: - mutex_unlock(&mhi_chan->mutex); - - return ret; - -error_pre_alloc: - - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_put(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); - - mutex_unlock(&mhi_chan->mutex); - __mhi_unprepare_channel(mhi_cntrl, mhi_chan); - - return ret; -} - -static void mhi_mark_stale_events(struct mhi_controller *mhi_cntrl, - struct mhi_event *mhi_event, - struct mhi_event_ctxt *er_ctxt, - int chan) -{ - struct mhi_tre *dev_rp, *local_rp; - struct mhi_ring *ev_ring; - unsigned long flags; - - MHI_LOG("Marking all events for chan:%d as stale\n", chan); - - ev_ring = &mhi_event->ring; - - /* mark all stale events related to channel as STALE event */ - spin_lock_irqsave(&mhi_event->lock, flags); - dev_rp = mhi_to_virtual(ev_ring, er_ctxt->rp); - - local_rp = ev_ring->rp; - while (dev_rp != local_rp) { - if (MHI_TRE_GET_EV_TYPE(local_rp) == - MHI_PKT_TYPE_TX_EVENT && - chan == MHI_TRE_GET_EV_CHID(local_rp)) - local_rp->dword[1] = MHI_TRE_EV_DWORD1(chan, - MHI_PKT_TYPE_STALE_EVENT); - local_rp++; - if (local_rp == (ev_ring->base + ev_ring->len)) - local_rp = ev_ring->base; - } - - - MHI_LOG("Finished marking events as stale events\n"); - spin_unlock_irqrestore(&mhi_event->lock, flags); -} - -static void mhi_reset_data_chan(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan) -{ - struct mhi_ring *buf_ring, *tre_ring; - struct mhi_result result; - - /* reset any pending buffers */ - buf_ring = &mhi_chan->buf_ring; - tre_ring = &mhi_chan->tre_ring; - result.transaction_status = -ENOTCONN; - result.bytes_xferd = 0; - while (tre_ring->rp != tre_ring->wp) { - struct mhi_buf_info *buf_info = buf_ring->rp; - - if (mhi_chan->dir == DMA_TO_DEVICE) { - if (atomic_dec_return(&mhi_cntrl->pending_pkts) == 0) - mhi_cntrl->runtime_put(mhi_cntrl, mhi_cntrl->priv_data); - } - - if (mhi_chan->dir == DMA_TO_DEVICE) - mhi_cntrl->wake_put(mhi_cntrl, false); - if (!buf_info->pre_mapped) - mhi_cntrl->unmap_single(mhi_cntrl, buf_info); - mhi_del_ring_element(mhi_cntrl, buf_ring); - mhi_del_ring_element(mhi_cntrl, tre_ring); - - if (mhi_chan->pre_alloc) { - kfree(buf_info->cb_buf); - } else { - result.buf_addr = buf_info->cb_buf; - mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); - } - } -} - -static void mhi_reset_rsc_chan(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan) -{ - struct mhi_ring *buf_ring, *tre_ring; - struct mhi_result result; - struct mhi_buf_info *buf_info; - - /* reset any pending buffers */ - buf_ring = &mhi_chan->buf_ring; - tre_ring = &mhi_chan->tre_ring; - result.transaction_status = -ENOTCONN; - result.bytes_xferd = 0; - - buf_info = buf_ring->base; - for (; (void *)buf_info < buf_ring->base + buf_ring->len; buf_info++) { - if (!buf_info->used) - continue; - - result.buf_addr = buf_info->cb_buf; - mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); - buf_info->used = false; - } -} - -void mhi_reset_chan(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan) -{ - - struct mhi_event *mhi_event; - struct mhi_event_ctxt *er_ctxt; - int chan = mhi_chan->chan; - - /* nothing to reset, client don't queue buffers */ - if (mhi_chan->offload_ch) - return; - - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_event = &mhi_cntrl->mhi_event[mhi_chan->er_index]; - er_ctxt = &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_chan->er_index]; - - mhi_mark_stale_events(mhi_cntrl, mhi_event, er_ctxt, chan); - - if (mhi_chan->xfer_type == MHI_XFER_RSC_DMA) - mhi_reset_rsc_chan(mhi_cntrl, mhi_chan); - else - mhi_reset_data_chan(mhi_cntrl, mhi_chan); - - read_unlock_bh(&mhi_cntrl->pm_lock); - MHI_LOG("Reset complete.\n"); -} - -static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan) -{ - int ret; - - MHI_LOG("Entered: unprepare channel:%d\n", mhi_chan->chan); - - /* no more processing events for this channel */ - mutex_lock(&mhi_chan->mutex); - write_lock_irq(&mhi_chan->lock); - if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED) { - MHI_LOG("chan:%d is already disabled\n", mhi_chan->chan); - write_unlock_irq(&mhi_chan->lock); - mutex_unlock(&mhi_chan->mutex); - return; - } - - mhi_chan->ch_state = MHI_CH_STATE_DISABLED; - write_unlock_irq(&mhi_chan->lock); - - reinit_completion(&mhi_chan->completion); - read_lock_bh(&mhi_cntrl->pm_lock); - if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - read_unlock_bh(&mhi_cntrl->pm_lock); - goto error_invalid_state; - } - - mhi_cntrl->wake_get(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); - - mhi_cntrl->runtime_get(mhi_cntrl, mhi_cntrl->priv_data); - ret = mhi_send_cmd(mhi_cntrl, mhi_chan, MHI_CMD_RESET_CHAN); - if (ret) { - MHI_ERR("Failed to send reset chan cmd\n"); - goto error_completion; - } - - /* even if it fails we will still reset */ - ret = wait_for_completion_timeout(&mhi_chan->completion, - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - if (!ret || mhi_chan->ccs != MHI_EV_CC_SUCCESS) - MHI_ERR("Failed to receive cmd completion, still resetting\n"); - -error_completion: - mhi_cntrl->runtime_put(mhi_cntrl, mhi_cntrl->priv_data); - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_put(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); - -error_invalid_state: - if (!mhi_chan->offload_ch) { - mhi_reset_chan(mhi_cntrl, mhi_chan); - mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan); - } - MHI_LOG("chan:%d successfully resetted\n", mhi_chan->chan); - mutex_unlock(&mhi_chan->mutex); -} - -int mhi_debugfs_mhi_states_show(struct seq_file *m, void *d) -{ - struct mhi_controller *mhi_cntrl = m->private; - int reg = 0; - int ret; - u32 val[4]; - - seq_printf(m, - "pm_state:%s dev_state:%s EE:%s M0:%u M2:%u M3:%u wake:%d dev_wake:%u alloc_size:%u\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_STATE_STR(mhi_cntrl->dev_state), - TO_MHI_EXEC_STR(mhi_cntrl->ee), - mhi_cntrl->M0, mhi_cntrl->M2, mhi_cntrl->M3, - mhi_cntrl->wake_set, - atomic_read(&mhi_cntrl->dev_wake), - atomic_read(&mhi_cntrl->alloc_size)); - - seq_printf(m, - "mhi_state:%s exec_env:%s\n", - TO_MHI_STATE_STR(mhi_get_mhi_state(mhi_cntrl)), TO_MHI_EXEC_STR(mhi_get_exec_env(mhi_cntrl))); - - seq_printf(m, "dump mhi reg addr:%p\n", mhi_cntrl->regs); - for (reg = 0; reg < 0x100; reg+=16) { - val[0] = val[1] = val[2] = val[3] = 0xFFFFFFFF; - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, reg+0, &val[0]); - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, reg+4, &val[1]); - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, reg+8, &val[2]); - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, reg+12, &val[3]); - seq_printf(m, "%02x: %08x %08x %08x %08x\n", reg, val[0], val[1], val[2], val[3]); - } - - seq_printf(m, "dump bhi reg addr:%p\n", mhi_cntrl->bhi); - for (reg = 0; reg < 0x100; reg+=16) { - val[0] = val[1] = val[2] = val[3] = 0xFFFFFFFF; - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, reg+0, &val[0]); - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, reg+4, &val[1]); - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, reg+8, &val[2]); - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, reg+12, &val[3]); - seq_printf(m, "%02x: %08x %08x %08x %08x\n", reg, val[0], val[1], val[2], val[3]); - } - - return 0; -} - -int mhi_debugfs_mhi_event_show(struct seq_file *m, void *d) -{ - struct mhi_controller *mhi_cntrl = m->private; - struct mhi_event *mhi_event; - struct mhi_event_ctxt *er_ctxt; - - int i; - - er_ctxt = mhi_cntrl->mhi_ctxt->er_ctxt; - mhi_event = mhi_cntrl->mhi_event; - for (i = 0; i < mhi_cntrl->total_ev_rings; i++, er_ctxt++, - mhi_event++) { - struct mhi_ring *ring = &mhi_event->ring; - - if (mhi_event->offload_ev) { - seq_printf(m, "Index:%d offload event ring\n", i); - } else { - seq_printf(m, - "Index:%d modc:%d modt:%d base:0x%0llx len:0x%llx", - i, er_ctxt->intmodc, er_ctxt->intmodt, - er_ctxt->rbase, er_ctxt->rlen); - seq_printf(m, - " rp:0x%llx wp:0x%llx local_rp:0x%llx local_wp:0x%llx db:0x%llx\n", - er_ctxt->rp, er_ctxt->wp, - (unsigned long long)mhi_to_physical(ring, ring->rp), - (unsigned long long)mhi_to_physical(ring, ring->wp), - (unsigned long long)mhi_event->db_cfg.db_val); - seq_printf(m, "used:%u\n", mhi_event->used_elements); - -#ifdef DEBUG_CHAN100_DB - if (mhi_event->mhi_chan && mhi_event->chan == 100) { - struct mhi_tre *tre = (struct mhi_tre *)ring->base; - size_t j; - - for (j = 0; j < ring->elements; j++, tre++) { - seq_printf(m, - "%08x: %llx, %08x, %08x\n", - (unsigned int)(j*sizeof(struct mhi_tre)), - tre->ptr, tre->dword[0], tre->dword[1]); - } - } -#endif - } - } - - return 0; -} - -int mhi_debugfs_mhi_chan_show(struct seq_file *m, void *d) -{ - struct mhi_controller *mhi_cntrl = m->private; - struct mhi_chan *mhi_chan; - struct mhi_chan_ctxt *chan_ctxt; - int i; - - mhi_chan = mhi_cntrl->mhi_chan; - chan_ctxt = mhi_cntrl->mhi_ctxt->chan_ctxt; - for (i = 0; i < mhi_cntrl->max_chan; i++, chan_ctxt++, mhi_chan++) { - struct mhi_ring *ring = &mhi_chan->tre_ring; - - if (mhi_chan->ch_state == MHI_CH_STATE_DISABLED) - continue; - - if (mhi_chan->offload_ch) { - seq_printf(m, "%s(%u) offload channel\n", - mhi_chan->name, mhi_chan->chan); - } else if (mhi_chan->mhi_dev) { - seq_printf(m, - "%s(%u) state:0x%x brstmode:0x%x pllcfg:0x%x type:0x%x erindex:%u", - mhi_chan->name, mhi_chan->chan, - chan_ctxt->chstate, chan_ctxt->brstmode, - chan_ctxt->pollcfg, chan_ctxt->chtype, - chan_ctxt->erindex); - seq_printf(m, - " base:0x%llx len:0x%llx rp:%llx wp:0x%llx local_rp:0x%llx local_wp:0x%llx db:0x%llx\n", - chan_ctxt->rbase, chan_ctxt->rlen, - chan_ctxt->rp, chan_ctxt->wp, - (unsigned long long)mhi_to_physical(ring, ring->rp), - (unsigned long long)mhi_to_physical(ring, ring->wp), - (unsigned long long)mhi_chan->db_cfg.db_val); - seq_printf(m, "used:%u, EOB:%u, EOT:%u, OOB:%u, DB_MODE:%u\n", mhi_chan->used_elements, - mhi_chan->used_events[MHI_EV_CC_EOB], mhi_chan->used_events[MHI_EV_CC_EOT], - mhi_chan->used_events[MHI_EV_CC_OOB],mhi_chan->used_events[MHI_EV_CC_DB_MODE]); - -#ifdef DEBUG_CHAN100_DB - if (mhi_chan->chan == 100) { - unsigned int n = 0; - seq_printf(m, "chan100_seq = %04x\n", atomic_read(&chan100_seq)%CHAN100_SIZE); - for (n = 0; n < CHAN100_SIZE; n++) { - seq_printf(m, "%04x: %08x\n", n, chan100_t[n]); - } - } -#endif - -#if 0 - if (ring->base && /*(i&1) &&*/ (i < MHI_CLIENT_IP_HW_0_OUT)) { - struct mhi_tre *tre = (struct mhi_tre *)ring->base; - size_t e; - - for (e = 0; e < ring->elements; e++, tre++) { - seq_printf(m, "[%03d] %llx, %08x, %08x\n", i, tre->ptr, tre->dword[0], tre->dword[1]); - } - } -#endif - } - } - - return 0; -} - -/* move channel to start state */ -int mhi_prepare_for_transfer(struct mhi_device *mhi_dev) -{ - int ret, dir; - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - struct mhi_chan *mhi_chan; - - for (dir = 0; dir < 2; dir++) { - mhi_chan = dir ? mhi_dev->dl_chan : mhi_dev->ul_chan; - - if (!mhi_chan) - continue; - - ret = __mhi_prepare_channel(mhi_cntrl, mhi_chan); - if (ret) { - MHI_ERR("Error moving chan %s,%d to START state\n", - mhi_chan->name, mhi_chan->chan); - goto error_open_chan; - } - - if (mhi_dev->dl_chan == mhi_dev->ul_chan) { - break; - } - } - - return 0; - -error_open_chan: - for (--dir; dir >= 0; dir--) { - mhi_chan = dir ? mhi_dev->dl_chan : mhi_dev->ul_chan; - - if (!mhi_chan) - continue; - - __mhi_unprepare_channel(mhi_cntrl, mhi_chan); - } - - return ret; -} -EXPORT_SYMBOL(mhi_prepare_for_transfer); - -void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - struct mhi_chan *mhi_chan; - int dir; - - for (dir = 0; dir < 2; dir++) { - mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; - - if (!mhi_chan) - continue; - - __mhi_unprepare_channel(mhi_cntrl, mhi_chan); - - if (mhi_dev->dl_chan == mhi_dev->ul_chan) { - break; - } - } -} -EXPORT_SYMBOL(mhi_unprepare_from_transfer); - -int mhi_get_no_free_descriptors(struct mhi_device *mhi_dev, - enum dma_data_direction dir) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? - mhi_dev->ul_chan : mhi_dev->dl_chan; - struct mhi_ring *tre_ring = &mhi_chan->tre_ring; - - if (mhi_chan->offload_ch) - return 0; - - return get_nr_avail_ring_elements(mhi_cntrl, tre_ring); -} -EXPORT_SYMBOL(mhi_get_no_free_descriptors); - -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 5,3,0 )) -static int __mhi_bdf_to_controller(struct device *dev, void *tmp) -{ - struct mhi_device *mhi_dev = to_mhi_device(dev); - struct mhi_device *match = tmp; - - /* return any none-zero value if match */ - if (mhi_dev->dev_type == MHI_CONTROLLER_TYPE && - mhi_dev->domain == match->domain && mhi_dev->bus == match->bus && - mhi_dev->slot == match->slot && mhi_dev->dev_id == match->dev_id) - return 1; - - return 0; -} -#else -static int __mhi_bdf_to_controller(struct device *dev, const void *tmp) -{ - struct mhi_device *mhi_dev = to_mhi_device(dev); - const struct mhi_device *match = tmp; - - /* return any none-zero value if match */ - if (mhi_dev->dev_type == MHI_CONTROLLER_TYPE && - mhi_dev->domain == match->domain && mhi_dev->bus == match->bus && - mhi_dev->slot == match->slot && mhi_dev->dev_id == match->dev_id) - return 1; - - return 0; -} -#endif - -struct mhi_controller *mhi_bdf_to_controller(u32 domain, - u32 bus, - u32 slot, - u32 dev_id) -{ - struct mhi_device tmp, *mhi_dev; - struct device *dev; - - tmp.domain = domain; - tmp.bus = bus; - tmp.slot = slot; - tmp.dev_id = dev_id; - - dev = bus_find_device(&mhi_bus_type, NULL, &tmp, - __mhi_bdf_to_controller); - if (!dev) - return NULL; - - mhi_dev = to_mhi_device(dev); - - return mhi_dev->mhi_cntrl; -} -EXPORT_SYMBOL(mhi_bdf_to_controller); - -int mhi_poll(struct mhi_device *mhi_dev, - u32 budget) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - struct mhi_chan *mhi_chan = mhi_dev->dl_chan; - struct mhi_event *mhi_event = &mhi_cntrl->mhi_event[mhi_chan->er_index]; - int ret; - - spin_lock_bh(&mhi_event->lock); - ret = mhi_event->process_event(mhi_cntrl, mhi_event, budget); - spin_unlock_bh(&mhi_event->lock); - - return ret; -} -EXPORT_SYMBOL(mhi_poll); - -int mhi_get_remote_time_sync(struct mhi_device *mhi_dev, - u64 *t_host, - u64 *t_dev) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - struct mhi_timesync *mhi_tsync = mhi_cntrl->mhi_tsync; - int ret; - - /* not all devices support time feature */ - if (!mhi_tsync) - return -EIO; - - /* bring to M0 state */ - ret = __mhi_device_get_sync(mhi_cntrl); - if (ret) - return ret; - - mutex_lock(&mhi_tsync->lpm_mutex); - - read_lock_bh(&mhi_cntrl->pm_lock); - if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) { - MHI_ERR("MHI is not in active state, pm_state:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - ret = -EIO; - goto error_invalid_state; - } - - /* disable link level low power modes */ - ret = mhi_cntrl->lpm_disable(mhi_cntrl, mhi_cntrl->priv_data); - if (ret) - goto error_invalid_state; - - /* - * time critical code to fetch device times, - * delay between these two steps should be - * deterministic as possible. - */ - preempt_disable(); - local_irq_disable(); - - *t_host = mhi_cntrl->time_get(mhi_cntrl, mhi_cntrl->priv_data); - *t_dev = readq_relaxed_no_log(mhi_tsync->time_reg); - - local_irq_enable(); - preempt_enable(); - - mhi_cntrl->lpm_enable(mhi_cntrl, mhi_cntrl->priv_data); - -error_invalid_state: - mhi_cntrl->wake_put(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); - mutex_unlock(&mhi_tsync->lpm_mutex); - - return ret; -} -EXPORT_SYMBOL(mhi_get_remote_time_sync); - -/** - * mhi_get_remote_time - Get external modem time relative to host time - * Trigger event to capture modem time, also capture host time so client - * can do a relative drift comparision. - * Recommended only tsync device calls this method and do not call this - * from atomic context - * @mhi_dev: Device associated with the channels - * @sequence:unique sequence id track event - * @cb_func: callback function to call back - */ -int mhi_get_remote_time(struct mhi_device *mhi_dev, - u32 sequence, - void (*cb_func)(struct mhi_device *mhi_dev, - u32 sequence, - u64 local_time, - u64 remote_time)) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - struct mhi_timesync *mhi_tsync = mhi_cntrl->mhi_tsync; - struct tsync_node *tsync_node; - int ret; - - /* not all devices support time feature */ - if (!mhi_tsync) - return -EIO; - - /* tsync db can only be rung in M0 state */ - ret = __mhi_device_get_sync(mhi_cntrl); - if (ret) - return ret; - - /* - * technically we can use GFP_KERNEL, but wants to avoid - * # of times scheduling out - */ - tsync_node = kzalloc(sizeof(*tsync_node), GFP_ATOMIC); - if (!tsync_node) { - ret = -ENOMEM; - goto error_no_mem; - } - - tsync_node->sequence = sequence; - tsync_node->cb_func = cb_func; - tsync_node->mhi_dev = mhi_dev; - - /* disable link level low power modes */ - mhi_cntrl->lpm_disable(mhi_cntrl, mhi_cntrl->priv_data); - - read_lock_bh(&mhi_cntrl->pm_lock); - if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) { - MHI_ERR("MHI is not in active state, pm_state:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - ret = -EIO; - goto error_invalid_state; - } - - spin_lock_irq(&mhi_tsync->lock); - list_add_tail(&tsync_node->node, &mhi_tsync->head); - spin_unlock_irq(&mhi_tsync->lock); - - /* - * time critical code, delay between these two steps should be - * deterministic as possible. - */ - preempt_disable(); - local_irq_disable(); - - tsync_node->local_time = - mhi_cntrl->time_get(mhi_cntrl, mhi_cntrl->priv_data); - writel_relaxed_no_log(tsync_node->sequence, mhi_tsync->db); - /* write must go thru immediately */ - wmb(); - - local_irq_enable(); - preempt_enable(); - - ret = 0; - -error_invalid_state: - if (ret) - kfree(tsync_node); - read_unlock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->lpm_enable(mhi_cntrl, mhi_cntrl->priv_data); - -error_no_mem: - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_put(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); - - return ret; -} -EXPORT_SYMBOL(mhi_get_remote_time); - -void mhi_debug_reg_dump(struct mhi_controller *mhi_cntrl) -{ - enum mhi_dev_state state; - enum mhi_ee ee; - int i, ret; - u32 val = 0; - void __iomem *mhi_base = mhi_cntrl->regs; - void __iomem *bhi_base = mhi_cntrl->bhi; - void __iomem *bhie_base = mhi_cntrl->bhie; - void __iomem *wake_db = mhi_cntrl->wake_db; - struct { - const char *name; - int offset; - void *base; - } debug_reg[] = { - { "MHI_CNTRL", MHICTRL, mhi_base}, - { "MHI_STATUS", MHISTATUS, mhi_base}, - { "MHI_WAKE_DB", 0, wake_db}, - { "BHI_EXECENV", BHI_EXECENV, bhi_base}, - { "BHI_STATUS", BHI_STATUS, bhi_base}, - { "BHI_ERRCODE", BHI_ERRCODE, bhi_base}, - { "BHI_ERRDBG1", BHI_ERRDBG1, bhi_base}, - { "BHI_ERRDBG2", BHI_ERRDBG2, bhi_base}, - { "BHI_ERRDBG3", BHI_ERRDBG3, bhi_base}, - { "BHIE_TXVEC_DB", BHIE_TXVECDB_OFFS, bhie_base}, - { "BHIE_TXVEC_STATUS", BHIE_TXVECSTATUS_OFFS, bhie_base}, - { "BHIE_RXVEC_DB", BHIE_RXVECDB_OFFS, bhie_base}, - { "BHIE_RXVEC_STATUS", BHIE_RXVECSTATUS_OFFS, bhie_base}, - { NULL }, - }; - - MHI_LOG("host pm_state:%s dev_state:%s ee:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_STATE_STR(mhi_cntrl->dev_state), - TO_MHI_EXEC_STR(mhi_cntrl->ee)); - - state = mhi_get_mhi_state(mhi_cntrl); - ee = mhi_get_exec_env(mhi_cntrl); - - MHI_LOG("device ee:%s dev_state:%s\n", TO_MHI_EXEC_STR(ee), - TO_MHI_STATE_STR(state)); - - for (i = 0; debug_reg[i].name; i++) { - ret = mhi_read_reg(mhi_cntrl, debug_reg[i].base, - debug_reg[i].offset, &val); - MHI_LOG("reg:%s val:0x%x, ret:%d\n", debug_reg[i].name, val, - ret); - } -} -EXPORT_SYMBOL(mhi_debug_reg_dump); diff --git a/quectel_MHI/src/core/mhi_pm.c b/quectel_MHI/src/core/mhi_pm.c deleted file mode 100644 index 117a6005e..000000000 --- a/quectel_MHI/src/core/mhi_pm.c +++ /dev/null @@ -1,1255 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mhi.h" -#include "mhi_internal.h" - -/* - * Not all MHI states transitions are sync transitions. Linkdown, SSR, and - * shutdown can happen anytime asynchronously. This function will transition to - * new state only if we're allowed to transitions. - * - * Priority increase as we go down, example while in any states from L0, start - * state from L1, L2, or L3 can be set. Notable exception to this rule is state - * DISABLE. From DISABLE state we can transition to only POR or state. Also - * for example while in L2 state, user cannot jump back to L1 or L0 states. - * Valid transitions: - * L0: DISABLE <--> POR - * POR <--> POR - * POR -> M0 -> M2 --> M0 - * POR -> FW_DL_ERR - * FW_DL_ERR <--> FW_DL_ERR - * M0 -> FW_DL_ERR - * M0 -> M3_ENTER -> M3 -> M3_EXIT --> M0 - * L1: SYS_ERR_DETECT -> SYS_ERR_PROCESS --> POR - * L2: SHUTDOWN_PROCESS -> DISABLE - * L3: LD_ERR_FATAL_DETECT <--> LD_ERR_FATAL_DETECT - * LD_ERR_FATAL_DETECT -> SHUTDOWN_PROCESS - */ -static struct mhi_pm_transitions const mhi_state_transitions[] = { - /* L0 States */ - { - MHI_PM_DISABLE, - MHI_PM_POR - }, - { - MHI_PM_POR, - MHI_PM_POR | MHI_PM_DISABLE | MHI_PM_M0 | - MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS | - MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_FW_DL_ERR - }, - { - MHI_PM_M0, - MHI_PM_M2 | MHI_PM_M3_ENTER | MHI_PM_SYS_ERR_DETECT | - MHI_PM_SHUTDOWN_PROCESS | MHI_PM_LD_ERR_FATAL_DETECT | - MHI_PM_FW_DL_ERR - }, - { - MHI_PM_M2, - MHI_PM_M0 | MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS | - MHI_PM_LD_ERR_FATAL_DETECT - }, - { - MHI_PM_M3_ENTER, - MHI_PM_M3 | MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS | - MHI_PM_LD_ERR_FATAL_DETECT - }, - { - MHI_PM_M3, - MHI_PM_M3_EXIT | MHI_PM_SYS_ERR_DETECT | - MHI_PM_SHUTDOWN_PROCESS | MHI_PM_LD_ERR_FATAL_DETECT - }, - { - MHI_PM_M3_EXIT, - MHI_PM_M0 | MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS | - MHI_PM_LD_ERR_FATAL_DETECT - }, - { - MHI_PM_FW_DL_ERR, - MHI_PM_FW_DL_ERR | MHI_PM_SYS_ERR_DETECT | - MHI_PM_SHUTDOWN_PROCESS | MHI_PM_LD_ERR_FATAL_DETECT - }, - /* L1 States */ - { - MHI_PM_SYS_ERR_DETECT, - MHI_PM_SYS_ERR_PROCESS | MHI_PM_SHUTDOWN_PROCESS | - MHI_PM_LD_ERR_FATAL_DETECT - }, - { - MHI_PM_SYS_ERR_PROCESS, - MHI_PM_POR | MHI_PM_SHUTDOWN_PROCESS | - MHI_PM_LD_ERR_FATAL_DETECT - }, - /* L2 States */ - { - MHI_PM_SHUTDOWN_PROCESS, - MHI_PM_DISABLE | MHI_PM_LD_ERR_FATAL_DETECT - }, - /* L3 States */ - { - MHI_PM_LD_ERR_FATAL_DETECT, - MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_SHUTDOWN_PROCESS - }, -}; - -enum MHI_PM_STATE __must_check mhi_tryset_pm_state( - struct mhi_controller *mhi_cntrl, - enum MHI_PM_STATE state) -{ - unsigned long cur_state = mhi_cntrl->pm_state; - int index = find_last_bit(&cur_state, 32); - - if (unlikely(index >= ARRAY_SIZE(mhi_state_transitions))) { - MHI_CRITICAL("cur_state:%s is not a valid pm_state\n", - to_mhi_pm_state_str(cur_state)); - return cur_state; - } - - if (unlikely(mhi_state_transitions[index].from_state != cur_state)) { - MHI_ERR("index:%u cur_state:%s != actual_state: %s\n", - index, to_mhi_pm_state_str(cur_state), - to_mhi_pm_state_str - (mhi_state_transitions[index].from_state)); - return cur_state; - } - - if (unlikely(!(mhi_state_transitions[index].to_states & state))) { - MHI_LOG( - "Not allowing pm state transition from:%s to:%s state\n", - to_mhi_pm_state_str(cur_state), - to_mhi_pm_state_str(state)); - return cur_state; - } - - MHI_LOG("Transition to pm state from:%s to:%s\n", - to_mhi_pm_state_str(cur_state), to_mhi_pm_state_str(state)); - - mhi_cntrl->pm_state = state; - return mhi_cntrl->pm_state; -} - -void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl, - enum mhi_dev_state state) -{ - if (state == MHI_STATE_RESET) { - mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, - MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 1); - } else { - mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, - MHICTRL_MHISTATE_MASK, MHICTRL_MHISTATE_SHIFT, state); - } -} - -/* set device wake */ -void mhi_assert_dev_wake(struct mhi_controller *mhi_cntrl, bool force) -{ - unsigned long flags; - -#if 1 //carl.yin 20200907, according to WIN driver, only in M2 state, need to assert, and do not need to deassert - if (mhi_cntrl->dev_state == MHI_STATE_M2) { - spin_lock_irqsave(&mhi_cntrl->wlock, flags); - mhi_write_db(mhi_cntrl, mhi_cntrl->wake_db, 1); - spin_unlock_irqrestore(&mhi_cntrl->wlock, flags); - } - return; -#endif - - /* if set, regardless of count set the bit if not set */ - if (unlikely(force)) { - spin_lock_irqsave(&mhi_cntrl->wlock, flags); - atomic_inc(&mhi_cntrl->dev_wake); - if (MHI_WAKE_DB_FORCE_SET_VALID(mhi_cntrl->pm_state) && - !mhi_cntrl->wake_set) { - mhi_write_db(mhi_cntrl, mhi_cntrl->wake_db, 1); - mhi_cntrl->wake_set = true; - } - spin_unlock_irqrestore(&mhi_cntrl->wlock, flags); - } else { - /* if resources requested already, then increment and exit */ - if (likely(atomic_add_unless(&mhi_cntrl->dev_wake, 1, 0))) - return; - - spin_lock_irqsave(&mhi_cntrl->wlock, flags); - if ((atomic_inc_return(&mhi_cntrl->dev_wake) == 1) && - MHI_WAKE_DB_SET_VALID(mhi_cntrl->pm_state) && - !mhi_cntrl->wake_set) { - mhi_write_db(mhi_cntrl, mhi_cntrl->wake_db, 1); - mhi_cntrl->wake_set = true; - } - spin_unlock_irqrestore(&mhi_cntrl->wlock, flags); - } -} - -/* clear device wake */ -void mhi_deassert_dev_wake(struct mhi_controller *mhi_cntrl, bool override) -{ - unsigned long flags; - -#if 1 //carl.yin 20200907, according to WIN driver, only in M2 state, need to assert, and do not need to deassert - return; -#endif - -#if 1 //Add by Quectel - if (atomic_read(&mhi_cntrl->dev_wake) == 0) - return; -#endif - - MHI_ASSERT(atomic_read(&mhi_cntrl->dev_wake) == 0, "dev_wake == 0"); - - /* resources not dropping to 0, decrement and exit */ - if (likely(atomic_add_unless(&mhi_cntrl->dev_wake, -1, 1))) - return; - - spin_lock_irqsave(&mhi_cntrl->wlock, flags); - if ((atomic_dec_return(&mhi_cntrl->dev_wake) == 0) && - MHI_WAKE_DB_CLEAR_VALID(mhi_cntrl->pm_state) && !override && - mhi_cntrl->wake_set) { - mhi_write_db(mhi_cntrl, mhi_cntrl->wake_db, 0); - mhi_cntrl->wake_set = false; - } - spin_unlock_irqrestore(&mhi_cntrl->wlock, flags); -} - -int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl) -{ - void __iomem *base = mhi_cntrl->regs; - u32 reset = 1, ready = 0; - struct mhi_event *mhi_event; - enum MHI_PM_STATE cur_state; - int ret, i; - - MHI_LOG("Waiting to enter READY state\n"); - - /* wait for RESET to be cleared and READY bit to be set */ - wait_event_timeout(mhi_cntrl->state_event, - MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state) || - mhi_read_reg_field(mhi_cntrl, base, MHICTRL, - MHICTRL_RESET_MASK, - MHICTRL_RESET_SHIFT, &reset) || - mhi_read_reg_field(mhi_cntrl, base, MHISTATUS, - MHISTATUS_READY_MASK, - MHISTATUS_READY_SHIFT, &ready) || - (!reset && ready), - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - - /* device enter into error state */ - if (MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state)) - return -EIO; - - /* device did not transition to ready state */ - if (reset || !ready) - return -ETIMEDOUT; - - MHI_LOG("Device in READY State\n"); - write_lock_irq(&mhi_cntrl->pm_lock); - cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_POR); - mhi_cntrl->dev_state = MHI_STATE_READY; - write_unlock_irq(&mhi_cntrl->pm_lock); - - if (cur_state != MHI_PM_POR) { - MHI_ERR("Error moving to state %s from %s\n", - to_mhi_pm_state_str(MHI_PM_POR), - to_mhi_pm_state_str(cur_state)); - return -EIO; - } - read_lock_bh(&mhi_cntrl->pm_lock); - if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) - goto error_mmio; - - ret = mhi_init_mmio(mhi_cntrl); - if (ret) { - MHI_ERR("Error programming mmio registers\n"); - goto error_mmio; - } - - /* add elements to all sw event rings */ - mhi_event = mhi_cntrl->mhi_event; - for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) { - struct mhi_ring *ring = &mhi_event->ring; - - if (mhi_event->offload_ev || mhi_event->hw_ring) - continue; - - ring->wp = ring->base + ring->len - ring->el_size; - *ring->ctxt_wp = ring->iommu_base + ring->len - ring->el_size; - /* needs to update to all cores */ - smp_wmb(); - - /* ring the db for event rings */ - spin_lock_irq(&mhi_event->lock); - mhi_ring_er_db(mhi_event); - spin_unlock_irq(&mhi_event->lock); - } - - /* set device into M0 state */ - mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M0); - read_unlock_bh(&mhi_cntrl->pm_lock); - - return 0; - -error_mmio: - read_unlock_bh(&mhi_cntrl->pm_lock); - - return -EIO; -} - -int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl) -{ - enum MHI_PM_STATE cur_state; - struct mhi_chan *mhi_chan; - int i; - - MHI_LOG("Entered With State:%s PM_STATE:%s\n", - TO_MHI_STATE_STR(mhi_cntrl->dev_state), - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - - write_lock_irq(&mhi_cntrl->pm_lock); - mhi_cntrl->dev_state = MHI_STATE_M0; - cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M0); - write_unlock_irq(&mhi_cntrl->pm_lock); - if (unlikely(cur_state != MHI_PM_M0)) { - MHI_ERR("Failed to transition to state %s from %s\n", - to_mhi_pm_state_str(MHI_PM_M0), - to_mhi_pm_state_str(cur_state)); - return -EIO; - } - mhi_cntrl->M0++; - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_get(mhi_cntrl, false); - - /* ring all event rings and CMD ring only if we're in mission mode */ - if (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) { - struct mhi_event *mhi_event = mhi_cntrl->mhi_event; - struct mhi_cmd *mhi_cmd = - &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING]; - - for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) { - if (mhi_event->offload_ev) - continue; - - spin_lock_irq(&mhi_event->lock); - mhi_ring_er_db(mhi_event); - spin_unlock_irq(&mhi_event->lock); - } - - /* only ring primary cmd ring */ - spin_lock_irq(&mhi_cmd->lock); - if (mhi_cmd->ring.rp != mhi_cmd->ring.wp) - mhi_ring_cmd_db(mhi_cntrl, mhi_cmd); - spin_unlock_irq(&mhi_cmd->lock); - } - - /* ring channel db registers */ - mhi_chan = mhi_cntrl->mhi_chan; - for (i = 0; i < mhi_cntrl->max_chan; i++, mhi_chan++) { - struct mhi_ring *tre_ring = &mhi_chan->tre_ring; - - write_lock_irq(&mhi_chan->lock); - if (mhi_chan->db_cfg.reset_req) - mhi_chan->db_cfg.db_mode = true; - - /* only ring DB if ring is not empty */ - if (tre_ring->base && tre_ring->wp != tre_ring->rp) - mhi_ring_chan_db(mhi_cntrl, mhi_chan); - write_unlock_irq(&mhi_chan->lock); - } - - mhi_cntrl->wake_put(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); - wake_up_all(&mhi_cntrl->state_event); - MHI_VERB("Exited\n"); - - return 0; -} - -void mhi_pm_m1_transition(struct mhi_controller *mhi_cntrl) -{ - enum MHI_PM_STATE state; - - write_lock_irq(&mhi_cntrl->pm_lock); - /* if it fails, means we transition to M3 */ - state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M2); - if (state == MHI_PM_M2) { - MHI_VERB("Entered M2 State\n"); - mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M2); - mhi_cntrl->dev_state = MHI_STATE_M2; - mhi_cntrl->M2++; - - write_unlock_irq(&mhi_cntrl->pm_lock); - wake_up_all(&mhi_cntrl->state_event); - - /* transfer pending, exit M2 immediately */ - if (unlikely(atomic_read(&mhi_cntrl->dev_wake))) { - MHI_VERB("Exiting M2 Immediately, count:%d\n", - atomic_read(&mhi_cntrl->dev_wake)); - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_get(mhi_cntrl, true); - mhi_cntrl->wake_put(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); - } else { - mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data, - MHI_CB_IDLE); - } - } else { - write_unlock_irq(&mhi_cntrl->pm_lock); - } -} - -int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl) -{ - enum MHI_PM_STATE state; - - write_lock_irq(&mhi_cntrl->pm_lock); - mhi_cntrl->dev_state = MHI_STATE_M3; - state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M3); - write_unlock_irq(&mhi_cntrl->pm_lock); - if (state != MHI_PM_M3) { - MHI_ERR("Failed to transition to state %s from %s\n", - to_mhi_pm_state_str(MHI_PM_M3), - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - return -EIO; - } - wake_up_all(&mhi_cntrl->state_event); - mhi_cntrl->M3++; - - MHI_LOG("Entered mhi_state:%s pm_state:%s\n", - TO_MHI_STATE_STR(mhi_cntrl->dev_state), - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - return 0; -} - -static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl) -{ - int i, ret; - struct mhi_event *mhi_event; - - MHI_LOG("Processing Mission Mode Transition\n"); - mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data, MHI_CB_EE_MISSION_MODE); - - /* force MHI to be in M0 state before continuing */ - ret = __mhi_device_get_sync(mhi_cntrl); - if (ret) - return ret; - - ret = -EIO; - - write_lock_irq(&mhi_cntrl->pm_lock); - if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) - mhi_cntrl->ee = mhi_get_exec_env(mhi_cntrl); - write_unlock_irq(&mhi_cntrl->pm_lock); - - read_lock_bh(&mhi_cntrl->pm_lock); - if (!MHI_IN_MISSION_MODE(mhi_cntrl->ee)) - goto error_mission_mode; - - wake_up_all(&mhi_cntrl->state_event); - - /* add elements to all HW event rings */ - if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) - goto error_mission_mode; - - mhi_event = mhi_cntrl->mhi_event; - for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) { - struct mhi_ring *ring = &mhi_event->ring; - - if (mhi_event->offload_ev || !mhi_event->hw_ring) - continue; - - ring->wp = ring->base + ring->len - ring->el_size; - *ring->ctxt_wp = ring->iommu_base + ring->len - ring->el_size; - /* all ring updates must get updated immediately */ - smp_wmb(); - - spin_lock_irq(&mhi_event->lock); - if (MHI_DB_ACCESS_VALID(mhi_cntrl->pm_state)) - mhi_ring_er_db(mhi_event); - spin_unlock_irq(&mhi_event->lock); - - } - - read_unlock_bh(&mhi_cntrl->pm_lock); - - /* setup support for time sync */ - mhi_init_timesync(mhi_cntrl); - - MHI_LOG("Adding new devices\n"); - - /* add supported devices */ - mhi_create_devices(mhi_cntrl); - - ret = 0; - - read_lock_bh(&mhi_cntrl->pm_lock); - -error_mission_mode: - mhi_cntrl->wake_put(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); - - MHI_LOG("Exit with ret:%d\n", ret); - - return ret; -} - -/* handles both sys_err and shutdown transitions */ -static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl, - enum MHI_PM_STATE transition_state) -{ - enum MHI_PM_STATE cur_state, prev_state; - struct mhi_event *mhi_event; - struct mhi_cmd_ctxt *cmd_ctxt; - struct mhi_cmd *mhi_cmd; - struct mhi_event_ctxt *er_ctxt; - int ret, i; - - MHI_LOG("Enter with from pm_state:%s MHI_STATE:%s to pm_state:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_STATE_STR(mhi_cntrl->dev_state), - to_mhi_pm_state_str(transition_state)); - - /* We must notify MHI control driver so it can clean up first */ - if (transition_state == MHI_PM_SYS_ERR_PROCESS) - mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data, - MHI_CB_SYS_ERROR); - - mutex_lock(&mhi_cntrl->pm_mutex); - write_lock_irq(&mhi_cntrl->pm_lock); - prev_state = mhi_cntrl->pm_state; - cur_state = mhi_tryset_pm_state(mhi_cntrl, transition_state); - if (cur_state == transition_state) { - mhi_cntrl->ee = MHI_EE_DISABLE_TRANSITION; - mhi_cntrl->dev_state = MHI_STATE_RESET; - } - write_unlock_irq(&mhi_cntrl->pm_lock); - - /* wake up any threads waiting for state transitions */ - wake_up_all(&mhi_cntrl->state_event); - - /* not handling sys_err, could be middle of shut down */ - if (cur_state != transition_state) { - MHI_LOG("Failed to transition to state:0x%x from:0x%x\n", - transition_state, cur_state); - mutex_unlock(&mhi_cntrl->pm_mutex); - return; - } - - /* trigger MHI RESET so device will not access host ddr */ - if (MHI_REG_ACCESS_VALID(prev_state)) { - u32 in_reset = -1; - unsigned long timeout = msecs_to_jiffies(mhi_cntrl->timeout_ms); - - MHI_LOG("Trigger device into MHI_RESET\n"); - mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET); - - /* wait for reset to be cleared */ - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_read_reg_field(mhi_cntrl, - mhi_cntrl->regs, MHICTRL, - MHICTRL_RESET_MASK, - MHICTRL_RESET_SHIFT, &in_reset) - || !in_reset, timeout); - if ((!ret || in_reset) && cur_state == MHI_PM_SYS_ERR_PROCESS) { - MHI_CRITICAL("Device failed to exit RESET state\n"); - mutex_unlock(&mhi_cntrl->pm_mutex); - return; - } - - /* Set the numbers of Event Rings supported */ - mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICFG, MHICFG_NER_MASK, MHICFG_NER_SHIFT, NUM_MHI_EVT_RINGS); - mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICFG, MHICFG_NHWER_MASK, MHICFG_NHWER_SHIFT, NUM_MHI_HW_EVT_RINGS); - - /* - * device cleares INTVEC as part of RESET processing, - * re-program it - */ - mhi_write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, mhi_cntrl->msi_irq_base); - } - - MHI_LOG("Waiting for all pending event ring processing to complete\n"); - mhi_event = mhi_cntrl->mhi_event; - for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) { - if (mhi_event->offload_ev) - continue; - tasklet_kill(&mhi_event->task); - } - - mutex_unlock(&mhi_cntrl->pm_mutex); - - MHI_LOG("Reset all active channels and remove mhi devices\n"); - mhi_cntrl->klog_slient = 1; - device_for_each_child(mhi_cntrl->dev, NULL, mhi_destroy_device); - mhi_cntrl->klog_slient = 0; - - MHI_LOG("Finish resetting channels\n"); - - MHI_LOG("Waiting for all pending threads to complete\n"); - wake_up_all(&mhi_cntrl->state_event); - flush_delayed_work(&mhi_cntrl->ready_worker); - flush_work(&mhi_cntrl->st_worker); - flush_work(&mhi_cntrl->fw_worker); - - mutex_lock(&mhi_cntrl->pm_mutex); - - MHI_ASSERT(atomic_read(&mhi_cntrl->dev_wake), "dev_wake != 0"); - - /* reset the ev rings and cmd rings */ - MHI_LOG("Resetting EV CTXT and CMD CTXT\n"); - mhi_cmd = mhi_cntrl->mhi_cmd; - cmd_ctxt = mhi_cntrl->mhi_ctxt->cmd_ctxt; - for (i = 0; i < NR_OF_CMD_RINGS; i++, mhi_cmd++, cmd_ctxt++) { - struct mhi_ring *ring = &mhi_cmd->ring; - - ring->rp = ring->base; - ring->wp = ring->base; - cmd_ctxt->rp = cmd_ctxt->rbase; - cmd_ctxt->wp = cmd_ctxt->rbase; - } - - mhi_event = mhi_cntrl->mhi_event; - er_ctxt = mhi_cntrl->mhi_ctxt->er_ctxt; - for (i = 0; i < mhi_cntrl->total_ev_rings; i++, er_ctxt++, - mhi_event++) { - struct mhi_ring *ring = &mhi_event->ring; - - /* do not touch offload er */ - if (mhi_event->offload_ev) - continue; - - ring->rp = ring->base; - ring->wp = ring->base; - er_ctxt->rp = er_ctxt->rbase; - er_ctxt->wp = er_ctxt->rbase; - } - - /* remove support for time sync */ - mhi_destroy_timesync(mhi_cntrl); - - if (cur_state == MHI_PM_SYS_ERR_PROCESS) { - if (mhi_get_exec_env(mhi_cntrl) == MHI_EE_EDL && mhi_get_mhi_state(mhi_cntrl) == MHI_STATE_RESET) { - write_lock_irq(&mhi_cntrl->pm_lock); - mhi_cntrl->ee = MHI_EE_EDL; - cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_POR); - write_unlock_irq(&mhi_cntrl->pm_lock); - } - else - mhi_ready_state_transition(mhi_cntrl); - } else { - /* move to disable state */ - write_lock_irq(&mhi_cntrl->pm_lock); - cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_DISABLE); - write_unlock_irq(&mhi_cntrl->pm_lock); - if (unlikely(cur_state != MHI_PM_DISABLE)) - MHI_ERR("Error moving from pm state:%s to state:%s\n", - to_mhi_pm_state_str(cur_state), - to_mhi_pm_state_str(MHI_PM_DISABLE)); - } - - MHI_LOG("Exit with pm_state:%s mhi_state:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_STATE_STR(mhi_cntrl->dev_state)); - - mutex_unlock(&mhi_cntrl->pm_mutex); -} - -int mhi_debugfs_trigger_reset(void *data, u64 val) -{ - struct mhi_controller *mhi_cntrl = data; - enum MHI_PM_STATE cur_state; - int ret; - - MHI_LOG("Trigger MHI Reset\n"); - - /* exit lpm first */ - mhi_cntrl->runtime_get(mhi_cntrl, mhi_cntrl->priv_data); - mhi_cntrl->runtime_put(mhi_cntrl, mhi_cntrl->priv_data); - - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_cntrl->dev_state == MHI_STATE_M0 || - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - - if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - MHI_ERR("Did not enter M0 state, cur_state:%s pm_state:%s\n", - TO_MHI_STATE_STR(mhi_cntrl->dev_state), - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - return -EIO; - } - - write_lock_irq(&mhi_cntrl->pm_lock); - cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_SYS_ERR_DETECT); - write_unlock_irq(&mhi_cntrl->pm_lock); - - if (cur_state == MHI_PM_SYS_ERR_DETECT) - schedule_work(&mhi_cntrl->syserr_worker); - - return 0; -} - -/* queue a new work item and scheduler work */ -int mhi_queue_state_transition(struct mhi_controller *mhi_cntrl, - enum MHI_ST_TRANSITION state) -{ - struct state_transition *item = kmalloc(sizeof(*item), GFP_ATOMIC); - unsigned long flags; - - if (!item) - return -ENOMEM; - - item->state = state; - spin_lock_irqsave(&mhi_cntrl->transition_lock, flags); - list_add_tail(&item->node, &mhi_cntrl->transition_list); - spin_unlock_irqrestore(&mhi_cntrl->transition_lock, flags); - - schedule_work(&mhi_cntrl->st_worker); - - return 0; -} - -void mhi_pm_sys_err_worker(struct work_struct *work) -{ - struct mhi_controller *mhi_cntrl = container_of(work, - struct mhi_controller, - syserr_worker); - - MHI_LOG("Enter with pm_state:%s MHI_STATE:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_STATE_STR(mhi_cntrl->dev_state)); - - mhi_pm_disable_transition(mhi_cntrl, MHI_PM_SYS_ERR_PROCESS); -} - -void mhi_pm_ready_worker(struct work_struct *work) -{ - struct mhi_controller *mhi_cntrl = container_of(work, - struct mhi_controller, - ready_worker.work); - enum mhi_ee ee = MHI_EE_MAX; - - if (mhi_cntrl->dev_state != MHI_STATE_RESET) - return; - - write_lock_irq(&mhi_cntrl->pm_lock); - if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) - ee = mhi_get_exec_env(mhi_cntrl); - write_unlock_irq(&mhi_cntrl->pm_lock); - - if (ee == MHI_EE_PTHRU) - schedule_delayed_work(&mhi_cntrl->ready_worker, msecs_to_jiffies(10)); - else if (ee == MHI_EE_AMSS || ee == MHI_EE_SBL) - mhi_queue_state_transition(mhi_cntrl, MHI_ST_TRANSITION_READY); - else if (ee == MHI_EE_EDL) - mhi_queue_state_transition(mhi_cntrl, MHI_ST_TRANSITION_PBL); -} - -void mhi_pm_st_worker(struct work_struct *work) -{ - struct state_transition *itr, *tmp; - LIST_HEAD(head); - struct mhi_controller *mhi_cntrl = container_of(work, - struct mhi_controller, - st_worker); - spin_lock_irq(&mhi_cntrl->transition_lock); - list_splice_tail_init(&mhi_cntrl->transition_list, &head); - spin_unlock_irq(&mhi_cntrl->transition_lock); - - list_for_each_entry_safe(itr, tmp, &head, node) { - list_del(&itr->node); - MHI_LOG("Transition to state:%s\n", - TO_MHI_STATE_TRANS_STR(itr->state)); - - if (mhi_cntrl->ee != mhi_get_exec_env(mhi_cntrl)) { - MHI_LOG("%s -> %s\n", TO_MHI_EXEC_STR(mhi_cntrl->ee), - TO_MHI_EXEC_STR(mhi_get_exec_env(mhi_cntrl))); - } - - switch (itr->state) { - case MHI_ST_TRANSITION_PBL: - write_lock_irq(&mhi_cntrl->pm_lock); - if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) - mhi_cntrl->ee = mhi_get_exec_env(mhi_cntrl); - write_unlock_irq(&mhi_cntrl->pm_lock); - if (MHI_IN_PBL(mhi_cntrl->ee)) - wake_up_all(&mhi_cntrl->state_event); - break; - case MHI_ST_TRANSITION_SBL: - write_lock_irq(&mhi_cntrl->pm_lock); - mhi_cntrl->ee = MHI_EE_SBL; - write_unlock_irq(&mhi_cntrl->pm_lock); - wake_up_all(&mhi_cntrl->state_event); - mhi_create_devices(mhi_cntrl); - break; - case MHI_ST_TRANSITION_MISSION_MODE: - mhi_pm_mission_mode_transition(mhi_cntrl); - break; - case MHI_ST_TRANSITION_READY: - mhi_ready_state_transition(mhi_cntrl); - break; - case MHI_ST_TRANSITION_FP: - write_lock_irq(&mhi_cntrl->pm_lock); - mhi_cntrl->ee = MHI_EE_FP; - write_unlock_irq(&mhi_cntrl->pm_lock); - wake_up(&mhi_cntrl->state_event); - mhi_create_devices(mhi_cntrl); - break; - default: - break; - } - kfree(itr); - } -} - -int mhi_async_power_up(struct mhi_controller *mhi_cntrl) -{ - int ret; - u32 val, regVal; - enum mhi_ee current_ee; - enum MHI_ST_TRANSITION next_state; - - MHI_LOG("Requested to power on\n"); - -#if 0 - if (mhi_cntrl->msi_allocated < mhi_cntrl->total_ev_rings) - return -EINVAL; -#endif - - if (mhi_get_mhi_state(mhi_cntrl) >= MHI_STATE_M0) { - MHI_LOG("mhi_state = %s\n", mhi_state_str[mhi_get_mhi_state(mhi_cntrl)]); - MHI_LOG("Trigger device into MHI_RESET\n"); - mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET); - msleep(50); - MHI_LOG("mhi_state = %s\n", mhi_state_str[mhi_get_mhi_state(mhi_cntrl)]); - } - -#if 1 //GLUE.SDX55_LE.1.0-00098-NOOP_TEST-1\common\hostdrivers\win\MhiHost MhiInitNewDev() - /* Check device Channels support */ - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, MHICFG, ®Val); -#if 0 - val = MHI_READ_REG_FIELD(regVal, MHICFG, NCH); - MHI_LOG("Device CHs: %d\n", val); - val = MHI_READ_REG_FIELD(regVal, MHICFG, NHWCH); - MHI_LOG("Device HW CHs: %d\n", val); - val = MHI_READ_REG_FIELD(regVal, MHICFG, NER); - MHI_LOG("Device ERs: %d\n", val); - val = MHI_READ_REG_FIELD(regVal, MHICFG, NHWER); - MHI_LOG("Device HW ERs: %d\n", val); -#endif - /* Set the numbers of Event Rings supported */ - MHI_WRITE_REG_FIELD(regVal, MHICFG, NER, NUM_MHI_EVT_RINGS); - MHI_WRITE_REG_FIELD(regVal, MHICFG, NHWER, NUM_MHI_HW_EVT_RINGS); - mhi_write_reg(mhi_cntrl, mhi_cntrl->regs, MHICFG, regVal); -#endif - - /* set to default wake if not set */ - if (!mhi_cntrl->wake_get || !mhi_cntrl->wake_put) { - mhi_cntrl->wake_get = mhi_assert_dev_wake; - mhi_cntrl->wake_put = mhi_deassert_dev_wake; - } - - mutex_lock(&mhi_cntrl->pm_mutex); - mhi_cntrl->pm_state = MHI_PM_DISABLE; - mhi_cntrl->dev_state = MHI_STATE_RESET; - - if (!mhi_cntrl->pre_init) { - /* setup device context */ - ret = mhi_init_dev_ctxt(mhi_cntrl); - if (ret) { - MHI_ERR("Error setting dev_context\n"); - goto error_dev_ctxt; - } - - ret = mhi_init_irq_setup(mhi_cntrl); - if (ret) { - MHI_ERR("Error setting up irq\n"); - goto error_setup_irq; - } - } - - /* setup bhi offset & intvec */ - write_lock_irq(&mhi_cntrl->pm_lock); - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, BHIOFF, &val); - if (ret) { - write_unlock_irq(&mhi_cntrl->pm_lock); - MHI_ERR("Error getting bhi offset\n"); - goto error_bhi_offset; - } - - mhi_cntrl->bhi = mhi_cntrl->regs + val; - - /* setup bhie offset */ - if (mhi_cntrl->fbc_download || true) { - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, BHIEOFF, &val); - if (ret) { - write_unlock_irq(&mhi_cntrl->pm_lock); - MHI_ERR("Error getting bhie offset\n"); - goto error_bhi_offset; - } - - mhi_cntrl->bhie = mhi_cntrl->regs + val; - } - - mhi_write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, mhi_cntrl->msi_irq_base); - mhi_cntrl->pm_state = MHI_PM_POR; - mhi_cntrl->ee = MHI_EE_MAX; - current_ee = mhi_get_exec_env(mhi_cntrl); - write_unlock_irq(&mhi_cntrl->pm_lock); - - MHI_LOG("dev_state:%s ee:%s\n", - TO_MHI_STATE_STR(mhi_get_mhi_state(mhi_cntrl)), - TO_MHI_EXEC_STR(mhi_get_exec_env(mhi_cntrl))); - - /* confirm device is in valid exec env */ - if (!MHI_IN_PBL(current_ee) && current_ee != MHI_EE_AMSS) { - //MHI_ERR("Not a valid ee for power on\n"); - //ret = -EIO; - //goto error_bhi_offset; - } - - /* transition to next state */ - next_state = MHI_IN_PBL(current_ee) ? - MHI_ST_TRANSITION_PBL : MHI_ST_TRANSITION_READY; - - //if (next_state == MHI_ST_TRANSITION_PBL) - // schedule_work(&mhi_cntrl->fw_worker); - - if (next_state == MHI_ST_TRANSITION_PBL) - schedule_delayed_work(&mhi_cntrl->ready_worker, msecs_to_jiffies(10)); - else - mhi_queue_state_transition(mhi_cntrl, next_state); - - mhi_init_debugfs(mhi_cntrl); - - mutex_unlock(&mhi_cntrl->pm_mutex); - - MHI_LOG("Power on setup success\n"); - - return 0; - -error_bhi_offset: - if (!mhi_cntrl->pre_init) - mhi_deinit_free_irq(mhi_cntrl); - -error_setup_irq: - if (!mhi_cntrl->pre_init) - mhi_deinit_dev_ctxt(mhi_cntrl); - -error_dev_ctxt: - mutex_unlock(&mhi_cntrl->pm_mutex); - - return ret; -} -EXPORT_SYMBOL(mhi_async_power_up); - -void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful) -{ - enum MHI_PM_STATE cur_state; - - /* if it's not graceful shutdown, force MHI to a linkdown state */ - if (!graceful) { - mutex_lock(&mhi_cntrl->pm_mutex); - write_lock_irq(&mhi_cntrl->pm_lock); - cur_state = mhi_tryset_pm_state(mhi_cntrl, - MHI_PM_LD_ERR_FATAL_DETECT); - write_unlock_irq(&mhi_cntrl->pm_lock); - mutex_unlock(&mhi_cntrl->pm_mutex); - if (cur_state != MHI_PM_LD_ERR_FATAL_DETECT) - MHI_ERR("Failed to move to state:%s from:%s\n", - to_mhi_pm_state_str(MHI_PM_LD_ERR_FATAL_DETECT), - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - } - mhi_pm_disable_transition(mhi_cntrl, MHI_PM_SHUTDOWN_PROCESS); - - mhi_deinit_debugfs(mhi_cntrl); - - if (!mhi_cntrl->pre_init) { - /* free all allocated resources */ - if (mhi_cntrl->fbc_image) { - mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image); - mhi_cntrl->fbc_image = NULL; - } - mhi_deinit_free_irq(mhi_cntrl); - mhi_deinit_dev_ctxt(mhi_cntrl); - } -} -EXPORT_SYMBOL(mhi_power_down); - -int mhi_sync_power_up(struct mhi_controller *mhi_cntrl) -{ - int ret = mhi_async_power_up(mhi_cntrl); - - if (ret) - return ret; - - wait_event_timeout(mhi_cntrl->state_event, - MHI_IN_MISSION_MODE(mhi_cntrl->ee) || - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - - return (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) ? 0 : -EIO; -} -EXPORT_SYMBOL(mhi_sync_power_up); - -int mhi_pm_suspend(struct mhi_controller *mhi_cntrl) -{ - int ret; - enum MHI_PM_STATE new_state; - struct mhi_chan *itr, *tmp; - - if (mhi_cntrl->pm_state == MHI_PM_DISABLE) - return -EINVAL; - - if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) - return -EIO; - - /* do a quick check to see if any pending data, then exit */ - if (atomic_read(&mhi_cntrl->dev_wake)) { - MHI_VERB("Busy, aborting M3\n"); - return -EBUSY; - } - - /* exit MHI out of M2 state */ - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_get(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); - - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_cntrl->dev_state == MHI_STATE_M0 || - mhi_cntrl->dev_state == MHI_STATE_M1 || - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - - if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - MHI_ERR( - "Did not enter M0||M1 state, cur_state:%s pm_state:%s\n", - TO_MHI_STATE_STR(mhi_cntrl->dev_state), - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - ret = -EIO; - goto error_m0_entry; - } - - write_lock_irq(&mhi_cntrl->pm_lock); - - /* we're asserting wake so count would be @ least 1 */ - if (atomic_read(&mhi_cntrl->dev_wake) > 1) { - MHI_VERB("Busy, aborting M3\n"); - write_unlock_irq(&mhi_cntrl->pm_lock); - ret = -EBUSY; - goto error_m0_entry; - } - - /* anytime after this, we will resume thru runtime pm framework */ - MHI_LOG("Allowing M3 transition\n"); - new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M3_ENTER); - if (new_state != MHI_PM_M3_ENTER) { - write_unlock_irq(&mhi_cntrl->pm_lock); - MHI_ERR("Error setting to pm_state:%s from pm_state:%s\n", - to_mhi_pm_state_str(MHI_PM_M3_ENTER), - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - - ret = -EIO; - goto error_m0_entry; - } - - /* set dev to M3 and wait for completion */ - mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M3); - mhi_cntrl->wake_put(mhi_cntrl, false); - write_unlock_irq(&mhi_cntrl->pm_lock); - MHI_LOG("Wait for M3 completion\n"); - - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_cntrl->dev_state == MHI_STATE_M3 || - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - - if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - MHI_ERR("Did not enter M3 state, cur_state:%s pm_state:%s\n", - TO_MHI_STATE_STR(mhi_cntrl->dev_state), - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - return -EIO; - } - - /* notify any clients we enter lpm */ - list_for_each_entry_safe(itr, tmp, &mhi_cntrl->lpm_chans, node) { - mutex_lock(&itr->mutex); - if (itr->mhi_dev) - mhi_notify(itr->mhi_dev, MHI_CB_LPM_ENTER); - mutex_unlock(&itr->mutex); - } - - return 0; - -error_m0_entry: - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_put(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); - - return ret; -} -EXPORT_SYMBOL(mhi_pm_suspend); - -int mhi_pm_resume(struct mhi_controller *mhi_cntrl) -{ - enum MHI_PM_STATE cur_state; - int ret; - struct mhi_chan *itr, *tmp; - - MHI_LOG("Entered with pm_state:%s dev_state:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_STATE_STR(mhi_cntrl->dev_state)); - - if (mhi_cntrl->pm_state == MHI_PM_DISABLE) - return 0; - - if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) - return -EIO; - - MHI_ASSERT(mhi_cntrl->pm_state != MHI_PM_M3, "mhi_pm_state != M3"); - - /* notify any clients we enter lpm */ - list_for_each_entry_safe(itr, tmp, &mhi_cntrl->lpm_chans, node) { - mutex_lock(&itr->mutex); - if (itr->mhi_dev) - mhi_notify(itr->mhi_dev, MHI_CB_LPM_EXIT); - mutex_unlock(&itr->mutex); - } - - write_lock_irq(&mhi_cntrl->pm_lock); - cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M3_EXIT); - if (cur_state != MHI_PM_M3_EXIT) { - write_unlock_irq(&mhi_cntrl->pm_lock); - MHI_ERR("Error setting to pm_state:%s from pm_state:%s\n", - to_mhi_pm_state_str(MHI_PM_M3_EXIT), - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - return -EIO; - } - - /* set dev to M0 and wait for completion */ - mhi_cntrl->wake_get(mhi_cntrl, true); - mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M0); - write_unlock_irq(&mhi_cntrl->pm_lock); - - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_cntrl->dev_state == MHI_STATE_M0 || - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_put(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); - - if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - MHI_ERR("Did not enter M0 state, cur_state:%s pm_state:%s\n", - TO_MHI_STATE_STR(mhi_cntrl->dev_state), - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - - /* - * It's possible device already in error state and we didn't - * process it due to low power mode, force a check - */ - mhi_intvec_threaded_handlr(0, mhi_cntrl); - return -EIO; - } - - return 0; -} - -int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl) -{ - int ret; - - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_get(mhi_cntrl, true); - if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) { - mhi_cntrl->runtime_get(mhi_cntrl, mhi_cntrl->priv_data); - mhi_cntrl->runtime_put(mhi_cntrl, mhi_cntrl->priv_data); - } - read_unlock_bh(&mhi_cntrl->pm_lock); - - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_cntrl->pm_state == MHI_PM_M0 || - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - - if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - MHI_ERR("Did not enter M0 state, cur_state:%s pm_state:%s\n", - TO_MHI_STATE_STR(mhi_cntrl->dev_state), - to_mhi_pm_state_str(mhi_cntrl->pm_state)); - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_put(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); - return -EIO; - } - - return 0; -} - -void mhi_device_get(struct mhi_device *mhi_dev) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - - atomic_inc(&mhi_dev->dev_wake); - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_get(mhi_cntrl, true); - read_unlock_bh(&mhi_cntrl->pm_lock); -} -EXPORT_SYMBOL(mhi_device_get); - -int mhi_device_get_sync(struct mhi_device *mhi_dev) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - int ret; - - ret = __mhi_device_get_sync(mhi_cntrl); - if (!ret) - atomic_inc(&mhi_dev->dev_wake); - - return ret; -} -EXPORT_SYMBOL(mhi_device_get_sync); - -void mhi_device_put(struct mhi_device *mhi_dev) -{ - struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - - atomic_dec(&mhi_dev->dev_wake); - read_lock_bh(&mhi_cntrl->pm_lock); - mhi_cntrl->wake_put(mhi_cntrl, false); - read_unlock_bh(&mhi_cntrl->pm_lock); -} -EXPORT_SYMBOL(mhi_device_put); - -int mhi_force_rddm_mode(struct mhi_controller *mhi_cntrl) -{ - int ret; - - MHI_LOG("Enter with pm_state:%s ee:%s\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_EXEC_STR(mhi_cntrl->ee)); - - MHI_LOG("Triggering SYS_ERR to force rddm state\n"); - mhi_set_mhi_state(mhi_cntrl, MHI_STATE_SYS_ERR); - - /* wait for rddm event */ - MHI_LOG("Waiting for device to enter RDDM state\n"); - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_cntrl->ee == MHI_EE_RDDM, - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - ret = ret ? 0 : -EIO; - - MHI_LOG("Exiting with pm_state:%s ee:%s ret:%d\n", - to_mhi_pm_state_str(mhi_cntrl->pm_state), - TO_MHI_EXEC_STR(mhi_cntrl->ee), ret); - - return ret; -} -EXPORT_SYMBOL(mhi_force_rddm_mode); diff --git a/quectel_MHI/src/core/mhi_sdx20.h b/quectel_MHI/src/core/mhi_sdx20.h deleted file mode 100644 index 2bc5dc7b3..000000000 --- a/quectel_MHI/src/core/mhi_sdx20.h +++ /dev/null @@ -1,362 +0,0 @@ -#ifndef __SDX20_MHI_H -#define __SDX20_MHI_H - -#include - -/* MHI control data structures alloted by the host, including - * channel context array, event context array, command context and rings */ - -/* Channel context state */ -enum mhi_dev_ch_ctx_state { - MHI_DEV_CH_STATE_DISABLED, - MHI_DEV_CH_STATE_ENABLED, - MHI_DEV_CH_STATE_RUNNING, - MHI_DEV_CH_STATE_SUSPENDED, - MHI_DEV_CH_STATE_STOP, - MHI_DEV_CH_STATE_ERROR, - MHI_DEV_CH_STATE_RESERVED, - MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF -}; - -/* Channel type */ -enum mhi_dev_ch_ctx_type { - MHI_DEV_CH_TYPE_NONE, - MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL, - MHI_DEV_CH_TYPE_INBOUND_CHANNEL, - MHI_DEV_CH_RESERVED -}; - -/* Channel context type */ -struct mhi_dev_ch_ctx { - enum mhi_dev_ch_ctx_state ch_state; - enum mhi_dev_ch_ctx_type ch_type; - uint32_t err_indx; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -enum mhi_dev_ring_element_type_id { - MHI_DEV_RING_EL_INVALID = 0, - MHI_DEV_RING_EL_NOOP = 1, - MHI_DEV_RING_EL_TRANSFER = 2, - MHI_DEV_RING_EL_RESET = 16, - MHI_DEV_RING_EL_STOP = 17, - MHI_DEV_RING_EL_START = 18, - MHI_DEV_RING_EL_MHI_STATE_CHG = 32, - MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33, - MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34, - MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64, - MHI_DEV_RING_EL_UNDEF -}; - -enum mhi_dev_ring_state { - RING_STATE_UINT = 0, - RING_STATE_IDLE, - RING_STATE_PENDING, -}; - -enum mhi_dev_ring_type { - RING_TYPE_CMD = 0, - RING_TYPE_ER, - RING_TYPE_CH, - RING_TYPE_INVAL -}; - -/* Event context interrupt moderation */ -enum mhi_dev_evt_ctx_int_mod_timer { - MHI_DEV_EVT_INT_MODERATION_DISABLED -}; - -/* Event ring type */ -enum mhi_dev_evt_ctx_event_ring_type { - MHI_DEV_EVT_TYPE_DEFAULT, - MHI_DEV_EVT_TYPE_VALID, - MHI_DEV_EVT_RESERVED -}; - -/* Event ring context type */ -struct mhi_dev_ev_ctx { - uint32_t res1:16; - enum mhi_dev_evt_ctx_int_mod_timer intmodt:16; - enum mhi_dev_evt_ctx_event_ring_type ertype; - uint32_t msivec; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -/* Command context */ -struct mhi_dev_cmd_ctx { - uint32_t res1; - uint32_t res2; - uint32_t res3; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -/* generic context */ -struct mhi_dev_gen_ctx { - uint32_t res1; - uint32_t res2; - uint32_t res3; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -/* Transfer ring element */ -struct mhi_dev_transfer_ring_element { - uint64_t data_buf_ptr; - uint32_t len:16; - uint32_t res1:16; - uint32_t chain:1; - uint32_t res2:7; - uint32_t ieob:1; - uint32_t ieot:1; - uint32_t bei:1; - uint32_t res3:5; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res4:8; -} __packed; - -/* Command ring element */ -/* Command ring No op command */ -struct mhi_dev_cmd_ring_op { - uint64_t res1; - uint32_t res2; - uint32_t res3:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command ring reset channel command */ -struct mhi_dev_cmd_ring_reset_channel_cmd { - uint64_t res1; - uint32_t res2; - uint32_t res3:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command ring stop channel command */ -struct mhi_dev_cmd_ring_stop_channel_cmd { - uint64_t res1; - uint32_t res2; - uint32_t res3:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command ring start channel command */ -struct mhi_dev_cmd_ring_start_channel_cmd { - uint64_t res1; - uint32_t seqnum; - uint32_t reliable:1; - uint32_t res2:15; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -enum mhi_dev_cmd_completion_code { - MHI_CMD_COMPL_CODE_INVALID = 0, - MHI_CMD_COMPL_CODE_SUCCESS = 1, - MHI_CMD_COMPL_CODE_EOT = 2, - MHI_CMD_COMPL_CODE_OVERFLOW = 3, - MHI_CMD_COMPL_CODE_EOB = 4, - MHI_CMD_COMPL_CODE_UNDEFINED = 16, - MHI_CMD_COMPL_CODE_RING_EL = 17, - MHI_CMD_COMPL_CODE_RES -}; - -/* Event ring elements */ -/* Transfer completion event */ -struct mhi_dev_event_ring_transfer_completion { - uint64_t ptr; - uint32_t len:16; - uint32_t res1:8; - enum mhi_dev_cmd_completion_code code:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command completion event */ -struct mhi_dev_event_ring_cmd_completion { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_cmd_completion_code code:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res3:8; -} __packed; - -enum mhi_dev_state { - MHI_DEV_RESET_STATE = 0, - MHI_DEV_READY_STATE, - MHI_DEV_M0_STATE, - MHI_DEV_M1_STATE, - MHI_DEV_M2_STATE, - MHI_DEV_M3_STATE, - MHI_DEV_MAX_STATE, - MHI_DEV_SYSERR_STATE = 0xff -}; - -/* MHI state change event */ -struct mhi_dev_event_ring_state_change { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_state mhistate:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res3:8; -} __packed; - -enum mhi_dev_execenv { - MHI_DEV_SBL_EE = 1, - MHI_DEV_AMSS_EE = 2, - MHI_DEV_UNRESERVED -}; - -/* EE state change event */ -struct mhi_dev_event_ring_ee_state_change { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_execenv execenv:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res3:8; -} __packed; - -/* Generic cmd to parse common details like type and channel id */ -struct mhi_dev_ring_generic { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_state mhistate:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -struct mhi_config { - uint32_t mhi_reg_len; - uint32_t version; - uint32_t event_rings; - uint32_t channels; - uint32_t chdb_offset; - uint32_t erdb_offset; -}; - -#define NUM_CHANNELS 128 -#define HW_CHANNEL_BASE 100 -#define HW_CHANNEL_END 107 -#define MHI_ENV_VALUE 2 -#define MHI_MASK_ROWS_CH_EV_DB 4 -#define TRB_MAX_DATA_SIZE 8192 -#define MHI_CTRL_STATE 25 -#define IPA_DMA_SYNC 1 -#define IPA_DMA_ASYNC 0 - -/*maximum trasnfer completion events buffer*/ -#define MAX_TR_EVENTS 50 -/*maximum event requests */ -#define MHI_MAX_EVT_REQ 50 - -/* Possible ring element types */ -union mhi_dev_ring_element_type { - struct mhi_dev_cmd_ring_op cmd_no_op; - struct mhi_dev_cmd_ring_reset_channel_cmd cmd_reset; - struct mhi_dev_cmd_ring_stop_channel_cmd cmd_stop; - struct mhi_dev_cmd_ring_start_channel_cmd cmd_start; - struct mhi_dev_transfer_ring_element cmd_transfer; - struct mhi_dev_event_ring_transfer_completion evt_tr_comp; - struct mhi_dev_event_ring_cmd_completion evt_cmd_comp; - struct mhi_dev_event_ring_state_change evt_state_change; - struct mhi_dev_event_ring_ee_state_change evt_ee_state; - struct mhi_dev_ring_generic generic; -}; - -/* Transfer ring element type */ -union mhi_dev_ring_ctx { - struct mhi_dev_cmd_ctx cmd; - struct mhi_dev_ev_ctx ev; - struct mhi_dev_ch_ctx ch; - struct mhi_dev_gen_ctx generic; -}; - -/* MHI host Control and data address region */ -struct mhi_host_addr { - uint32_t ctrl_base_lsb; - uint32_t ctrl_base_msb; - uint32_t ctrl_limit_lsb; - uint32_t ctrl_limit_msb; - uint32_t data_base_lsb; - uint32_t data_base_msb; - uint32_t data_limit_lsb; - uint32_t data_limit_msb; -}; - -/* MHI physical and virtual address region */ -struct mhi_meminfo { - struct device *dev; - uintptr_t pa_aligned; - uintptr_t pa_unaligned; - uintptr_t va_aligned; - uintptr_t va_unaligned; - uintptr_t size; -}; - -struct mhi_addr { - uint64_t host_pa; - uintptr_t device_pa; - uintptr_t device_va; - size_t size; - dma_addr_t phy_addr; - void *virt_addr; - bool use_ipa_dma; -}; - -struct mhi_interrupt_state { - uint32_t mask; - uint32_t status; -}; - -enum mhi_dev_channel_state { - MHI_DEV_CH_UNINT, - MHI_DEV_CH_STARTED, - MHI_DEV_CH_PENDING_START, - MHI_DEV_CH_PENDING_STOP, - MHI_DEV_CH_STOPPED, - MHI_DEV_CH_CLOSED, -}; - -enum mhi_dev_ch_operation { - MHI_DEV_OPEN_CH, - MHI_DEV_CLOSE_CH, - MHI_DEV_READ_CH, - MHI_DEV_READ_WR, - MHI_DEV_POLL, -}; - -enum mhi_ctrl_info { - MHI_STATE_CONFIGURED = 0, - MHI_STATE_CONNECTED = 1, - MHI_STATE_DISCONNECTED = 2, - MHI_STATE_INVAL, -}; - -enum mhi_dev_tr_compl_evt_type { - SEND_EVENT_BUFFER, - SEND_EVENT_RD_OFFSET, -}; - -enum mhi_dev_transfer_type { - MHI_DEV_DMA_SYNC, - MHI_DEV_DMA_ASYNC, -}; -#endif /* _SDX20_MHI_H_ */ diff --git a/quectel_MHI/src/core/sdx20_mhi.h b/quectel_MHI/src/core/sdx20_mhi.h deleted file mode 100644 index 29c4a3a25..000000000 --- a/quectel_MHI/src/core/sdx20_mhi.h +++ /dev/null @@ -1,426 +0,0 @@ -#ifndef __SDX20_MHI_H -#define __SDX20_MHI_H - -#include - -/* MHI control data structures alloted by the host, including - * channel context array, event context array, command context and rings */ - -/* Channel context state */ -enum mhi_dev_ch_ctx_state { - MHI_DEV_CH_STATE_DISABLED, - MHI_DEV_CH_STATE_ENABLED, - MHI_DEV_CH_STATE_RUNNING, - MHI_DEV_CH_STATE_SUSPENDED, - MHI_DEV_CH_STATE_STOP, - MHI_DEV_CH_STATE_ERROR, - MHI_DEV_CH_STATE_RESERVED, - MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF -}; - -/* Channel type */ -enum mhi_dev_ch_ctx_type { - MHI_DEV_CH_TYPE_NONE, - MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL, - MHI_DEV_CH_TYPE_INBOUND_CHANNEL, - MHI_DEV_CH_RESERVED -}; - -/* Channel context type */ -struct mhi_dev_ch_ctx { - enum mhi_dev_ch_ctx_state ch_state; - enum mhi_dev_ch_ctx_type ch_type; - uint32_t err_indx; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -enum mhi_dev_ring_element_type_id { - MHI_DEV_RING_EL_INVALID = 0, - MHI_DEV_RING_EL_NOOP = 1, - MHI_DEV_RING_EL_TRANSFER = 2, - MHI_DEV_RING_EL_RESET = 16, - MHI_DEV_RING_EL_STOP = 17, - MHI_DEV_RING_EL_START = 18, - MHI_DEV_RING_EL_MHI_STATE_CHG = 32, - MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33, - MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34, - MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64, - MHI_DEV_RING_EL_UNDEF -}; - -enum mhi_dev_ring_state { - RING_STATE_UINT = 0, - RING_STATE_IDLE, - RING_STATE_PENDING, -}; - -enum mhi_dev_ring_type { - RING_TYPE_CMD = 0, - RING_TYPE_ER, - RING_TYPE_CH, - RING_TYPE_INVAL -}; - -/* Event context interrupt moderation */ -enum mhi_dev_evt_ctx_int_mod_timer { - MHI_DEV_EVT_INT_MODERATION_DISABLED -}; - -/* Event ring type */ -enum mhi_dev_evt_ctx_event_ring_type { - MHI_DEV_EVT_TYPE_DEFAULT, - MHI_DEV_EVT_TYPE_VALID, - MHI_DEV_EVT_RESERVED -}; - -/* Event ring context type */ -struct mhi_dev_ev_ctx { - uint32_t res1:16; - enum mhi_dev_evt_ctx_int_mod_timer intmodt:16; - enum mhi_dev_evt_ctx_event_ring_type ertype; - uint32_t msivec; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -/* Command context */ -struct mhi_dev_cmd_ctx { - uint32_t res1; - uint32_t res2; - uint32_t res3; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -/* generic context */ -struct mhi_dev_gen_ctx { - uint32_t res1; - uint32_t res2; - uint32_t res3; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -/* Transfer ring element */ -struct mhi_dev_transfer_ring_element { - uint64_t data_buf_ptr; - uint32_t len:16; - uint32_t res1:16; - uint32_t chain:1; - uint32_t res2:7; - uint32_t ieob:1; - uint32_t ieot:1; - uint32_t bei:1; - uint32_t res3:5; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res4:8; -} __packed; - -/* Command ring element */ -/* Command ring No op command */ -struct mhi_dev_cmd_ring_op { - uint64_t res1; - uint32_t res2; - uint32_t res3:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command ring reset channel command */ -struct mhi_dev_cmd_ring_reset_channel_cmd { - uint64_t res1; - uint32_t res2; - uint32_t res3:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command ring stop channel command */ -struct mhi_dev_cmd_ring_stop_channel_cmd { - uint64_t res1; - uint32_t res2; - uint32_t res3:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command ring start channel command */ -struct mhi_dev_cmd_ring_start_channel_cmd { - uint64_t res1; - uint32_t seqnum; - uint32_t reliable:1; - uint32_t res2:15; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -enum mhi_dev_cmd_completion_code { - MHI_CMD_COMPL_CODE_INVALID = 0, - MHI_CMD_COMPL_CODE_SUCCESS = 1, - MHI_CMD_COMPL_CODE_EOT = 2, - MHI_CMD_COMPL_CODE_OVERFLOW = 3, - MHI_CMD_COMPL_CODE_EOB = 4, - MHI_CMD_COMPL_CODE_UNDEFINED = 16, - MHI_CMD_COMPL_CODE_RING_EL = 17, - MHI_CMD_COMPL_CODE_RES -}; - -/* Event ring elements */ -/* Transfer completion event */ -struct mhi_dev_event_ring_transfer_completion { - uint64_t ptr; - uint32_t len:16; - uint32_t res1:8; - enum mhi_dev_cmd_completion_code code:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command completion event */ -struct mhi_dev_event_ring_cmd_completion { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_cmd_completion_code code:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res3:8; -} __packed; - -enum mhi_dev_state { - MHI_DEV_RESET_STATE = 0, - MHI_DEV_READY_STATE, - MHI_DEV_M0_STATE, - MHI_DEV_M1_STATE, - MHI_DEV_M2_STATE, - MHI_DEV_M3_STATE, - MHI_DEV_MAX_STATE, - MHI_DEV_SYSERR_STATE = 0xff -}; - -/* MHI state change event */ -struct mhi_dev_event_ring_state_change { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_state mhistate:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res3:8; -} __packed; - -enum mhi_dev_execenv { - MHI_DEV_SBL_EE = 1, - MHI_DEV_AMSS_EE = 2, - MHI_DEV_UNRESERVED -}; - -/* EE state change event */ -struct mhi_dev_event_ring_ee_state_change { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_execenv execenv:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res3:8; -} __packed; - -/* Generic cmd to parse common details like type and channel id */ -struct mhi_dev_ring_generic { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_state mhistate:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -struct mhi_config { - uint32_t mhi_reg_len; - uint32_t version; - uint32_t event_rings; - uint32_t channels; - uint32_t chdb_offset; - uint32_t erdb_offset; -}; - -#define NUM_CHANNELS 128 -#define HW_CHANNEL_BASE 100 -#define HW_CHANNEL_END 107 -#define MHI_ENV_VALUE 2 -#define MHI_MASK_ROWS_CH_EV_DB 4 -#define TRB_MAX_DATA_SIZE 8192 -#define MHI_CTRL_STATE 25 -#define IPA_DMA_SYNC 1 -#define IPA_DMA_ASYNC 0 - -/*maximum trasnfer completion events buffer*/ -#define MAX_TR_EVENTS 50 -/*maximum event requests */ -#define MHI_MAX_EVT_REQ 50 - -/* Possible ring element types */ -union mhi_dev_ring_element_type { - struct mhi_dev_cmd_ring_op cmd_no_op; - struct mhi_dev_cmd_ring_reset_channel_cmd cmd_reset; - struct mhi_dev_cmd_ring_stop_channel_cmd cmd_stop; - struct mhi_dev_cmd_ring_start_channel_cmd cmd_start; - struct mhi_dev_transfer_ring_element tre; - struct mhi_dev_event_ring_transfer_completion evt_tr_comp; - struct mhi_dev_event_ring_cmd_completion evt_cmd_comp; - struct mhi_dev_event_ring_state_change evt_state_change; - struct mhi_dev_event_ring_ee_state_change evt_ee_state; - struct mhi_dev_ring_generic generic; -}; - -/* Transfer ring element type */ -union mhi_dev_ring_ctx { - struct mhi_dev_cmd_ctx cmd; - struct mhi_dev_ev_ctx ev; - struct mhi_dev_ch_ctx ch; - struct mhi_dev_gen_ctx generic; -}; - -/* MHI host Control and data address region */ -struct mhi_host_addr { - uint32_t ctrl_base_lsb; - uint32_t ctrl_base_msb; - uint32_t ctrl_limit_lsb; - uint32_t ctrl_limit_msb; - uint32_t data_base_lsb; - uint32_t data_base_msb; - uint32_t data_limit_lsb; - uint32_t data_limit_msb; -}; - -/* MHI physical and virtual address region */ -struct mhi_meminfo { - struct device *dev; - uintptr_t pa_aligned; - uintptr_t pa_unaligned; - uintptr_t va_aligned; - uintptr_t va_unaligned; - uintptr_t size; -}; - -struct mhi_addr { - uint64_t host_pa; - uintptr_t device_pa; - uintptr_t device_va; - size_t size; - dma_addr_t phy_addr; - void *virt_addr; - bool use_ipa_dma; -}; - -struct mhi_interrupt_state { - uint32_t mask; - uint32_t status; -}; - -enum mhi_dev_channel_state { - MHI_DEV_CH_UNINT, - MHI_DEV_CH_STARTED, - MHI_DEV_CH_PENDING_START, - MHI_DEV_CH_PENDING_STOP, - MHI_DEV_CH_STOPPED, - MHI_DEV_CH_CLOSED, -}; - -enum mhi_dev_ch_operation { - MHI_DEV_OPEN_CH, - MHI_DEV_CLOSE_CH, - MHI_DEV_READ_CH, - MHI_DEV_READ_WR, - MHI_DEV_POLL, -}; - -enum mhi_ctrl_info { - MHI_STATE_CONFIGURED = 0, - MHI_STATE_CONNECTED = 1, - MHI_STATE_DISCONNECTED = 2, - MHI_STATE_INVAL, -}; - -enum mhi_dev_tr_compl_evt_type { - SEND_EVENT_BUFFER, - SEND_EVENT_RD_OFFSET, -}; - -enum mhi_dev_transfer_type { - MHI_DEV_DMA_SYNC, - MHI_DEV_DMA_ASYNC, -}; - -#if 0 -/* SW channel client list */ -enum mhi_client_channel { - MHI_CLIENT_LOOPBACK_OUT = 0, - MHI_CLIENT_LOOPBACK_IN = 1, - MHI_CLIENT_SAHARA_OUT = 2, - MHI_CLIENT_SAHARA_IN = 3, - MHI_CLIENT_DIAG_OUT = 4, - MHI_CLIENT_DIAG_IN = 5, - MHI_CLIENT_SSR_OUT = 6, - MHI_CLIENT_SSR_IN = 7, - MHI_CLIENT_QDSS_OUT = 8, - MHI_CLIENT_QDSS_IN = 9, - MHI_CLIENT_EFS_OUT = 10, - MHI_CLIENT_EFS_IN = 11, - MHI_CLIENT_MBIM_OUT = 12, - MHI_CLIENT_MBIM_IN = 13, - MHI_CLIENT_QMI_OUT = 14, - MHI_CLIENT_QMI_IN = 15, - MHI_CLIENT_IP_CTRL_0_OUT = 16, - MHI_CLIENT_IP_CTRL_0_IN = 17, - MHI_CLIENT_IP_CTRL_1_OUT = 18, - MHI_CLIENT_IP_CTRL_1_IN = 19, - MHI_CLIENT_DCI_OUT = 20, - MHI_CLIENT_DCI_IN = 21, - MHI_CLIENT_IP_CTRL_3_OUT = 22, - MHI_CLIENT_IP_CTRL_3_IN = 23, - MHI_CLIENT_IP_CTRL_4_OUT = 24, - MHI_CLIENT_IP_CTRL_4_IN = 25, - MHI_CLIENT_IP_CTRL_5_OUT = 26, - MHI_CLIENT_IP_CTRL_5_IN = 27, - MHI_CLIENT_IP_CTRL_6_OUT = 28, - MHI_CLIENT_IP_CTRL_6_IN = 29, - MHI_CLIENT_IP_CTRL_7_OUT = 30, - MHI_CLIENT_IP_CTRL_7_IN = 31, - MHI_CLIENT_DUN_OUT = 32, - MHI_CLIENT_DUN_IN = 33, - MHI_CLIENT_IP_SW_0_OUT = 34, - MHI_CLIENT_IP_SW_0_IN = 35, - MHI_CLIENT_IP_SW_1_OUT = 36, - MHI_CLIENT_IP_SW_1_IN = 37, - MHI_CLIENT_IP_SW_2_OUT = 38, - MHI_CLIENT_IP_SW_2_IN = 39, - MHI_CLIENT_IP_SW_3_OUT = 40, - MHI_CLIENT_IP_SW_3_IN = 41, - MHI_CLIENT_CSVT_OUT = 42, - MHI_CLIENT_CSVT_IN = 43, - MHI_CLIENT_SMCT_OUT = 44, - MHI_CLIENT_SMCT_IN = 45, - MHI_CLIENT_IP_SW_4_OUT = 46, - MHI_CLIENT_IP_SW_4_IN = 47, - MHI_MAX_SOFTWARE_CHANNELS = 48, - MHI_CLIENT_TEST_OUT = 60, - MHI_CLIENT_TEST_IN = 61, - MHI_CLIENT_RESERVED_1_LOWER = 62, - MHI_CLIENT_RESERVED_1_UPPER = 99, - MHI_CLIENT_IP_HW_0_OUT = 100, - MHI_CLIENT_IP_HW_0_IN = 101, - MHI_CLIENT_RESERVED_2_LOWER = 102, - MHI_CLIENT_RESERVED_2_UPPER = 127, - MHI_MAX_CHANNELS = 102, -}; -#endif -#endif /* _SDX20_MHI_H_ */ diff --git a/quectel_MHI/src/devices/Kconfig b/quectel_MHI/src/devices/Kconfig deleted file mode 100644 index d92e95b3f..000000000 --- a/quectel_MHI/src/devices/Kconfig +++ /dev/null @@ -1,33 +0,0 @@ -menu "MHI device support" - -config MHI_NETDEV - tristate "MHI NETDEV" - depends on MHI_BUS - help - MHI based net device driver for transferring IP traffic - between host and modem. By enabling this driver, clients - can transfer data using standard network interface. Over - the air traffic goes thru mhi netdev interface. - -config MHI_UCI - tristate "MHI UCI" - depends on MHI_BUS - help - MHI based uci driver is for transferring data between host and - modem using standard file operations from user space. Open, read, - write, ioctl, and close operations are supported by this driver. - Please check mhi_uci_match_table for all supported channels that - are exposed to userspace. - -config MHI_SATELLITE - tristate "MHI SATELLITE" - depends on MHI_BUS - help - MHI proxy satellite device driver enables NON-HLOS MHI satellite - drivers to communicate with device over PCIe link without host - involvement. Host facilitates propagation of events from device - to NON-HLOS MHI satellite drivers, channel states, and power - management over IPC communication. It helps in HLOS power - savings. - -endmenu diff --git a/quectel_MHI/src/devices/Makefile b/quectel_MHI/src/devices/Makefile deleted file mode 100644 index e720069fd..000000000 --- a/quectel_MHI/src/devices/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_MHI_NETDEV) +=mhi_netdev.o -obj-$(CONFIG_MHI_UCI) +=mhi_uci.o -obj-$(CONFIG_MHI_SATELLITE) +=mhi_satellite.o diff --git a/quectel_MHI/src/devices/mhi_netdev.c b/quectel_MHI/src/devices/mhi_netdev.c deleted file mode 100644 index ed7b24ba4..000000000 --- a/quectel_MHI/src/devices/mhi_netdev.c +++ /dev/null @@ -1,1063 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/ - -#include -#include -#include -#include -//#include -#include -#include -#include -//#include -#if 1 -static inline void *ipc_log_context_create(int max_num_pages, - const char *modname, uint16_t user_version) -{ return NULL; } -static inline int ipc_log_string(void *ilctxt, const char *fmt, ...) -{ return -EINVAL; } -#endif -#include -#include -#include -#include -#include -#include "../core/mhi.h" - -#define QUECTEL_NO_DTS - -extern void rmnet_data_init(struct net_device *real_dev, u32 nr_rmnet_devs); -extern void rmnet_data_deinit(struct net_device *real_dev, u32 nr_rmnet_devs); - -static const unsigned char node_id[ETH_ALEN] = {0x02, 0x50, 0xf4, 0x00, 0x00, 0x00}; -static const unsigned char default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3, 0x00, 0x00, 0x00}; -static void qmap_hex_dump(const char *tag, unsigned char *data, unsigned len) { - uint i; - uint *d = (uint *)data; - - printk(KERN_DEBUG "%s data=%p, len=%x\n", tag, data, len); - len = (len+3)/4; - for (i = 0; i < len; i+=4) { - printk(KERN_DEBUG "%08x %08x %08x %08x %08x\n", i*4, d[i+0], d[i+1], d[i+2], d[i+3]); - } -} - -#define MHI_NETDEV_DRIVER_NAME "mhi_netdev" -#define WATCHDOG_TIMEOUT (30 * HZ) -#define IPC_LOG_PAGES (100) -#define MAX_NETBUF_SIZE (128) - -#ifdef CONFIG_MHI_DEBUG - -#define IPC_LOG_LVL (MHI_MSG_LVL_VERBOSE) - -#define MHI_ASSERT(cond, msg) do { \ - if (cond) \ - panic(msg); \ -} while (0) - -#define MSG_VERB(fmt, ...) do { \ - if (mhi_netdev->msg_lvl <= MHI_MSG_LVL_VERBOSE) \ - pr_err("[D][%s] " fmt, __func__, ##__VA_ARGS__);\ - if (mhi_netdev->ipc_log && (mhi_netdev->ipc_log_lvl <= \ - MHI_MSG_LVL_VERBOSE)) \ - ipc_log_string(mhi_netdev->ipc_log, "[D][%s] " fmt, \ - __func__, ##__VA_ARGS__); \ -} while (0) - -#else - -#define IPC_LOG_LVL (MHI_MSG_LVL_ERROR) - -#define MHI_ASSERT(cond, msg) do { \ - if (cond) { \ - MSG_ERR(msg); \ - WARN_ON(cond); \ - } \ -} while (0) - -#define MSG_VERB(fmt, ...) - -#endif - -#define MSG_LOG(fmt, ...) do { \ - if (mhi_netdev->msg_lvl <= MHI_MSG_LVL_INFO) \ - pr_err("[I][%s] " fmt, __func__, ##__VA_ARGS__);\ - if (mhi_netdev->ipc_log && (mhi_netdev->ipc_log_lvl <= \ - MHI_MSG_LVL_INFO)) \ - ipc_log_string(mhi_netdev->ipc_log, "[I][%s] " fmt, \ - __func__, ##__VA_ARGS__); \ -} while (0) - -#define MSG_ERR(fmt, ...) do { \ - if (mhi_netdev->msg_lvl <= MHI_MSG_LVL_ERROR) \ - pr_err("[E][%s] " fmt, __func__, ##__VA_ARGS__); \ - if (mhi_netdev->ipc_log && (mhi_netdev->ipc_log_lvl <= \ - MHI_MSG_LVL_ERROR)) \ - ipc_log_string(mhi_netdev->ipc_log, "[E][%s] " fmt, \ - __func__, ##__VA_ARGS__); \ -} while (0) - -struct mhi_net_chain { - struct sk_buff *head, *tail; /* chained skb */ -}; - -struct mhi_netdev { - int alias; - struct mhi_device *mhi_dev; - struct mhi_netdev *rsc_dev; /* rsc linked node */ - bool is_rsc_dev; - int wake; - - u32 mru; - u32 order; - const char *interface_name; - struct napi_struct *napi; - struct net_device *ndev; - bool ethernet_interface; - - struct mhi_netbuf **netbuf_pool; - int pool_size; /* must be power of 2 */ - int current_index; - bool chain_skb; - struct mhi_net_chain *chain; - - struct dentry *dentry; - enum MHI_DEBUG_LEVEL msg_lvl; - enum MHI_DEBUG_LEVEL ipc_log_lvl; - void *ipc_log; - - //struct rmnet_port port; -}; - -struct mhi_netdev_priv { - struct mhi_netdev *mhi_netdev; -}; - -/* Try not to make this structure bigger than 128 bytes, since this take space - * in payload packet. - * Example: If MRU = 16K, effective MRU = 16K - sizeof(mhi_netbuf) - */ -struct mhi_netbuf { - struct mhi_buf mhi_buf; /* this must be first element */ - void (*unmap)(struct device *dev, dma_addr_t addr, size_t size, - enum dma_data_direction dir); -}; - -static struct mhi_driver mhi_netdev_driver; -static void mhi_netdev_create_debugfs(struct mhi_netdev *mhi_netdev); - -static __be16 mhi_netdev_ip_type_trans(u8 data) -{ - __be16 protocol = 0; - - /* determine L3 protocol */ - switch (data & 0xf0) { - case 0x40: - protocol = htons(ETH_P_IP); - break; - case 0x60: - protocol = htons(ETH_P_IPV6); - break; - default: - /* default is QMAP */ - protocol = htons(ETH_P_MAP); - break; - } - protocol = htons(ETH_P_MAP); //carl.yin fix set - return protocol; -} - -static struct mhi_netbuf *mhi_netdev_alloc(struct device *dev, - gfp_t gfp, - unsigned int order) -{ - struct page *page; - struct mhi_netbuf *netbuf; - struct mhi_buf *mhi_buf; - void *vaddr; - - page = __dev_alloc_pages(gfp, order); - if (!page) - return NULL; - - vaddr = page_address(page); - - /* we going to use the end of page to store cached data */ - netbuf = vaddr + (PAGE_SIZE << order) - sizeof(*netbuf); - - mhi_buf = (struct mhi_buf *)netbuf; - mhi_buf->page = page; - mhi_buf->buf = vaddr; - mhi_buf->len = (void *)netbuf - vaddr; - mhi_buf->dma_addr = dma_map_page(dev, page, 0, mhi_buf->len, - DMA_FROM_DEVICE); - if (dma_mapping_error(dev, mhi_buf->dma_addr)) { - __free_pages(mhi_buf->page, order); - return NULL; - } - - return netbuf; -} - -static void mhi_netdev_unmap_page(struct device *dev, - dma_addr_t dma_addr, - size_t len, - enum dma_data_direction dir) -{ - dma_unmap_page(dev, dma_addr, len, dir); -} - -static int mhi_netdev_tmp_alloc(struct mhi_netdev *mhi_netdev, int nr_tre) -{ - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - struct device *dev = mhi_dev->dev.parent; - const u32 order = mhi_netdev->order; - int i, ret; - - for (i = 0; i < nr_tre; i++) { - struct mhi_buf *mhi_buf; - struct mhi_netbuf *netbuf = mhi_netdev_alloc(dev, GFP_ATOMIC, - order); - if (!netbuf) - return -ENOMEM; - - mhi_buf = (struct mhi_buf *)netbuf; - netbuf->unmap = mhi_netdev_unmap_page; - - ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, mhi_buf, - mhi_buf->len, MHI_EOT); - if (unlikely(ret)) { - MSG_ERR("Failed to queue transfer, ret:%d\n", ret); - mhi_netdev_unmap_page(dev, mhi_buf->dma_addr, - mhi_buf->len, DMA_FROM_DEVICE); - __free_pages(mhi_buf->page, order); - return ret; - } - } - - return 0; -} - -static void mhi_netdev_queue(struct mhi_netdev *mhi_netdev) -{ - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - struct device *dev = mhi_dev->dev.parent; - struct mhi_netbuf *netbuf; - struct mhi_buf *mhi_buf; - struct mhi_netbuf **netbuf_pool = mhi_netdev->netbuf_pool; - int nr_tre = mhi_get_no_free_descriptors(mhi_dev, DMA_FROM_DEVICE); - int i, peak, cur_index, ret; - const int pool_size = mhi_netdev->pool_size - 1, max_peak = 4; - - MSG_VERB("Enter free_desc:%d\n", nr_tre); - - if (!nr_tre) - return; - - /* try going thru reclaim pool first */ - for (i = 0; i < nr_tre; i++) { - /* peak for the next buffer, we going to peak several times, - * and we going to give up if buffers are not yet free - */ - cur_index = mhi_netdev->current_index; - netbuf = NULL; - for (peak = 0; peak < max_peak; peak++) { - struct mhi_netbuf *tmp = netbuf_pool[cur_index]; - - mhi_buf = &tmp->mhi_buf; - - cur_index = (cur_index + 1) & pool_size; - - /* page == 1 idle, buffer is free to reclaim */ - if (page_count(mhi_buf->page) == 1) { - netbuf = tmp; - break; - } - } - - /* could not find a free buffer */ - if (!netbuf) - break; - - /* increment reference count so when network stack is done - * with buffer, the buffer won't be freed - */ - get_page(mhi_buf->page); - dma_sync_single_for_device(dev, mhi_buf->dma_addr, mhi_buf->len, - DMA_FROM_DEVICE); - ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, mhi_buf, - mhi_buf->len, MHI_EOT); - if (unlikely(ret)) { - MSG_ERR("Failed to queue buffer, ret:%d\n", ret); - netbuf->unmap(dev, mhi_buf->dma_addr, mhi_buf->len, - DMA_FROM_DEVICE); - put_page(mhi_buf->page); - return; - } - mhi_netdev->current_index = cur_index; - } - - /* recyling did not work, buffers are still busy allocate temp pkts */ - if (i < nr_tre) - mhi_netdev_tmp_alloc(mhi_netdev, nr_tre - i); -} - -/* allocating pool of memory */ -static int mhi_netdev_alloc_pool(struct mhi_netdev *mhi_netdev) -{ - int i; - struct mhi_netbuf *netbuf, **netbuf_pool; - struct mhi_buf *mhi_buf; - const u32 order = mhi_netdev->order; - struct device *dev = mhi_netdev->mhi_dev->dev.parent; - - netbuf_pool = kmalloc_array(mhi_netdev->pool_size, sizeof(*netbuf_pool), - GFP_KERNEL); - if (!netbuf_pool) - return -ENOMEM; - - for (i = 0; i < mhi_netdev->pool_size; i++) { - /* allocate paged data */ - netbuf = mhi_netdev_alloc(dev, GFP_KERNEL, order); - if (!netbuf) - goto error_alloc_page; - - netbuf->unmap = dma_sync_single_for_cpu; - netbuf_pool[i] = netbuf; - } - - mhi_netdev->netbuf_pool = netbuf_pool; - - return 0; - -error_alloc_page: - for (--i; i >= 0; i--) { - netbuf = netbuf_pool[i]; - mhi_buf = &netbuf->mhi_buf; - dma_unmap_page(dev, mhi_buf->dma_addr, mhi_buf->len, - DMA_FROM_DEVICE); - __free_pages(mhi_buf->page, order); - } - - kfree(netbuf_pool); - - return -ENOMEM; -} - -static void mhi_netdev_free_pool(struct mhi_netdev *mhi_netdev) -{ - int i; - struct mhi_netbuf *netbuf, **netbuf_pool = mhi_netdev->netbuf_pool; - struct device *dev = mhi_netdev->mhi_dev->dev.parent; - struct mhi_buf *mhi_buf; - - for (i = 0; i < mhi_netdev->pool_size; i++) { - netbuf = netbuf_pool[i]; - mhi_buf = &netbuf->mhi_buf; - dma_unmap_page(dev, mhi_buf->dma_addr, mhi_buf->len, - DMA_FROM_DEVICE); - __free_pages(mhi_buf->page, mhi_netdev->order); - } - - kfree(mhi_netdev->netbuf_pool); - mhi_netdev->netbuf_pool = NULL; -} - -static int mhi_netdev_poll(struct napi_struct *napi, int budget) -{ - struct net_device *dev = napi->dev; - struct mhi_netdev_priv *mhi_netdev_priv = netdev_priv(dev); - struct mhi_netdev *mhi_netdev = mhi_netdev_priv->mhi_netdev; - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - struct mhi_netdev *rsc_dev = mhi_netdev->rsc_dev; - struct mhi_net_chain *chain = mhi_netdev->chain; - int rx_work = 0; - - MSG_VERB("Entered\n"); - - rx_work = mhi_poll(mhi_dev, budget); - - /* chained skb, push it to stack */ - if (chain && chain->head) { - netif_receive_skb(chain->head); - chain->head = NULL; - } - - if (rx_work < 0) { - MSG_ERR("Error polling ret:%d\n", rx_work); - napi_complete(napi); - return 0; - } - - /* queue new buffers */ - mhi_netdev_queue(mhi_netdev); - - if (rsc_dev) - mhi_netdev_queue(rsc_dev); - - /* complete work if # of packet processed less than allocated budget */ - if (rx_work < budget) - napi_complete(napi); - - MSG_VERB("polled %d pkts\n", rx_work); - - return rx_work; -} - -static int mhi_netdev_open(struct net_device *dev) -{ - struct mhi_netdev_priv *mhi_netdev_priv = netdev_priv(dev); - struct mhi_netdev *mhi_netdev = mhi_netdev_priv->mhi_netdev; - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - - MSG_LOG("Opened net dev interface\n"); - - /* tx queue may not necessarily be stopped already - * so stop the queue if tx path is not enabled - */ - if (!mhi_dev->ul_chan) - netif_stop_queue(dev); - else - netif_start_queue(dev); - - return 0; - -} - -static int mhi_netdev_change_mtu(struct net_device *dev, int new_mtu) -{ - struct mhi_netdev_priv *mhi_netdev_priv = netdev_priv(dev); - struct mhi_netdev *mhi_netdev = mhi_netdev_priv->mhi_netdev; - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - - if (new_mtu < 0 || mhi_dev->mtu < new_mtu) - return -EINVAL; - - dev->mtu = new_mtu; - return 0; -} - -static int mhi_netdev_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct mhi_netdev_priv *mhi_netdev_priv = netdev_priv(dev); - struct mhi_netdev *mhi_netdev = mhi_netdev_priv->mhi_netdev; - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - int res = 0; - - MSG_VERB("Entered\n"); - - //printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - //qmap_hex_dump(__func__, skb->data, 32); - if (skb->protocol != htons(ETH_P_MAP)) { - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } - - res = mhi_queue_transfer(mhi_dev, DMA_TO_DEVICE, skb, skb->len, - MHI_EOT); - if (res) { - MSG_VERB("Failed to queue with reason:%d\n", res); - netif_stop_queue(dev); - res = NETDEV_TX_BUSY; - } - - MSG_VERB("Exited\n"); - - return res; -} - -#if 0 -static int mhi_netdev_ioctl_extended(struct net_device *dev, struct ifreq *ifr) -{ - struct rmnet_ioctl_extended_s ext_cmd; - int rc = 0; - struct mhi_netdev_priv *mhi_netdev_priv = netdev_priv(dev); - struct mhi_netdev *mhi_netdev = mhi_netdev_priv->mhi_netdev; - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - - rc = copy_from_user(&ext_cmd, ifr->ifr_ifru.ifru_data, - sizeof(struct rmnet_ioctl_extended_s)); - if (rc) - return rc; - - switch (ext_cmd.extended_ioctl) { - case RMNET_IOCTL_GET_SUPPORTED_FEATURES: - ext_cmd.u.data = 0; - break; - case RMNET_IOCTL_GET_DRIVER_NAME: - strlcpy(ext_cmd.u.if_name, mhi_netdev->interface_name, - sizeof(ext_cmd.u.if_name)); - break; - case RMNET_IOCTL_SET_SLEEP_STATE: - if (ext_cmd.u.data && mhi_netdev->wake) { - /* Request to enable LPM */ - MSG_VERB("Enable MHI LPM"); - mhi_netdev->wake--; - mhi_device_put(mhi_dev); - } else if (!ext_cmd.u.data && !mhi_netdev->wake) { - /* Request to disable LPM */ - MSG_VERB("Disable MHI LPM"); - mhi_netdev->wake++; - mhi_device_get(mhi_dev); - } - break; - default: - rc = -EINVAL; - break; - } - - rc = copy_to_user(ifr->ifr_ifru.ifru_data, &ext_cmd, - sizeof(struct rmnet_ioctl_extended_s)); - return rc; -} - -static int mhi_netdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - int rc = 0; - struct rmnet_ioctl_data_s ioctl_data; - - switch (cmd) { - case RMNET_IOCTL_SET_LLP_IP: /* set RAWIP protocol */ - break; - case RMNET_IOCTL_GET_LLP: /* get link protocol state */ - ioctl_data.u.operation_mode = RMNET_MODE_LLP_IP; - if (copy_to_user(ifr->ifr_ifru.ifru_data, &ioctl_data, - sizeof(struct rmnet_ioctl_data_s))) - rc = -EFAULT; - break; - case RMNET_IOCTL_GET_OPMODE: /* get operation mode */ - ioctl_data.u.operation_mode = RMNET_MODE_LLP_IP; - if (copy_to_user(ifr->ifr_ifru.ifru_data, &ioctl_data, - sizeof(struct rmnet_ioctl_data_s))) - rc = -EFAULT; - break; - case RMNET_IOCTL_SET_QOS_ENABLE: - rc = -EINVAL; - break; - case RMNET_IOCTL_SET_QOS_DISABLE: - rc = 0; - break; - case RMNET_IOCTL_OPEN: - case RMNET_IOCTL_CLOSE: - /* we just ignore them and return success */ - rc = 0; - break; - case RMNET_IOCTL_EXTENDED: - rc = mhi_netdev_ioctl_extended(dev, ifr); - break; - default: - /* don't fail any IOCTL right now */ - rc = 0; - break; - } - - return rc; -} -#endif - -static void mhi_netdev_get_drvinfo (struct net_device *ndev, struct ethtool_drvinfo *info) -{ - //struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev); - - strlcpy (info->driver, "pcie_mhi", sizeof info->driver); - strlcpy (info->version, PCIE_MHI_DRIVER_VERSION, sizeof info->version); -} - -static const struct ethtool_ops mhi_netdev_ethtool_ops = { - .get_drvinfo = mhi_netdev_get_drvinfo, -}; - -static const struct net_device_ops mhi_netdev_ops_ip = { - .ndo_open = mhi_netdev_open, - .ndo_start_xmit = mhi_netdev_xmit, - //.ndo_do_ioctl = mhi_netdev_ioctl, - .ndo_change_mtu = mhi_netdev_change_mtu, - .ndo_set_mac_address = 0, - .ndo_validate_addr = 0, -}; - -static void mhi_netdev_setup(struct net_device *dev) -{ - dev->netdev_ops = &mhi_netdev_ops_ip; - ether_setup(dev); - - /* set this after calling ether_setup */ - dev->header_ops = 0; /* No header */ - dev->type = ARPHRD_RAWIP; - dev->hard_header_len = 0; - dev->addr_len = 0; - dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); - dev->watchdog_timeo = WATCHDOG_TIMEOUT; - - dev->ethtool_ops = &mhi_netdev_ethtool_ops; - memcpy (dev->dev_addr, node_id, sizeof node_id); - dev->flags |= IFF_NOARP; -} - -/* enable mhi_netdev netdev, call only after grabbing mhi_netdev.mutex */ -static int mhi_netdev_enable_iface(struct mhi_netdev *mhi_netdev) -{ - int ret = 0; - char ifalias[IFALIASZ]; - char ifname[IFNAMSIZ]; - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; -#ifndef QUECTEL_NO_DTS - struct device_node *of_node = mhi_dev->dev.of_node; -#endif - struct mhi_netdev_priv *mhi_netdev_priv; - - mhi_netdev->alias = 0;//of_alias_get_id(of_node, "mhi-netdev"); - if (mhi_netdev->alias < 0) - mhi_netdev->alias = 0; - -#ifdef QUECTEL_NO_DTS - mhi_netdev->interface_name = "rmnet_mhi"; -#else - - ret = of_property_read_string(of_node, "mhi,interface-name", - &mhi_netdev->interface_name); -#endif - if (ret) - mhi_netdev->interface_name = mhi_netdev_driver.driver.name; - - snprintf(ifalias, sizeof(ifalias), "%s_%04x_%02u.%02u.%02u_%u", - mhi_netdev->interface_name, mhi_dev->dev_id, mhi_dev->domain, - mhi_dev->bus, mhi_dev->slot, mhi_netdev->alias); - - snprintf(ifname, sizeof(ifname), "%s%%d", mhi_netdev->interface_name); - -#ifdef QUECTEL_NO_DTS - mhi_netdev->ethernet_interface = 0; -#else - mhi_netdev->ethernet_interface = of_property_read_bool(of_node, - "mhi,ethernet-interface"); -#endif - rtnl_lock(); - mhi_netdev->ndev = alloc_netdev(sizeof(*mhi_netdev_priv), - ifname, NET_NAME_PREDICTABLE, - mhi_netdev_setup); - if (!mhi_netdev->ndev) { - rtnl_unlock(); - return -ENOMEM; - } - - mhi_netdev->ndev->mtu = mhi_dev->mtu; - SET_NETDEV_DEV(mhi_netdev->ndev, &mhi_dev->dev); - //dev_set_alias(mhi_netdev->ndev, ifalias, strlen(ifalias)); - mhi_netdev_priv = netdev_priv(mhi_netdev->ndev); - mhi_netdev_priv->mhi_netdev = mhi_netdev; - rtnl_unlock(); - - mhi_netdev->napi = devm_kzalloc(&mhi_dev->dev, - sizeof(*mhi_netdev->napi), GFP_KERNEL); - if (!mhi_netdev->napi) { - ret = -ENOMEM; - goto napi_alloc_fail; - } - - netif_napi_add(mhi_netdev->ndev, mhi_netdev->napi, - mhi_netdev_poll, NAPI_POLL_WEIGHT); - ret = register_netdev(mhi_netdev->ndev); - if (ret) { - MSG_ERR("Network device registration failed\n"); - goto net_dev_reg_fail; - } - - napi_enable(mhi_netdev->napi); - - MSG_LOG("Exited.\n"); - - return 0; - -net_dev_reg_fail: - netif_napi_del(mhi_netdev->napi); - -napi_alloc_fail: - free_netdev(mhi_netdev->ndev); - mhi_netdev->ndev = NULL; - - return ret; -} - -static void mhi_netdev_xfer_ul_cb(struct mhi_device *mhi_dev, - struct mhi_result *mhi_result) -{ - struct mhi_netdev *mhi_netdev = mhi_device_get_devdata(mhi_dev); - struct sk_buff *skb = mhi_result->buf_addr; - struct net_device *ndev = mhi_netdev->ndev; - - ndev->stats.tx_packets++; - ndev->stats.tx_bytes += skb->len; - dev_kfree_skb(skb); - - if (netif_queue_stopped(ndev)) - netif_wake_queue(ndev); -} - -static void mhi_netdev_push_skb(struct mhi_netdev *mhi_netdev, - struct mhi_buf *mhi_buf, - struct mhi_result *mhi_result) -{ - struct sk_buff *skb; - - skb = alloc_skb(0, GFP_ATOMIC); - if (!skb) { - __free_pages(mhi_buf->page, mhi_netdev->order); - return; - } - - if (!mhi_netdev->ethernet_interface) { - skb_add_rx_frag(skb, 0, mhi_buf->page, 0, - mhi_result->bytes_xferd, mhi_netdev->mru); - skb->dev = mhi_netdev->ndev; - skb->protocol = mhi_netdev_ip_type_trans(*(u8 *)mhi_buf->buf); - if (skb_linearize(skb)) - return; - } else { - skb_add_rx_frag(skb, 0, mhi_buf->page, ETH_HLEN, - mhi_result->bytes_xferd - ETH_HLEN, - mhi_netdev->mru); - skb->dev = mhi_netdev->ndev; - skb->protocol = mhi_netdev_ip_type_trans(((u8 *)mhi_buf->buf)[ETH_HLEN]); - } - netif_receive_skb(skb); -} - -static void mhi_netdev_xfer_dl_cb(struct mhi_device *mhi_dev, - struct mhi_result *mhi_result) -{ - struct mhi_netdev *mhi_netdev = mhi_device_get_devdata(mhi_dev); - struct mhi_netbuf *netbuf = mhi_result->buf_addr; - struct mhi_buf *mhi_buf = &netbuf->mhi_buf; - struct sk_buff *skb; - struct net_device *ndev = mhi_netdev->ndev; - struct device *dev = mhi_dev->dev.parent; - struct mhi_net_chain *chain = mhi_netdev->chain; - - netbuf->unmap(dev, mhi_buf->dma_addr, mhi_buf->len, DMA_FROM_DEVICE); - - /* modem is down, drop the buffer */ - if (mhi_result->transaction_status == -ENOTCONN) { - __free_pages(mhi_buf->page, mhi_netdev->order); - return; - } - - ndev->stats.rx_packets++; - ndev->stats.rx_bytes += mhi_result->bytes_xferd; - - if (unlikely(!chain)) { - mhi_netdev_push_skb(mhi_netdev, mhi_buf, mhi_result); - return; - } - - /* we support chaining */ - skb = alloc_skb(0, GFP_ATOMIC); - if (likely(skb)) { - if (!mhi_netdev->ethernet_interface) { - skb_add_rx_frag(skb, 0, mhi_buf->page, 0, - mhi_result->bytes_xferd, mhi_netdev->mru); - } else { - skb_add_rx_frag(skb, 0, mhi_buf->page, ETH_HLEN, - mhi_result->bytes_xferd - ETH_HLEN, - mhi_netdev->mru); - } - - /* this is first on list */ - if (!chain->head) { - skb->dev = ndev; - if (!mhi_netdev->ethernet_interface) { - skb->protocol = - mhi_netdev_ip_type_trans(*(u8 *)mhi_buf->buf); - } else { - skb->protocol = - mhi_netdev_ip_type_trans(((u8 *)mhi_buf->buf)[ETH_HLEN]); - } - chain->head = skb; - } else { - skb_shinfo(chain->tail)->frag_list = skb; - } - - chain->tail = skb; - } else { - __free_pages(mhi_buf->page, mhi_netdev->order); - } -} - -static void mhi_netdev_status_cb(struct mhi_device *mhi_dev, enum MHI_CB mhi_cb) -{ - struct mhi_netdev *mhi_netdev = mhi_device_get_devdata(mhi_dev); - - if (mhi_cb != MHI_CB_PENDING_DATA) - return; - - napi_schedule(mhi_netdev->napi); -} - -#ifdef CONFIG_DEBUG_FS - -struct dentry *dentry; - -static void mhi_netdev_create_debugfs(struct mhi_netdev *mhi_netdev) -{ - char node_name[32]; - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - - /* Both tx & rx client handle contain same device info */ - snprintf(node_name, sizeof(node_name), "%s_%04x_%02u.%02u.%02u_%u", - mhi_netdev->interface_name, mhi_dev->dev_id, mhi_dev->domain, - mhi_dev->bus, mhi_dev->slot, mhi_netdev->alias); - - if (IS_ERR_OR_NULL(dentry)) - return; - - mhi_netdev->dentry = debugfs_create_dir(node_name, dentry); - if (IS_ERR_OR_NULL(mhi_netdev->dentry)) - return; -} - -static void mhi_netdev_create_debugfs_dir(void) -{ - dentry = debugfs_create_dir(MHI_NETDEV_DRIVER_NAME, 0); -} - -#else - -static void mhi_netdev_create_debugfs(struct mhi_netdev_private *mhi_netdev) -{ -} - -static void mhi_netdev_create_debugfs_dir(void) -{ -} - -#endif - -static void mhi_netdev_remove(struct mhi_device *mhi_dev) -{ - struct mhi_netdev *mhi_netdev = mhi_device_get_devdata(mhi_dev); - - MSG_LOG("Remove notification received\n"); - - /* rsc parent takes cares of the cleanup */ - if (mhi_netdev->is_rsc_dev) { - mhi_netdev_free_pool(mhi_netdev); - return; - } - - rmnet_data_deinit(mhi_netdev->ndev, 1); - netif_stop_queue(mhi_netdev->ndev); - napi_disable(mhi_netdev->napi); - unregister_netdev(mhi_netdev->ndev); - netif_napi_del(mhi_netdev->napi); - free_netdev(mhi_netdev->ndev); - mhi_netdev_free_pool(mhi_netdev); - - if (!IS_ERR_OR_NULL(mhi_netdev->dentry)) - debugfs_remove_recursive(mhi_netdev->dentry); -} - -static int mhi_netdev_match(struct device *dev, void *data) -{ - /* if phandle dt == device dt, we found a match */ - return (dev->of_node == data); -} - -static void mhi_netdev_clone_dev(struct mhi_netdev *mhi_netdev, - struct mhi_netdev *parent) -{ - mhi_netdev->ndev = parent->ndev; - mhi_netdev->napi = parent->napi; - mhi_netdev->ipc_log = parent->ipc_log; - mhi_netdev->msg_lvl = parent->msg_lvl; - mhi_netdev->ipc_log_lvl = parent->ipc_log_lvl; - mhi_netdev->is_rsc_dev = true; - mhi_netdev->chain = parent->chain; -} - -static int mhi_netdev_probe(struct mhi_device *mhi_dev, - const struct mhi_device_id *id) -{ - int ret; - struct mhi_netdev *mhi_netdev, *p_netdev = NULL; -#ifndef QUECTEL_NO_DTS - struct device_node *of_node = mhi_dev->dev.of_node; -#endif - int nr_tre; - char node_name[32]; - struct device_node *phandle; - bool no_chain; - -#ifndef QUECTEL_NO_DTS - if (!of_node) - return -ENODEV; -#endif - - mhi_netdev = devm_kzalloc(&mhi_dev->dev, sizeof(*mhi_netdev), - GFP_KERNEL); - if (!mhi_netdev) - return -ENOMEM; - - mhi_netdev->mhi_dev = mhi_dev; - mhi_device_set_devdata(mhi_dev, mhi_netdev); - -#ifdef QUECTEL_NO_DTS - mhi_netdev->mru = 16384; - ret = 0; -#else - ret = of_property_read_u32(of_node, "mhi,mru", &mhi_netdev->mru); -#endif - if (ret) - return -ENODEV; - - /* MRU must be multiplication of page size */ - mhi_netdev->order = __ilog2_u32(mhi_netdev->mru / PAGE_SIZE); - if ((PAGE_SIZE << mhi_netdev->order) < mhi_netdev->mru) - return -EINVAL; - - /* check if this device shared by a parent device */ -#ifdef QUECTEL_NO_DTS - phandle = NULL; -#else - phandle = of_parse_phandle(of_node, "mhi,rsc-parent", 0); -#endif - if (phandle) { - struct device *dev; - struct mhi_device *pdev; - /* find the parent device */ - dev = driver_find_device(mhi_dev->dev.driver, NULL, phandle, - mhi_netdev_match); - if (!dev) - return -ENODEV; - - /* this device is shared with parent device. so we won't be - * creating a new network interface. Clone parent - * information to child node - */ - pdev = to_mhi_device(dev); - p_netdev = mhi_device_get_devdata(pdev); - mhi_netdev_clone_dev(mhi_netdev, p_netdev); - put_device(dev); - } else { - mhi_netdev->msg_lvl = MHI_MSG_LVL_ERROR; -#ifdef QUECTEL_NO_DTS - no_chain = 0; -#else - no_chain = of_property_read_bool(of_node, - "mhi,disable-chain-skb"); -#endif - if (!no_chain) { - mhi_netdev->chain = devm_kzalloc(&mhi_dev->dev, - sizeof(*mhi_netdev->chain), - GFP_KERNEL); - if (!mhi_netdev->chain) - return -ENOMEM; - } - - ret = mhi_netdev_enable_iface(mhi_netdev); - if (ret) - return ret; - - /* create ipc log buffer */ - snprintf(node_name, sizeof(node_name), - "%s_%04x_%02u.%02u.%02u_%u", - mhi_netdev->interface_name, mhi_dev->dev_id, - mhi_dev->domain, mhi_dev->bus, mhi_dev->slot, - mhi_netdev->alias); - mhi_netdev->ipc_log = ipc_log_context_create(IPC_LOG_PAGES, - node_name, 0); - mhi_netdev->ipc_log_lvl = IPC_LOG_LVL; - - mhi_netdev_create_debugfs(mhi_netdev); - } - - /* move mhi channels to start state */ - ret = mhi_prepare_for_transfer(mhi_dev); - if (ret) { - MSG_ERR("Failed to start channels ret %d\n", ret); - goto error_start; - } - - rmnet_data_init(mhi_netdev->ndev, 1); - - /* setup pool size ~2x ring length*/ - nr_tre = mhi_get_no_free_descriptors(mhi_dev, DMA_FROM_DEVICE); - mhi_netdev->pool_size = 1 << __ilog2_u32(nr_tre); - if (nr_tre > mhi_netdev->pool_size) - mhi_netdev->pool_size <<= 1; - mhi_netdev->pool_size <<= 1; - - /* allocate memory pool */ - ret = mhi_netdev_alloc_pool(mhi_netdev); - if (ret) - goto error_start; - - /* link child node with parent node if it's children dev */ - if (p_netdev) - p_netdev->rsc_dev = mhi_netdev; - - /* now we have a pool of buffers allocated, queue to hardware - * by triggering a napi_poll - */ - napi_schedule(mhi_netdev->napi); - - return 0; - -error_start: - if (phandle) - return ret; - - netif_stop_queue(mhi_netdev->ndev); - napi_disable(mhi_netdev->napi); - unregister_netdev(mhi_netdev->ndev); - netif_napi_del(mhi_netdev->napi); - free_netdev(mhi_netdev->ndev); - - return ret; -} - -static const struct mhi_device_id mhi_netdev_match_table[] = { - { .chan = "IP_HW0" }, - { .chan = "IP_HW_ADPL" }, - { .chan = "IP_HW0_RSC" }, - { .chan = "IP_SW0" }, - {}, -}; - -static struct mhi_driver mhi_netdev_driver = { - .id_table = mhi_netdev_match_table, - .probe = mhi_netdev_probe, - .remove = mhi_netdev_remove, - .ul_xfer_cb = mhi_netdev_xfer_ul_cb, - .dl_xfer_cb = mhi_netdev_xfer_dl_cb, - .status_cb = mhi_netdev_status_cb, - .driver = { - .name = "mhi_netdev", - .owner = THIS_MODULE, - } -}; - -static int __init mhi_netdev_init(void) -{ - BUILD_BUG_ON(sizeof(struct mhi_netbuf) > MAX_NETBUF_SIZE); - mhi_netdev_create_debugfs_dir(); - - return mhi_driver_register(&mhi_netdev_driver); -} -//module_init(mhi_netdev_init); - -int __init mhi_device_netdev_init(struct dentry *parent) -{ - BUILD_BUG_ON(sizeof(struct mhi_netbuf) > MAX_NETBUF_SIZE); - - return mhi_netdev_init(); -} - -void mhi_device_netdev_exit(void) -{ -#ifdef CONFIG_DEBUG_FS - debugfs_remove_recursive(dentry); -#endif - mhi_driver_unregister(&mhi_netdev_driver); -} - diff --git a/quectel_MHI/src/devices/mhi_netdev_quectel.c b/quectel_MHI/src/devices/mhi_netdev_quectel.c deleted file mode 100644 index f38742ffb..000000000 --- a/quectel_MHI/src/devices/mhi_netdev_quectel.c +++ /dev/null @@ -1,3421 +0,0 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//#define CONFIG_IPQ5018_RATE_CONTROL //Only used with spf11.5 for IPQ5018 -#if defined(CONFIG_IPQ5018_RATE_CONTROL) -//#include -#include -#endif - -#include "../core/mhi.h" -//#define MHI_NETDEV_ONE_CARD_MODE -//#define ANDROID_gki //some fuction not allow used in this TEST - -#ifndef ETH_P_MAP -#define ETH_P_MAP 0xDA1A -#endif - -#ifndef ARPHRD_RAWIP -#define ARPHRD_RAWIP ARPHRD_NONE -#endif - -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 4,2,0 )) -static bool netdev_is_rx_handler_busy(struct net_device *dev) -{ - ASSERT_RTNL(); - return dev && rtnl_dereference(dev->rx_handler); -} -#endif - -struct rmnet_nss_cb { - int (*nss_create)(struct net_device *dev); - int (*nss_free)(struct net_device *dev); - int (*nss_tx)(struct sk_buff *skb); -}; -static struct rmnet_nss_cb __read_mostly *nss_cb = NULL; -#if defined(CONFIG_PINCTRL_IPQ807x) || defined(CONFIG_PINCTRL_IPQ5018) || defined(CONFIG_PINCTRL_IPQ8074) -//#ifdef CONFIG_RMNET_DATA //spf12.x have no macro defined, just for spf11.x -#define CONFIG_QCA_NSS_DRV -#define CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY -/* define at qca/src/linux-4.4/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c */ //for spf11.x -/* define at qsdk/qca/src/datarmnet/core/rmnet_config.c */ //for spf12.x -/* set at qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c */ -/* need add DEPENDS:= kmod-rmnet-core in feeds/makefile */ -extern struct rmnet_nss_cb *rmnet_nss_callbacks __rcu __read_mostly; -//#endif -#endif - - -int mhi_netdev_use_xfer_type_dma(unsigned chan) -{ - (void)chan; -#ifdef CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY - return 1; -#endif - return 0; -} - - -static const unsigned char node_id[ETH_ALEN] = {0x02, 0x50, 0xf4, 0x00, 0x00, 0x00}; -static const unsigned char default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3, 0x00, 0x00, 0x00}; - -#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) -#define QUECTEL_BRIDGE_MODE -#endif - -#define QUECTEL_RMNET_MODE - -#ifdef QUECTEL_BRIDGE_MODE -static uint __read_mostly bridge_mode = 0/*|BIT(1)*/; -module_param( bridge_mode, uint, S_IRUGO ); -#endif - -struct qmap_hdr { - u8 cd_rsvd_pad; - u8 mux_id; - u16 pkt_len; -} __packed; -#define QUECTEL_QMAP_MUX_ID 0x81 - -enum rmnet_map_v5_header_type { - RMNET_MAP_HEADER_TYPE_UNKNOWN, - RMNET_MAP_HEADER_TYPE_COALESCING = 0x1, - RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD = 0x2, - RMNET_MAP_HEADER_TYPE_ENUM_LENGTH -}; - -enum rmnet_map_commands { - RMNET_MAP_COMMAND_NONE, - RMNET_MAP_COMMAND_FLOW_DISABLE, - RMNET_MAP_COMMAND_FLOW_ENABLE, - RMNET_MAP_COMMAND_FLOW_START = 7, - RMNET_MAP_COMMAND_FLOW_END = 8, - /* These should always be the last 2 elements */ - RMNET_MAP_COMMAND_UNKNOWN, - RMNET_MAP_COMMAND_ENUM_LENGTH -}; - -#define RMNET_MAP_COMMAND_REQUEST 0 -#define RMNET_MAP_COMMAND_ACK 1 -#define RMNET_MAP_COMMAND_UNSUPPORTED 2 -#define RMNET_MAP_COMMAND_INVALID 3 - -/* Main QMAP header */ -struct rmnet_map_header { - u8 pad_len:6; - u8 next_hdr:1; - u8 cd_bit:1; - u8 mux_id; - __be16 pkt_len; -} __aligned(1); - -/* QMAP v5 headers */ -struct rmnet_map_v5_csum_header { - u8 next_hdr:1; - u8 header_type:7; - u8 hw_reserved:7; - u8 csum_valid_required:1; - __be16 reserved; -} __aligned(1); - -struct rmnet_map_control_command { - u8 command_name; - u8 cmd_type:2; - u8 reserved:6; - u16 reserved2; - u32 transaction_id; - union { - struct { - u8 reserved2; - u8 ip_family:2; - u8 reserved:6; - __be16 flow_control_seq_num; - __be32 qos_id; - } flow_control; - u8 data[0]; - }; -} __aligned(1); - -struct mhi_mbim_hdr { - struct usb_cdc_ncm_nth16 nth16; - struct usb_cdc_ncm_ndp16 ndp16; - struct usb_cdc_ncm_dpe16 dpe16[2]; -} __attribute__ ((packed)); - -#define QCUSB_MRECEIVE_MAX_BUFFER_SIZE (1024*32) //maybe 31KB is enough -#define QCUSB_MTRANSMIT_MAX_BUFFER_SIZE (1024*16) -#define NTB_OUT_MAX_DATAGRAMS 16 - -static const struct usb_cdc_ncm_ntb_parameters ncmNTBParams = { - .bmNtbFormatsSupported = USB_CDC_NCM_NTB16_SUPPORTED, - .dwNtbInMaxSize = QCUSB_MRECEIVE_MAX_BUFFER_SIZE, - .wNdpInDivisor = 0x04, - .wNdpInPayloadRemainder = 0x0, - .wNdpInAlignment = 0x4, - - .dwNtbOutMaxSize = QCUSB_MTRANSMIT_MAX_BUFFER_SIZE, - .wNdpOutDivisor = 0x04, - .wNdpOutPayloadRemainder = 0x0, - .wNdpOutAlignment = 0x4, - .wNtbOutMaxDatagrams = NTB_OUT_MAX_DATAGRAMS, -}; - -#if 0 -static void qmap_hex_dump(const char *tag, unsigned char *data, unsigned len) { - uint i; - uint *d = (uint *)data; - - printk(KERN_DEBUG "%s data=%p, len=%x\n", tag, data, len); - len = (len+3)/4; - for (i = 0; i < len; i+=4) { - printk(KERN_DEBUG "%08x %08x %08x %08x %08x\n", i*4, d[i+0], d[i+1], d[i+2], d[i+3]); - } -} -#else -static void qmap_hex_dump(const char *tag, unsigned char *data, unsigned len) { -} -#endif - -#define MBIM_MUX_ID_SDX7X 112 //sdx7x is 112-126, others is 0-14 - -static uint __read_mostly mhi_mbim_enabled = 0; -module_param(mhi_mbim_enabled, uint, S_IRUGO); -int mhi_netdev_mbin_enabled(void) { return mhi_mbim_enabled; } - -static uint __read_mostly qmap_mode = 1; -module_param(qmap_mode, uint, S_IRUGO); - -static uint __read_mostly poll_weight = NAPI_POLL_WEIGHT; -module_param(poll_weight, uint, S_IRUGO); - -#define MHI_NETDEV_DRIVER_NAME "mhi_netdev" -#define WATCHDOG_TIMEOUT (30 * HZ) - -#define MSG_VERB(fmt, ...) do { \ - if (mhi_netdev->msg_lvl <= MHI_MSG_LVL_VERBOSE) \ - pr_err("[D][%s] " fmt, __func__, ##__VA_ARGS__);\ -} while (0) - -#define MHI_ASSERT(cond, msg) do { \ - if (cond) { \ - MSG_ERR(msg); \ - WARN_ON(cond); \ - } \ -} while (0) - -#define MSG_LOG(fmt, ...) do { \ - if (mhi_netdev->msg_lvl <= MHI_MSG_LVL_INFO) \ - pr_err("[I][%s] " fmt, __func__, ##__VA_ARGS__);\ -} while (0) - -#define MSG_ERR(fmt, ...) do { \ - if (mhi_netdev->msg_lvl <= MHI_MSG_LVL_ERROR) \ - pr_err("[E][%s] " fmt, __func__, ##__VA_ARGS__); \ -} while (0) - -struct mhi_stats { - u32 rx_int; - u32 tx_full; - u32 tx_pkts; - u32 rx_budget_overflow; - u32 tx_allocated; - u32 rx_allocated; - u32 alloc_failed; -}; - -/* important: do not exceed sk_buf->cb (48 bytes) */ -struct mhi_skb_priv { - void *buf; - size_t size; - struct mhi_netdev *bind_netdev; -}; - -struct skb_data { /* skb->cb is one of these */ - struct mhi_netdev *bind_netdev; - unsigned int length; - unsigned int packets; -}; - -#define MHI_NETDEV_STATUS64 1 - -typedef struct { - uint size; - uint rx_urb_size; - uint ep_type; - uint iface_id; - uint MuxId; - uint ul_data_aggregation_max_datagrams; //0x17 - uint ul_data_aggregation_max_size ;//0x18 - uint dl_minimum_padding; //0x1A -} QMAP_SETTING; - -typedef struct { - unsigned int size; - unsigned int rx_urb_size; - unsigned int ep_type; - unsigned int iface_id; - unsigned int qmap_mode; - unsigned int qmap_version; - unsigned int dl_minimum_padding; - char ifname[8][16]; - unsigned char mux_id[8]; -} RMNET_INFO; - -typedef struct { - u16 tx_seq; - u16 rx_seq; - u32 rx_max; -} MHI_MBIM_CTX; - -enum mhi_net_type { - MHI_NET_UNKNOW, - MHI_NET_RMNET, - MHI_NET_MBIM, - MHI_NET_ETHER -}; - -#ifdef CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY -/* Try not to make this structure bigger than 128 bytes, since this take space - * in payload packet. - * Example: If MRU = 16K, effective MRU = 16K - sizeof(mhi_netbuf) - */ -struct mhi_netbuf { - struct mhi_buf mhi_buf; /* this must be first element */ - void (*unmap)(struct device *dev, dma_addr_t addr, size_t size, - enum dma_data_direction dir); -}; - -struct mhi_net_chain { - struct sk_buff *head, *tail; /* chained skb */ -}; -#endif - -//#define TS_DEBUG -struct mhi_netdev { - int alias; - struct mhi_device *mhi_dev; - spinlock_t rx_lock; - bool enabled; - rwlock_t pm_lock; /* state change lock */ - int (*rx_queue)(struct mhi_netdev *mhi_netdev, gfp_t gfp_t); - struct delayed_work alloc_work; - int wake; - - struct sk_buff_head tx_allocated; - struct sk_buff_head rx_allocated; - struct sk_buff_head qmap_chain; - struct sk_buff_head skb_chain; -#ifdef TS_DEBUG - uint clear_ts; - struct timespec diff_ts; - struct timespec qmap_ts; - struct timespec skb_ts; -#endif - - MHI_MBIM_CTX mbim_ctx; - u32 mbim_mux_id; - - u32 mru; - u32 max_mtu; - const char *interface_name; - struct napi_struct napi; - struct net_device *ndev; - enum mhi_net_type net_type; - struct sk_buff *frag_skb; - bool recycle_buf; - -#ifdef CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY - u32 order; - struct mhi_netbuf **netbuf_pool; - int pool_size; /* must be power of 2 */ - int current_index; - struct mhi_net_chain chain; -#endif - -#if defined(MHI_NETDEV_STATUS64) - struct pcpu_sw_netstats __percpu *stats64; -#endif - struct mhi_stats stats; - - struct dentry *dentry; - enum MHI_DEBUG_LEVEL msg_lvl; - - struct net_device *mpQmapNetDev[8]; - u32 qmap_mode; - u32 qmap_version; // 5 ~ QMAP V1, 9 ~ QMAP V5 - u32 qmap_size; - u32 link_state; - u32 flow_control; - u32 dl_minimum_padding; - -#ifdef QUECTEL_BRIDGE_MODE - uint bridge_mode; - uint bridge_ipv4; - unsigned char bridge_mac[ETH_ALEN]; -#endif - uint use_rmnet_usb; - RMNET_INFO rmnet_info; - -#if defined(CONFIG_IPQ5018_RATE_CONTROL) - u64 first_jiffy; - u64 bytes_received_1; - u64 bytes_received_2; - u32 cntfrq_per_msec; - bool mhi_rate_control; -#endif - - u32 rmnet_map_command_stats[RMNET_MAP_COMMAND_ENUM_LENGTH]; -}; - -struct mhi_netdev_priv { - struct mhi_netdev *mhi_netdev; -}; - -struct qmap_priv { - void *pQmapDev; - struct net_device *real_dev; - struct net_device *self_dev; - u8 offset_id; - u8 mux_id; - u8 qmap_version; // 5~v1, 9~v5 - -#if defined(MHI_NETDEV_STATUS64) - struct pcpu_sw_netstats __percpu *stats64; -#endif - - spinlock_t agg_lock; - struct sk_buff *agg_skb; - unsigned agg_count; - struct timespec64 agg_time; - struct hrtimer agg_hrtimer; - struct work_struct agg_wq; - -#ifdef QUECTEL_BRIDGE_MODE - uint bridge_mode; - uint bridge_ipv4; - unsigned char bridge_mac[ETH_ALEN]; -#endif - uint use_qca_nss; -}; - -static struct mhi_netdev *ndev_to_mhi(struct net_device *ndev) { - struct mhi_netdev_priv *mhi_netdev_priv = netdev_priv(ndev); - struct mhi_netdev *mhi_netdev = mhi_netdev_priv->mhi_netdev; - return mhi_netdev; -} - -static struct mhi_driver mhi_netdev_driver; -static void mhi_netdev_create_debugfs(struct mhi_netdev *mhi_netdev); - -#if 0 -static void mhi_netdev_skb_destructor(struct sk_buff *skb) -{ - struct mhi_skb_priv *skb_priv = (struct mhi_skb_priv *)(skb->cb); - struct mhi_netdev *mhi_netdev = skb_priv->mhi_netdev; - - skb->data = skb->head; - skb_reset_tail_pointer(skb); - skb->len = 0; - MHI_ASSERT(skb->data != skb_priv->buf, "incorrect buf"); - skb_queue_tail(&mhi_netdev->rx_allocated, skb); -} -#endif - -#ifdef QUECTEL_BRIDGE_MODE -static const struct net_device_ops mhi_netdev_ops_ip; -static const struct net_device_ops rmnet_vnd_ops; - -static int is_qmap_netdev(const struct net_device *ndev) { - return ndev->netdev_ops == &rmnet_vnd_ops; -} - -static int bridge_arp_reply(struct net_device *net, struct sk_buff *skb, uint bridge_ipv4) { - struct arphdr *parp; - u8 *arpptr, *sha; - u8 sip[4], tip[4], ipv4[4]; - struct sk_buff *reply = NULL; - - ipv4[0] = (bridge_ipv4 >> 24) & 0xFF; - ipv4[1] = (bridge_ipv4 >> 16) & 0xFF; - ipv4[2] = (bridge_ipv4 >> 8) & 0xFF; - ipv4[3] = (bridge_ipv4 >> 0) & 0xFF; - - parp = arp_hdr(skb); - - if (parp->ar_hrd == htons(ARPHRD_ETHER) && parp->ar_pro == htons(ETH_P_IP) - && parp->ar_op == htons(ARPOP_REQUEST) && parp->ar_hln == 6 && parp->ar_pln == 4) { - arpptr = (u8 *)parp + sizeof(struct arphdr); - sha = arpptr; - arpptr += net->addr_len; /* sha */ - memcpy(sip, arpptr, sizeof(sip)); - arpptr += sizeof(sip); - arpptr += net->addr_len; /* tha */ - memcpy(tip, arpptr, sizeof(tip)); - - pr_info("%s sip = %d.%d.%d.%d, tip=%d.%d.%d.%d, ipv4=%d.%d.%d.%d\n", netdev_name(net), - sip[0], sip[1], sip[2], sip[3], tip[0], tip[1], tip[2], tip[3], ipv4[0], ipv4[1], ipv4[2], ipv4[3]); - //wwan0 sip = 10.151.137.255, tip=10.151.138.0, ipv4=10.151.137.255 -#ifndef ANDROID_gki - if (tip[0] == ipv4[0] && tip[1] == ipv4[1] && (tip[2]&0xFC) == (ipv4[2]&0xFC) && tip[3] != ipv4[3]) - reply = arp_create(ARPOP_REPLY, ETH_P_ARP, *((__be32 *)sip), net, *((__be32 *)tip), sha, default_modem_addr, sha); -#endif - - if (reply) { - skb_reset_mac_header(reply); - __skb_pull(reply, skb_network_offset(reply)); - reply->ip_summed = CHECKSUM_UNNECESSARY; - reply->pkt_type = PACKET_HOST; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)) - netif_rx(reply); -#else - netif_rx_ni(reply); -#endif - } - return 1; - } - - return 0; -} - -static struct sk_buff *bridge_mode_tx_fixup(struct net_device *net, struct sk_buff *skb, uint bridge_ipv4, unsigned char *bridge_mac) { - struct ethhdr *ehdr; - const struct iphdr *iph; - - skb_reset_mac_header(skb); - ehdr = eth_hdr(skb); - - if (ehdr->h_proto == htons(ETH_P_ARP)) { - if (bridge_ipv4) - bridge_arp_reply(net, skb, bridge_ipv4); - return NULL; - } - - iph = ip_hdr(skb); - //DBG("iphdr: "); - //PrintHex((void *)iph, sizeof(struct iphdr)); - -// 1 0.000000000 0.0.0.0 255.255.255.255 DHCP 362 DHCP Request - Transaction ID 0xe7643ad7 - if (ehdr->h_proto == htons(ETH_P_IP) && iph->protocol == IPPROTO_UDP && iph->saddr == 0x00000000 && iph->daddr == 0xFFFFFFFF) { - //if (udp_hdr(skb)->dest == htons(67)) //DHCP Request - { - memcpy(bridge_mac, ehdr->h_source, ETH_ALEN); - pr_info("%s PC Mac Address: %02x:%02x:%02x:%02x:%02x:%02x\n", netdev_name(net), - bridge_mac[0], bridge_mac[1], bridge_mac[2], bridge_mac[3], bridge_mac[4], bridge_mac[5]); - } - } - - if (memcmp(ehdr->h_source, bridge_mac, ETH_ALEN)) { - return NULL; - } - - return skb; -} - -static void bridge_mode_rx_fixup(struct mhi_netdev *mhi_netdev, struct net_device *net, struct sk_buff *skb) { - uint bridge_mode = 0; - unsigned char *bridge_mac; - - if (mhi_netdev->qmap_mode > 0) { - struct qmap_priv *priv = netdev_priv(net); - bridge_mode = priv->bridge_mode; - bridge_mac = priv->bridge_mac; - } - else { - bridge_mode = mhi_netdev->bridge_mode; - bridge_mac = mhi_netdev->bridge_mac; - } - - if (bridge_mode) - memcpy(eth_hdr(skb)->h_dest, bridge_mac, ETH_ALEN); -} - -static ssize_t bridge_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *ndev = to_net_dev(dev); - uint bridge_mode = 0; - - if (is_qmap_netdev(ndev)) { - struct qmap_priv *priv = netdev_priv(ndev); - bridge_mode = priv->bridge_mode; - } - else { - struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev); - bridge_mode = mhi_netdev->bridge_mode; - } - - return snprintf(buf, PAGE_SIZE, "%u\n", bridge_mode); -} - -static ssize_t bridge_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct net_device *ndev = to_net_dev(dev); - uint bridge_mode = simple_strtoul(buf, NULL, 0); - - if (ndev->type != ARPHRD_ETHER) { - if (bridge_mode) - netdev_err(ndev, "netdevice is not ARPHRD_ETHER\n"); - return count; - } - - if (is_qmap_netdev(ndev)) { - struct qmap_priv *priv = netdev_priv(ndev); - priv->bridge_mode = bridge_mode; - } - else { - struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev); - mhi_netdev->bridge_mode = bridge_mode; - } - - return count; -} - - -static ssize_t bridge_ipv4_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *ndev = to_net_dev(dev); - unsigned int bridge_ipv4 = 0; - unsigned char ipv4[4]; - - if (is_qmap_netdev(ndev)) { - struct qmap_priv *priv = netdev_priv(ndev); - bridge_ipv4 = priv->bridge_ipv4; - } - else { - struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev); - bridge_ipv4 = mhi_netdev->bridge_ipv4; - } - - ipv4[0] = (bridge_ipv4 >> 24) & 0xFF; - ipv4[1] = (bridge_ipv4 >> 16) & 0xFF; - ipv4[2] = (bridge_ipv4 >> 8) & 0xFF; - ipv4[3] = (bridge_ipv4 >> 0) & 0xFF; - - return snprintf(buf, PAGE_SIZE, "%d.%d.%d.%d\n", ipv4[0], ipv4[1], ipv4[2], ipv4[3]); -} - -static ssize_t bridge_ipv4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct net_device *ndev = to_net_dev(dev); - - if (is_qmap_netdev(ndev)) { - struct qmap_priv *priv = netdev_priv(ndev); - priv->bridge_ipv4 = simple_strtoul(buf, NULL, 16); - } - else { - struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev); - mhi_netdev->bridge_ipv4 = simple_strtoul(buf, NULL, 16); - } - - return count; -} - -static DEVICE_ATTR(bridge_mode, S_IWUSR | S_IRUGO, bridge_mode_show, bridge_mode_store); -static DEVICE_ATTR(bridge_ipv4, S_IWUSR | S_IRUGO, bridge_ipv4_show, bridge_ipv4_store); - -#ifndef MHI_NETDEV_ONE_CARD_MODE -static struct attribute *pcie_mhi_qmap_sysfs_attrs[] = { - &dev_attr_bridge_mode.attr, - &dev_attr_bridge_ipv4.attr, - NULL, -}; - -static struct attribute_group pcie_mhi_qmap_sysfs_attr_group = { - .attrs = pcie_mhi_qmap_sysfs_attrs, -}; -#endif -#endif - -static struct sk_buff * add_mbim_hdr(struct sk_buff *skb, u8 mux_id) { - struct mhi_mbim_hdr *mhdr; - __le32 sign; - u8 *c; - u16 tci = mux_id; - unsigned int skb_len = skb->len; - - if (qmap_mode > 1) - tci += 1; //rmnet_mhi0.X map to session X - - if (skb_headroom(skb) < sizeof(struct mhi_mbim_hdr)) { - printk("skb_headroom small! headroom is %u, need %zd\n", skb_headroom(skb), sizeof(struct mhi_mbim_hdr)); - return NULL; - } - - skb_push(skb, sizeof(struct mhi_mbim_hdr)); - - mhdr = (struct mhi_mbim_hdr *)skb->data; - - //printk("%s %p\n", __func__, skb->data); - mhdr->nth16.dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN); - mhdr->nth16.wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16)); -/* - Sequence number. The transmitter of a block shall set this to zero in the first NTB transferred after every 'function reset' event, - and shall increment for every NTB subsequently transferred. - The effect of an out-of-sequence block on the receiver is not specified. - Thespecification allows the receiver to decide whether tocheck the sequence number, - and to decide how to respond if it's incorrect. The sequence number is pri-marily supplied for debugging purposes. -*/ - //mhdr->nth16.wSequence = cpu_to_le16(mhi_netdev->tx_seq++); -/* - Size of this NTB in bytes. Represented in little-endian form. - NTB size (IN/OUT) shall not exceed dwNtbInMaxSize or dwNtbOutMaxSize respectively -*/ - mhdr->nth16.wBlockLength = cpu_to_le16(skb->len); -/* - Offset, in little endian, of the first NDP16 from byte zeroof the NTB. - This value must be a multiple of 4, and must be >= 0x000C -*/ - mhdr->nth16.wNdpIndex = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16)); - - sign = cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN); - c = (u8 *)&sign; - c[3] = tci; - - mhdr->ndp16.dwSignature = sign; - mhdr->ndp16.wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16) + sizeof(struct usb_cdc_ncm_dpe16) * 2); - mhdr->ndp16.wNextNdpIndex = 0; - - mhdr->ndp16.dpe16[0].wDatagramIndex = sizeof(struct mhi_mbim_hdr); - mhdr->ndp16.dpe16[0].wDatagramLength = skb_len; - - mhdr->ndp16.dpe16[1].wDatagramIndex = 0; - mhdr->ndp16.dpe16[1].wDatagramLength = 0; - - return skb; -} - -static struct sk_buff * add_qhdr(struct sk_buff *skb, u8 mux_id) { - struct qmap_hdr *qhdr; - int pad = 0; - - pad = skb->len%4; - if (pad) { - pad = 4 - pad; - if (skb_tailroom(skb) < pad) { - printk("skb_tailroom small!\n"); - pad = 0; - } - if (pad) - __skb_put(skb, pad); - } - - qhdr = (struct qmap_hdr *)skb_push(skb, sizeof(struct qmap_hdr)); - qhdr->cd_rsvd_pad = pad; - qhdr->mux_id = mux_id; - qhdr->pkt_len = cpu_to_be16(skb->len - sizeof(struct qmap_hdr)); - - return skb; -} - -static struct sk_buff * add_qhdr_v5(struct sk_buff *skb, u8 mux_id) { - struct rmnet_map_header *map_header; - struct rmnet_map_v5_csum_header *ul_header; - u32 padding, map_datalen; - - map_datalen = skb->len; - padding = map_datalen%4; - if (padding) { - padding = 4 - padding; - if (skb_tailroom(skb) < padding) { - printk("skb_tailroom small!\n"); - padding = 0; - } - if (padding) - __skb_put(skb, padding); - } - - map_header = (struct rmnet_map_header *)skb_push(skb, (sizeof(struct rmnet_map_header) + sizeof(struct rmnet_map_v5_csum_header))); - map_header->cd_bit = 0; - map_header->next_hdr = 1; - map_header->pad_len = padding; - map_header->mux_id = mux_id; - map_header->pkt_len = htons(map_datalen + padding); - - ul_header = (struct rmnet_map_v5_csum_header *)(map_header + 1); - memset(ul_header, 0, sizeof(*ul_header)); - ul_header->header_type = RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD; - if (skb->ip_summed == CHECKSUM_PARTIAL) { -#if 0 //TODO - skb->ip_summed = CHECKSUM_NONE; - /* Ask for checksum offloading */ - ul_header->csum_valid_required = 1; -#endif - } - - return skb; -} - -static void rmnet_map_send_ack(struct mhi_netdev *pQmapDev, - unsigned char type, - struct rmnet_map_header *map_header) -{ - struct rmnet_map_control_command *cmd; - struct sk_buff *skb; - size_t skb_len = sizeof(struct rmnet_map_header) + sizeof(struct rmnet_map_control_command); - - skb = alloc_skb(skb_len, GFP_ATOMIC); - if (!skb) - return; - - skb_put(skb, skb_len); - memcpy(skb->data, map_header, skb_len); - cmd = (struct rmnet_map_control_command *)(skb->data + sizeof(struct rmnet_map_header)); - cmd->cmd_type = type & 0x03; - skb->protocol = htons(ETH_P_MAP); - skb->dev = pQmapDev->ndev; - dev_queue_xmit(skb); -} - -static int rmnet_data_vnd_do_flow_control(struct net_device *dev, - uint32_t map_flow_id, - uint16_t v4_seq, - uint16_t v6_seq, - int enable) -{ - //TODO - return 0; -} - -static uint8_t rmnet_map_do_flow_control(struct mhi_netdev *pQmapDev, - struct rmnet_map_header *map_header, - int enable) { - struct net_device *ndev = pQmapDev->ndev; - struct rmnet_map_control_command *cmd; - struct net_device *vnd; - uint8_t mux_id; - uint16_t ip_family; - uint16_t fc_seq; - uint32_t qos_id; - int r; - - cmd = (struct rmnet_map_control_command *)(map_header + 1); - - mux_id = map_header->mux_id - QUECTEL_QMAP_MUX_ID; - if (mux_id >= pQmapDev->qmap_mode) { - netdev_info(ndev, "drop qmap unknow mux_id %x\n", map_header->mux_id); - return RMNET_MAP_COMMAND_UNSUPPORTED; - } - - vnd = pQmapDev->mpQmapNetDev[mux_id]; - if (vnd == NULL) { - netdev_info(ndev, "drop qmap unknow mux_id %x\n", map_header->mux_id); - return RMNET_MAP_COMMAND_UNSUPPORTED; - } - - ip_family = cmd->flow_control.ip_family; - fc_seq = ntohs(cmd->flow_control.flow_control_seq_num); - qos_id = ntohl(cmd->flow_control.qos_id); - - if (enable) - pQmapDev->flow_control |= (1 << mux_id); - else - pQmapDev->flow_control &= ~(1 << mux_id); - /* Ignore the ip family and pass the sequence number for both v4 and v6 - * sequence. User space does not support creating dedicated flows for - * the 2 protocols - */ - r = rmnet_data_vnd_do_flow_control(vnd, qos_id, fc_seq, fc_seq, enable); - netdev_dbg(vnd, "qos_id:0x%08X, ip_family:%hd, fc_seq %hd, en:%d", - qos_id, ip_family & 3, fc_seq, enable); - - return RMNET_MAP_COMMAND_ACK; -} - -static void rmnet_data_map_command(struct mhi_netdev *pQmapDev, - struct rmnet_map_header *map_header) { - struct net_device *ndev = pQmapDev->ndev; - struct rmnet_map_control_command *cmd; - unsigned char command_name; - unsigned char rc = 0; - - cmd = (struct rmnet_map_control_command *)(map_header + 1); - command_name = cmd->command_name; - - if (command_name < RMNET_MAP_COMMAND_ENUM_LENGTH) - pQmapDev->rmnet_map_command_stats[command_name]++; - - switch (command_name) { - case RMNET_MAP_COMMAND_FLOW_ENABLE: - rc = rmnet_map_do_flow_control(pQmapDev, map_header, 1); - break; - - case RMNET_MAP_COMMAND_FLOW_DISABLE: - rc = rmnet_map_do_flow_control(pQmapDev, map_header, 0); - break; - - default: - pQmapDev->rmnet_map_command_stats[RMNET_MAP_COMMAND_UNKNOWN]++; - netdev_info(ndev, "UNSupport MAP command: %d", command_name); - rc = RMNET_MAP_COMMAND_UNSUPPORTED; - break; - } - - if (rc == RMNET_MAP_COMMAND_ACK) - rmnet_map_send_ack(pQmapDev, rc, map_header); - - return; -} - -#ifndef MHI_NETDEV_ONE_CARD_MODE -static void rmnet_vnd_upate_rx_stats(struct net_device *net, - unsigned rx_packets, unsigned rx_bytes) { -#if defined(MHI_NETDEV_STATUS64) - struct qmap_priv *dev = netdev_priv(net); - struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->stats64); - - u64_stats_update_begin(&stats64->syncp); -#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)) - stats64->rx_packets += rx_packets; - stats64->rx_bytes += rx_bytes; -#else - u64_stats_add(&stats64->rx_packets, rx_packets); - u64_stats_add(&stats64->rx_bytes, rx_bytes); -#endif - u64_stats_update_end(&stats64->syncp); -#else - priv->self_dev->stats.rx_packets += rx_packets; - priv->self_dev->stats.rx_bytes += rx_bytes; -#endif -} - -static void rmnet_vnd_upate_tx_stats(struct net_device *net, - unsigned tx_packets, unsigned tx_bytes) { -#if defined(MHI_NETDEV_STATUS64) - struct qmap_priv *dev = netdev_priv(net); - struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->stats64); - - u64_stats_update_begin(&stats64->syncp); -#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)) - stats64->tx_packets += tx_packets; - stats64->tx_bytes += tx_bytes; -#else - u64_stats_add(&stats64->tx_packets, tx_packets); - u64_stats_add(&stats64->tx_bytes, tx_bytes); -#endif - u64_stats_update_end(&stats64->syncp); -#else - net->stats.rx_packets += tx_packets; - net->stats.rx_bytes += tx_bytes; -#endif -} - -#if defined(MHI_NETDEV_STATUS64) -#ifdef ANDROID_gki -static void _netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, - const struct net_device_stats *netdev_stats) -{ -#if BITS_PER_LONG == 64 - BUILD_BUG_ON(sizeof(*stats64) < sizeof(*netdev_stats)); - memcpy(stats64, netdev_stats, sizeof(*netdev_stats)); - /* zero out counters that only exist in rtnl_link_stats64 */ - memset((char *)stats64 + sizeof(*netdev_stats), 0, - sizeof(*stats64) - sizeof(*netdev_stats)); -#else - size_t i, n = sizeof(*netdev_stats) / sizeof(unsigned long); - const unsigned long *src = (const unsigned long *)netdev_stats; - u64 *dst = (u64 *)stats64; - - BUILD_BUG_ON(n > sizeof(*stats64) / sizeof(u64)); - for (i = 0; i < n; i++) - dst[i] = src[i]; - /* zero out counters that only exist in rtnl_link_stats64 */ - memset((char *)stats64 + n * sizeof(u64), 0, - sizeof(*stats64) - n * sizeof(u64)); -#endif -} -#else -static void my_netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, - const struct net_device_stats *netdev_stats) -{ - netdev_stats_to_stats64(stats64, netdev_stats); -} -#endif - -static struct rtnl_link_stats64 *_rmnet_vnd_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats) -{ - struct qmap_priv *dev = netdev_priv(net); - unsigned int start; - int cpu; - - my_netdev_stats_to_stats64(stats, &net->stats); - - if (nss_cb && dev->use_qca_nss) { // rmnet_nss.c:rmnet_nss_tx() will update rx stats - stats->rx_packets = 0; - stats->rx_bytes = 0; - } - - for_each_possible_cpu(cpu) { - struct pcpu_sw_netstats *stats64; -#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)) - u64 rx_packets, rx_bytes; - u64 tx_packets, tx_bytes; - - stats64 = per_cpu_ptr(dev->stats64, cpu); - - do { - start = u64_stats_fetch_begin_irq(&stats64->syncp); - rx_packets = stats64->rx_packets; - rx_bytes = stats64->rx_bytes; - tx_packets = stats64->tx_packets; - tx_bytes = stats64->tx_bytes; - } while (u64_stats_fetch_retry_irq(&stats64->syncp, start)); - - stats->rx_packets += rx_packets; - stats->rx_bytes += rx_bytes; - stats->tx_packets += tx_packets; - stats->tx_bytes += tx_bytes; -#else - u64_stats_t rx_packets, rx_bytes; - u64_stats_t tx_packets, tx_bytes; - - stats64 = per_cpu_ptr(dev->stats64, cpu); - - do { - start = u64_stats_fetch_begin(&stats64->syncp); - rx_packets = stats64->rx_packets; - rx_bytes = stats64->rx_bytes; - tx_packets = stats64->tx_packets; - tx_bytes = stats64->tx_bytes; - } while (u64_stats_fetch_retry(&stats64->syncp, start)); - - stats->rx_packets += u64_stats_read(&rx_packets); - stats->rx_bytes += u64_stats_read(&rx_bytes); - stats->tx_packets += u64_stats_read(&tx_packets); - stats->tx_bytes += u64_stats_read(&tx_bytes); -#endif - } - - return stats; -} - -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 4,10,0 )) //bc1f44709cf27fb2a5766cadafe7e2ad5e9cb221 -static void rmnet_vnd_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats) { - _rmnet_vnd_get_stats64(net, stats); -} -#else -static struct rtnl_link_stats64 *rmnet_vnd_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats) { - return _rmnet_vnd_get_stats64(net, stats); -} -#endif -#endif - -static void rmnet_vnd_tx_agg_work(struct work_struct *work) -{ - struct qmap_priv *priv = - container_of(work, struct qmap_priv, agg_wq); - struct sk_buff *skb = NULL; - unsigned long flags; - - spin_lock_irqsave(&priv->agg_lock, flags); - if (likely(priv->agg_skb)) { - skb = priv->agg_skb; - priv->agg_skb = NULL; - priv->agg_count = 0; - skb->protocol = htons(ETH_P_MAP); - skb->dev = priv->real_dev; - ktime_get_ts64(&priv->agg_time); - } - spin_unlock_irqrestore(&priv->agg_lock, flags); - - if (skb) - dev_queue_xmit(skb); -} - -static enum hrtimer_restart rmnet_vnd_tx_agg_timer_cb(struct hrtimer *timer) -{ - struct qmap_priv *priv = - container_of(timer, struct qmap_priv, agg_hrtimer); - - schedule_work(&priv->agg_wq); - return HRTIMER_NORESTART; -} - -static int rmnet_vnd_tx_agg(struct sk_buff *skb, struct qmap_priv *priv) { - skb->protocol = htons(ETH_P_MAP); - skb->dev = priv->real_dev; - - return dev_queue_xmit(skb); -} - - -static int rmnet_vnd_open(struct net_device *dev) -{ - struct qmap_priv *priv = netdev_priv(dev); - struct net_device *real_dev = priv->real_dev; - - if (!(priv->real_dev->flags & IFF_UP)) - return -ENETDOWN; - - if (netif_carrier_ok(real_dev)) - netif_carrier_on(dev); - - return 0; -} - -static int rmnet_vnd_stop(struct net_device *pNet) -{ - netif_carrier_off(pNet); - return 0; -} - -static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb, - struct net_device *pNet) -{ - int err; - struct qmap_priv *priv = netdev_priv(pNet); - struct mhi_netdev *mhi_netdev = ndev_to_mhi(priv->real_dev); - int skb_len = skb->len; - - if (netif_queue_stopped(priv->real_dev)) { - netif_stop_queue(pNet); - return NETDEV_TX_BUSY; - } - - //printk("%s 1 skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - if (pNet->type == ARPHRD_ETHER) { - skb_reset_mac_header(skb); - -#ifdef QUECTEL_BRIDGE_MODE - if (priv->bridge_mode && bridge_mode_tx_fixup(pNet, skb, priv->bridge_ipv4, priv->bridge_mac) == NULL) { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } -#endif - - if (skb_pull(skb, ETH_HLEN) == NULL) { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } - } - //printk("%s 2 skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - if (mhi_netdev->net_type == MHI_NET_MBIM) { - if (add_mbim_hdr(skb, priv->mux_id) == NULL) { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } - } - else { - if (priv->qmap_version == 5) { - add_qhdr(skb, priv->mux_id); - } - else if (priv->qmap_version == 9) { - add_qhdr_v5(skb, priv->mux_id); - } - else { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } - } - //printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - - err = rmnet_vnd_tx_agg(skb, priv); - if (err == NET_XMIT_SUCCESS) { - rmnet_vnd_upate_tx_stats(pNet, 1, skb_len); - } else { - pNet->stats.tx_errors++; - } - - return err; -} - -static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu) -{ - struct mhi_netdev *mhi_netdev; - - mhi_netdev = (struct mhi_netdev *)ndev_to_mhi(rmnet_dev); - - if (mhi_netdev == NULL) { - printk("warning, mhi_netdev == null\n"); - return -EINVAL; - } - - if (new_mtu < 0 ) - return -EINVAL; - - if (new_mtu > mhi_netdev->max_mtu) { - printk("warning, set mtu=%d greater than max mtu=%d\n", new_mtu, mhi_netdev->max_mtu); - return -EINVAL; - } - - rmnet_dev->mtu = new_mtu; - return 0; -} - -/* drivers may override default ethtool_ops in their bind() routine */ -static const struct ethtool_ops rmnet_vnd_ethtool_ops = { - .get_link = ethtool_op_get_link, -}; - -static void rmnet_vnd_rawip_setup(struct net_device *rmnet_dev) -{ - rmnet_dev->needed_headroom = 16; - - /* Raw IP mode */ - rmnet_dev->header_ops = NULL; /* No header */ -//for Qualcomm's NSS, must set type as ARPHRD_RAWIP, or NSS performace is very bad. - rmnet_dev->type = ARPHRD_RAWIP; // do not support moify mac, for dev_set_mac_address() need ARPHRD_ETHER - rmnet_dev->hard_header_len = 0; -//for Qualcomm's SFE, do not add IFF_POINTOPOINT to type, or SFE donot work. - rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); -} - -static const struct net_device_ops rmnet_vnd_ops = { - .ndo_open = rmnet_vnd_open, - .ndo_stop = rmnet_vnd_stop, - .ndo_start_xmit = rmnet_vnd_start_xmit, -#if defined(MHI_NETDEV_STATUS64) - .ndo_get_stats64 = rmnet_vnd_get_stats64, -#endif - .ndo_change_mtu = rmnet_vnd_change_mtu, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static rx_handler_result_t qca_nss_rx_handler(struct sk_buff **pskb) -{ - struct sk_buff *skb = *pskb; - - if (!skb) - return RX_HANDLER_CONSUMED; - - //printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - - if (skb->pkt_type == PACKET_LOOPBACK) - return RX_HANDLER_PASS; - - /* Check this so that we dont loop around netif_receive_skb */ - if (skb->cb[0] == 1) { - skb->cb[0] = 0; - - return RX_HANDLER_PASS; - } - - if (nss_cb) { - nss_cb->nss_tx(skb); - return RX_HANDLER_CONSUMED; - } - - return RX_HANDLER_PASS; -} -#endif - -static void rmnet_mbim_rx_handler(void *dev, struct sk_buff *skb_in) -{ - struct mhi_netdev *pQmapDev = (struct mhi_netdev *)dev; - struct mhi_netdev *mhi_netdev = (struct mhi_netdev *)dev; - MHI_MBIM_CTX *ctx = &pQmapDev->mbim_ctx; - //struct net_device *ndev = pQmapDev->ndev; - struct usb_cdc_ncm_nth16 *nth16; - int ndpoffset, len; - u16 wSequence; - struct sk_buff_head skb_chain; - struct sk_buff *qmap_skb; - - __skb_queue_head_init(&skb_chain); - - if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth16) + sizeof(struct usb_cdc_ncm_ndp16))) { - MSG_ERR("frame too short\n"); - goto error; - } - - nth16 = (struct usb_cdc_ncm_nth16 *)skb_in->data; - - if (nth16->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH16_SIGN)) { - MSG_ERR("invalid NTH16 signature <%#010x>\n", le32_to_cpu(nth16->dwSignature)); - goto error; - } - - len = le16_to_cpu(nth16->wBlockLength); - if (len > ctx->rx_max) { - MSG_ERR("unsupported NTB block length %u/%u\n", len, ctx->rx_max); - goto error; - } - - wSequence = le16_to_cpu(nth16->wSequence); - if (ctx->rx_seq != wSequence) { - MSG_ERR("sequence number glitch prev=%d curr=%d\n", ctx->rx_seq, wSequence); - } - ctx->rx_seq = wSequence + 1; - - ndpoffset = nth16->wNdpIndex; - - while (ndpoffset > 0) { - struct usb_cdc_ncm_ndp16 *ndp16 ; - struct usb_cdc_ncm_dpe16 *dpe16; - int nframes, x; - u8 *c; - u16 tci = 0; - struct net_device *qmap_net; - - if (skb_in->len < (ndpoffset + sizeof(struct usb_cdc_ncm_ndp16))) { - MSG_ERR("invalid NDP offset <%u>\n", ndpoffset); - goto error; - } - - ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset); - - if (le16_to_cpu(ndp16->wLength) < 0x10) { - MSG_ERR("invalid DPT16 length <%u>\n", le16_to_cpu(ndp16->wLength)); - goto error; - } - - nframes = ((le16_to_cpu(ndp16->wLength) - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16)); - - if (skb_in->len < (sizeof(struct usb_cdc_ncm_ndp16) + nframes * (sizeof(struct usb_cdc_ncm_dpe16)))) { - MSG_ERR("Invalid nframes = %d\n", nframes); - goto error; - } - - switch (ndp16->dwSignature & cpu_to_le32(0x00ffffff)) { - case cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN): - c = (u8 *)&ndp16->dwSignature; - tci = c[3]; - /* tag IPS<0> packets too if MBIM_IPS0_VID exists */ - //if (!tci && info->flags & FLAG_IPS0_VLAN) - // tci = MBIM_IPS0_VID; - break; - case cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN): - c = (u8 *)&ndp16->dwSignature; - tci = c[3] + 256; - break; - default: - MSG_ERR("unsupported NDP signature <0x%08x>\n", le32_to_cpu(ndp16->dwSignature)); - goto error; - } - - if ((qmap_mode == 1 && tci != mhi_netdev->mbim_mux_id) || (qmap_mode > 1 && (tci - mhi_netdev->mbim_mux_id) > qmap_mode)){ - MSG_ERR("unsupported tci %d by now\n", tci); - goto error; - } - tci = abs(tci); - qmap_net = pQmapDev->mpQmapNetDev[qmap_mode == 1 ? 0 : tci - 1 - mhi_netdev->mbim_mux_id]; - - dpe16 = ndp16->dpe16; - - for (x = 0; x < nframes; x++, dpe16++) { - int offset = le16_to_cpu(dpe16->wDatagramIndex); - int skb_len = le16_to_cpu(dpe16->wDatagramLength); - - if (offset == 0 || skb_len == 0) { - break; - } - - /* sanity checking */ - if (((offset + skb_len) > skb_in->len) || (skb_len > ctx->rx_max)) { - MSG_ERR("invalid frame detected (ignored) x=%d, offset=%d, skb_len=%u\n", x, offset, skb_len); - goto error; - } - - qmap_skb = netdev_alloc_skb(qmap_net, skb_len); - if (!qmap_skb) { - mhi_netdev->stats.alloc_failed++; - //MSG_ERR("skb_clone fail\n"); //do not print in softirq - goto error; - } - - switch (skb_in->data[offset] & 0xf0) { - case 0x40: -#ifdef CONFIG_QCA_NSS_PACKET_FILTER - { - struct iphdr *ip4h = (struct iphdr *)(&skb_in->data[offset]); - if (ip4h->protocol == IPPROTO_ICMP) { - qmap_skb->cb[0] = 1; - } - } -#endif - qmap_skb->protocol = htons(ETH_P_IP); - break; - case 0x60: -#ifdef CONFIG_QCA_NSS_PACKET_FILTER - { - struct ipv6hdr *ip6h = (struct ipv6hdr *)(&skb_in->data[offset]); - if (ip6h->nexthdr == NEXTHDR_ICMP) { - qmap_skb->cb[0] = 1; - } - } -#endif - qmap_skb->protocol = htons(ETH_P_IPV6); - break; - default: - MSG_ERR("unknow skb->protocol %02x\n", skb_in->data[offset]); - goto error; - } - - skb_put(qmap_skb, skb_len); - memcpy(qmap_skb->data, skb_in->data + offset, skb_len); - - skb_reset_transport_header(qmap_skb); - skb_reset_network_header(qmap_skb); - qmap_skb->pkt_type = PACKET_HOST; - skb_set_mac_header(qmap_skb, 0); - - if (qmap_skb->dev->type == ARPHRD_ETHER) { - skb_push(qmap_skb, ETH_HLEN); - skb_reset_mac_header(qmap_skb); - memcpy(eth_hdr(qmap_skb)->h_source, default_modem_addr, ETH_ALEN); - memcpy(eth_hdr(qmap_skb)->h_dest, qmap_net->dev_addr, ETH_ALEN); - eth_hdr(qmap_skb)->h_proto = qmap_skb->protocol; -#ifdef QUECTEL_BRIDGE_MODE - bridge_mode_rx_fixup(pQmapDev, qmap_net, qmap_skb); -#endif - __skb_pull(qmap_skb, ETH_HLEN); - } - -#ifndef MHI_NETDEV_ONE_CARD_MODE - rmnet_vnd_upate_rx_stats(qmap_net, 1, skb_len); -#endif - __skb_queue_tail(&skb_chain, qmap_skb); - } - - /* are there more NDPs to process? */ - ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex); - } - -error: - while ((qmap_skb = __skb_dequeue (&skb_chain))) { - netif_receive_skb(qmap_skb); - } -} - -static void rmnet_qmi_rx_handler(void *dev, struct sk_buff *skb_in) -{ - struct mhi_netdev *pQmapDev = (struct mhi_netdev *)dev; - struct net_device *ndev = pQmapDev->ndev; - struct sk_buff *qmap_skb; - struct sk_buff_head skb_chain; - uint dl_minimum_padding = 0; - - if (pQmapDev->qmap_version == 9) - dl_minimum_padding = pQmapDev->dl_minimum_padding; - - __skb_queue_head_init(&skb_chain); - - while (skb_in->len > sizeof(struct qmap_hdr)) { - struct rmnet_map_header *map_header = (struct rmnet_map_header *)skb_in->data; - struct rmnet_map_v5_csum_header *ul_header = NULL; - size_t hdr_size = sizeof(struct rmnet_map_header); - struct net_device *qmap_net; - int pkt_len = ntohs(map_header->pkt_len); - int skb_len; - __be16 protocol; - int mux_id; - int skip_nss = 0; - - if (map_header->next_hdr) { - ul_header = (struct rmnet_map_v5_csum_header *)(map_header + 1); - hdr_size += sizeof(struct rmnet_map_v5_csum_header); - } - - skb_len = pkt_len - (map_header->pad_len&0x3F); -#if 0 //just for debug dl_minimum_padding BUG - if ((skb_in->data[hdr_size] & 0xf0) == 0x45) { - struct iphdr *ip4h = (struct iphdr *)(&skb_in->data[hdr_size]); - if (ntohs(ip4h->tot_len) != skb_len) { - netdev_info(ndev, "tot_len=%d skb_len=%d\n", ntohs(ip4h->tot_len), skb_len); - } - } -#endif - skb_len -= dl_minimum_padding; - - mux_id = map_header->mux_id - QUECTEL_QMAP_MUX_ID; - if (mux_id >= pQmapDev->qmap_mode) { - netdev_info(ndev, "drop qmap unknow mux_id %x\n", map_header->mux_id); - goto error_pkt; - } - mux_id = abs(mux_id); - qmap_net = pQmapDev->mpQmapNetDev[mux_id]; - if (qmap_net == NULL) { - netdev_info(ndev, "drop qmap unknow mux_id %x\n", map_header->mux_id); - goto skip_pkt; - } - - if (skb_len > qmap_net->mtu) { - netdev_info(ndev, "drop skb_len=%x larger than qmap mtu=%d\n", skb_len, qmap_net->mtu); - goto error_pkt; - } - - if (skb_in->len < (pkt_len + hdr_size)) { - netdev_info(ndev, "drop qmap unknow pkt, len=%d, pkt_len=%d\n", skb_in->len, pkt_len); - goto error_pkt; - } - - if (map_header->cd_bit) { - rmnet_data_map_command(pQmapDev, map_header); - goto skip_pkt; - } - - switch (skb_in->data[hdr_size] & 0xf0) { - case 0x40: -#ifdef CONFIG_QCA_NSS_PACKET_FILTER - { - struct iphdr *ip4h = (struct iphdr *)(&skb_in->data[hdr_size]); - if (ip4h->protocol == IPPROTO_ICMP) { - skip_nss = 1; - } - } -#endif - protocol = htons(ETH_P_IP); - break; - case 0x60: -#ifdef CONFIG_QCA_NSS_PACKET_FILTER - { - struct ipv6hdr *ip6h = (struct ipv6hdr *)(&skb_in->data[hdr_size]); - if (ip6h->nexthdr == NEXTHDR_ICMP) { - skip_nss = 1; - } - } -#endif - protocol = htons(ETH_P_IPV6); - break; - default: - netdev_info(ndev, "unknow skb->protocol %02x\n", skb_in->data[hdr_size]); - goto error_pkt; - } - -//for Qualcomm's SFE, do not use skb_clone(), or SFE 's performace is very bad. -//for Qualcomm's NSS, do not use skb_clone(), or NSS 's performace is very bad. - qmap_skb = netdev_alloc_skb(qmap_net, skb_len); - if (qmap_skb) { - skb_put(qmap_skb, skb_len); - memcpy(qmap_skb->data, skb_in->data + hdr_size, skb_len); - } - - if (qmap_skb == NULL) { - pQmapDev->stats.alloc_failed++; - //netdev_info(ndev, "fail to alloc skb, pkt_len = %d\n", skb_len); //do not print in softirq - goto error_pkt; - } - - skb_reset_transport_header(qmap_skb); - skb_reset_network_header(qmap_skb); - qmap_skb->pkt_type = PACKET_HOST; - skb_set_mac_header(qmap_skb, 0); - qmap_skb->protocol = protocol; - - if(skip_nss) - qmap_skb->cb[0] = 1; - - if (ul_header && ul_header->header_type == RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD - && ul_header->csum_valid_required) { -#if 0 //TODO - qmap_skb->ip_summed = CHECKSUM_UNNECESSARY; -#endif - } - - if (qmap_skb->dev->type == ARPHRD_ETHER) { - skb_push(qmap_skb, ETH_HLEN); - skb_reset_mac_header(qmap_skb); - memcpy(eth_hdr(qmap_skb)->h_source, default_modem_addr, ETH_ALEN); - memcpy(eth_hdr(qmap_skb)->h_dest, qmap_net->dev_addr, ETH_ALEN); - eth_hdr(qmap_skb)->h_proto = protocol; -#ifdef QUECTEL_BRIDGE_MODE - bridge_mode_rx_fixup(pQmapDev, qmap_net, qmap_skb); -#endif - __skb_pull(qmap_skb, ETH_HLEN); - } - -#ifndef MHI_NETDEV_ONE_CARD_MODE - rmnet_vnd_upate_rx_stats(qmap_net, 1, skb_len); -#endif - __skb_queue_tail(&skb_chain, qmap_skb); - -skip_pkt: - skb_pull(skb_in, pkt_len + hdr_size); - } - -error_pkt: - while ((qmap_skb = __skb_dequeue (&skb_chain))) { - netif_receive_skb(qmap_skb); - } -} - -#ifndef MHI_NETDEV_ONE_CARD_MODE -static rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) -{ - struct sk_buff *skb = *pskb; - struct mhi_netdev *mhi_netdev; - - if (!skb) - goto done; - - //printk("%s skb=%p, protocol=%x, len=%d\n", __func__, skb, skb->protocol, skb->len); - - if (skb->pkt_type == PACKET_LOOPBACK) - return RX_HANDLER_PASS; - - if (skb->protocol != htons(ETH_P_MAP)) { - WARN_ON(1); - return RX_HANDLER_PASS; - } - /* when open hyfi function, run cm will make system crash */ - //dev = rcu_dereference(skb->dev->rx_handler_data); - mhi_netdev = (struct mhi_netdev *)ndev_to_mhi(skb->dev); - - if (mhi_netdev == NULL) { - WARN_ON(1); - return RX_HANDLER_PASS; - } - - if (mhi_netdev->net_type == MHI_NET_MBIM) - rmnet_mbim_rx_handler(mhi_netdev, skb); - else - rmnet_qmi_rx_handler(mhi_netdev, skb); - - if (!skb_cloned(skb)) { - if (skb_queue_len(&mhi_netdev->rx_allocated) < 128) { - skb->data = skb->head; - skb_reset_tail_pointer(skb); - skb->len = 0; - skb_queue_tail(&mhi_netdev->rx_allocated, skb); - return RX_HANDLER_CONSUMED; - } - } - - consume_skb(skb); - -done: - return RX_HANDLER_CONSUMED; -} - -static struct net_device * rmnet_vnd_register_device(struct mhi_netdev *pQmapDev, u8 offset_id, u8 mux_id) -{ - struct net_device *real_dev = pQmapDev->ndev; - struct net_device *qmap_net; - struct qmap_priv *priv; - int err; - int use_qca_nss = !!nss_cb; - unsigned char temp_addr[ETH_ALEN]; - - qmap_net = alloc_etherdev(sizeof(*priv)); - if (!qmap_net) - return NULL; - - SET_NETDEV_DEV(qmap_net, &real_dev->dev); - priv = netdev_priv(qmap_net); - priv->offset_id = offset_id; - priv->real_dev = pQmapDev->ndev; - priv->self_dev = qmap_net; - priv->pQmapDev = pQmapDev; - priv->qmap_version = pQmapDev->qmap_version; - priv->mux_id = mux_id; - sprintf(qmap_net->name, "%.12s.%d", real_dev->name, offset_id + 1); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)) - __dev_addr_set(qmap_net, real_dev->dev_addr, ETH_ALEN); -#else - memcpy (qmap_net->dev_addr, real_dev->dev_addr, ETH_ALEN); -#endif - //qmap_net->dev_addr[5] = offset_id + 1; - //eth_random_addr(qmap_net->dev_addr); - memcpy(temp_addr, qmap_net->dev_addr, ETH_ALEN); - temp_addr[5] = offset_id + 1; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)) - __dev_addr_set(qmap_net, temp_addr, ETH_ALEN); -#else - memcpy(qmap_net->dev_addr, temp_addr, ETH_ALEN); -#endif -#if defined(MHI_NETDEV_STATUS64) - priv->stats64 = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); - if (!priv->stats64) - goto out_free_newdev; -#endif - -#ifdef QUECTEL_BRIDGE_MODE - priv->bridge_mode = !!(pQmapDev->bridge_mode & BIT(offset_id)); - qmap_net->sysfs_groups[0] = &pcie_mhi_qmap_sysfs_attr_group; - if (priv->bridge_mode) - use_qca_nss = 0; -#endif - - priv->agg_skb = NULL; - priv->agg_count = 0; - hrtimer_init(&priv->agg_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - priv->agg_hrtimer.function = rmnet_vnd_tx_agg_timer_cb; - INIT_WORK(&priv->agg_wq, rmnet_vnd_tx_agg_work); - ktime_get_ts64(&priv->agg_time); - spin_lock_init(&priv->agg_lock); - priv->use_qca_nss = 0; - - qmap_net->ethtool_ops = &rmnet_vnd_ethtool_ops; - qmap_net->netdev_ops = &rmnet_vnd_ops; - qmap_net->flags |= IFF_NOARP; - qmap_net->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0) - qmap_net->max_mtu = pQmapDev->max_mtu; -#endif - - if (nss_cb && use_qca_nss) { - rmnet_vnd_rawip_setup(qmap_net); - } -#ifdef CONFIG_PINCTRL_IPQ9574 - rmnet_vnd_rawip_setup(qmap_net); -#endif - if (pQmapDev->net_type == MHI_NET_MBIM) { - qmap_net->needed_headroom = sizeof(struct mhi_mbim_hdr); - } - - err = register_netdev(qmap_net); - pr_info("%s(%s)=%d\n", __func__, qmap_net->name, err); - if (err == -EEXIST) { - //'ifdown wan' for openwrt, why? - } - if (err < 0) - goto out_free_newdev; - - netif_device_attach (qmap_net); - netif_carrier_off(qmap_net); - - if (nss_cb && use_qca_nss) { - int rc = nss_cb->nss_create(qmap_net); - WARN_ON(rc); - if (rc) { - /* Log, but don't fail the device creation */ - netdev_err(qmap_net, "Device will not use NSS path: %d\n", rc); - } else { - priv->use_qca_nss = 1; - netdev_info(qmap_net, "NSS context created\n"); - rtnl_lock(); - netdev_rx_handler_register(qmap_net, qca_nss_rx_handler, NULL); - rtnl_unlock(); - } - } - - return qmap_net; - -out_free_newdev: - free_netdev(qmap_net); - return qmap_net; -} - -static void rmnet_vnd_unregister_device(struct net_device *qmap_net) { - struct qmap_priv *priv; - unsigned long flags; - - pr_info("%s(%s)\n", __func__, qmap_net->name); - netif_carrier_off(qmap_net); - - priv = netdev_priv(qmap_net); - hrtimer_cancel(&priv->agg_hrtimer); - cancel_work_sync(&priv->agg_wq); - - spin_lock_irqsave(&priv->agg_lock, flags); - if (priv->agg_skb) { - kfree_skb(priv->agg_skb); - priv->agg_skb = NULL; - priv->agg_count = 0; - } - spin_unlock_irqrestore(&priv->agg_lock, flags); - - if (nss_cb && priv->use_qca_nss) { - rtnl_lock(); - netdev_rx_handler_unregister(qmap_net); - rtnl_unlock(); - nss_cb->nss_free(qmap_net); - } -#if defined(MHI_NETDEV_STATUS64) - free_percpu(priv->stats64); -#endif - unregister_netdev (qmap_net); - free_netdev(qmap_net); -} -#endif - -static void rmnet_info_set(struct mhi_netdev *pQmapDev, RMNET_INFO *rmnet_info) -{ - rmnet_info->size = sizeof(RMNET_INFO); - rmnet_info->rx_urb_size = pQmapDev->qmap_size; - rmnet_info->ep_type = 3; //DATA_EP_TYPE_PCIE - rmnet_info->iface_id = 4; - rmnet_info->qmap_mode = pQmapDev->qmap_mode; - rmnet_info->qmap_version = pQmapDev->qmap_version; - rmnet_info->dl_minimum_padding = 0; -} - -static ssize_t qmap_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *ndev = to_net_dev(dev); - struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev); - - return snprintf(buf, PAGE_SIZE, "%u\n", mhi_netdev->qmap_mode); -} - -static DEVICE_ATTR(qmap_mode, S_IRUGO, qmap_mode_show, NULL); - -static ssize_t qmap_size_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *ndev = to_net_dev(dev); - struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev); - - return snprintf(buf, PAGE_SIZE, "%u\n", mhi_netdev->qmap_size); -} - -static DEVICE_ATTR(qmap_size, S_IRUGO, qmap_size_show, NULL); - -static ssize_t link_state_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *ndev = to_net_dev(dev); - struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev); - - return snprintf(buf, PAGE_SIZE, "0x%x\n", mhi_netdev->link_state); -} - -static ssize_t link_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct net_device *ndev = to_net_dev(dev); - struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev); - //struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - unsigned link_state = 0; - unsigned old_link = mhi_netdev->link_state; - uint offset_id = 0; - - link_state = simple_strtoul(buf, NULL, 0); - if (mhi_netdev->qmap_mode > 1) { - offset_id = ((link_state&0xF) - 1); - - if (0 < link_state && link_state <= mhi_netdev->qmap_mode) - mhi_netdev->link_state |= (1 << offset_id); - else if (0x80 < link_state && link_state <= (0x80 + mhi_netdev->qmap_mode)) - mhi_netdev->link_state &= ~(1 << offset_id); - } - else { - mhi_netdev->link_state = !!link_state; - } - - if (old_link != mhi_netdev->link_state) { - struct net_device *qmap_net = mhi_netdev->mpQmapNetDev[offset_id]; - - if (mhi_netdev->link_state) - netif_carrier_on(mhi_netdev->ndev); - else { - netif_carrier_off(mhi_netdev->ndev); - } - - if (qmap_net) { - if (mhi_netdev->link_state & (1 << offset_id)) - netif_carrier_on(qmap_net); - else - netif_carrier_off(qmap_net); - } - - dev_info(dev, "link_state 0x%x -> 0x%x\n", old_link, mhi_netdev->link_state); - } - - return count; -} - -static DEVICE_ATTR(link_state, S_IWUSR | S_IRUGO, link_state_show, link_state_store); - -static struct attribute *pcie_mhi_sysfs_attrs[] = { - &dev_attr_qmap_mode.attr, - &dev_attr_qmap_size.attr, - &dev_attr_link_state.attr, -#ifdef QUECTEL_BRIDGE_MODE - &dev_attr_bridge_mode.attr, - &dev_attr_bridge_ipv4.attr, -#endif - NULL, -}; - -static struct attribute_group pcie_mhi_sysfs_attr_group = { - .attrs = pcie_mhi_sysfs_attrs, -}; - -static void mhi_netdev_upate_rx_stats(struct mhi_netdev *mhi_netdev, - unsigned rx_packets, unsigned rx_bytes) { -#if defined(MHI_NETDEV_STATUS64) - struct pcpu_sw_netstats *stats64 = this_cpu_ptr(mhi_netdev->stats64); - - u64_stats_update_begin(&stats64->syncp); -#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)) - stats64->rx_packets += rx_packets; - stats64->rx_bytes += rx_bytes; -#else - u64_stats_add(&stats64->rx_packets, rx_packets); - u64_stats_add(&stats64->rx_bytes, rx_bytes); -#endif - u64_stats_update_begin(&stats64->syncp); -#else - mhi_netdev->ndev->stats.rx_packets += rx_packets; - mhi_netdev->ndev->stats.rx_bytes += rx_bytes; -#endif -} - -static void mhi_netdev_upate_tx_stats(struct mhi_netdev *mhi_netdev, - unsigned tx_packets, unsigned tx_bytes) { -#if defined(MHI_NETDEV_STATUS64) - struct pcpu_sw_netstats *stats64 = this_cpu_ptr(mhi_netdev->stats64); - - u64_stats_update_begin(&stats64->syncp); -#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)) - stats64->tx_packets += tx_packets; - stats64->tx_bytes += tx_bytes; -#else - u64_stats_add(&stats64->tx_packets, tx_packets); - u64_stats_add(&stats64->tx_bytes, tx_bytes); -#endif - u64_stats_update_begin(&stats64->syncp); -#else - mhi_netdev->ndev->stats.tx_packets += tx_packets; - mhi_netdev->ndev->stats.tx_bytes += tx_bytes; -#endif -} - -static __be16 mhi_netdev_ip_type_trans(u8 data) -{ - __be16 protocol = 0; - - /* determine L3 protocol */ - switch (data & 0xf0) { - case 0x40: - protocol = htons(ETH_P_IP); - break; - case 0x60: - protocol = htons(ETH_P_IPV6); - break; - default: - protocol = htons(ETH_P_MAP); - break; - } - - return protocol; -} - -static int mhi_netdev_alloc_skb(struct mhi_netdev *mhi_netdev, gfp_t gfp_t) -{ - u32 cur_mru = mhi_netdev->mru; - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - struct mhi_skb_priv *skb_priv; - int ret; - struct sk_buff *skb; - int no_tre = mhi_get_no_free_descriptors(mhi_dev, DMA_FROM_DEVICE); - int i; - - for (i = 0; i < no_tre; i++) { - skb = skb_dequeue(&mhi_netdev->rx_allocated); - if (!skb) { - skb = alloc_skb(/*32+*/cur_mru, gfp_t); - if (skb) - mhi_netdev->stats.rx_allocated++; - } - if (!skb) - return -ENOMEM; - - read_lock_bh(&mhi_netdev->pm_lock); - if (unlikely(!mhi_netdev->enabled)) { - MSG_ERR("Interface not enabled\n"); - ret = -EIO; - goto error_queue; - } - - skb_priv = (struct mhi_skb_priv *)skb->cb; - skb_priv->buf = skb->data; - skb_priv->size = cur_mru; - skb_priv->bind_netdev = mhi_netdev; - skb->dev = mhi_netdev->ndev; - //skb_reserve(skb, 32); //for ethernet header - - spin_lock_bh(&mhi_netdev->rx_lock); - ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, skb, - skb_priv->size, MHI_EOT); - spin_unlock_bh(&mhi_netdev->rx_lock); - - if (ret) { - skb_priv->bind_netdev = NULL; - MSG_ERR("Failed to queue skb, ret:%d\n", ret); - ret = -EIO; - goto error_queue; - } - - read_unlock_bh(&mhi_netdev->pm_lock); - } - - return 0; - -error_queue: - skb->destructor = NULL; - read_unlock_bh(&mhi_netdev->pm_lock); - dev_kfree_skb_any(skb); - - return ret; -} - -static void mhi_netdev_alloc_work(struct work_struct *work) -{ - struct mhi_netdev *mhi_netdev = container_of(work, struct mhi_netdev, - alloc_work.work); - /* sleep about 1 sec and retry, that should be enough time - * for system to reclaim freed memory back. - */ - const int sleep_ms = 1000; - int retry = 60; - int ret; - - MSG_LOG("Entered\n"); - do { - ret = mhi_netdev_alloc_skb(mhi_netdev, GFP_KERNEL); - /* sleep and try again */ - if (ret == -ENOMEM) { - schedule_delayed_work(&mhi_netdev->alloc_work, msecs_to_jiffies(20)); - return; - msleep(sleep_ms); - retry--; - } - } while (ret == -ENOMEM && retry); - - MSG_LOG("Exit with status:%d retry:%d\n", ret, retry); -} - -static void mhi_netdev_dealloc(struct mhi_netdev *mhi_netdev) -{ - struct sk_buff *skb; - - skb = skb_dequeue(&mhi_netdev->rx_allocated); - while (skb) { - skb->destructor = NULL; - kfree_skb(skb); - skb = skb_dequeue(&mhi_netdev->rx_allocated); - } -} - -#ifdef CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY -static struct mhi_netbuf *mhi_netdev_alloc(struct device *dev, - gfp_t gfp, - unsigned int order) -{ - struct page *page; - struct mhi_netbuf *netbuf; - struct mhi_buf *mhi_buf; - void *vaddr; - - page = __dev_alloc_pages(gfp, order); - if (!page) - return NULL; - - vaddr = page_address(page); - - /* we going to use the end of page to store cached data */ - netbuf = vaddr + (PAGE_SIZE << order) - sizeof(*netbuf); - - mhi_buf = (struct mhi_buf *)netbuf; - mhi_buf->page = page; - mhi_buf->buf = vaddr; - mhi_buf->len = (void *)netbuf - vaddr; - mhi_buf->dma_addr = dma_map_page(dev, page, 0, mhi_buf->len, - DMA_FROM_DEVICE); - if (dma_mapping_error(dev, mhi_buf->dma_addr)) { - __free_pages(mhi_buf->page, order); - return NULL; - } - - return netbuf; -} - -static void mhi_netdev_unmap_page(struct device *dev, - dma_addr_t dma_addr, - size_t len, - enum dma_data_direction dir) -{ - dma_unmap_page(dev, dma_addr, len, dir); -} - -static int mhi_netdev_tmp_alloc(struct mhi_netdev *mhi_netdev, int nr_tre) -{ - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - struct device *dev = mhi_dev->dev.parent; - const u32 order = mhi_netdev->order; - int i, ret; - - for (i = 0; i < nr_tre; i++) { - struct mhi_buf *mhi_buf; - struct mhi_netbuf *netbuf = mhi_netdev_alloc(dev, GFP_ATOMIC, - order); - if (!netbuf) - return -ENOMEM; - - mhi_buf = (struct mhi_buf *)netbuf; - netbuf->unmap = mhi_netdev_unmap_page; - - ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, mhi_buf, - mhi_buf->len, MHI_EOT); - if (unlikely(ret)) { - MSG_ERR("Failed to queue transfer, ret:%d\n", ret); - mhi_netdev_unmap_page(dev, mhi_buf->dma_addr, - mhi_buf->len, DMA_FROM_DEVICE); - __free_pages(mhi_buf->page, order); - return ret; - } - } - - return 0; -} - -static void mhi_netdev_queue(struct mhi_netdev *mhi_netdev) -{ - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - struct device *dev = mhi_dev->dev.parent; - struct mhi_netbuf *netbuf; - struct mhi_buf *mhi_buf; - struct mhi_netbuf **netbuf_pool = mhi_netdev->netbuf_pool; - int nr_tre = mhi_get_no_free_descriptors(mhi_dev, DMA_FROM_DEVICE); - int i, peak, cur_index, ret; - const int pool_size = mhi_netdev->pool_size - 1, max_peak = 4; - - MSG_VERB("Enter free_desc:%d\n", nr_tre); - - if (!nr_tre) - return; - - /* try going thru reclaim pool first */ - for (i = 0; i < nr_tre; i++) { - /* peak for the next buffer, we going to peak several times, - * and we going to give up if buffers are not yet free - */ - cur_index = mhi_netdev->current_index; - netbuf = NULL; - for (peak = 0; peak < max_peak; peak++) { - struct mhi_netbuf *tmp = netbuf_pool[cur_index]; - - mhi_buf = &tmp->mhi_buf; - - cur_index = (cur_index + 1) & pool_size; - - /* page == 1 idle, buffer is free to reclaim */ -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 5,4,0 )) - if (atomic_read(&mhi_buf->page->_count) == 1) -#else - if (atomic_read(&mhi_buf->page->_refcount) == 1) -#endif - { - netbuf = tmp; - break; - } - } - - /* could not find a free buffer */ - if (!netbuf) - break; - - /* increment reference count so when network stack is done - * with buffer, the buffer won't be freed - */ -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 5,4,0 )) - atomic_inc(&mhi_buf->page->_count); -#else - atomic_inc(&mhi_buf->page->_refcount); -#endif - dma_sync_single_for_device(dev, mhi_buf->dma_addr, mhi_buf->len, - DMA_FROM_DEVICE); - ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, mhi_buf, - mhi_buf->len, MHI_EOT); - if (unlikely(ret)) { - MSG_ERR("Failed to queue buffer, ret:%d\n", ret); - netbuf->unmap(dev, mhi_buf->dma_addr, mhi_buf->len, - DMA_FROM_DEVICE); -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 5,4,0 )) - atomic_dec(&mhi_buf->page->_count); -#else - atomic_dec(&mhi_buf->page->_refcount); -#endif - return; - } - mhi_netdev->current_index = cur_index; - } - - /* recyling did not work, buffers are still busy allocate temp pkts */ - if (i < nr_tre) - mhi_netdev_tmp_alloc(mhi_netdev, nr_tre - i); -} - -/* allocating pool of memory */ -static int mhi_netdev_alloc_pool(struct mhi_netdev *mhi_netdev) -{ - int i; - struct mhi_netbuf *netbuf, **netbuf_pool; - struct mhi_buf *mhi_buf; - const u32 order = mhi_netdev->order; - struct device *dev = mhi_netdev->mhi_dev->dev.parent; - - netbuf_pool = kmalloc_array(mhi_netdev->pool_size, sizeof(*netbuf_pool), - GFP_KERNEL); - if (!netbuf_pool) - return -ENOMEM; - - for (i = 0; i < mhi_netdev->pool_size; i++) { - /* allocate paged data */ - netbuf = mhi_netdev_alloc(dev, GFP_KERNEL, order); - if (!netbuf) - goto error_alloc_page; - - netbuf->unmap = dma_sync_single_for_cpu; - netbuf_pool[i] = netbuf; - } - - mhi_netdev->netbuf_pool = netbuf_pool; - - return 0; - -error_alloc_page: - for (--i; i >= 0; i--) { - netbuf = netbuf_pool[i]; - mhi_buf = &netbuf->mhi_buf; - dma_unmap_page(dev, mhi_buf->dma_addr, mhi_buf->len, - DMA_FROM_DEVICE); - __free_pages(mhi_buf->page, order); - } - - kfree(netbuf_pool); - - return -ENOMEM; -} - -static void mhi_netdev_free_pool(struct mhi_netdev *mhi_netdev) -{ - int i; - struct mhi_netbuf *netbuf, **netbuf_pool = mhi_netdev->netbuf_pool; - struct device *dev = mhi_netdev->mhi_dev->dev.parent; - struct mhi_buf *mhi_buf; - - for (i = 0; i < mhi_netdev->pool_size; i++) { - netbuf = netbuf_pool[i]; - mhi_buf = &netbuf->mhi_buf; - dma_unmap_page(dev, mhi_buf->dma_addr, mhi_buf->len, - DMA_FROM_DEVICE); - __free_pages(mhi_buf->page, mhi_netdev->order); - } - - kfree(mhi_netdev->netbuf_pool); - mhi_netdev->netbuf_pool = NULL; -} - -static int mhi_netdev_poll(struct napi_struct *napi, int budget) -{ - struct net_device *dev = napi->dev; - struct mhi_netdev_priv *mhi_netdev_priv = netdev_priv(dev); - struct mhi_netdev *mhi_netdev = mhi_netdev_priv->mhi_netdev; - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - struct mhi_net_chain *chain = &mhi_netdev->chain; - int rx_work = 0; - - MSG_VERB("Entered\n"); - - rx_work = mhi_poll(mhi_dev, budget); - - /* chained skb, push it to stack */ - if (chain && chain->head) { - netif_receive_skb(chain->head); - chain->head = NULL; - } - - if (rx_work < 0) { - MSG_ERR("Error polling ret:%d\n", rx_work); - napi_complete(napi); - return 0; - } - - /* queue new buffers */ - mhi_netdev_queue(mhi_netdev); - - /* complete work if # of packet processed less than allocated budget */ - if (rx_work < budget) - napi_complete(napi); - - MSG_VERB("polled %d pkts\n", rx_work); - - return rx_work; -} -#else -static int mhi_netdev_poll(struct napi_struct *napi, int budget) -{ - struct net_device *dev = napi->dev; - struct mhi_netdev_priv *mhi_netdev_priv = netdev_priv(dev); - struct mhi_netdev *mhi_netdev = mhi_netdev_priv->mhi_netdev; - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - struct sk_buff *skb; - int rx_work = 0; - int ret; - - MSG_VERB("Entered\n"); - - rx_work = mhi_poll(mhi_dev, budget); - - if (rx_work < 0) { - MSG_ERR("Error polling ret:%d\n", rx_work); - napi_complete(napi); - return 0; - } - - if (mhi_netdev->net_type == MHI_NET_MBIM || mhi_netdev->net_type == MHI_NET_RMNET) { - while ((skb = skb_dequeue (&mhi_netdev->qmap_chain))) { -#ifdef MHI_NETDEV_ONE_CARD_MODE - int recly_skb = 0; - - mhi_netdev_upate_rx_stats(mhi_netdev, 1, skb->len); - if (mhi_netdev->net_type == MHI_NET_MBIM) - rmnet_mbim_rx_handler(mhi_netdev, skb); - else - rmnet_qmi_rx_handler(mhi_netdev, skb); - - if (!skb_cloned(skb)) { - if (skb_queue_len(&mhi_netdev->rx_allocated) < 128) { - skb->data = skb->head; - skb_reset_tail_pointer(skb); - skb->len = 0; - skb_queue_tail(&mhi_netdev->rx_allocated, skb); - recly_skb = 1; - } - } - if (recly_skb == 0) - dev_kfree_skb(skb); -#else - mhi_netdev_upate_rx_stats(mhi_netdev, 1, skb->len); - skb->dev = mhi_netdev->ndev; - skb->protocol = htons(ETH_P_MAP); - netif_receive_skb(skb); -#endif - } - } - else if (mhi_netdev->net_type == MHI_NET_ETHER) { - while ((skb = skb_dequeue (&mhi_netdev->qmap_chain))) { - mhi_netdev_upate_rx_stats(mhi_netdev, 1, skb->len); - skb->dev = mhi_netdev->ndev; - skb->protocol = mhi_netdev_ip_type_trans(skb->data[0]); - netif_receive_skb(skb); - } - } - - /* queue new buffers */ - if (!delayed_work_pending(&mhi_netdev->alloc_work)) { - ret = mhi_netdev->rx_queue(mhi_netdev, GFP_ATOMIC); - if (ret == -ENOMEM) { - //MSG_LOG("out of tre, queuing bg worker\n"); //do not print in softirq - mhi_netdev->stats.alloc_failed++; - schedule_delayed_work(&mhi_netdev->alloc_work, msecs_to_jiffies(20)); - } - } - - /* complete work if # of packet processed less than allocated budget */ - if (rx_work < budget) - napi_complete(napi); - - MSG_VERB("polled %d pkts\n", rx_work); - - return rx_work; -} -#endif - -static int mhi_netdev_open(struct net_device *ndev) -{ - struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev); - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - - MSG_LOG("Opened net dev interface\n"); - - /* tx queue may not necessarily be stopped already - * so stop the queue if tx path is not enabled - */ - if (!mhi_dev->ul_chan) - netif_stop_queue(ndev); - else - netif_start_queue(ndev); - - return 0; - -} - -static int mhi_netdev_change_mtu(struct net_device *ndev, int new_mtu) -{ - struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev); - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - - if (new_mtu < 0 || mhi_dev->mtu < new_mtu) - return -EINVAL; - - ndev->mtu = new_mtu; - return 0; -} - -static netdev_tx_t mhi_netdev_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct mhi_netdev_priv *mhi_netdev_priv = netdev_priv(dev); - struct mhi_netdev *mhi_netdev = mhi_netdev_priv->mhi_netdev; - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - int res = 0; - struct skb_data *entry = (struct skb_data *)(skb->cb); - - entry->packets = 1; - entry->length = skb->len; - entry->bind_netdev = mhi_netdev; - - MSG_VERB("Entered\n"); - - //printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - //qmap_hex_dump(__func__, skb->data, 32); - -#ifdef MHI_NETDEV_ONE_CARD_MODE - //printk("%s dev->type=%d\n", __func__, dev->type); - - if (dev->type == ARPHRD_ETHER) { - skb_reset_mac_header(skb); - -#ifdef QUECTEL_BRIDGE_MODE - if (mhi_netdev->bridge_mode && bridge_mode_tx_fixup(dev, skb, mhi_netdev->bridge_ipv4, mhi_netdev->bridge_mac) == NULL) { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } -#endif - - if ((mhi_netdev->net_type == MHI_NET_RMNET || mhi_netdev->net_type == MHI_NET_MBIM) - && (skb_pull(skb, ETH_HLEN) == NULL)) { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } - } - - if (mhi_netdev->net_type == MHI_NET_MBIM) { - if (add_mbim_hdr(skb, mhi_netdev->mbim_mux_id) == NULL) { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } - } - else if (mhi_netdev->net_type == MHI_NET_RMNET) { - if (mhi_netdev->qmap_version == 5) { - add_qhdr(skb, QUECTEL_QMAP_MUX_ID); - } - else if (mhi_netdev->qmap_version == 9) { - add_qhdr_v5(skb, QUECTEL_QMAP_MUX_ID); - } - else { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } - } -#else - if ((mhi_netdev->net_type == MHI_NET_RMNET || mhi_netdev->net_type == MHI_NET_MBIM) - && skb->protocol != htons(ETH_P_MAP)) { - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } -#endif - - if (mhi_netdev->net_type == MHI_NET_MBIM) { - struct mhi_mbim_hdr *mhdr = (struct mhi_mbim_hdr *)skb->data; - mhdr->nth16.wSequence = cpu_to_le16(mhi_netdev->mbim_ctx.tx_seq++); - } - - if (unlikely(mhi_get_no_free_descriptors(mhi_dev, DMA_TO_DEVICE) < 16)) { - u32 i = 0; - for (i = 0; i < mhi_netdev->qmap_mode; i++) { - struct net_device *qmap_net = mhi_netdev->mpQmapNetDev[i]; - if (qmap_net) { - netif_stop_queue(qmap_net); - } - } - - netif_stop_queue(dev); - } - - res = mhi_queue_transfer(mhi_dev, DMA_TO_DEVICE, skb, skb->len, - MHI_EOT); - - //printk("%s transfer res=%d\n", __func__, res); - if (unlikely(res)) { - dev_kfree_skb_any(skb); - dev->stats.tx_errors++; - } - - MSG_VERB("Exited\n"); - - return NETDEV_TX_OK; -} - -#if defined(MHI_NETDEV_STATUS64) -static struct rtnl_link_stats64 * _mhi_netdev_get_stats64(struct net_device *ndev, struct rtnl_link_stats64 *stats) -{ - struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev); - - unsigned int start; - int cpu; - - netdev_stats_to_stats64(stats, &ndev->stats); - - for_each_possible_cpu(cpu) { - struct pcpu_sw_netstats *stats64; -#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)) - u64 rx_packets, rx_bytes; - u64 tx_packets, tx_bytes; - - stats64 = per_cpu_ptr(mhi_netdev->stats64, cpu); - - do { - start = u64_stats_fetch_begin_irq(&stats64->syncp); - rx_packets = stats64->rx_packets; - rx_bytes = stats64->rx_bytes; - tx_packets = stats64->tx_packets; - tx_bytes = stats64->tx_bytes; - } while (u64_stats_fetch_retry_irq(&stats64->syncp, start)); - - stats->rx_packets += rx_packets; - stats->rx_bytes += rx_bytes; - stats->tx_packets += tx_packets; - stats->tx_bytes += tx_bytes; -#else - u64_stats_t rx_packets, rx_bytes; - u64_stats_t tx_packets, tx_bytes; - - stats64 = per_cpu_ptr(mhi_netdev->stats64, cpu); - - do { - start = u64_stats_fetch_begin(&stats64->syncp); - rx_packets = stats64->rx_packets; - rx_bytes = stats64->rx_bytes; - tx_packets = stats64->tx_packets; - tx_bytes = stats64->tx_bytes; - } while (u64_stats_fetch_retry(&stats64->syncp, start)); - - stats->rx_packets += u64_stats_read(&rx_packets); - stats->rx_bytes += u64_stats_read(&rx_bytes); - stats->tx_packets += u64_stats_read(&tx_packets); - stats->tx_bytes += u64_stats_read(&tx_bytes); -#endif - } - - return stats; -} - -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 4,10,0 )) //bc1f44709cf27fb2a5766cadafe7e2ad5e9cb221 -static void mhi_netdev_get_stats64(struct net_device *ndev, struct rtnl_link_stats64 *stats) { - _mhi_netdev_get_stats64(ndev, stats); -} -#else -static struct rtnl_link_stats64 * mhi_netdev_get_stats64(struct net_device *ndev, struct rtnl_link_stats64 *stats) { - _mhi_netdev_get_stats64(ndev, stats); - return stats; -} -#endif -#endif - -static int qmap_setting_store(struct mhi_netdev *mhi_netdev, QMAP_SETTING *qmap_settings, size_t size) { - if (qmap_settings->size != size) { - netdev_err(mhi_netdev->ndev, "ERROR: qmap_settings.size donot match!\n"); - return -EOPNOTSUPP; - } - - mhi_netdev->dl_minimum_padding = qmap_settings->dl_minimum_padding; - - return 0; -} - -static int qmap_ndo_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - struct mhi_netdev *mhi_netdev = ndev_to_mhi(dev); - int rc = -EOPNOTSUPP; - uint link_state = 0; - QMAP_SETTING qmap_settings = {0}; - - switch (cmd) { - case 0x89F1: //SIOCDEVPRIVATE - rc = copy_from_user(&link_state, ifr->ifr_ifru.ifru_data, sizeof(link_state)); - if (!rc) { - char buf[32]; - snprintf(buf, sizeof(buf), "%u", link_state); - link_state_store(&dev->dev, NULL, buf, strlen(buf)); - } - break; - - case 0x89F2: //SIOCDEVPRIVATE - rc = copy_from_user(&qmap_settings, ifr->ifr_ifru.ifru_data, sizeof(qmap_settings)); - if (!rc) { - rc = qmap_setting_store(mhi_netdev, &qmap_settings, sizeof(qmap_settings)); - } - break; - - case 0x89F3: //SIOCDEVPRIVATE - if (mhi_netdev->use_rmnet_usb) { - rc = copy_to_user(ifr->ifr_ifru.ifru_data, &mhi_netdev->rmnet_info, sizeof(mhi_netdev->rmnet_info)); - } - break; - - default: - break; - } - - return rc; -} - -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 5,14,0 )) //b9067f5dc4a07c8e24e01a1b277c6722d91be39e -#define use_ndo_siocdevprivate -#endif -#ifdef use_ndo_siocdevprivate -static int qmap_ndo_siocdevprivate(struct net_device *dev, struct ifreq *ifr, void __user *data, int cmd) { - return qmap_ndo_do_ioctl(dev, ifr, cmd); -} -#endif - -static const struct net_device_ops mhi_netdev_ops_ip = { - .ndo_open = mhi_netdev_open, - .ndo_start_xmit = mhi_netdev_xmit, - //.ndo_do_ioctl = mhi_netdev_ioctl, - .ndo_change_mtu = mhi_netdev_change_mtu, -#if defined(MHI_NETDEV_STATUS64) - .ndo_get_stats64 = mhi_netdev_get_stats64, -#endif - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, - .ndo_do_ioctl = qmap_ndo_do_ioctl, -#ifdef use_ndo_siocdevprivate - .ndo_siocdevprivate = qmap_ndo_siocdevprivate, -#endif -}; - -static void mhi_netdev_get_drvinfo (struct net_device *ndev, struct ethtool_drvinfo *info) -{ - //struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev); - - strscpy (info->driver, "pcie_mhi", sizeof info->driver); - strscpy (info->version, PCIE_MHI_DRIVER_VERSION, sizeof info->version); -} - -static const struct ethtool_ops mhi_netdev_ethtool_ops = { - .get_drvinfo = mhi_netdev_get_drvinfo, - .get_link = ethtool_op_get_link, -}; - -static void mhi_netdev_setup(struct net_device *dev) -{ - dev->netdev_ops = &mhi_netdev_ops_ip; - ether_setup(dev); - - dev->ethtool_ops = &mhi_netdev_ethtool_ops; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)) - __dev_addr_set (dev, node_id, sizeof node_id); -#else - memcpy (dev->dev_addr, node_id, sizeof node_id); -#endif - /* set this after calling ether_setup */ - dev->header_ops = 0; /* No header */ - dev->hard_header_len = 0; - dev->type = ARPHRD_NONE; - dev->addr_len = 0; - dev->flags |= IFF_NOARP; - dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); //POINTOPOINT will make SFE work wrong - dev->watchdog_timeo = WATCHDOG_TIMEOUT; - //on OpenWrt, if set rmnet_mhi0.1 as WAN, '/sbin/netifd' will auto create VLAN for rmnet_mhi0 - dev->features |= (NETIF_F_VLAN_CHALLENGED); - -#ifdef MHI_NETDEV_ONE_CARD_MODE - if (mhi_mbim_enabled) { - dev->needed_headroom = sizeof(struct mhi_mbim_hdr); - } -#endif -} - -/* enable mhi_netdev netdev, call only after grabbing mhi_netdev.mutex */ -static int mhi_netdev_enable_iface(struct mhi_netdev *mhi_netdev) -{ - int ret = 0; -#if 0 - char ifalias[IFALIASZ]; -#endif - char ifname[IFNAMSIZ]; - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - int no_tre; - - MSG_LOG("Prepare the channels for transfer\n"); - - ret = mhi_prepare_for_transfer(mhi_dev); - if (ret) { - MSG_ERR("Failed to start TX chan ret %d\n", ret); - goto mhi_failed_to_start; - } - - /* first time enabling the node */ - if (!mhi_netdev->ndev) { - struct mhi_netdev_priv *mhi_netdev_priv; - -#if 0 - snprintf(ifalias, sizeof(ifalias), "%s_%04x_%02u.%02u.%02u_%u", - mhi_netdev->interface_name, mhi_dev->dev_id, - mhi_dev->domain, mhi_dev->bus, mhi_dev->slot, - mhi_netdev->alias); -#endif - - snprintf(ifname, sizeof(ifname), "%s%d", - mhi_netdev->interface_name, mhi_netdev->mhi_dev->mhi_cntrl->cntrl_idx); - - rtnl_lock(); -#ifdef NET_NAME_PREDICTABLE - mhi_netdev->ndev = alloc_netdev(sizeof(*mhi_netdev_priv), - ifname, NET_NAME_PREDICTABLE, - mhi_netdev_setup); -#else - mhi_netdev->ndev = alloc_netdev(sizeof(*mhi_netdev_priv), - ifname, - mhi_netdev_setup); -#endif - - if (!mhi_netdev->ndev) { - ret = -ENOMEM; - rtnl_unlock(); - goto net_dev_alloc_fail; - } - - //mhi_netdev->ndev->mtu = mhi_dev->mtu; - SET_NETDEV_DEV(mhi_netdev->ndev, &mhi_dev->dev); -#if 0 - dev_set_alias(mhi_netdev->ndev, ifalias, strlen(ifalias)); -#endif - mhi_netdev_priv = netdev_priv(mhi_netdev->ndev); - mhi_netdev_priv->mhi_netdev = mhi_netdev; - - if (mhi_netdev->net_type == MHI_NET_RMNET || mhi_netdev->net_type == MHI_NET_MBIM) { -#ifdef QUECTEL_BRIDGE_MODE - mhi_netdev->bridge_mode = bridge_mode; -#endif - mhi_netdev->ndev->sysfs_groups[0] = &pcie_mhi_sysfs_attr_group; - } - else if (mhi_netdev->net_type == MHI_NET_ETHER) { - mhi_netdev->ndev->mtu = mhi_netdev->mru; - } - rtnl_unlock(); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0) - mhi_netdev->ndev->max_mtu = mhi_netdev->max_mtu; //first net card -#endif - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)) - netif_napi_add_weight(mhi_netdev->ndev, &mhi_netdev->napi, mhi_netdev_poll, poll_weight); -#else - netif_napi_add(mhi_netdev->ndev, &mhi_netdev->napi, mhi_netdev_poll, poll_weight); -#endif - ret = register_netdev(mhi_netdev->ndev); - if (ret) { - MSG_ERR("Network device registration failed\n"); - goto net_dev_reg_fail; - } - - netif_carrier_off(mhi_netdev->ndev); - } - - write_lock_irq(&mhi_netdev->pm_lock); - mhi_netdev->enabled = true; - write_unlock_irq(&mhi_netdev->pm_lock); - -#ifdef CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY - /* MRU must be multiplication of page size */ - mhi_netdev->order = 1; - while ((PAGE_SIZE << mhi_netdev->order) < mhi_netdev->mru) - mhi_netdev->order += 1; - - /* setup pool size ~2x ring length*/ - no_tre = mhi_get_no_free_descriptors(mhi_dev, DMA_FROM_DEVICE); - mhi_netdev->pool_size = 1 << __ilog2_u32(no_tre); - if (no_tre > mhi_netdev->pool_size) - mhi_netdev->pool_size <<= 1; - mhi_netdev->pool_size <<= 1; - - /* allocate memory pool */ - ret = mhi_netdev_alloc_pool(mhi_netdev); - if (ret) { - MSG_ERR("mhi_netdev_alloc_pool Fail!\n"); - goto error_start; - } - - napi_enable(&mhi_netdev->napi); - - /* now we have a pool of buffers allocated, queue to hardware - * by triggering a napi_poll - */ - napi_schedule(&mhi_netdev->napi); -error_start: -#else - /* queue buffer for rx path */ - no_tre = mhi_get_no_free_descriptors(mhi_dev, DMA_FROM_DEVICE); - ret = mhi_netdev_alloc_skb(mhi_netdev, GFP_KERNEL); - if (ret) - schedule_delayed_work(&mhi_netdev->alloc_work, msecs_to_jiffies(20)); - - napi_enable(&mhi_netdev->napi); -#endif - - MSG_LOG("Exited.\n"); - - return 0; - -net_dev_reg_fail: - netif_napi_del(&mhi_netdev->napi); - free_netdev(mhi_netdev->ndev); - mhi_netdev->ndev = NULL; - -net_dev_alloc_fail: - mhi_unprepare_from_transfer(mhi_dev); - -mhi_failed_to_start: - MSG_ERR("Exited ret %d.\n", ret); - - return ret; -} - -static void mhi_netdev_xfer_ul_cb(struct mhi_device *mhi_dev, - struct mhi_result *mhi_result) -{ - struct mhi_netdev *mhi_netdev = mhi_device_get_devdata(mhi_dev); - struct sk_buff *skb = mhi_result->buf_addr; - struct net_device *ndev = mhi_netdev->ndev; - struct skb_data *entry = (struct skb_data *)(skb->cb); - - if (entry->bind_netdev != mhi_netdev) { - MSG_ERR("%s error!\n", __func__); - return; - } - - if (likely(mhi_result->transaction_status == 0)) { - mhi_netdev_upate_tx_stats(mhi_netdev, entry->packets, entry->length); - - if (netif_queue_stopped(ndev) && mhi_netdev->enabled - && mhi_get_no_free_descriptors(mhi_dev, DMA_TO_DEVICE) > 32) { - int i = 0; - - netif_wake_queue(ndev); - for (i = 0; i < mhi_netdev->qmap_mode; i++) { - struct net_device *qmap_net = mhi_netdev->mpQmapNetDev[i]; - if (qmap_net) { - if (netif_queue_stopped(qmap_net)) - netif_wake_queue(qmap_net); - } - } - } - } - - entry->bind_netdev = NULL; - entry->packets = 1; - entry->length = 0; - dev_kfree_skb(skb); -} - -#ifdef CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY -static void mhi_netdev_xfer_dl_cb(struct mhi_device *mhi_dev, - struct mhi_result *mhi_result) -{ - struct mhi_netdev *mhi_netdev = mhi_device_get_devdata(mhi_dev); - struct mhi_netbuf *netbuf = mhi_result->buf_addr; - struct mhi_buf *mhi_buf = &netbuf->mhi_buf; - struct sk_buff *skb; - struct net_device *ndev = mhi_netdev->ndev; - struct device *dev = mhi_dev->dev.parent; - struct mhi_net_chain *chain = &mhi_netdev->chain; - - netbuf->unmap(dev, mhi_buf->dma_addr, mhi_buf->len, DMA_FROM_DEVICE); - - /* modem is down, drop the buffer */ - if (mhi_result->transaction_status == -ENOTCONN) { - __free_pages(mhi_buf->page, mhi_netdev->order); - return; - } - - mhi_netdev_upate_rx_stats(mhi_netdev, 1, mhi_result->bytes_xferd); - - /* we support chaining */ - skb = alloc_skb(0, GFP_ATOMIC); - if (likely(skb)) { - skb_add_rx_frag(skb, 0, mhi_buf->page, 0, - mhi_result->bytes_xferd, mhi_netdev->mru); - - /* this is first on list */ - if (!chain->head) { - skb->dev = ndev; - skb->protocol = htons(ETH_P_MAP); - chain->head = skb; - } else { - skb_shinfo(chain->tail)->frag_list = skb; - } - - chain->tail = skb; - } else { - __free_pages(mhi_buf->page, mhi_netdev->order); - } -} -#else -static void mhi_netdev_xfer_dl_cb(struct mhi_device *mhi_dev, - struct mhi_result *mhi_result) -{ - struct mhi_netdev *mhi_netdev = mhi_device_get_devdata(mhi_dev); - struct sk_buff *skb = mhi_result->buf_addr; - struct mhi_skb_priv *skb_priv = (struct mhi_skb_priv *)(skb->cb); - - if (unlikely(skb_priv->bind_netdev != mhi_netdev)) { - MSG_ERR("%s error!\n", __func__); - return; - } - - if (unlikely(mhi_result->transaction_status)) { - if (mhi_result->transaction_status != -ENOTCONN) - MSG_ERR("%s transaction_status = %d!\n", __func__, mhi_result->transaction_status); - skb_priv->bind_netdev = NULL; - dev_kfree_skb(skb); - return; - } - -#if defined(CONFIG_IPQ5018_RATE_CONTROL) - if (likely(mhi_netdev->mhi_rate_control)) { - u32 time_interval = 0; - u32 time_difference = 0; - u32 cntfrq; - u64 second_jiffy; - u64 bytes_received_2; - struct net_device *ndev = mhi_netdev->ndev; - - if (mhi_netdev->first_jiffy) { - #if LINUX_VERSION_CODE < KERNEL_VERSION(5,2,0) - second_jiffy = arch_counter_get_cntvct(); - #else - second_jiffy = __arch_counter_get_cntvct(); - #endif - bytes_received_2 = mhi_netdev->bytes_received_2; - if ((second_jiffy > mhi_netdev->first_jiffy) && - (bytes_received_2 > mhi_netdev->bytes_received_1)) { - - time_difference = (second_jiffy - mhi_netdev->first_jiffy); - time_interval = (time_difference / mhi_netdev->cntfrq_per_msec); - - /* 1.8Gbps is 225,000,000bytes per second */ - /* We wills sample at 100ms interval */ - /* For 1ms 225000 bytes */ - /* For 100ms 22,500,000 bytes */ - /* For 10ms 2,250,000 bytes */ - - /* 1.7Gbps is 212,500,000bytes per second */ - /* We wills sample at 100ms interval */ - /* For 1ms 212500 bytes */ - /* For 100ms 21,250,000 bytes */ - /* For 10ms 2,125,000 bytes */ - - /* 1.6Gbps is 200,000,000bytes per second */ - /* We wills sample at 100ms interval */ - /* For 1ms 200,000 bytes */ - /* For 100ms 20,000,000 bytes */ - /* For 10ms 2,000,000 bytes */ - - if (time_interval < 100) { - if ((bytes_received_2 - mhi_netdev->bytes_received_1) > 22500000) { - ndev->stats.rx_dropped ++; - dev_kfree_skb(skb); - return; - } - } else { - mhi_netdev->first_jiffy = second_jiffy; - mhi_netdev->bytes_received_1 = bytes_received_2; - } - } else { - mhi_netdev->first_jiffy = second_jiffy; - mhi_netdev->bytes_received_1 = bytes_received_2; - } - } else { - #if LINUX_VERSION_CODE < KERNEL_VERSION(5,2,0) - mhi_netdev->first_jiffy = arch_counter_get_cntvct(); - #else - mhi_netdev->first_jiffy = __arch_counter_get_cntvct(); - #endif - - cntfrq = arch_timer_get_cntfrq(); - mhi_netdev->cntfrq_per_msec = cntfrq / 1000; - } - mhi_netdev->bytes_received_2 += mhi_result->bytes_xferd; - } -#endif - -#if 0 - { - static size_t bytes_xferd = 0; - if (mhi_result->bytes_xferd > bytes_xferd) { - bytes_xferd = mhi_result->bytes_xferd; - printk(KERN_DEBUG "bytes_xferd=%zd\n", bytes_xferd); - } - } -#endif - - skb_put(skb, mhi_result->bytes_xferd); - - qmap_hex_dump(__func__, skb->data, skb->len); - - skb_priv->bind_netdev = NULL; - skb_queue_tail(&mhi_netdev->qmap_chain, skb); -} -#endif - -static void mhi_netdev_status_cb(struct mhi_device *mhi_dev, enum MHI_CB mhi_cb) -{ - struct mhi_netdev *mhi_netdev = mhi_device_get_devdata(mhi_dev); - - if (mhi_cb != MHI_CB_PENDING_DATA) - return; - - if (napi_schedule_prep(&mhi_netdev->napi)) { - __napi_schedule(&mhi_netdev->napi); - mhi_netdev->stats.rx_int++; - return; - } -} - -#ifdef CONFIG_DEBUG_FS - -struct dentry *mhi_netdev_debugfs_dentry; - -static int mhi_netdev_init_debugfs_states_show(struct seq_file *m, void *d) -{ - struct mhi_netdev *mhi_netdev = m->private; - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - -#ifdef TS_DEBUG - struct timespec now_ts, diff_ts; - getnstimeofday(&now_ts); - diff_ts = timespec_sub(now_ts, mhi_netdev->diff_ts); - mhi_netdev->diff_ts = now_ts; -#endif - - seq_printf(m, - "tx_tre:%d rx_tre:%d qmap_chain:%u skb_chain:%u tx_allocated:%u rx_allocated:%u\n", - mhi_get_no_free_descriptors(mhi_dev, DMA_TO_DEVICE), - mhi_get_no_free_descriptors(mhi_dev, DMA_FROM_DEVICE), - mhi_netdev->qmap_chain.qlen, - mhi_netdev->skb_chain.qlen, - mhi_netdev->tx_allocated.qlen, - mhi_netdev->rx_allocated.qlen); - - seq_printf(m, - "netif_queue_stopped:%d, link_state:0x%x, flow_control:0x%x\n", - netif_queue_stopped(mhi_netdev->ndev), mhi_netdev->link_state, mhi_netdev->flow_control); - - seq_printf(m, - "rmnet_map_command_stats: %u, %u, %u, %u, %u, %u, %u, %u, %u, %u\n", - mhi_netdev->rmnet_map_command_stats[RMNET_MAP_COMMAND_NONE], - mhi_netdev->rmnet_map_command_stats[RMNET_MAP_COMMAND_FLOW_DISABLE], - mhi_netdev->rmnet_map_command_stats[RMNET_MAP_COMMAND_FLOW_ENABLE], - mhi_netdev->rmnet_map_command_stats[3], - mhi_netdev->rmnet_map_command_stats[4], - mhi_netdev->rmnet_map_command_stats[5], - mhi_netdev->rmnet_map_command_stats[6], - mhi_netdev->rmnet_map_command_stats[RMNET_MAP_COMMAND_FLOW_START], - mhi_netdev->rmnet_map_command_stats[RMNET_MAP_COMMAND_FLOW_END], - mhi_netdev->rmnet_map_command_stats[RMNET_MAP_COMMAND_UNKNOWN]); - -#ifdef TS_DEBUG - seq_printf(m, - "qmap_ts:%ld.%ld, skb_ts:%ld.%ld, diff_ts:%ld.%ld\n", - mhi_netdev->qmap_ts.tv_sec, mhi_netdev->qmap_ts.tv_nsec, - mhi_netdev->skb_ts.tv_sec, mhi_netdev->skb_ts.tv_nsec, - diff_ts.tv_sec, diff_ts.tv_nsec); - mhi_netdev->clear_ts = 1; -#endif - - return 0; -} - -static int mhi_netdev_init_debugfs_states_open(struct inode *inode, - struct file *fp) -{ - return single_open(fp, mhi_netdev_init_debugfs_states_show, inode->i_private); -} - -static const struct file_operations mhi_netdev_debugfs_state_ops = { - .open = mhi_netdev_init_debugfs_states_open, - .release = single_release, - .read = seq_read, -}; - -static int mhi_netdev_debugfs_trigger_reset(void *data, u64 val) -{ - struct mhi_netdev *mhi_netdev = data; - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - int ret; - - MSG_LOG("Triggering channel reset\n"); - - /* disable the interface so no data processing */ - write_lock_irq(&mhi_netdev->pm_lock); - mhi_netdev->enabled = false; - write_unlock_irq(&mhi_netdev->pm_lock); - napi_disable(&mhi_netdev->napi); - - /* disable all hardware channels */ - mhi_unprepare_from_transfer(mhi_dev); - - /* clean up all alocated buffers */ - mhi_netdev_dealloc(mhi_netdev); - - MSG_LOG("Restarting iface\n"); - - ret = mhi_netdev_enable_iface(mhi_netdev); - if (ret) - return ret; - - return 0; -} -DEFINE_SIMPLE_ATTRIBUTE(mhi_netdev_debugfs_trigger_reset_fops, NULL, - mhi_netdev_debugfs_trigger_reset, "%llu\n"); - -static void mhi_netdev_create_debugfs(struct mhi_netdev *mhi_netdev) -{ - char node_name[32]; - int i; - const umode_t mode = 0600; - struct mhi_device *mhi_dev = mhi_netdev->mhi_dev; - struct dentry *dentry = mhi_netdev_debugfs_dentry; - - const struct { - char *name; - u32 *ptr; - } debugfs_table[] = { - { - "rx_int", - &mhi_netdev->stats.rx_int - }, - { - "tx_full", - &mhi_netdev->stats.tx_full - }, - { - "tx_pkts", - &mhi_netdev->stats.tx_pkts - }, - { - "rx_budget_overflow", - &mhi_netdev->stats.rx_budget_overflow - }, - { - "rx_allocated", - &mhi_netdev->stats.rx_allocated - }, - { - "tx_allocated", - &mhi_netdev->stats.tx_allocated - }, - { - "alloc_failed", - &mhi_netdev->stats.alloc_failed - }, - { - NULL, NULL - }, - }; - - /* Both tx & rx client handle contain same device info */ - snprintf(node_name, sizeof(node_name), "%s_%04x_%02u.%02u.%02u_%u", - mhi_netdev->interface_name, mhi_dev->dev_id, mhi_dev->domain, - mhi_dev->bus, mhi_dev->slot, mhi_netdev->alias); - - if (IS_ERR_OR_NULL(dentry)) - return; - - mhi_netdev->dentry = debugfs_create_dir(node_name, dentry); - if (IS_ERR_OR_NULL(mhi_netdev->dentry)) - return; - - debugfs_create_u32("msg_lvl", mode, mhi_netdev->dentry, - (u32 *)&mhi_netdev->msg_lvl); - - /* Add debug stats table */ - for (i = 0; debugfs_table[i].name; i++) { - debugfs_create_u32(debugfs_table[i].name, mode, - mhi_netdev->dentry, - debugfs_table[i].ptr); - } - - debugfs_create_file("reset", mode, mhi_netdev->dentry, mhi_netdev, - &mhi_netdev_debugfs_trigger_reset_fops); - debugfs_create_file("states", 0444, mhi_netdev->dentry, mhi_netdev, - &mhi_netdev_debugfs_state_ops); -} - -static void mhi_netdev_create_debugfs_dir(struct dentry *parent) -{ - mhi_netdev_debugfs_dentry = debugfs_create_dir(MHI_NETDEV_DRIVER_NAME, parent); -} - -#else - -static void mhi_netdev_create_debugfs(struct mhi_netdev *mhi_netdev) -{ -} - -static void mhi_netdev_create_debugfs_dir(struct dentry *parent) -{ -} - -#endif - -static void mhi_netdev_remove(struct mhi_device *mhi_dev) -{ - struct mhi_netdev *mhi_netdev = mhi_device_get_devdata(mhi_dev); - struct sk_buff *skb; - - MSG_LOG("Remove notification received\n"); -#ifndef MHI_NETDEV_ONE_CARD_MODE -#ifndef CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY - - unsigned i; - write_lock_irq(&mhi_netdev->pm_lock); - mhi_netdev->enabled = false; - write_unlock_irq(&mhi_netdev->pm_lock); - - for (i = 0; i < mhi_netdev->qmap_mode; i++) { - if (mhi_netdev->mpQmapNetDev[i] - && mhi_netdev->mpQmapNetDev[i] != mhi_netdev->ndev) { - rmnet_vnd_unregister_device(mhi_netdev->mpQmapNetDev[i]); - } - mhi_netdev->mpQmapNetDev[i] = NULL; - } - - rtnl_lock(); - if (mhi_netdev->ndev - && rtnl_dereference(mhi_netdev->ndev->rx_handler) == rmnet_rx_handler) - netdev_rx_handler_unregister(mhi_netdev->ndev); - rtnl_unlock(); -#endif -#endif - while ((skb = skb_dequeue (&mhi_netdev->skb_chain))) - dev_kfree_skb_any(skb); - while ((skb = skb_dequeue (&mhi_netdev->qmap_chain))) - dev_kfree_skb_any(skb); - while ((skb = skb_dequeue (&mhi_netdev->rx_allocated))) - dev_kfree_skb_any(skb); - while ((skb = skb_dequeue (&mhi_netdev->tx_allocated))) - dev_kfree_skb_any(skb); - - napi_disable(&mhi_netdev->napi); - netif_napi_del(&mhi_netdev->napi); - mhi_netdev_dealloc(mhi_netdev); - unregister_netdev(mhi_netdev->ndev); -#if defined(MHI_NETDEV_STATUS64) - free_percpu(mhi_netdev->stats64); -#endif - free_netdev(mhi_netdev->ndev); - flush_delayed_work(&mhi_netdev->alloc_work); -#ifdef CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY - mhi_netdev_free_pool(mhi_netdev); -#endif - - if (!IS_ERR_OR_NULL(mhi_netdev->dentry)) - debugfs_remove_recursive(mhi_netdev->dentry); -} - -static int mhi_netdev_probe(struct mhi_device *mhi_dev, - const struct mhi_device_id *id) -{ - int ret; - struct mhi_netdev *mhi_netdev; - unsigned i; - - mhi_netdev = devm_kzalloc(&mhi_dev->dev, sizeof(*mhi_netdev), - GFP_KERNEL); - if (!mhi_netdev) - return -ENOMEM; - - if (!strcmp(id->chan, "IP_HW0")) { - if (mhi_mbim_enabled) - mhi_netdev->net_type = MHI_NET_MBIM; - else - mhi_netdev->net_type = MHI_NET_RMNET; - } - else if (!strcmp(id->chan, "IP_SW0")) { - mhi_netdev->net_type = MHI_NET_ETHER; - } - else { - return -EINVAL; - } - - mhi_netdev->alias = 0; - - mhi_netdev->mhi_dev = mhi_dev; - mhi_device_set_devdata(mhi_dev, mhi_netdev); - - mhi_netdev->mru = (15*1024); ///etc/data/qnicorn_config.xml dataformat_agg_dl_size 15*1024 - mhi_netdev->max_mtu = mhi_netdev->mru - (sizeof(struct rmnet_map_v5_csum_header) + sizeof(struct rmnet_map_header)); - if (mhi_netdev->net_type == MHI_NET_MBIM) { - mhi_netdev->mru = ncmNTBParams.dwNtbInMaxSize; - mhi_netdev->mbim_ctx.rx_max = mhi_netdev->mru; - mhi_netdev->max_mtu = mhi_netdev->mru - sizeof(struct mhi_mbim_hdr); - } - else if (mhi_netdev->net_type == MHI_NET_ETHER) { - mhi_netdev->mru = 8*1024; - mhi_netdev->max_mtu = mhi_netdev->mru; - } - mhi_netdev->qmap_size = mhi_netdev->mru; - -#if defined(MHI_NETDEV_STATUS64) - mhi_netdev->stats64 = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); - if (!mhi_netdev->stats64) - return -ENOMEM; -#endif - - if (!strcmp(id->chan, "IP_HW0")) - mhi_netdev->interface_name = "rmnet_mhi"; - else if (!strcmp(id->chan, "IP_SW0")) - mhi_netdev->interface_name = "mhi_swip"; - else - mhi_netdev->interface_name = id->chan; - - mhi_netdev->qmap_mode = qmap_mode; - mhi_netdev->qmap_version = 5; - mhi_netdev->use_rmnet_usb = 1; - if ((mhi_dev->vendor == 0x17cb && mhi_dev->dev_id == 0x0306) - || (mhi_dev->vendor == 0x17cb && mhi_dev->dev_id == 0x0308) - || (mhi_dev->vendor == 0x1eac && mhi_dev->dev_id == 0x1004) - || (mhi_dev->vendor == 0x17cb && mhi_dev->dev_id == 0x011a) - || (mhi_dev->vendor == 0x1eac && mhi_dev->dev_id == 0x100b) - || (mhi_dev->vendor == 0x17cb && mhi_dev->dev_id == 0x0309) - ) { - mhi_netdev->qmap_version = 9; - } - if (mhi_netdev->net_type == MHI_NET_ETHER) { - mhi_netdev->qmap_mode = 1; - mhi_netdev->qmap_version = 0; - mhi_netdev->use_rmnet_usb = 0; - } - - mhi_netdev->mbim_mux_id = 0; - if (mhi_dev->vendor == 0x17cb && mhi_dev->dev_id == 0x0309) { - mhi_netdev->mbim_mux_id = MBIM_MUX_ID_SDX7X; - } - rmnet_info_set(mhi_netdev, &mhi_netdev->rmnet_info); - - mhi_netdev->rx_queue = mhi_netdev_alloc_skb; - - spin_lock_init(&mhi_netdev->rx_lock); - rwlock_init(&mhi_netdev->pm_lock); - INIT_DELAYED_WORK(&mhi_netdev->alloc_work, mhi_netdev_alloc_work); - skb_queue_head_init(&mhi_netdev->qmap_chain); - skb_queue_head_init(&mhi_netdev->skb_chain); - skb_queue_head_init(&mhi_netdev->tx_allocated); - skb_queue_head_init(&mhi_netdev->rx_allocated); - - mhi_netdev->msg_lvl = MHI_MSG_LVL_INFO; - - /* setup network interface */ - ret = mhi_netdev_enable_iface(mhi_netdev); - if (ret) { - pr_err("Error mhi_netdev_enable_iface ret:%d\n", ret); - return ret; - } - - mhi_netdev_create_debugfs(mhi_netdev); - - if (mhi_netdev->net_type == MHI_NET_ETHER) { - mhi_netdev->mpQmapNetDev[0] = mhi_netdev->ndev; - netif_carrier_on(mhi_netdev->ndev); - } -#ifdef MHI_NETDEV_ONE_CARD_MODE - else if (1) { - mhi_netdev->mpQmapNetDev[0] = mhi_netdev->ndev; - strcpy(mhi_netdev->rmnet_info.ifname[0], mhi_netdev->mpQmapNetDev[0]->name); - mhi_netdev->rmnet_info.mux_id[0] = QUECTEL_QMAP_MUX_ID; - if (mhi_mbim_enabled) { - mhi_netdev->rmnet_info.mux_id[0] = mhi_netdev->mbim_mux_id; - } - } -#else - -#ifdef CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY - else if (1) { - BUG_ON(mhi_netdev->net_type != MHI_NET_RMNET); - for (i = 0; i < mhi_netdev->qmap_mode; i++) { - mhi_netdev->rmnet_info.mux_id[i] = QUECTEL_QMAP_MUX_ID + i; - strcpy(mhi_netdev->rmnet_info.ifname[i], "use_rmnet_data"); - } - } -#endif - else if (mhi_netdev->use_rmnet_usb) { - for (i = 0; i < mhi_netdev->qmap_mode; i++) { - u8 mux_id = QUECTEL_QMAP_MUX_ID + i; - if (mhi_mbim_enabled) { - mux_id = mhi_netdev->mbim_mux_id + i; - } - mhi_netdev->mpQmapNetDev[i] = rmnet_vnd_register_device(mhi_netdev, i, mux_id); - if (mhi_netdev->mpQmapNetDev[i]) { - strcpy(mhi_netdev->rmnet_info.ifname[i], mhi_netdev->mpQmapNetDev[i]->name); - mhi_netdev->rmnet_info.mux_id[i] = mux_id; - } - } - - rtnl_lock(); - /* when open hyfi function, run cm will make system crash */ - //netdev_rx_handler_register(mhi_netdev->ndev, rmnet_rx_handler, mhi_netdev); - netdev_rx_handler_register(mhi_netdev->ndev, rmnet_rx_handler, NULL); - rtnl_unlock(); - } - -#if defined(CONFIG_IPQ5018_RATE_CONTROL) - mhi_netdev->mhi_rate_control = 1; -#endif -#endif - - return 0; -} - -static const struct mhi_device_id mhi_netdev_match_table[] = { - { .chan = "IP_HW0" }, - { .chan = "IP_SW0" }, - // ADPL do not register as a netcard. xingduo.du 2023-02-20 - // { .chan = "IP_HW_ADPL" }, - { }, -}; - -static struct mhi_driver mhi_netdev_driver = { - .id_table = mhi_netdev_match_table, - .probe = mhi_netdev_probe, - .remove = mhi_netdev_remove, - .ul_xfer_cb = mhi_netdev_xfer_ul_cb, - .dl_xfer_cb = mhi_netdev_xfer_dl_cb, - .status_cb = mhi_netdev_status_cb, - .driver = { - .name = "mhi_netdev", - .owner = THIS_MODULE, - } -}; - -int __init mhi_device_netdev_init(struct dentry *parent) -{ -#ifdef CONFIG_QCA_NSS_DRV - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (!nss_cb) { - printk(KERN_ERR "mhi_device_netdev_init: driver load must after '/etc/modules.d/42-rmnet-nss'\n"); - } -#endif - - mhi_netdev_create_debugfs_dir(parent); - - return mhi_driver_register(&mhi_netdev_driver); -} - -void mhi_device_netdev_exit(void) -{ -#ifdef CONFIG_DEBUG_FS - debugfs_remove_recursive(mhi_netdev_debugfs_dentry); -#endif - mhi_driver_unregister(&mhi_netdev_driver); -} - -void mhi_netdev_quectel_avoid_unused_function(void) { -#ifdef CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY - qmap_hex_dump(NULL, NULL, 0); - mhi_netdev_ip_type_trans(0); -#else -#endif -} diff --git a/quectel_MHI/src/devices/mhi_satellite.c b/quectel_MHI/src/devices/mhi_satellite.c deleted file mode 100644 index d1071ecbe..000000000 --- a/quectel_MHI/src/devices/mhi_satellite.c +++ /dev/null @@ -1,1153 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2019, The Linux Foundation. All rights reserved.*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MHI_SAT_DRIVER_NAME "mhi_satellite" - -static bool mhi_sat_defer_init = true; /* set by default */ - -/* logging macros */ -#define IPC_LOG_PAGES (10) -#define IPC_LOG_LVL (MHI_MSG_LVL_INFO) -#define KLOG_LVL (MHI_MSG_LVL_ERROR) - -#define MHI_SUBSYS_LOG(fmt, ...) do { \ - if (!subsys) \ - break; \ - if (mhi_sat_driver.klog_lvl <= MHI_MSG_LVL_INFO) \ - pr_info("[I][%s][%s] " fmt, __func__, subsys->name, \ - ##__VA_ARGS__);\ - if (subsys->ipc_log && mhi_sat_driver.ipc_log_lvl <= \ - MHI_MSG_LVL_INFO) \ - ipc_log_string(subsys->ipc_log, "[I][%s] " fmt, __func__, \ - ##__VA_ARGS__); \ -} while (0) - -#define MHI_SAT_LOG(fmt, ...) do { \ - if (!subsys || !sat_cntrl) \ - break; \ - if (mhi_sat_driver.klog_lvl <= MHI_MSG_LVL_INFO) \ - pr_info("[I][%s][%s][%x] " fmt, __func__, subsys->name, \ - sat_cntrl->dev_id, ##__VA_ARGS__);\ - if (subsys->ipc_log && mhi_sat_driver.ipc_log_lvl <= \ - MHI_MSG_LVL_INFO) \ - ipc_log_string(subsys->ipc_log, "[I][%s][%x] " fmt, __func__, \ - sat_cntrl->dev_id, ##__VA_ARGS__); \ -} while (0) - -#define MHI_SAT_ERR(fmt, ...) do { \ - if (!subsys || !sat_cntrl) \ - break; \ - if (mhi_sat_driver.klog_lvl <= MHI_MSG_LVL_ERROR) \ - pr_err("[E][%s][%s][%x] " fmt, __func__, subsys->name, \ - sat_cntrl->dev_id, ##__VA_ARGS__); \ - if (subsys->ipc_log && mhi_sat_driver.ipc_log_lvl <= \ - MHI_MSG_LVL_ERROR) \ - ipc_log_string(subsys->ipc_log, "[E][%s][%x] " fmt, __func__, \ - sat_cntrl->dev_id, ##__VA_ARGS__); \ -} while (0) - -#define MHI_SAT_ASSERT(cond, msg) do { \ - if (cond) \ - panic(msg); \ -} while (0) - -/* mhi sys error command */ -#define MHI_TRE_CMD_SYS_ERR_PTR (0) -#define MHI_TRE_CMD_SYS_ERR_D0 (0) -#define MHI_TRE_CMD_SYS_ERR_D1 (MHI_PKT_TYPE_SYS_ERR_CMD << 16) - -/* mhi state change event */ -#define MHI_TRE_EVT_MHI_STATE_PTR (0) -#define MHI_TRE_EVT_MHI_STATE_D0(state) (state << 24) -#define MHI_TRE_EVT_MHI_STATE_D1 (MHI_PKT_TYPE_STATE_CHANGE_EVENT << 16) - -/* mhi exec env change event */ -#define MHI_TRE_EVT_EE_PTR (0) -#define MHI_TRE_EVT_EE_D0(ee) (ee << 24) -#define MHI_TRE_EVT_EE_D1 (MHI_PKT_TYPE_EE_EVENT << 16) - -/* mhi config event */ -#define MHI_TRE_EVT_CFG_PTR(base_addr) (base_addr) -#define MHI_TRE_EVT_CFG_D0(er_base, num) ((er_base << 16) | (num & 0xFFFF)) -#define MHI_TRE_EVT_CFG_D1 (MHI_PKT_TYPE_CFG_EVENT << 16) - -/* command completion event */ -#define MHI_TRE_EVT_CMD_COMPLETION_PTR(ptr) (ptr) -#define MHI_TRE_EVT_CMD_COMPLETION_D0(code) (code << 24) -#define MHI_TRE_EVT_CMD_COMPLETION_D1 (MHI_PKT_TYPE_CMD_COMPLETION_EVENT << 16) - -/* packet parser macros */ -#define MHI_TRE_GET_PTR(tre) ((tre)->ptr) -#define MHI_TRE_GET_SIZE(tre) ((tre)->dword[0]) -#define MHI_TRE_GET_CCS(tre) (((tre)->dword[0] >> 24) & 0xFF) -#define MHI_TRE_GET_ID(tre) (((tre)->dword[1] >> 24) & 0xFF) -#define MHI_TRE_GET_TYPE(tre) (((tre)->dword[1] >> 16) & 0xFF) -#define MHI_TRE_IS_ER_CTXT_TYPE(tre) (((tre)->dword[1]) & 0x1) - -/* creates unique device ID based on connection topology */ -#define MHI_SAT_CREATE_DEVICE_ID(dev, domain, bus, slot) \ - ((dev & 0xFFFF) << 16 | (domain & 0xF) << 12 | (bus & 0xFF) << 4 | \ - (slot & 0xF)) - -/* mhi core definitions */ -#define MHI_CTXT_TYPE_GENERIC (0xA) - -struct __packed mhi_generic_ctxt { - u32 reserved0; - u32 type; - u32 reserved1; - u64 ctxt_base; - u64 ctxt_size; - u64 reserved[2]; -}; - -enum mhi_pkt_type { - MHI_PKT_TYPE_INVALID = 0x0, - MHI_PKT_TYPE_RESET_CHAN_CMD = 0x10, - MHI_PKT_TYPE_STOP_CHAN_CMD = 0x11, - MHI_PKT_TYPE_START_CHAN_CMD = 0x12, - MHI_PKT_TYPE_STATE_CHANGE_EVENT = 0x20, - MHI_PKT_TYPE_CMD_COMPLETION_EVENT = 0x21, - MHI_PKT_TYPE_EE_EVENT = 0x40, - MHI_PKT_TYPE_CTXT_UPDATE_CMD = 0x64, - MHI_PKT_TYPE_IOMMU_MAP_CMD = 0x65, - MHI_PKT_TYPE_CFG_EVENT = 0x6E, - MHI_PKT_TYPE_SYS_ERR_CMD = 0xFF, -}; - -enum mhi_cmd_type { - MHI_CMD_TYPE_RESET = 0x10, - MHI_CMD_TYPE_STOP = 0x11, - MHI_CMD_TYPE_START = 0x12, -}; - -/* mhi event completion codes */ -enum mhi_ev_ccs { - MHI_EV_CC_INVALID = 0x0, - MHI_EV_CC_SUCCESS = 0x1, - MHI_EV_CC_BAD_TRE = 0x11, -}; - -/* satellite subsystem definitions */ -enum subsys_id { - SUBSYS_ADSP, - SUBSYS_CDSP, - SUBSYS_SLPI, - SUBSYS_MODEM, - SUBSYS_MAX, -}; - -static const char * const subsys_names[SUBSYS_MAX] = { - [SUBSYS_ADSP] = "adsp", - [SUBSYS_CDSP] = "cdsp", - [SUBSYS_SLPI] = "slpi", - [SUBSYS_MODEM] = "modem", -}; - -struct mhi_sat_subsys { - const char *name; - - struct rpmsg_device *rpdev; /* rpmsg device */ - - /* - * acquire either mutex or spinlock to walk controller list - * acquire both when modifying list - */ - struct list_head cntrl_list; /* controllers list */ - struct mutex cntrl_mutex; /* mutex to walk/modify controllers list */ - spinlock_t cntrl_lock; /* lock to walk/modify controllers list */ - - void *ipc_log; -}; - -/* satellite IPC definitions */ -#define SAT_MAJOR_VERSION (1) -#define SAT_MINOR_VERSION (0) -#define SAT_RESERVED_SEQ_NUM (0xFFFF) -#define SAT_MSG_SIZE(n) (sizeof(struct sat_header) + \ - (n * sizeof(struct sat_tre))) -#define SAT_TRE_SIZE(msg_size) (msg_size - sizeof(struct sat_header)) -#define SAT_TRE_OFFSET(msg) (msg + sizeof(struct sat_header)) -#define SAT_TRE_NUM_PKTS(payload_size) ((payload_size) / sizeof(struct sat_tre)) - -/* satellite IPC msg type */ -enum sat_msg_id { - SAT_MSG_ID_ACK = 0xA, - SAT_MSG_ID_CMD = 0xC, - SAT_MSG_ID_EVT = 0xE, -}; - -/* satellite IPC context type */ -enum sat_ctxt_type { - SAT_CTXT_TYPE_CHAN = 0x0, - SAT_CTXT_TYPE_EVENT = 0x1, - SAT_CTXT_TYPE_MAX, -}; - -/* satellite IPC context string */ -#define TO_SAT_CTXT_TYPE_STR(type) (type >= SAT_CTXT_TYPE_MAX ? "INVALID" : \ - sat_ctxt_str[type]) - -const char * const sat_ctxt_str[SAT_CTXT_TYPE_MAX] = { - [SAT_CTXT_TYPE_CHAN] = "CCA", - [SAT_CTXT_TYPE_EVENT] = "ECA", -}; - -/* satellite IPC transfer ring element */ -struct __packed sat_tre { - u64 ptr; - u32 dword[2]; -}; - -/* satellite IPC header */ -struct __packed sat_header { - u16 major_ver; - u16 minor_ver; - u16 msg_id; - u16 seq; - u16 reply_seq; - u16 payload_size; - u32 dev_id; - u8 reserved[8]; -}; - -/* satellite driver definitions */ -struct mhi_sat_packet { - struct list_head node; - - struct mhi_sat_cntrl *cntrl; /* satellite controller reference */ - void *msg; /* incoming message */ -}; - -struct mhi_sat_cntrl { - struct list_head node; - - struct mhi_controller *mhi_cntrl; /* device MHI controller reference */ - struct mhi_sat_subsys *subsys; - - struct list_head dev_list; - struct list_head addr_map_list; /* IOMMU mapped addresses list */ - struct mutex list_mutex; /* mutex for devices and address map lists */ - - struct list_head packet_list; - spinlock_t pkt_lock; /* lock to walk/modify received packets list */ - - struct work_struct connect_work; /* subsystem connection worker */ - struct work_struct process_work; /* incoming packets processor */ - - /* mhi core/controller configurations */ - u32 dev_id; /* unique device ID with BDF as per connection topology */ - int er_base; /* event rings base index */ - int er_max; /* event rings max index */ - int num_er; /* total number of event rings */ - - /* satellite controller function counts */ - int num_devices; /* mhi devices current count */ - int max_devices; /* count of maximum devices for subsys/controller */ - u16 seq; /* internal sequence number for all outgoing packets */ - bool active; /* flag set if hello packet/MHI_CFG event was sent */ - - /* command completion variables */ - u16 last_cmd_seq; /* sequence number of last sent command packet */ - enum mhi_ev_ccs last_cmd_ccs; /* last command completion event code */ - struct completion completion; /* command completion event wait */ - struct mutex cmd_wait_mutex; /* command completion wait mutex */ -}; - -struct mhi_sat_device { - struct list_head node; - - struct mhi_device *mhi_dev; /* mhi device pointer */ - struct mhi_sat_cntrl *cntrl; /* parent controller */ - - bool chan_started; -}; - -struct mhi_sat_driver { - enum MHI_DEBUG_LEVEL ipc_log_lvl; /* IPC log level */ - enum MHI_DEBUG_LEVEL klog_lvl; /* klog/dmesg levels */ - - struct mhi_sat_subsys *subsys; /* pointer to subsystem array */ - unsigned int num_subsys; - - struct dentry *dentry; /* debugfs directory */ - bool deferred_init_done; /* flag for deferred init protection */ -}; - -static struct mhi_sat_driver mhi_sat_driver; - -static struct mhi_sat_subsys *find_subsys_by_name(const char *name) -{ - int i; - struct mhi_sat_subsys *subsys = mhi_sat_driver.subsys; - - for (i = 0; i < mhi_sat_driver.num_subsys; i++, subsys++) { - if (!strcmp(name, subsys->name)) - return subsys; - } - - return NULL; -} - -static struct mhi_sat_cntrl *find_sat_cntrl_by_id(struct mhi_sat_subsys *subsys, - u32 dev_id) -{ - struct mhi_sat_cntrl *sat_cntrl; - unsigned long flags; - - spin_lock_irqsave(&subsys->cntrl_lock, flags); - list_for_each_entry(sat_cntrl, &subsys->cntrl_list, node) { - if (sat_cntrl->dev_id == dev_id) { - spin_unlock_irqrestore(&subsys->cntrl_lock, flags); - return sat_cntrl; - } - } - spin_unlock_irqrestore(&subsys->cntrl_lock, flags); - - return NULL; -} - -static struct mhi_sat_device *find_sat_dev_by_id( - struct mhi_sat_cntrl *sat_cntrl, int id, - enum sat_ctxt_type evt) -{ - struct mhi_sat_device *sat_dev; - int compare_id; - - mutex_lock(&sat_cntrl->list_mutex); - list_for_each_entry(sat_dev, &sat_cntrl->dev_list, node) { - compare_id = (evt == SAT_CTXT_TYPE_EVENT) ? - sat_dev->mhi_dev->dl_event_id : - sat_dev->mhi_dev->dl_chan_id; - - if (compare_id == id) { - mutex_unlock(&sat_cntrl->list_mutex); - return sat_dev; - } - } - mutex_unlock(&sat_cntrl->list_mutex); - - return NULL; -} - -static bool mhi_sat_isvalid_header(struct sat_header *hdr, int len) -{ - /* validate payload size */ - if (len >= sizeof(*hdr) && (len != hdr->payload_size + sizeof(*hdr))) - return false; - - /* validate SAT IPC version */ - if (hdr->major_ver != SAT_MAJOR_VERSION && - hdr->minor_ver != SAT_MINOR_VERSION) - return false; - - /* validate msg ID */ - if (hdr->msg_id != SAT_MSG_ID_CMD && hdr->msg_id != SAT_MSG_ID_EVT) - return false; - - return true; -} - -static int mhi_sat_wait_cmd_completion(struct mhi_sat_cntrl *sat_cntrl) -{ - struct mhi_sat_subsys *subsys = sat_cntrl->subsys; - int ret; - - reinit_completion(&sat_cntrl->completion); - - MHI_SAT_LOG("Wait for command completion\n"); - ret = wait_for_completion_timeout(&sat_cntrl->completion, - msecs_to_jiffies(sat_cntrl->mhi_cntrl->timeout_ms)); - if (!ret || sat_cntrl->last_cmd_ccs != MHI_EV_CC_SUCCESS) { - MHI_SAT_ERR("Command completion failure:seq:%u:ret:%d:ccs:%d\n", - sat_cntrl->last_cmd_seq, ret, sat_cntrl->last_cmd_ccs); - return -EIO; - } - - MHI_SAT_LOG("Command completion successful for seq:%u\n", - sat_cntrl->last_cmd_seq); - - return 0; -} - -static int mhi_sat_send_msg(struct mhi_sat_cntrl *sat_cntrl, - enum sat_msg_id type, u16 reply_seq, - void *msg, u16 msg_size) -{ - struct mhi_sat_subsys *subsys = sat_cntrl->subsys; - struct sat_header *hdr = msg; - - /* create sequence number for controller */ - sat_cntrl->seq++; - if (sat_cntrl->seq == SAT_RESERVED_SEQ_NUM) - sat_cntrl->seq = 0; - - /* populate header */ - hdr->major_ver = SAT_MAJOR_VERSION; - hdr->minor_ver = SAT_MINOR_VERSION; - hdr->msg_id = type; - hdr->seq = sat_cntrl->seq; - hdr->reply_seq = reply_seq; - hdr->payload_size = SAT_TRE_SIZE(msg_size); - hdr->dev_id = sat_cntrl->dev_id; - - /* save last sent command sequence number for completion event */ - if (type == SAT_MSG_ID_CMD) - sat_cntrl->last_cmd_seq = sat_cntrl->seq; - - return rpmsg_send(subsys->rpdev->ept, msg, msg_size); -} - -static void mhi_sat_process_cmds(struct mhi_sat_cntrl *sat_cntrl, - struct sat_header *hdr, struct sat_tre *pkt) -{ - struct mhi_sat_subsys *subsys = sat_cntrl->subsys; - int num_pkts = SAT_TRE_NUM_PKTS(hdr->payload_size), i; - - for (i = 0; i < num_pkts; i++, pkt++) { - enum mhi_ev_ccs code = MHI_EV_CC_INVALID; - - switch (MHI_TRE_GET_TYPE(pkt)) { - case MHI_PKT_TYPE_IOMMU_MAP_CMD: - { - struct mhi_buf *buf; - struct mhi_controller *mhi_cntrl = sat_cntrl->mhi_cntrl; - dma_addr_t iova = DMA_ERROR_CODE; - - buf = kmalloc(sizeof(*buf), GFP_ATOMIC); - if (!buf) - goto iommu_map_cmd_completion; - - buf->phys_addr = MHI_TRE_GET_PTR(pkt); - buf->len = MHI_TRE_GET_SIZE(pkt); - - iova = dma_map_resource(mhi_cntrl->dev, buf->phys_addr, - buf->len, DMA_BIDIRECTIONAL, 0); - if (dma_mapping_error(mhi_cntrl->dev, iova)) { - kfree(buf); - goto iommu_map_cmd_completion; - } - - buf->dma_addr = iova; - - mutex_lock(&sat_cntrl->list_mutex); - list_add_tail(&buf->node, - &sat_cntrl->addr_map_list); - mutex_unlock(&sat_cntrl->list_mutex); - - code = MHI_EV_CC_SUCCESS; - -iommu_map_cmd_completion: - MHI_SAT_LOG("IOMMU MAP 0x%llx CMD processing %s\n", - MHI_TRE_GET_PTR(pkt), - (code == MHI_EV_CC_SUCCESS) ? "successful" : - "failed"); - - pkt->ptr = MHI_TRE_EVT_CMD_COMPLETION_PTR(iova); - pkt->dword[0] = MHI_TRE_EVT_CMD_COMPLETION_D0(code); - pkt->dword[1] = MHI_TRE_EVT_CMD_COMPLETION_D1; - break; - } - case MHI_PKT_TYPE_CTXT_UPDATE_CMD: - { - u64 ctxt_ptr = MHI_TRE_GET_PTR(pkt); - u64 ctxt_size = MHI_TRE_GET_SIZE(pkt); - int id = MHI_TRE_GET_ID(pkt); - enum sat_ctxt_type evt = MHI_TRE_IS_ER_CTXT_TYPE(pkt); - struct mhi_generic_ctxt gen_ctxt; - struct mhi_buf buf; - struct mhi_sat_device *sat_dev = find_sat_dev_by_id( - sat_cntrl, id, evt); - int ret; - - MHI_SAT_ASSERT(!sat_dev, - "No device with given chan/evt ID"); - - memset(&gen_ctxt, 0, sizeof(gen_ctxt)); - memset(&buf, 0, sizeof(buf)); - - gen_ctxt.type = MHI_CTXT_TYPE_GENERIC; - gen_ctxt.ctxt_base = ctxt_ptr; - gen_ctxt.ctxt_size = ctxt_size; - - buf.buf = &gen_ctxt; - buf.len = sizeof(gen_ctxt); - buf.name = TO_SAT_CTXT_TYPE_STR(evt); - - ret = mhi_device_configure(sat_dev->mhi_dev, - DMA_BIDIRECTIONAL, &buf, 1); - if (!ret) - code = MHI_EV_CC_SUCCESS; - - MHI_SAT_LOG("CTXT UPDATE CMD %s:%d processing %s\n", - buf.name, id, (code == MHI_EV_CC_SUCCESS) ? - "successful" : "failed"); - - pkt->ptr = MHI_TRE_EVT_CMD_COMPLETION_PTR(0); - pkt->dword[0] = MHI_TRE_EVT_CMD_COMPLETION_D0(code); - pkt->dword[1] = MHI_TRE_EVT_CMD_COMPLETION_D1; - break; - } - case MHI_PKT_TYPE_START_CHAN_CMD: - { - int id = MHI_TRE_GET_ID(pkt); - struct mhi_sat_device *sat_dev = find_sat_dev_by_id( - sat_cntrl, id, - SAT_CTXT_TYPE_CHAN); - int ret; - - MHI_SAT_ASSERT(!sat_dev, - "No device with given channel ID\n"); - - MHI_SAT_ASSERT(sat_dev->chan_started, - "Channel already started!"); - - ret = mhi_prepare_for_transfer(sat_dev->mhi_dev); - if (!ret) { - sat_dev->chan_started = true; - code = MHI_EV_CC_SUCCESS; - } - - MHI_SAT_LOG("START CHANNEL %d CMD processing %s\n", - id, (code == MHI_EV_CC_SUCCESS) ? "successful" : - "failure"); - - pkt->ptr = MHI_TRE_EVT_CMD_COMPLETION_PTR(0); - pkt->dword[0] = MHI_TRE_EVT_CMD_COMPLETION_D0(code); - pkt->dword[1] = MHI_TRE_EVT_CMD_COMPLETION_D1; - break; - } - case MHI_PKT_TYPE_RESET_CHAN_CMD: - { - int id = MHI_TRE_GET_ID(pkt); - struct mhi_sat_device *sat_dev = - find_sat_dev_by_id(sat_cntrl, id, - SAT_CTXT_TYPE_CHAN); - - MHI_SAT_ASSERT(!sat_dev, - "No device with given channel ID\n"); - - MHI_SAT_ASSERT(!sat_dev->chan_started, - "Resetting unstarted channel!"); - - mhi_unprepare_from_transfer(sat_dev->mhi_dev); - sat_dev->chan_started = false; - - MHI_SAT_LOG( - "RESET CHANNEL %d CMD processing successful\n", - id); - - pkt->ptr = MHI_TRE_EVT_CMD_COMPLETION_PTR(0); - pkt->dword[0] = MHI_TRE_EVT_CMD_COMPLETION_D0( - MHI_EV_CC_SUCCESS); - pkt->dword[1] = MHI_TRE_EVT_CMD_COMPLETION_D1; - break; - } - default: - MHI_SAT_ASSERT(1, "Unhandled command!"); - break; - } - } -} - -static void mhi_sat_process_worker(struct work_struct *work) -{ - struct mhi_sat_cntrl *sat_cntrl = container_of(work, - struct mhi_sat_cntrl, process_work); - struct mhi_sat_subsys *subsys = sat_cntrl->subsys; - struct mhi_sat_packet *packet, *tmp; - struct sat_header *hdr; - struct sat_tre *pkt; - LIST_HEAD(head); - - MHI_SAT_LOG("Entered\n"); - - spin_lock_irq(&sat_cntrl->pkt_lock); - list_splice_tail_init(&sat_cntrl->packet_list, &head); - spin_unlock_irq(&sat_cntrl->pkt_lock); - - list_for_each_entry_safe(packet, tmp, &head, node) { - hdr = packet->msg; - pkt = SAT_TRE_OFFSET(packet->msg); - - list_del(&packet->node); - - mhi_sat_process_cmds(sat_cntrl, hdr, pkt); - - /* send response event(s) */ - mhi_sat_send_msg(sat_cntrl, SAT_MSG_ID_EVT, hdr->seq, - packet->msg, - SAT_MSG_SIZE(SAT_TRE_NUM_PKTS( - hdr->payload_size))); - - kfree(packet); - } - - MHI_SAT_LOG("Exited\n"); -} - -static void mhi_sat_connect_worker(struct work_struct *work) -{ - struct mhi_sat_cntrl *sat_cntrl = container_of(work, - struct mhi_sat_cntrl, connect_work); - struct mhi_sat_subsys *subsys = sat_cntrl->subsys; - struct sat_tre *pkt; - void *msg; - int ret; - - if (!subsys->rpdev || sat_cntrl->max_devices != sat_cntrl->num_devices - || sat_cntrl->active) - return; - - MHI_SAT_LOG("Entered\n"); - - msg = kmalloc(SAT_MSG_SIZE(3), GFP_ATOMIC); - if (!msg) - return; - - sat_cntrl->active = true; - - pkt = SAT_TRE_OFFSET(msg); - - /* prepare #1 MHI_CFG HELLO event */ - pkt->ptr = MHI_TRE_EVT_CFG_PTR(sat_cntrl->mhi_cntrl->base_addr); - pkt->dword[0] = MHI_TRE_EVT_CFG_D0(sat_cntrl->er_base, - sat_cntrl->num_er); - pkt->dword[1] = MHI_TRE_EVT_CFG_D1; - pkt++; - - /* prepare M0 event */ - pkt->ptr = MHI_TRE_EVT_MHI_STATE_PTR; - pkt->dword[0] = MHI_TRE_EVT_MHI_STATE_D0(MHI_STATE_M0); - pkt->dword[1] = MHI_TRE_EVT_MHI_STATE_D1; - pkt++; - - /* prepare AMSS event */ - pkt->ptr = MHI_TRE_EVT_EE_PTR; - pkt->dword[0] = MHI_TRE_EVT_EE_D0(MHI_EE_AMSS); - pkt->dword[1] = MHI_TRE_EVT_EE_D1; - - ret = mhi_sat_send_msg(sat_cntrl, SAT_MSG_ID_EVT, SAT_RESERVED_SEQ_NUM, - msg, SAT_MSG_SIZE(3)); - kfree(msg); - if (ret) { - MHI_SAT_ERR("Failed to send hello packet:%d\n", ret); - sat_cntrl->active = false; - return; - } - - MHI_SAT_LOG("Device 0x%x sent hello packet\n", sat_cntrl->dev_id); -} - -static void mhi_sat_process_events(struct mhi_sat_cntrl *sat_cntrl, - struct sat_header *hdr, struct sat_tre *pkt) -{ - int num_pkts = SAT_TRE_NUM_PKTS(hdr->payload_size); - int i; - - for (i = 0; i < num_pkts; i++, pkt++) { - if (MHI_TRE_GET_TYPE(pkt) == - MHI_PKT_TYPE_CMD_COMPLETION_EVENT) { - if (hdr->reply_seq != sat_cntrl->last_cmd_seq) - continue; - - sat_cntrl->last_cmd_ccs = MHI_TRE_GET_CCS(pkt); - complete(&sat_cntrl->completion); - } - } -} - -static int mhi_sat_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, - void *priv, u32 src) -{ - struct mhi_sat_subsys *subsys = dev_get_drvdata(&rpdev->dev); - struct sat_header *hdr = data; - struct sat_tre *pkt = SAT_TRE_OFFSET(data); - struct mhi_sat_cntrl *sat_cntrl; - struct mhi_sat_packet *packet; - - MHI_SAT_ASSERT(!mhi_sat_isvalid_header(hdr, len), "Invalid header!\n"); - - /* find controller packet was sent for */ - sat_cntrl = find_sat_cntrl_by_id(subsys, hdr->dev_id); - - MHI_SAT_ASSERT(!sat_cntrl, "Packet for unknown device!\n"); - - /* handle events directly regardless of controller active state */ - if (hdr->msg_id == SAT_MSG_ID_EVT) { - mhi_sat_process_events(sat_cntrl, hdr, pkt); - return 0; - } - - /* Inactive controller cannot process incoming commands */ - if (unlikely(!sat_cntrl->active)) { - MHI_SAT_ERR("Message for inactive controller!\n"); - return 0; - } - - /* offload commands to process worker */ - packet = kmalloc(sizeof(*packet) + len, GFP_ATOMIC); - if (!packet) - return 0; - - packet->cntrl = sat_cntrl; - packet->msg = packet + 1; - memcpy(packet->msg, data, len); - - spin_lock_irq(&sat_cntrl->pkt_lock); - list_add_tail(&packet->node, &sat_cntrl->packet_list); - spin_unlock_irq(&sat_cntrl->pkt_lock); - - schedule_work(&sat_cntrl->process_work); - - return 0; -} - -static void mhi_sat_rpmsg_remove(struct rpmsg_device *rpdev) -{ - struct mhi_sat_subsys *subsys = dev_get_drvdata(&rpdev->dev); - struct mhi_sat_cntrl *sat_cntrl; - struct mhi_sat_device *sat_dev; - struct mhi_buf *buf, *tmp; - - MHI_SUBSYS_LOG("Enter\n"); - - /* unprepare each controller/device from transfer */ - mutex_lock(&subsys->cntrl_mutex); - list_for_each_entry(sat_cntrl, &subsys->cntrl_list, node) { - sat_cntrl->active = false; - - flush_work(&sat_cntrl->connect_work); - flush_work(&sat_cntrl->process_work); - - mutex_lock(&sat_cntrl->list_mutex); - list_for_each_entry(sat_dev, &sat_cntrl->dev_list, node) { - if (sat_dev->chan_started) { - mhi_unprepare_from_transfer(sat_dev->mhi_dev); - sat_dev->chan_started = false; - } - } - - list_for_each_entry_safe(buf, tmp, &sat_cntrl->addr_map_list, - node) { - dma_unmap_resource(sat_cntrl->mhi_cntrl->dev, - buf->dma_addr, buf->len, - DMA_BIDIRECTIONAL, 0); - list_del(&buf->node); - kfree(buf); - } - mutex_unlock(&sat_cntrl->list_mutex); - - MHI_SAT_LOG("Removed RPMSG link\n"); - } - mutex_unlock(&subsys->cntrl_mutex); - - subsys->rpdev = NULL; -} - -static int mhi_sat_rpmsg_probe(struct rpmsg_device *rpdev) -{ - struct mhi_sat_subsys *subsys; - struct mhi_sat_cntrl *sat_cntrl; - const char *subsys_name; - int ret; - - ret = of_property_read_string(rpdev->dev.parent->of_node, "label", - &subsys_name); - if (ret) - return ret; - - /* find which subsystem has probed */ - subsys = find_subsys_by_name(subsys_name); - if (!subsys) - return -EINVAL; - - MHI_SUBSYS_LOG("Received RPMSG probe\n"); - - dev_set_drvdata(&rpdev->dev, subsys); - - subsys->rpdev = rpdev; - - /* schedule work for each controller as GLINK has connected */ - spin_lock_irq(&subsys->cntrl_lock); - list_for_each_entry(sat_cntrl, &subsys->cntrl_list, node) - schedule_work(&sat_cntrl->connect_work); - spin_unlock_irq(&subsys->cntrl_lock); - - return 0; -} - -static struct rpmsg_device_id mhi_sat_rpmsg_match_table[] = { - { .name = "mhi_sat" }, - { }, -}; - -static struct rpmsg_driver mhi_sat_rpmsg_driver = { - .id_table = mhi_sat_rpmsg_match_table, - .probe = mhi_sat_rpmsg_probe, - .remove = mhi_sat_rpmsg_remove, - .callback = mhi_sat_rpmsg_cb, - .drv = { - .name = "mhi,sat_rpmsg", - }, -}; - -static void mhi_sat_dev_status_cb(struct mhi_device *mhi_dev, - enum MHI_CB mhi_cb) -{ -} - -static void mhi_sat_dev_remove(struct mhi_device *mhi_dev) -{ - struct mhi_sat_device *sat_dev = mhi_device_get_devdata(mhi_dev); - struct mhi_sat_cntrl *sat_cntrl = sat_dev->cntrl; - struct mhi_sat_subsys *subsys = sat_cntrl->subsys; - struct mhi_buf *buf, *tmp; - struct sat_tre *pkt; - void *msg; - int ret; - - /* remove device node from probed list */ - mutex_lock(&sat_cntrl->list_mutex); - list_del(&sat_dev->node); - mutex_unlock(&sat_cntrl->list_mutex); - - sat_cntrl->num_devices--; - - /* prepare SYS_ERR command if first device is being removed */ - if (sat_cntrl->active) { - sat_cntrl->active = false; - - /* flush all pending work */ - flush_work(&sat_cntrl->connect_work); - flush_work(&sat_cntrl->process_work); - - msg = kmalloc(SAT_MSG_SIZE(1), GFP_KERNEL); - - MHI_SAT_ASSERT(!msg, "Unable to malloc for SYS_ERR message!\n"); - - pkt = SAT_TRE_OFFSET(msg); - pkt->ptr = MHI_TRE_CMD_SYS_ERR_PTR; - pkt->dword[0] = MHI_TRE_CMD_SYS_ERR_D0; - pkt->dword[1] = MHI_TRE_CMD_SYS_ERR_D1; - - /* acquire cmd_wait_mutex before sending command */ - mutex_lock(&sat_cntrl->cmd_wait_mutex); - - ret = mhi_sat_send_msg(sat_cntrl, SAT_MSG_ID_CMD, - SAT_RESERVED_SEQ_NUM, msg, - SAT_MSG_SIZE(1)); - kfree(msg); - if (ret) { - MHI_SAT_ERR("Failed to notify SYS_ERR\n"); - mutex_unlock(&sat_cntrl->cmd_wait_mutex); - goto exit_sys_err_send; - } - - MHI_SAT_LOG("SYS_ERR command sent\n"); - - /* blocking call to wait for command completion event */ - mhi_sat_wait_cmd_completion(sat_cntrl); - - mutex_unlock(&sat_cntrl->cmd_wait_mutex); - } - -exit_sys_err_send: - /* exit if some devices are still present */ - if (sat_cntrl->num_devices) - return; - - /* remove address mappings */ - mutex_lock(&sat_cntrl->list_mutex); - list_for_each_entry_safe(buf, tmp, &sat_cntrl->addr_map_list, node) { - dma_unmap_resource(sat_cntrl->mhi_cntrl->dev, buf->dma_addr, - buf->len, DMA_BIDIRECTIONAL, 0); - list_del(&buf->node); - kfree(buf); - } - mutex_unlock(&sat_cntrl->list_mutex); - - /* remove controller */ - mutex_lock(&subsys->cntrl_mutex); - spin_lock_irq(&subsys->cntrl_lock); - list_del(&sat_cntrl->node); - spin_unlock_irq(&subsys->cntrl_lock); - mutex_unlock(&subsys->cntrl_mutex); - - mutex_destroy(&sat_cntrl->cmd_wait_mutex); - mutex_destroy(&sat_cntrl->list_mutex); - MHI_SAT_LOG("Satellite controller node removed\n"); - kfree(sat_cntrl); -} - -static int mhi_sat_dev_probe(struct mhi_device *mhi_dev, - const struct mhi_device_id *id) -{ - struct mhi_sat_device *sat_dev; - struct mhi_sat_cntrl *sat_cntrl; - struct device_node *of_node = mhi_dev->dev.of_node; - struct mhi_sat_subsys *subsys = &mhi_sat_driver.subsys[id->driver_data]; - u32 dev_id = MHI_SAT_CREATE_DEVICE_ID(mhi_dev->dev_id, mhi_dev->domain, - mhi_dev->bus, mhi_dev->slot); - int ret; - - /* find controller with unique device ID based on topology */ - sat_cntrl = find_sat_cntrl_by_id(subsys, dev_id); - if (!sat_cntrl) { - sat_cntrl = kzalloc(sizeof(*sat_cntrl), GFP_KERNEL); - if (!sat_cntrl) - return -ENOMEM; - - /* - * max_devices will be read from device tree node. Set it to - * -1 before it is populated to avoid false positive when - * RPMSG probe schedules connect worker but no device has - * probed in which case num_devices and max_devices are both - * zero. - */ - sat_cntrl->max_devices = -1; - sat_cntrl->dev_id = dev_id; - sat_cntrl->er_base = mhi_dev->dl_event_id; - sat_cntrl->mhi_cntrl = mhi_dev->mhi_cntrl; - sat_cntrl->last_cmd_seq = SAT_RESERVED_SEQ_NUM; - sat_cntrl->subsys = subsys; - init_completion(&sat_cntrl->completion); - mutex_init(&sat_cntrl->list_mutex); - mutex_init(&sat_cntrl->cmd_wait_mutex); - spin_lock_init(&sat_cntrl->pkt_lock); - INIT_WORK(&sat_cntrl->connect_work, mhi_sat_connect_worker); - INIT_WORK(&sat_cntrl->process_work, mhi_sat_process_worker); - INIT_LIST_HEAD(&sat_cntrl->dev_list); - INIT_LIST_HEAD(&sat_cntrl->addr_map_list); - INIT_LIST_HEAD(&sat_cntrl->packet_list); - - mutex_lock(&subsys->cntrl_mutex); - spin_lock_irq(&subsys->cntrl_lock); - list_add(&sat_cntrl->node, &subsys->cntrl_list); - spin_unlock_irq(&subsys->cntrl_lock); - mutex_unlock(&subsys->cntrl_mutex); - - MHI_SAT_LOG("Controller allocated for 0x%x\n", dev_id); - } - - /* set maximum devices for subsystem from device tree */ - if (of_node) { - ret = of_property_read_u32(of_node, "mhi,max-devices", - &sat_cntrl->max_devices); - if (ret) { - MHI_SAT_ERR("Could not find max-devices in DT node\n"); - return -EINVAL; - } - } - - /* get event ring base and max indexes */ - sat_cntrl->er_base = min(sat_cntrl->er_base, mhi_dev->dl_event_id); - sat_cntrl->er_max = max(sat_cntrl->er_base, mhi_dev->dl_event_id); - - sat_dev = devm_kzalloc(&mhi_dev->dev, sizeof(*sat_dev), GFP_KERNEL); - if (!sat_dev) - return -ENOMEM; - - sat_dev->mhi_dev = mhi_dev; - sat_dev->cntrl = sat_cntrl; - - mutex_lock(&sat_cntrl->list_mutex); - list_add(&sat_dev->node, &sat_cntrl->dev_list); - mutex_unlock(&sat_cntrl->list_mutex); - - mhi_device_set_devdata(mhi_dev, sat_dev); - - sat_cntrl->num_devices++; - - /* schedule connect worker if all devices for controller have probed */ - if (sat_cntrl->num_devices == sat_cntrl->max_devices) { - /* number of event rings is 1 more than difference in IDs */ - sat_cntrl->num_er = (sat_cntrl->er_max - sat_cntrl->er_base) + - 1; - MHI_SAT_LOG("All satellite channels probed!\n"); - schedule_work(&sat_cntrl->connect_work); - } - - return 0; -} - -/* .driver_data stores subsys id */ -static const struct mhi_device_id mhi_sat_dev_match_table[] = { - /* ADSP */ - { .chan = "ADSP_0", .driver_data = SUBSYS_ADSP }, - { .chan = "ADSP_1", .driver_data = SUBSYS_ADSP }, - { .chan = "ADSP_2", .driver_data = SUBSYS_ADSP }, - { .chan = "ADSP_3", .driver_data = SUBSYS_ADSP }, - { .chan = "ADSP_4", .driver_data = SUBSYS_ADSP }, - { .chan = "ADSP_5", .driver_data = SUBSYS_ADSP }, - { .chan = "ADSP_6", .driver_data = SUBSYS_ADSP }, - { .chan = "ADSP_7", .driver_data = SUBSYS_ADSP }, - { .chan = "ADSP_8", .driver_data = SUBSYS_ADSP }, - { .chan = "ADSP_9", .driver_data = SUBSYS_ADSP }, - /* CDSP */ - { .chan = "CDSP_0", .driver_data = SUBSYS_CDSP }, - { .chan = "CDSP_1", .driver_data = SUBSYS_CDSP }, - { .chan = "CDSP_2", .driver_data = SUBSYS_CDSP }, - { .chan = "CDSP_3", .driver_data = SUBSYS_CDSP }, - { .chan = "CDSP_4", .driver_data = SUBSYS_CDSP }, - { .chan = "CDSP_5", .driver_data = SUBSYS_CDSP }, - { .chan = "CDSP_6", .driver_data = SUBSYS_CDSP }, - { .chan = "CDSP_7", .driver_data = SUBSYS_CDSP }, - { .chan = "CDSP_8", .driver_data = SUBSYS_CDSP }, - { .chan = "CDSP_9", .driver_data = SUBSYS_CDSP }, - /* SLPI */ - { .chan = "SLPI_0", .driver_data = SUBSYS_SLPI }, - { .chan = "SLPI_1", .driver_data = SUBSYS_SLPI }, - { .chan = "SLPI_2", .driver_data = SUBSYS_SLPI }, - { .chan = "SLPI_3", .driver_data = SUBSYS_SLPI }, - { .chan = "SLPI_4", .driver_data = SUBSYS_SLPI }, - { .chan = "SLPI_5", .driver_data = SUBSYS_SLPI }, - { .chan = "SLPI_6", .driver_data = SUBSYS_SLPI }, - { .chan = "SLPI_7", .driver_data = SUBSYS_SLPI }, - { .chan = "SLPI_8", .driver_data = SUBSYS_SLPI }, - { .chan = "SLPI_9", .driver_data = SUBSYS_SLPI }, - /* MODEM */ - { .chan = "MODEM_0", .driver_data = SUBSYS_MODEM }, - { .chan = "MODEM_1", .driver_data = SUBSYS_MODEM }, - { .chan = "MODEM_2", .driver_data = SUBSYS_MODEM }, - { .chan = "MODEM_3", .driver_data = SUBSYS_MODEM }, - { .chan = "MODEM_4", .driver_data = SUBSYS_MODEM }, - { .chan = "MODEM_5", .driver_data = SUBSYS_MODEM }, - { .chan = "MODEM_6", .driver_data = SUBSYS_MODEM }, - { .chan = "MODEM_7", .driver_data = SUBSYS_MODEM }, - { .chan = "MODEM_8", .driver_data = SUBSYS_MODEM }, - { .chan = "MODEM_9", .driver_data = SUBSYS_MODEM }, - {}, -}; - -static struct mhi_driver mhi_sat_dev_driver = { - .id_table = mhi_sat_dev_match_table, - .probe = mhi_sat_dev_probe, - .remove = mhi_sat_dev_remove, - .status_cb = mhi_sat_dev_status_cb, - .driver = { - .name = MHI_SAT_DRIVER_NAME, - .owner = THIS_MODULE, - }, -}; - -int mhi_sat_trigger_init(void *data, u64 val) -{ - struct mhi_sat_subsys *subsys; - int i, ret; - - if (mhi_sat_driver.deferred_init_done) - return -EIO; - - ret = register_rpmsg_driver(&mhi_sat_rpmsg_driver); - if (ret) - goto error_sat_trigger_init; - - ret = mhi_driver_register(&mhi_sat_dev_driver); - if (ret) - goto error_sat_trigger_register; - - mhi_sat_driver.deferred_init_done = true; - - return 0; - -error_sat_trigger_register: - unregister_rpmsg_driver(&mhi_sat_rpmsg_driver); - -error_sat_trigger_init: - subsys = mhi_sat_driver.subsys; - for (i = 0; i < mhi_sat_driver.num_subsys; i++, subsys++) { - ipc_log_context_destroy(subsys->ipc_log); - mutex_destroy(&subsys->cntrl_mutex); - } - kfree(mhi_sat_driver.subsys); - mhi_sat_driver.subsys = NULL; - - return ret; -} - -DEFINE_SIMPLE_ATTRIBUTE(mhi_sat_debugfs_fops, NULL, - mhi_sat_trigger_init, "%llu\n"); - -static int mhi_sat_init(void) -{ - struct mhi_sat_subsys *subsys; - int i, ret; - - subsys = kcalloc(SUBSYS_MAX, sizeof(*subsys), GFP_KERNEL); - if (!subsys) - return -ENOMEM; - - mhi_sat_driver.subsys = subsys; - mhi_sat_driver.num_subsys = SUBSYS_MAX; - mhi_sat_driver.klog_lvl = KLOG_LVL; - mhi_sat_driver.ipc_log_lvl = IPC_LOG_LVL; - - for (i = 0; i < mhi_sat_driver.num_subsys; i++, subsys++) { - char log[32]; - - subsys->name = subsys_names[i]; - mutex_init(&subsys->cntrl_mutex); - spin_lock_init(&subsys->cntrl_lock); - INIT_LIST_HEAD(&subsys->cntrl_list); - scnprintf(log, sizeof(log), "mhi_sat_%s", subsys->name); - subsys->ipc_log = ipc_log_context_create(IPC_LOG_PAGES, log, 0); - } - - /* create debugfs entry if defer_init is enabled */ - if (mhi_sat_defer_init) { - mhi_sat_driver.dentry = debugfs_create_dir("mhi_sat", NULL); - if (IS_ERR_OR_NULL(mhi_sat_driver.dentry)) { - ret = -ENODEV; - goto error_sat_init; - } - - debugfs_create_file("debug", 0444, mhi_sat_driver.dentry, NULL, - &mhi_sat_debugfs_fops); - - return 0; - } - - ret = register_rpmsg_driver(&mhi_sat_rpmsg_driver); - if (ret) - goto error_sat_init; - - ret = mhi_driver_register(&mhi_sat_dev_driver); - if (ret) - goto error_sat_register; - - return 0; - -error_sat_register: - unregister_rpmsg_driver(&mhi_sat_rpmsg_driver); - -error_sat_init: - subsys = mhi_sat_driver.subsys; - for (i = 0; i < mhi_sat_driver.num_subsys; i++, subsys++) { - ipc_log_context_destroy(subsys->ipc_log); - mutex_destroy(&subsys->cntrl_mutex); - } - kfree(mhi_sat_driver.subsys); - mhi_sat_driver.subsys = NULL; - - return ret; -} - -module_init(mhi_sat_init); - diff --git a/quectel_MHI/src/devices/mhi_uci.c b/quectel_MHI/src/devices/mhi_uci.c deleted file mode 100644 index 474b0228b..000000000 --- a/quectel_MHI/src/devices/mhi_uci.c +++ /dev/null @@ -1,981 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#if 1 -static inline void *ipc_log_context_create(int max_num_pages, - const char *modname, uint16_t user_version) -{ return NULL; } -static inline int ipc_log_string(void *ilctxt, const char *fmt, ...) -{ return -EINVAL; } -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../core/mhi.h" - -#define DEVICE_NAME "mhi" -#define MHI_UCI_DRIVER_NAME "mhi_uci_q" - -struct uci_chan { - wait_queue_head_t wq; - spinlock_t lock; - struct list_head pending; /* user space waiting to read */ - struct uci_buf *cur_buf; /* current buffer user space reading */ - size_t rx_size; -}; - -struct uci_buf { - struct page *page; - void *data; - size_t len; - unsigned nr_trb; - struct list_head node; -}; - -struct uci_dev { - struct list_head node; - dev_t devt; - struct device *dev; - struct mhi_device *mhi_dev; - const char *chan; - struct mutex mutex; /* sync open and close */ - struct mutex r_mutex; - struct mutex w_mutex; - struct uci_chan ul_chan; - struct uci_chan dl_chan; - size_t mtu; - int ref_count; - bool enabled; - unsigned rx_error; - unsigned nr_trb; - unsigned nr_trbs; - struct uci_buf *uci_buf; - struct ktermios termios; - size_t bytes_xferd; -}; - -struct mhi_uci_drv { - struct list_head head; - struct mutex lock; - struct class *class; - int major; - dev_t dev_t; -}; - -static int uci_msg_lvl = MHI_MSG_LVL_ERROR; -module_param( uci_msg_lvl, uint, S_IRUGO | S_IWUSR); - -#define MSG_VERB(fmt, ...) do { \ - if (uci_msg_lvl <= MHI_MSG_LVL_VERBOSE) \ - pr_err("[D][%s] " fmt, __func__, ##__VA_ARGS__); \ - } while (0) - -#define MSG_LOG(fmt, ...) do { \ - if (uci_msg_lvl <= MHI_MSG_LVL_INFO) \ - pr_err("[I][%s] " fmt, __func__, ##__VA_ARGS__); \ - } while (0) - -#define MSG_ERR(fmt, ...) do { \ - if (uci_msg_lvl <= MHI_MSG_LVL_ERROR) \ - pr_err("[E][%s] " fmt, __func__, ##__VA_ARGS__); \ - } while (0) - -#define MAX_UCI_DEVICES (64) -#define QUEC_MHI_UCI_ALWAYS_OPEN //by now, sdx20 can not handle "start-reset-start" operation, so the simply solution is keep start state - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)) -#ifdef TCGETS2 -__weak int user_termios_to_kernel_termios(struct ktermios *k, - struct termios2 __user *u) -{ - return copy_from_user(k, u, sizeof(struct termios2)); -} -__weak int kernel_termios_to_user_termios(struct termios2 __user *u, - struct ktermios *k) -{ - return copy_to_user(u, k, sizeof(struct termios2)); -} -__weak int user_termios_to_kernel_termios_1(struct ktermios *k, - struct termios __user *u) -{ - return copy_from_user(k, u, sizeof(struct termios)); -} -__weak int kernel_termios_to_user_termios_1(struct termios __user *u, - struct ktermios *k) -{ - return copy_to_user(u, k, sizeof(struct termios)); -} - -#else - -__weak int user_termios_to_kernel_termios(struct ktermios *k, - struct termios __user *u) -{ - return copy_from_user(k, u, sizeof(struct termios)); -} -__weak int kernel_termios_to_user_termios(struct termios __user *u, - struct ktermios *k) -{ - return copy_to_user(u, k, sizeof(struct termios)); -} -#endif /* TCGETS2 */ -#endif - -static DECLARE_BITMAP(uci_minors, MAX_UCI_DEVICES); -static struct mhi_uci_drv mhi_uci_drv; - -static int mhi_queue_inbound(struct uci_dev *uci_dev) -{ - struct mhi_device *mhi_dev = uci_dev->mhi_dev; - int nr_trbs = mhi_get_no_free_descriptors(mhi_dev, DMA_FROM_DEVICE); - size_t mtu = uci_dev->mtu; - void *buf; - struct uci_buf *uci_buf; - int ret = -EIO, i; - - if (uci_dev->uci_buf == NULL) { - uci_dev->nr_trb = 0; - uci_dev->nr_trbs = (nr_trbs + 1); - uci_dev->uci_buf = kmalloc_array(uci_dev->nr_trbs, sizeof(*uci_buf), GFP_KERNEL); - if (!uci_dev->uci_buf) - return -ENOMEM; - - uci_buf = uci_dev->uci_buf; - for (i = 0; i < uci_dev->nr_trbs; i++, uci_buf++) { - uci_buf->page = alloc_pages(GFP_KERNEL, get_order(mtu)); - if (!uci_buf->page) - return -ENOMEM; - uci_buf->data = page_address(uci_buf->page); - uci_buf->len = 0; - uci_buf->nr_trb = i; - if (mhi_dev->dl_chan_id == MHI_CLIENT_DUN_IN) { - //MSG_ERR("[%d] = %p\n", i, uci_buf->data); - } - } - } - - for (i = 0; i < nr_trbs; i++) { - #if 0 - buf = kmalloc(mtu + sizeof(*uci_buf), GFP_KERNEL); - if (!buf) - return -ENOMEM; - - uci_buf = buf + mtu; - uci_buf->data = buf; - #else - uci_buf = &uci_dev->uci_buf[i]; - buf = uci_buf->data; - #endif - - MSG_VERB("Allocated buf %d of %d size %zu\n", i, nr_trbs, mtu); - - ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, buf, mtu, - MHI_EOT); - if (ret) { - #if 0 - kfree(buf); - #endif - MSG_ERR("Failed to queue buffer %d\n", i); - return ret; - } - } - - return ret; -} - -static long mhi_uci_ioctl(struct file *file, - unsigned int cmd, - unsigned long arg) -{ - struct uci_dev *uci_dev = file->private_data; - struct mhi_device *mhi_dev = uci_dev->mhi_dev; - long ret = -ERESTARTSYS; - - mutex_lock(&uci_dev->mutex); - if (uci_dev->enabled) - ret = mhi_ioctl(mhi_dev, cmd, arg); - - if (uci_dev->enabled) { - switch (cmd) { - case TCGETS: -#ifndef TCGETS2 - ret = kernel_termios_to_user_termios((struct termios __user *)arg, &uci_dev->termios); -#else - ret = kernel_termios_to_user_termios_1((struct termios __user *)arg, &uci_dev->termios); -#endif - break; - - case TCSETSF: - case TCSETS: -#ifndef TCGETS2 - ret = user_termios_to_kernel_termios(&uci_dev->termios, (struct termios __user *)arg); -#else - ret = user_termios_to_kernel_termios_1(&uci_dev->termios, (struct termios __user *)arg); -#endif - break; - - case TCFLSH: - ret = 0; - break; - - default: - break; - } - } - mutex_unlock(&uci_dev->mutex); - - return ret; -} - -static int mhi_uci_release(struct inode *inode, struct file *file) -{ - struct uci_dev *uci_dev = file->private_data; - - mutex_lock(&uci_dev->mutex); - uci_dev->ref_count--; - if (!uci_dev->ref_count) { - struct uci_chan *uci_chan; - - MSG_LOG("Last client left, closing node\n"); - - if (uci_dev->enabled) - mhi_unprepare_from_transfer(uci_dev->mhi_dev); - - /* clean inbound channel */ - uci_chan = &uci_dev->dl_chan; - if (uci_dev->uci_buf) { - unsigned nr_trb = 0; - - for (nr_trb = 0; nr_trb < uci_dev->nr_trbs; nr_trb++) { - if (uci_dev->uci_buf[nr_trb].page) - __free_pages(uci_dev->uci_buf[nr_trb].page, get_order(uci_dev->mtu)); - } - kfree(uci_dev->uci_buf); - } - - uci_chan->cur_buf = NULL; - - if (!uci_dev->enabled) { - MSG_LOG("Node is deleted, freeing dev node\n"); - mutex_unlock(&uci_dev->mutex); - mutex_destroy(&uci_dev->mutex); - clear_bit(MINOR(uci_dev->devt), uci_minors); - kfree(uci_dev); - return 0; - } - } - - MSG_LOG("exit: ref_count:%d\n", uci_dev->ref_count); - - mutex_unlock(&uci_dev->mutex); - - return 0; -} - -static unsigned int mhi_uci_poll(struct file *file, poll_table *wait) -{ - struct uci_dev *uci_dev = file->private_data; - struct mhi_device *mhi_dev = uci_dev->mhi_dev; - struct uci_chan *uci_chan; - unsigned int mask = 0; - - poll_wait(file, &uci_dev->dl_chan.wq, wait); - // ADPL and QDSS do not need poll write. xingduo.du 2023-02-16 - // poll_wait(file, &uci_dev->ul_chan.wq, wait); - - uci_chan = &uci_dev->dl_chan; - spin_lock_bh(&uci_chan->lock); - if (!uci_dev->enabled) { - mask = POLLERR; - } else if (!list_empty(&uci_chan->pending) || uci_chan->cur_buf) { - MSG_VERB("Client can read from node\n"); - mask |= POLLIN | POLLRDNORM; - } - spin_unlock_bh(&uci_chan->lock); - - // ADPL and QDSS are single channel, ul_chan not be initilized. xingduo.du 2023-02-27 - if (mhi_dev->ul_chan) { - poll_wait(file, &uci_dev->ul_chan.wq, wait); - uci_chan = &uci_dev->ul_chan; - spin_lock_bh(&uci_chan->lock); - if (!uci_dev->enabled) { - mask |= POLLERR; - } else if (mhi_get_no_free_descriptors(mhi_dev, DMA_TO_DEVICE) > 0) { - MSG_VERB("Client can write to node\n"); - mask |= POLLOUT | POLLWRNORM; - } - - if (!uci_dev->enabled) - mask |= POLLHUP; - if (uci_dev->rx_error) - mask |= POLLERR; - - spin_unlock_bh(&uci_chan->lock); - } - - MSG_LOG("Client attempted to poll, returning mask 0x%x\n", mask); - - return mask; -} - -static ssize_t mhi_uci_write(struct file *file, - const char __user *buf, - size_t count, - loff_t *offp) -{ - struct uci_dev *uci_dev = file->private_data; - struct mhi_device *mhi_dev = uci_dev->mhi_dev; - struct uci_chan *uci_chan = &uci_dev->ul_chan; - size_t bytes_xfered = 0; - int ret, nr_avail; - - if (!buf || !count || uci_dev->rx_error) - return -EINVAL; - - /* confirm channel is active */ - spin_lock_bh(&uci_chan->lock); - if (!uci_dev->enabled) { - spin_unlock_bh(&uci_chan->lock); - return -ERESTARTSYS; - } - - MSG_VERB("Enter: to xfer:%zu bytes\n", count); - - while (count) { - size_t xfer_size; - void *kbuf; - enum MHI_FLAGS flags; - - spin_unlock_bh(&uci_chan->lock); - - nr_avail = mhi_get_no_free_descriptors(mhi_dev, DMA_TO_DEVICE); - if ((nr_avail == 0) && (file->f_flags & O_NONBLOCK)) - return -EAGAIN; - - /* wait for free descriptors */ - ret = wait_event_interruptible(uci_chan->wq, - (!uci_dev->enabled) || - (nr_avail = mhi_get_no_free_descriptors(mhi_dev, - DMA_TO_DEVICE)) > 0); - - if (ret == -ERESTARTSYS || !uci_dev->enabled) { - MSG_LOG("Exit signal caught for node or not enabled\n"); - return -ERESTARTSYS; - } - - xfer_size = min_t(size_t, count, uci_dev->mtu); - kbuf = kmalloc(xfer_size, GFP_KERNEL); - if (!kbuf) { - MSG_ERR("Failed to allocate memory %zu\n", xfer_size); - return -ENOMEM; - } - - ret = copy_from_user(kbuf, buf, xfer_size); - if (unlikely(ret)) { - kfree(kbuf); - return ret; - } - - spin_lock_bh(&uci_chan->lock); - - /* if ring is full after this force EOT */ - if (nr_avail > 1 && (count - xfer_size)) - flags = MHI_CHAIN; - else - flags = MHI_EOT; - - if (uci_dev->enabled) - ret = mhi_queue_transfer(mhi_dev, DMA_TO_DEVICE, kbuf, - xfer_size, flags); - else - ret = -ERESTARTSYS; - - if (ret) { - kfree(kbuf); - goto sys_interrupt; - } - - bytes_xfered += xfer_size; - count -= xfer_size; - buf += xfer_size; - } - - spin_unlock_bh(&uci_chan->lock); - MSG_VERB("Exit: Number of bytes xferred:%zu\n", bytes_xfered); - - return bytes_xfered; - -sys_interrupt: - spin_unlock_bh(&uci_chan->lock); - - return ret; -} - -static ssize_t mhi_uci_read(struct file *file, - char __user *buf, - size_t count, - loff_t *ppos) -{ - struct uci_dev *uci_dev = file->private_data; - struct mhi_device *mhi_dev = uci_dev->mhi_dev; - struct uci_chan *uci_chan = &uci_dev->dl_chan; - struct uci_buf *uci_buf; - char *ptr; - size_t to_copy; - int ret = 0; - - if (!buf || uci_dev->rx_error) - return -EINVAL; - - MSG_VERB("Client provided buf len:%zu\n", count); - - /* confirm channel is active */ - spin_lock_bh(&uci_chan->lock); - if (!uci_dev->enabled) { - spin_unlock_bh(&uci_chan->lock); - return -ERESTARTSYS; - } - - /* No data available to read, wait */ - if (!uci_chan->cur_buf && list_empty(&uci_chan->pending)) { - MSG_VERB("No data available to read waiting\n"); - - spin_unlock_bh(&uci_chan->lock); - - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - - ret = wait_event_interruptible(uci_chan->wq, - (!uci_dev->enabled || - !list_empty(&uci_chan->pending))); - if (ret == -ERESTARTSYS) { - MSG_LOG("Exit signal caught for node\n"); - return -ERESTARTSYS; - } - - spin_lock_bh(&uci_chan->lock); - if (!uci_dev->enabled) { - MSG_LOG("node is disabled\n"); - ret = -ERESTARTSYS; - goto read_error; - } - } - - /* new read, get the next descriptor from the list */ - if (!uci_chan->cur_buf) { - uci_buf = list_first_entry_or_null(&uci_chan->pending, - struct uci_buf, node); - if (unlikely(!uci_buf)) { - ret = -EIO; - goto read_error; - } - - if (uci_buf->node.next == LIST_POISON1 || uci_buf->node.prev == LIST_POISON1) { - dump_stack(); - ret = -EIO; - MSG_ERR("chan[%d] data=%p, len=%zd, nr_trb=%d\n", - mhi_dev->dl_chan_id, uci_buf->data, uci_buf->len, uci_buf->nr_trb); - goto read_error; - } - - list_del(&uci_buf->node); - uci_chan->cur_buf = uci_buf; - uci_chan->rx_size = uci_buf->len; - MSG_VERB("Got pkt of size:%zu\n", uci_chan->rx_size); - } - - uci_buf = uci_chan->cur_buf; - spin_unlock_bh(&uci_chan->lock); - - /* Copy the buffer to user space */ - to_copy = min_t(size_t, count, uci_chan->rx_size); - ptr = uci_buf->data + (uci_buf->len - uci_chan->rx_size); - ret = copy_to_user(buf, ptr, to_copy); - if (ret) - return ret; - - MSG_VERB("Copied %zu of %zu bytes\n", to_copy, uci_chan->rx_size); - uci_chan->rx_size -= to_copy; - - /* we finished with this buffer, queue it back to hardware */ - if (!uci_chan->rx_size) { - spin_lock_bh(&uci_chan->lock); - uci_chan->cur_buf = NULL; - - if (uci_dev->enabled) -#if 1 //this can make the address in ring do not change - { - if (uci_buf->page) { - unsigned nr_trb = uci_buf->nr_trb ? (uci_buf->nr_trb - 1) : (uci_dev->nr_trbs - 1); - - uci_buf = &uci_dev->uci_buf[nr_trb]; - ret = mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, - uci_buf->data, uci_dev->mtu, - MHI_EOT); - } else { - kfree(uci_buf); - ret = 0; - } - } -#endif - else - ret = -ERESTARTSYS; - - if (ret) { - MSG_ERR("Failed to recycle element for chan:%d , ret=%d\n", mhi_dev->ul_chan_id, ret); -#if 0 - kfree(uci_buf->data); -#endif - goto read_error; - } - - spin_unlock_bh(&uci_chan->lock); - } - - MSG_VERB("Returning %zu bytes\n", to_copy); - - return to_copy; - -read_error: - spin_unlock_bh(&uci_chan->lock); - - return ret; -} - -static ssize_t mhi_uci_write_mutex(struct file *file, - const char __user *buf, - size_t count, - loff_t *offp) -{ - struct uci_dev *uci_dev = file->private_data; - int ret; - - ret = mutex_lock_interruptible(&uci_dev->w_mutex); /*concurrent writes */ - if (ret < 0) - return -ERESTARTSYS; - - ret = mhi_uci_write(file, buf, count, offp); - mutex_unlock(&uci_dev->w_mutex); - - return ret; -} - -static ssize_t mhi_uci_read_mutex(struct file *file, - char __user *buf, - size_t count, - loff_t *ppos) -{ - struct uci_dev *uci_dev = file->private_data; - int ret; - - ret = mutex_lock_interruptible(&uci_dev->r_mutex); /*concurrent reads */ - if (ret < 0) - return -ERESTARTSYS; - - ret = mhi_uci_read(file, buf, count, ppos); - mutex_unlock(&uci_dev->r_mutex); - - return ret; -} - -static int mhi_uci_open(struct inode *inode, struct file *filp) -{ - struct uci_dev *uci_dev = NULL, *tmp_dev; - int ret = -EIO; - struct uci_chan *dl_chan; - - mutex_lock(&mhi_uci_drv.lock); - list_for_each_entry(tmp_dev, &mhi_uci_drv.head, node) { - if (tmp_dev->devt == inode->i_rdev) { - uci_dev = tmp_dev; - break; - } - } - - /* could not find a minor node */ - if (!uci_dev) - goto error_exit; - - mutex_lock(&uci_dev->mutex); - if (!uci_dev->enabled) { - MSG_ERR("Node exist, but not in active state!\n"); - goto error_open_chan; - } - - uci_dev->ref_count++; - - MSG_LOG("Node open, ref counts %u\n", uci_dev->ref_count); - - if (uci_dev->ref_count == 1) { - MSG_LOG("Starting channel\n"); - ret = mhi_prepare_for_transfer(uci_dev->mhi_dev); - if (ret) { - MSG_ERR("Error starting transfer channels\n"); - uci_dev->ref_count--; - goto error_open_chan; - } - - ret = mhi_queue_inbound(uci_dev); - if (ret) - goto error_rx_queue; - -#ifdef QUEC_MHI_UCI_ALWAYS_OPEN - uci_dev->ref_count++; -#endif - } - - filp->private_data = uci_dev; - mutex_unlock(&uci_dev->mutex); - mutex_unlock(&mhi_uci_drv.lock); - - return 0; - - error_rx_queue: - dl_chan = &uci_dev->dl_chan; - mhi_unprepare_from_transfer(uci_dev->mhi_dev); - if (uci_dev->uci_buf) { - unsigned nr_trb = 0; - - for (nr_trb = 0; nr_trb < uci_dev->nr_trbs; nr_trb++) { - if (uci_dev->uci_buf[nr_trb].page) - __free_pages(uci_dev->uci_buf[nr_trb].page, get_order(uci_dev->mtu)); - } - kfree(uci_dev->uci_buf); - } - - error_open_chan: - mutex_unlock(&uci_dev->mutex); - -error_exit: - mutex_unlock(&mhi_uci_drv.lock); - - return ret; -} - -static const struct file_operations mhidev_fops = { - .open = mhi_uci_open, - .release = mhi_uci_release, - .read = mhi_uci_read_mutex, - .write = mhi_uci_write_mutex, - .poll = mhi_uci_poll, - .unlocked_ioctl = mhi_uci_ioctl, -}; - -static void mhi_uci_remove(struct mhi_device *mhi_dev) -{ - struct uci_dev *uci_dev = mhi_device_get_devdata(mhi_dev); - - MSG_LOG("Enter\n"); - - - mutex_lock(&mhi_uci_drv.lock); - mutex_lock(&uci_dev->mutex); - - /* disable the node */ - spin_lock_irq(&uci_dev->dl_chan.lock); - spin_lock_irq(&uci_dev->ul_chan.lock); - uci_dev->enabled = false; - spin_unlock_irq(&uci_dev->ul_chan.lock); - spin_unlock_irq(&uci_dev->dl_chan.lock); - wake_up(&uci_dev->dl_chan.wq); - wake_up(&uci_dev->ul_chan.wq); - - /* delete the node to prevent new opens */ - device_destroy(mhi_uci_drv.class, uci_dev->devt); - uci_dev->dev = NULL; - list_del(&uci_dev->node); - -#ifdef QUEC_MHI_UCI_ALWAYS_OPEN - if (uci_dev->ref_count > 0) - uci_dev->ref_count--; -#endif - - /* safe to free memory only if all file nodes are closed */ - if (!uci_dev->ref_count) { - mutex_unlock(&uci_dev->mutex); - mutex_destroy(&uci_dev->mutex); - clear_bit(MINOR(uci_dev->devt), uci_minors); - kfree(uci_dev); - mutex_unlock(&mhi_uci_drv.lock); - return; - } - - MSG_LOG("Exit\n"); - mutex_unlock(&uci_dev->mutex); - mutex_unlock(&mhi_uci_drv.lock); - -} - -static int mhi_uci_probe(struct mhi_device *mhi_dev, - const struct mhi_device_id *id) -{ - struct uci_dev *uci_dev; - int minor; - char node_name[32]; - int dir; - - uci_dev = kzalloc(sizeof(*uci_dev), GFP_KERNEL); - if (!uci_dev) - return -ENOMEM; - - mutex_init(&uci_dev->mutex); - mutex_init(&uci_dev->r_mutex); - mutex_init(&uci_dev->w_mutex); - uci_dev->mhi_dev = mhi_dev; - - minor = find_first_zero_bit(uci_minors, MAX_UCI_DEVICES); - if (minor >= MAX_UCI_DEVICES) { - kfree(uci_dev); - return -ENOSPC; - } - - mutex_lock(&uci_dev->mutex); - mutex_lock(&mhi_uci_drv.lock); - - uci_dev->devt = MKDEV(mhi_uci_drv.major, minor); -#if 1 - if (mhi_dev->mhi_cntrl->cntrl_idx) - uci_dev->dev = device_create(mhi_uci_drv.class, &mhi_dev->dev, - uci_dev->devt, uci_dev, - DEVICE_NAME "_%s%d", - mhi_dev->chan_name, mhi_dev->mhi_cntrl->cntrl_idx); - else - uci_dev->dev = device_create(mhi_uci_drv.class, &mhi_dev->dev, - uci_dev->devt, uci_dev, - DEVICE_NAME "_%s", - mhi_dev->chan_name); -#else - uci_dev->dev = device_create(mhi_uci_drv.class, &mhi_dev->dev, - uci_dev->devt, uci_dev, - DEVICE_NAME "_%04x_%02u.%02u.%02u%s%d", - mhi_dev->dev_id, mhi_dev->domain, - mhi_dev->bus, mhi_dev->slot, "_pipe_", - mhi_dev->ul_chan_id); -#endif - - set_bit(minor, uci_minors); - - /* create debugging buffer */ - snprintf(node_name, sizeof(node_name), "mhi_uci_%04x_%02u.%02u.%02u_%d", - mhi_dev->dev_id, mhi_dev->domain, mhi_dev->bus, mhi_dev->slot, - mhi_dev->ul_chan_id); - - for (dir = 0; dir < 2; dir++) { - struct uci_chan *uci_chan = (dir) ? - &uci_dev->ul_chan : &uci_dev->dl_chan; - spin_lock_init(&uci_chan->lock); - init_waitqueue_head(&uci_chan->wq); - INIT_LIST_HEAD(&uci_chan->pending); - } - - uci_dev->termios = tty_std_termios; - - uci_dev->mtu = min_t(size_t, id->driver_data, mhi_dev->mtu); - mhi_device_set_devdata(mhi_dev, uci_dev); - uci_dev->enabled = true; - - list_add(&uci_dev->node, &mhi_uci_drv.head); - mutex_unlock(&mhi_uci_drv.lock); - mutex_unlock(&uci_dev->mutex); - - MSG_LOG("channel:%s successfully probed\n", mhi_dev->chan_name); - - return 0; -}; - -static void mhi_ul_xfer_cb(struct mhi_device *mhi_dev, - struct mhi_result *mhi_result) -{ - struct uci_dev *uci_dev = mhi_device_get_devdata(mhi_dev); - struct uci_chan *uci_chan = &uci_dev->ul_chan; - - MSG_VERB("status:%d xfer_len:%zu\n", mhi_result->transaction_status, - mhi_result->bytes_xferd); - - kfree(mhi_result->buf_addr); - if (!mhi_result->transaction_status) - wake_up(&uci_chan->wq); -} - -static void mhi_dl_xfer_cb(struct mhi_device *mhi_dev, - struct mhi_result *mhi_result) -{ - struct uci_dev *uci_dev = mhi_device_get_devdata(mhi_dev); - struct uci_chan *uci_chan = &uci_dev->dl_chan; - unsigned long flags; - struct uci_buf *buf; - unsigned nr_trb = uci_dev->nr_trb; - - buf = &uci_dev->uci_buf[nr_trb]; - if (buf == NULL) { - MSG_ERR("buf = NULL"); - return; - } - if (buf->nr_trb != nr_trb || buf->data != mhi_result->buf_addr) - { - uci_dev->rx_error++; - MSG_ERR("chan[%d]: uci_buf[%u] = %p , mhi_result[%u] = %p\n", - mhi_dev->dl_chan_id, buf->nr_trb, buf->data, nr_trb, mhi_result->buf_addr); - return; - } - - uci_dev->nr_trb++; - if (uci_dev->nr_trb == uci_dev->nr_trbs) - uci_dev->nr_trb = 0; - - if (mhi_result->transaction_status == -ENOTCONN) { - return; - } - - if (mhi_result->bytes_xferd > uci_dev->mtu || mhi_result->bytes_xferd <= 0) - { - MSG_ERR("chan[%d]: bytes_xferd = %zd , mtu = %zd\n", - mhi_dev->dl_chan_id, mhi_result->bytes_xferd, uci_dev->mtu); - return; - } - if (mhi_result->bytes_xferd > uci_dev->bytes_xferd) - { - uci_dev->bytes_xferd = mhi_result->bytes_xferd; - //MSG_ERR("chan[%d]: bytes_xferd = %zd , mtu = %zd\n", - // mhi_dev->dl_chan_id, mhi_result->bytes_xferd, uci_dev->mtu); - } - - MSG_VERB("status:%d receive_len:%zu\n", mhi_result->transaction_status, - mhi_result->bytes_xferd); - - spin_lock_irqsave(&uci_chan->lock, flags); -#if 0 - buf = mhi_result->buf_addr + uci_dev->mtu; - buf->data = mhi_result->buf_addr; -#endif - buf->len = mhi_result->bytes_xferd; - if (mhi_dev->dl_chan_id == MHI_CLIENT_DUN_IN - || mhi_dev->dl_chan_id == MHI_CLIENT_QMI_IN - || mhi_dev->dl_chan_id == MHI_CLIENT_MBIM_IN) - { - struct uci_buf *tmp_buf = NULL; - int skip_buf = 0; - -#ifdef QUEC_MHI_UCI_ALWAYS_OPEN - if (uci_dev->ref_count == 1) - skip_buf++; -#endif - if (!skip_buf) - tmp_buf = (struct uci_buf *)kmalloc(buf->len + sizeof(struct uci_buf), GFP_ATOMIC);; - - if (tmp_buf) { - tmp_buf->page = NULL; - tmp_buf->data = ((void *)tmp_buf) + sizeof(struct uci_buf); - tmp_buf->len = buf->len; - memcpy(tmp_buf->data, buf->data, buf->len); - } - - if (buf) { - struct uci_buf *uci_buf = buf; - unsigned nr_trb = uci_buf->nr_trb ? (uci_buf->nr_trb - 1) : (uci_dev->nr_trbs - 1); - - uci_buf = &uci_dev->uci_buf[nr_trb]; - mhi_queue_transfer(mhi_dev, DMA_FROM_DEVICE, uci_buf->data, uci_dev->mtu, MHI_EOT); - } - - buf = tmp_buf; - } - - if (buf) - list_add_tail(&buf->node, &uci_chan->pending); - spin_unlock_irqrestore(&uci_chan->lock, flags); - -#ifdef CONFIG_PM_SLEEP - if (mhi_dev->dev.power.wakeup) - __pm_wakeup_event(mhi_dev->dev.power.wakeup, 0); -#endif - - wake_up(&uci_chan->wq); -} - -// repaire sdx6x module can not read qdb file. xingduo.du 2023-01-18 -#define DIAG_MAX_PCIE_PKT_SZ 8192 //define by module - -/* .driver_data stores max mtu */ -static const struct mhi_device_id mhi_uci_match_table[] = { - { .chan = "LOOPBACK", .driver_data = 0x1000 }, - { .chan = "SAHARA", .driver_data = 0x4000 }, - { .chan = "EDL", .driver_data = 0x4000 }, - { .chan = "DIAG", .driver_data = DIAG_MAX_PCIE_PKT_SZ }, - { .chan = "MBIM", .driver_data = 0x1000 }, - { .chan = "QMI0", .driver_data = 0x1000 }, - { .chan = "QMI1", .driver_data = 0x1000 }, - { .chan = "DUN", .driver_data = 0x1000 }, -#ifdef ENABLE_ADPL - { .chan = "ADPL", .driver_data = 0x1000 }, -#endif -#ifdef ENABLE_QDSS - { .chan = "QDSS", .driver_data = 0x1000 }, -#endif - {}, -}; - -static struct mhi_driver mhi_uci_driver = { - .id_table = mhi_uci_match_table, - .remove = mhi_uci_remove, - .probe = mhi_uci_probe, - .ul_xfer_cb = mhi_ul_xfer_cb, - .dl_xfer_cb = mhi_dl_xfer_cb, - .driver = { - .name = MHI_UCI_DRIVER_NAME, - .owner = THIS_MODULE, - }, -}; - -int mhi_device_uci_init(void) -{ - int ret; - - ret = register_chrdev(0, MHI_UCI_DRIVER_NAME, &mhidev_fops); - if (ret < 0) - return ret; - - mhi_uci_drv.major = ret; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)) - mhi_uci_drv.class = class_create(MHI_UCI_DRIVER_NAME); -#else - mhi_uci_drv.class = class_create(THIS_MODULE, MHI_UCI_DRIVER_NAME); -#endif - if (IS_ERR(mhi_uci_drv.class)) { - unregister_chrdev(mhi_uci_drv.major, MHI_UCI_DRIVER_NAME); - return -ENODEV; - } - - mutex_init(&mhi_uci_drv.lock); - INIT_LIST_HEAD(&mhi_uci_drv.head); - - ret = mhi_driver_register(&mhi_uci_driver); - if (ret) { - class_destroy(mhi_uci_drv.class); - unregister_chrdev(mhi_uci_drv.major, MHI_UCI_DRIVER_NAME); - } - - return ret; -} - -void mhi_device_uci_exit(void) -{ - mhi_driver_unregister(&mhi_uci_driver); - class_destroy(mhi_uci_drv.class); - unregister_chrdev(mhi_uci_drv.major, MHI_UCI_DRIVER_NAME); -} diff --git a/quectel_MHI/src/log/AT_OVER_PCIE.txt b/quectel_MHI/src/log/AT_OVER_PCIE.txt deleted file mode 100644 index bc4850473..000000000 --- a/quectel_MHI/src/log/AT_OVER_PCIE.txt +++ /dev/null @@ -1,31 +0,0 @@ -root@imx6qsabresd:~# busybox microcom /dev/mhi_DUN -[ 384.652992] [I][mhi_uci_open] Node open, ref counts 1 -[ 384.658144] [I][mhi_uci_open] Starting channel -[ 384.662612] [I][__mhi_prepare_channel] Entered: preparing channel:32 -[ 384.680397] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1 -[ 384.685890] [I][__mhi_prepare_channel] Chan:32 successfully moved to start state -[ 384.693312] [I][__mhi_prepare_channel] Entered: preparing channel:33 -[ 384.708692] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1 -[ 384.714324] [I][__mhi_prepare_channel] Chan:33 successfully moved to start state - -RDY - -+CFUN: 1 - -+CPIN: READY - -+QUSIM: 1 - -+QIND: SMS DONE - -+QIND: PB DONE -ati -Quectel -EM20 -Revision: EM20GR01A01M4G - -OK -at+cpin? -+CPIN: READY - -OK diff --git a/quectel_MHI/src/log/MBIM_OVER_PCIE.txt b/quectel_MHI/src/log/MBIM_OVER_PCIE.txt deleted file mode 100644 index 927a9b2ad..000000000 --- a/quectel_MHI/src/log/MBIM_OVER_PCIE.txt +++ /dev/null @@ -1,145 +0,0 @@ -root@OpenWrt:~# insmod pcie_mhi.ko mhi_mbim_enabled=1 -root@OpenWrt:~# dmesg | grep mhi -[ 65.587160] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.6 -[ 65.597089] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306 -[ 65.602250] mhi_q 0000:01:00.0: BAR 0: assigned [mem 0x20300000-0x20300fff 64bit] -[ 65.611690] mhi_q 0000:01:00.0: enabling device (0140 -> 0142) -[ 65.619307] [I][mhi_init_pci_dev] msi_required = 5, msi_allocated = 5, msi_irq = 63 -[ 65.619327] [I][mhi_power_up] dev_state:RESET -[ 65.619331] [I][mhi_async_power_up] Requested to power on -[ 65.619449] [I][mhi_alloc_coherent] size = 114688, dma_handle = 6fca0000 -[ 65.619462] [I][mhi_init_dev_ctxt] mhi_ctxt->ctrl_seg = c221e000 -[ 65.619731] [I][mhi_async_power_up] dev_state:RESET ee:AMSS -[ 65.619747] [I][mhi_pm_st_worker] Transition to state:READY -[ 65.619760] [I][mhi_pm_st_worker] INVALID_EE -> AMSS -[ 65.619764] [I][mhi_ready_state_transition] Waiting to enter READY state -[ 65.619885] [I][mhi_async_power_up] Power on setup success -[ 65.619897] [I][mhi_pci_probe] Return successful -[ 65.665114] [I][mhi_ready_state_transition] Device in READY State -[ 65.665125] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, pm_state:POR -[ 65.665131] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, INVALID_EE -[ 65.665133] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:POR -[ 65.665137] [I][mhi_init_mmio] Initializing MMIO -[ 65.665142] [I][mhi_init_mmio] CHDBOFF:0x300 -[ 65.665151] [I][mhi_init_mmio] ERDBOFF:0x700 -[ 65.665156] [I][mhi_init_mmio] Programming all MMIO values. -[ 65.786283] [I][mhi_dump_tre] carl_ev evt_state_change mhistate=2 -[ 65.786289] [I][mhi_process_ctrl_ev_ring] MHI state change event to state:M0 -[ 65.786295] [I][mhi_pm_m0_transition] Entered With State:READY PM_STATE:POR -[ 65.786300] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:M0 -[ 65.789734] [I][mhi_dump_tre] carl_ev evt_ee_state execenv=2 -[ 65.789739] [I][mhi_process_ctrl_ev_ring] MHI EE received event:AMSS -[ 65.789756] [I][mhi_pm_st_worker] Transition to state:MISSION MODE -[ 65.789767] [I][mhi_pm_st_worker] INVALID_EE -> AMSS -[ 65.789771] [I][mhi_pm_mission_mode_transition] Processing Mission Mode Transition -[ 65.789787] [I][mhi_init_timesync] No timesync capability found -[ 65.789791] [I][mhi_pm_mission_mode_transition] Adding new devices -[ 65.790570] [I][mhi_dtr_probe] Enter for DTR control channel -[ 65.790577] [I][__mhi_prepare_channel] Entered: preparing channel:18 -[ 65.797036] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1 -[ 65.797051] [I][__mhi_prepare_channel] Chan:18 successfully moved to start state -[ 65.797055] [I][__mhi_prepare_channel] Entered: preparing channel:19 -[ 65.802457] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1 -[ 65.802469] [I][__mhi_prepare_channel] Chan:19 successfully moved to start state -[ 65.802485] [I][mhi_dtr_probe] Exit with ret:0 -[ 65.802748] [I][mhi_netdev_enable_iface] Prepare the channels for transfer -[ 65.802772] [I][__mhi_prepare_channel] Entered: preparing channel:100 -[ 65.825279] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1 -[ 65.825293] [I][__mhi_prepare_channel] Chan:100 successfully moved to start state -[ 65.825297] [I][__mhi_prepare_channel] Entered: preparing channel:101 -[ 65.835565] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1 -[ 65.835578] [I][__mhi_prepare_channel] Chan:101 successfully moved to start state -[ 65.839141] [I][mhi_netdev_enable_iface] Exited. -[ 65.839875] rmnet_vnd_register_device(rmnet_mhi0.1)=0 -[ 65.843278] net rmnet_mhi0 rmnet_mhi0.1: NSS context created -[ 65.861808] [I][mhi_pm_mission_mode_transition] Exit with ret:0 -[ 68.625595] [I][__mhi_prepare_channel] Entered: preparing channel:12 -[ 68.634610] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1 -[ 68.634622] [I][__mhi_prepare_channel] Chan:12 successfully moved to start state -[ 68.634625] [I][__mhi_prepare_channel] Entered: preparing channel:13 -[ 68.644978] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1 -[ 68.644987] [I][__mhi_prepare_channel] Chan:13 successfully moved to start state -[ 69.170666] net rmnet_mhi0: link_state 0x0 -> 0x1 -[ 69.177035] [I][mhi_netdev_open] Opened net dev interface -[ 71.655431] [I][mhi_netdev_open] Opened net dev interface - -root@OpenWrt:~# ./quectel-CM & -[04-02_04:14:12:134] Quectel_QConnectManager_Linux_V1.6.0.5 -[04-02_04:14:12:134] Find /sys/bus/usb/devices/4-1 idVendor=0x2c7c idProduct=0x800, bus=0x004, dev=0x002 -[04-02_04:14:12:135] network interface '' or qmidev '' is not exist -[04-02_04:14:12:135] netcard driver = pcie_mhi, driver version = V1.3.0.6 -[04-02_04:14:12:135] Modem works in MBIM mode -[04-02_04:14:12:135] apn (null), user (null), passwd (null), auth 0 -[04-02_04:14:12:135] IP Proto MBIMContextIPTypeIPv4 -[04-02_04:14:12:154] mbim_read_thread is created -sh: can't create /sys/class/net/rmnet_mhi0/mbim/link_state: nonexistent directory -[04-02_04:14:12:156] system(echo 0 > /sys/class/net/rmnet_mhi0/mbim/link_state)=256 -[04-02_04:14:12:185] system(ip address flush dev rmnet_mhi0)=0 -[04-02_04:14:12:187] system(ip link set dev rmnet_mhi0 down)=0 -[04-02_04:14:12:188] mbim_open_device() -[04-02_04:14:12:605] mbim_device_caps_query() -[04-02_04:14:12:610] DeviceId: 869710030002905 -[04-02_04:14:12:610] HardwareInfo: 0 -[04-02_04:14:12:610] mbim_set_radio_state( 1 ) -[04-02_04:14:12:613] HwRadioState: 1, SwRadioState: 1 -[04-02_04:14:12:613] mbim_subscriber_status_query() -[04-02_04:14:12:620] SubscriberReadyState NotInitialized -> Initialized -[04-02_04:14:12:620] mbim_register_state_query() -[04-02_04:14:12:625] RegisterState Unknown -> Home -[04-02_04:14:12:625] mbim_packet_service_query() -[04-02_04:14:12:629] PacketServiceState Unknown -> Attached -[04-02_04:14:12:629] mbim_query_connect(sessionID=0) -[04-02_04:14:12:633] ActivationState Unknown -> Deactivated -[04-02_04:14:12:633] mbim_set_connect(onoff=1, sessionID=0) -[ 69.170666] net rmnet_mhi0: link_state 0x0 -> 0x1 -[04-02_04:14:12:680] ActivationState Deactivated -> Activated -[ 69.177035] [I][mhi_netdev_open] Opened net dev interface -[04-02_04:14:12:680] mbim_ip_config(sessionID=0) -[04-02_04:14:12:683] < SessionId = 0 -[04-02_04:14:12:683] < IPv4ConfigurationAvailable = 0xf -[04-02_04:14:12:683] < IPv6ConfigurationAvailable = 0x0 -[04-02_04:14:12:683] < IPv4AddressCount = 0x1 -[04-02_04:14:12:683] < IPv4AddressOffset = 0x3c -[04-02_04:14:12:683] < IPv6AddressCount = 0x0 -[04-02_04:14:12:683] < IPv6AddressOffset = 0x0 -[04-02_04:14:12:683] < IPv4 = 10.129.59.93/30 -[04-02_04:14:12:683] < gw = 10.129.59.94 -[04-02_04:14:12:683] < dns1 = 211.138.180.2 -[04-02_04:14:12:683] < dns2 = 211.138.180.3 -[04-02_04:14:12:683] < ipv4 mtu = 1500 -sh: can't create /sys/class/net/rmnet_mhi0/mbim/link_state: nonexistent directory -[04-02_04:14:12:684] system(echo 1 > /sys/class/net/rmnet_mhi0/mbim/link_state)=256 -[04-02_04:14:12:689] system(ip link set dev rmnet_mhi0 up)=0 -[04-02_04:14:12:692] system(ip -4 address flush dev rmnet_mhi0)=0 -[04-02_04:14:12:694] system(ip -4 address add 10.129.59.93/30 dev rmnet_mhi0)=0 -[04-02_04:14:12:697] system(ip -4 route add default via 10.129.59.94 dev rmnet_mhi0)=0 -[04-02_04:14:12:699] system(ip -4 link set dev rmnet_mhi0 mtu 1500)=0 - -root@OpenWrt:~# ifconfig rmnet_mhi0 -rmnet_mhi0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 - UP RUNNING NOARP MTU:1500 Metric:1 - RX packets:99379 errors:0 dropped:0 overruns:0 frame:0 - TX packets:176569 errors:0 dropped:0 overruns:0 carrier:0 - collisions:0 txqueuelen:1000 - RX bytes:1528181052 (1.4 GiB) TX bytes:62467192 (59.5 MiB) - -root@OpenWrt:~# ifconfig rmnet_mhi0.1 -rmnet_mhi0.1 Link encap:UNSPEC HWaddr 02-50-F4-00-00-00-00-00-00-00-00-00-00-00-00-00 - inet addr:10.129.59.93 Mask:255.255.255.252 - inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link - UP RUNNING NOARP MTU:1500 Metric:1 - RX packets:1089360 errors:0 dropped:0 overruns:0 frame:0 - TX packets:176581 errors:0 dropped:0 overruns:0 carrier:0 - collisions:0 txqueuelen:1000 - RX bytes:1521449058 (1.4 GiB) TX bytes:57525792 (54.8 MiB) - -# adjust CPU load balancing -root@OpenWrt:~# echo 2 > /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus -root@OpenWrt:~# echo 4 > /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus -root@OpenWrt:~# echo 2000 > /proc/sys/net/core/netdev_max_backlog -root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus -2 -root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus -4 -root@OpenWrt:~# cat /proc/sys/net/core/netdev_max_backlog -2000 diff --git a/quectel_MHI/src/log/QMI_OVER_PCIE.txt b/quectel_MHI/src/log/QMI_OVER_PCIE.txt deleted file mode 100644 index 14a64f9ff..000000000 --- a/quectel_MHI/src/log/QMI_OVER_PCIE.txt +++ /dev/null @@ -1,134 +0,0 @@ -disable ccflags-y += -DCONFIG_MHI_NETDEV_MBIM in pcie_mhi/Makefile - -root@OpenWrt:~# insmod pcie_mhi.ko - -root@OpenWrt:~# dmesg | grep mhi -[ 138.483252] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.6 -[ 138.492350] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306 -[ 138.497564] mhi_q 0000:01:00.0: BAR 0: assigned [mem 0x20300000-0x20300fff 64bit] -[ 138.506952] mhi_q 0000:01:00.0: enabling device (0140 -> 0142) -[ 138.514562] [I][mhi_init_pci_dev] msi_required = 5, msi_allocated = 5, msi_irq = 63 -[ 138.514581] [I][mhi_power_up] dev_state:RESET -[ 138.514587] [I][mhi_async_power_up] Requested to power on -[ 138.514728] [I][mhi_alloc_coherent] size = 114688, dma_handle = 72160000 -[ 138.514734] [I][mhi_init_dev_ctxt] mhi_ctxt->ctrl_seg = c221f000 -[ 138.515030] [I][mhi_async_power_up] dev_state:RESET ee:AMSS -[ 138.515056] [I][mhi_pm_st_worker] Transition to state:READY -[ 138.515067] [I][mhi_pm_st_worker] INVALID_EE -> AMSS -[ 138.515073] [I][mhi_ready_state_transition] Waiting to enter READY state -[ 138.515210] [I][mhi_async_power_up] Power on setup success -[ 138.515227] [I][mhi_pci_probe] Return successful -[ 138.589013] [I][mhi_ready_state_transition] Device in READY State -[ 138.589029] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, pm_state:POR -[ 138.589038] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, INVALID_EE -[ 138.589041] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:POR -[ 138.589046] [I][mhi_init_mmio] Initializing MMIO -[ 138.589050] [I][mhi_init_mmio] CHDBOFF:0x300 -[ 138.589060] [I][mhi_init_mmio] ERDBOFF:0x700 -[ 138.589065] [I][mhi_init_mmio] Programming all MMIO values. -[ 138.706124] [I][mhi_dump_tre] carl_ev evt_state_change mhistate=2 -[ 138.706132] [I][mhi_process_ctrl_ev_ring] MHI state change event to state:M0 -[ 138.706140] [I][mhi_pm_m0_transition] Entered With State:READY PM_STATE:POR -[ 138.706146] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:M0 -[ 138.708699] [I][mhi_dump_tre] carl_ev evt_ee_state execenv=2 -[ 138.708706] [I][mhi_process_ctrl_ev_ring] MHI EE received event:AMSS -[ 138.708726] [I][mhi_pm_st_worker] Transition to state:MISSION MODE -[ 138.708736] [I][mhi_pm_st_worker] INVALID_EE -> AMSS -[ 138.708742] [I][mhi_pm_mission_mode_transition] Processing Mission Mode Transition -[ 138.708758] [I][mhi_init_timesync] No timesync capability found -[ 138.708764] [I][mhi_pm_mission_mode_transition] Adding new devices -[ 138.709785] [I][mhi_dtr_probe] Enter for DTR control channel -[ 138.709794] [I][__mhi_prepare_channel] Entered: preparing channel:18 -[ 138.715378] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1 -[ 138.715397] [I][__mhi_prepare_channel] Chan:18 successfully moved to start state -[ 138.715403] [I][__mhi_prepare_channel] Entered: preparing channel:19 -[ 138.720201] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1 -[ 138.720218] [I][__mhi_prepare_channel] Chan:19 successfully moved to start state -[ 138.720236] [I][mhi_dtr_probe] Exit with ret:0 -[ 138.720590] [I][mhi_netdev_enable_iface] Prepare the channels for transfer -[ 138.720630] [I][__mhi_prepare_channel] Entered: preparing channel:100 -[ 138.757230] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1 -[ 138.757253] [I][__mhi_prepare_channel] Chan:100 successfully moved to start state -[ 138.757259] [I][__mhi_prepare_channel] Entered: preparing channel:101 -[ 138.774352] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1 -[ 138.774370] [I][__mhi_prepare_channel] Chan:101 successfully moved to start state -[ 138.778137] [I][mhi_netdev_enable_iface] Exited. -[ 138.779018] rmnet_vnd_register_device(rmnet_mhi0.1)=0 -[ 138.782283] net rmnet_mhi0 rmnet_mhi0.1: NSS context created -[ 138.800865] [I][mhi_pm_mission_mode_transition] Exit with ret:0 - -root@OpenWrt:~# ./quectel-CM & -root@OpenWrt:~# [04-02_04:12:16:477] Quectel_QConnectManager_Linux_V1.6.0.5 -[04-02_04:12:16:477] Find /sys/bus/usb/devices/4-1 idVendor=0x2c7c idProduct=0x800, bus=0x004, dev=0x002 -[04-02_04:12:16:478] network interface '' or qmidev '' is not exist -[04-02_04:12:16:478] netcard driver = pcie_mhi, driver version = V1.3.0.6 -[04-02_04:12:16:479] qmap_mode = 1, qmap_version = 9, qmap_size = 16384, muxid = 0x81, qmap_netcard = rmnet_mhi0.1 -[04-02_04:12:16:479] Modem works in QMI mode -[04-02_04:12:16:505] cdc_wdm_fd = 7 -[04-02_04:12:17:506] QmiThreadSendQMITimeout pthread_cond_timeout_np timeout -[04-02_04:12:18:516] Get clientWDS = 19 -[04-02_04:12:18:520] Get clientDMS = 1 -[04-02_04:12:18:524] Get clientNAS = 3 -[04-02_04:12:18:527] Get clientUIM = 1 -[04-02_04:12:18:531] Get clientWDA = 1 -[04-02_04:12:18:535] requestBaseBandVersion RM500QGLAAR03A01M4G_BETA_20200107F 1 [Dec 30 2019 17:00:00] -[04-02_04:12:18:539] qmap_settings.rx_urb_size = 16384 -[04-02_04:12:18:539] qmap_settings.ul_data_aggregation_max_datagrams = 16 -[04-02_04:12:18:539] qmap_settings.ul_data_aggregation_max_size = 8192 -[04-02_04:12:18:539] qmap_settings.dl_minimum_padding = 0 -[04-02_04:12:18:550] requestSetLoopBackState(loopback_state=1, replication_factor=14) -[04-02_04:12:18:557] requestGetSIMStatus SIMStatus: SIM_ABSENT -[04-02_04:12:18:560] requestGetProfile[1] ///0 -[04-02_04:12:18:563] requestRegistrationState2 MCC: 0, MNC: 0, PS: Detached, DataCap: UNKNOW -[04-02_04:12:18:565] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED -[04-02_04:12:18:566] ifconfig rmnet_mhi0.1 down -[04-02_04:12:18:571] ifconfig rmnet_mhi0.1 0.0.0.0 -ifconfig: SIOCSIFFLAGS: Network is down -[04-02_04:12:18:575] SetLoopBackInd: loopback_state=1, replication_factor=14 -[04-02_04:12:18:591] requestSetupDataCall WdsConnectionIPv4Handle: 0xe40182a0 -[04-02_04:12:18:601] ifconfig rmnet_mhi0 up -[04-02_04:12:18:607] ifconfig rmnet_mhi0.1 up -[04-02_04:12:18:613] you are use OpenWrt? -[04-02_04:12:18:614] should not calling udhcpc manually? -[04-02_04:12:18:614] should modify /etc/config/network as below? -[04-02_04:12:18:614] config interface wan -[04-02_04:12:18:614] option ifname rmnet_mhi0.1 -[04-02_04:12:18:614] option proto dhcp -[04-02_04:12:18:614] should use "/sbin/ifstaus wan" to check rmnet_mhi0.1 's status? -[04-02_04:12:18:614] busybox udhcpc -f -n -q -t 5 -i rmnet_mhi0.1 -udhcpc: started, v1.28.3 -udhcpc: sending discover -udhcpc: sending select for 192.168.48.171 -udhcpc: lease of 192.168.48.171 obtained, lease time 7200 -[04-02_04:12:18:809] udhcpc: ifconfig rmnet_mhi0.1 192.168.48.171 netmask 255.255.255.248 broadcast + -[04-02_04:12:18:819] udhcpc: setting default routers: 192.168.48.172 - -root@OpenWrt:~# ifconfig rmnet_mhi0 -rmnet_mhi0 Link encap:Ethernet HWaddr 02:50:F4:00:00:00 - inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link - UP RUNNING NOARP MTU:1500 Metric:1 - RX packets:2 errors:0 dropped:0 overruns:0 frame:0 - TX packets:2 errors:0 dropped:0 overruns:0 carrier:0 - collisions:0 txqueuelen:1000 - RX bytes:608 (608.0 B) TX bytes:672 (672.0 B) - -root@OpenWrt:~# ifconfig rmnet_mhi0.1 -rmnet_mhi0.1 Link encap:UNSPEC HWaddr 02-50-F4-00-00-00-00-00-00-00-00-00-00-00-00-00 - inet addr:192.168.48.171 Mask:255.255.255.248 - inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link - UP RUNNING NOARP MTU:1500 Metric:1 - RX packets:2 errors:0 dropped:0 overruns:0 frame:0 - TX packets:2 errors:0 dropped:0 overruns:0 carrier:0 - collisions:0 txqueuelen:1000 - RX bytes:592 (592.0 B) TX bytes:656 (656.0 B) - -# adjust CPU load balancing -root@OpenWrt:~# echo 2 > /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus -root@OpenWrt:~# echo 4 > /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus -root@OpenWrt:~# echo 2000 > /proc/sys/net/core/netdev_max_backlog -root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus -2 -root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus -4 -root@OpenWrt:~# cat /proc/sys/net/core/netdev_max_backlog -2000 \ No newline at end of file diff --git a/quectel_MHI/src/log/QXDM_OVER_PCIE.txt b/quectel_MHI/src/log/QXDM_OVER_PCIE.txt deleted file mode 100644 index f7cff5674..000000000 --- a/quectel_MHI/src/log/QXDM_OVER_PCIE.txt +++ /dev/null @@ -1,14 +0,0 @@ -root@imx6qsabresd:~# ./QLog -p /dev/mhi_DIAG -s log & -root@imx6qsabresd:~# [000.000]QLog Version: Quectel_QLog_Linux&Android_V1.2.4 -[ 298.597963] [I][mhi_uci_open] Node open, ref counts 1 -[ 298.605601] [I][mhi_uci_open] Starting channel -[ 298.612159] [I][__mhi_prepare_channel] Entered: preparing channel:4 -[ 298.629906] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1 -[ 298.635415] [I][__mhi_prepare_channel] Chan:4 successfully moved to start state -[ 298.642749] [I][__mhi_prepare_channel] Entered: preparing channel:5 -[ 298.658043] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1 -[ 298.663543] [I][__mhi_prepare_channel] Chan:5 successfully moved to start state -[000.075]open /dev/mhi_DIAG ttyfd = 3 -[000.075]Press CTRL+C to stop catch log. -[000.096]qlog_logfile_create log/20160920_145758_0000.qmdl logfd=4 -[005.268]recv: 0M 70K 490B in 5181 msec diff --git a/quectel_QMI_WWAN/Makefile b/quectel_QMI_WWAN/Makefile deleted file mode 100644 index b27dfad76..000000000 --- a/quectel_QMI_WWAN/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -# -# Copyright (C) 2015 OpenWrt.org -# -# This is free software, licensed under the GNU General Public License v2. -# See /LICENSE for more information. -# - -include $(TOPDIR)/rules.mk - -PKG_NAME:=qmi_wwan_q -PKG_VERSION:=3.0 -PKG_RELEASE:=2 - -include $(INCLUDE_DIR)/kernel.mk -include $(INCLUDE_DIR)/package.mk - -define KernelPackage/qmi_wwan_q - SUBMENU:=WWAN Support - TITLE:=Quectel Linux USB QMI WWAN Driver - DEPENDS:=+kmod-usb-net +kmod-usb-wdm - FILES:=$(PKG_BUILD_DIR)/qmi_wwan_q.ko - AUTOLOAD:=$(call AutoLoad,81,qmi_wwan_q) -endef - -define KernelPackage/qmi_wwan_q/description - Quectel Linux USB QMI WWAN Driver -endef - -MAKE_OPTS:= \ - ARCH="$(LINUX_KARCH)" \ - CROSS_COMPILE="$(TARGET_CROSS)" \ - CXXFLAGS="$(TARGET_CXXFLAGS)" \ - M="$(PKG_BUILD_DIR)" \ - $(EXTRA_KCONFIG) - -define Build/Prepare - mkdir -p $(PKG_BUILD_DIR) - $(CP) ./src/* $(PKG_BUILD_DIR)/ -endef - -define Build/Compile - $(MAKE) -C "$(LINUX_DIR)" \ - $(MAKE_OPTS) \ - modules -endef - -$(eval $(call KernelPackage,qmi_wwan_q)) diff --git a/quectel_QMI_WWAN/src/Makefile b/quectel_QMI_WWAN/src/Makefile deleted file mode 100644 index 160cb8ea0..000000000 --- a/quectel_QMI_WWAN/src/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -obj-m += qmi_wwan_q.o - -PWD := $(shell pwd) -OUTPUTDIR=/lib/modules/`uname -r`/kernel/drivers/net/usb/ - -ifeq ($(ARCH),) -ARCH := $(shell uname -m) -endif -ifeq ($(CROSS_COMPILE),) -CROSS_COMPILE := -endif -ifeq ($(KDIR),) -KDIR := /lib/modules/$(shell uname -r)/build -ifeq ($(ARCH),i686) -ifeq ($(wildcard $KDIR/arch/$ARCH),) -ARCH=i386 -endif -endif -endif - -ifneq ($(findstring &,${PWD}),) -$(warning "${PWD}") -$(warning "current directory contain special char '&' !") -$(error "please remove it!") -endif - -default: - $(MAKE) ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} -C $(KDIR) M=$(PWD) modules - -install: default - cp $(PWD)/qmi_wwan_q.ko /lib/modules/$(shell uname -r)/kernel/drivers/net/usb/ - depmod - -clean: - rm -rf *~ .tmp_versions modules.order Module.symvers - find . -type f -name "*~" -o -name "*.o" -o -name "*.ko" -o -name "*.cmd" -o -name "*.mod.c" | xargs rm -rf diff --git a/quectel_QMI_WWAN/src/ReleaseNote.txt b/quectel_QMI_WWAN/src/ReleaseNote.txt deleted file mode 100644 index d40804bea..000000000 --- a/quectel_QMI_WWAN/src/ReleaseNote.txt +++ /dev/null @@ -1,146 +0,0 @@ -Release Notes - -[V1.2.2] -Date: 9/7/2022 -enhancement: - 1. Optimization, the network card send queue wakeup is changed from callback to tasklet - 2. Add the function of returning LAN packets in bridge mode - 3. support ndo ioctl on kernel>5.14 - 4. Allow setting MTU greater than 1500 -fix: - -[V1.2.1] -Date: 9/26/2021 -enhancement: - 1. support IPQ5018's NSS - 2. use 'qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c' instead myself rmnet_nss.c - and qmi_wwan_q.ko must load after rmnet_nss.ko -fix: - -[V1.2.0.25] -Date: 9/17/2021 -enhancement: -fix: - 1. add sdx6x platfrom support - -[V1.2.0.24] -Date: 9/6/2021 -enhancement: -fix: - 1. add BG95 support - 2. support Linux 5.14.0 - -[V1.2.0.23] -Date: 3/23/2021 -enhancement: -fix: - 1. add sdx12 platfrom support - -[V1.2.0.22] -Date: 2/5/2021 -enhancement: -fix: - 1. fix panic (memory-access-after-free) when do modem reboot stress test - -[V1.2.0.21] -Date: 2/4/2021 -enhancement: - 1. Code refactoring - QMAP and rmnet -fix: - 1. qmap_qmi_wwan_rx_fixup: change skb_dequeue to __skb_dequeue - -[V1.2.0.20] -Date: 11/2/2020 -enhancement: -fix: - 1. LTE-A modems can not obtain IP by DHCP - -[V1.2.0.19] -Date: 10/9/2020 -enhancement: -fix: - 1. X55 can not access internet after usb resume - -[V1.2.0.18] -Date: 10/9/2020 -enhancement: -fix: - 1. X55: rename rmnet_usb0.1 to wwan0_1 - 1.1 if there is '.', openwrt will think it is vlan, and auto create vlan - 1.2 if there is '.', android will think it is not vaild - 1.3 if named as rmnet_usb0, and SOC is QCOM, QCOM's netmgr will auto manager it - -[V1.2.0.17] -Date: 9/14/2020 -enhancement: - 1. Code refactoring - QMAP size and version -fix: - -[V1.2.0.16] -Date: 9/14/2020 -enhancement: - 1. rx_fixup() check if this is enough skb_headroom() to fill ethernet header -fix: - 1. fix "WARNING: suspicious RCU usage" - -[V1.2.0.15] -Date: 9/10/2020 -enhancement: -fix: - 1. fix compile errors on kernel 3.10~3.13 - -[V1.2.0.14] -Date: 7/24/2020 -enhancement: -fix: - 1. fix QMAP V5 bug on Big Endian CPU - -[V1.2.0.13] -Date: 6/22/2020 -enhancement: -fix: - 1. fix no data traffic when do Upload TPUT test - -[V1.2.0.12] -Date: 5/29/2020 -enhancement: -fix: - 1. IPQ8074: when enable hyfi, quectel-CM will crash system crash - -[V1.2.0.9] -Date: 5/13/2020 -enhancement: -fix: - 1. IPQ8074: enable CONFIG_QCA_NSS_DRV by CONFIG_PINCTRL_IPQ807x (from CONFIG_ARCH_IPQ807x) - -[V1.2.0.8] -Date: 5/9/2020 -enhancement: -fix: - 1. fix compile errors on kernel V3.10 - -[V1.2.0.7] -Date: 4/25/2020 -enhancement: - 1. X55 support bridge mode -fix: - -[V1.2.0.6] -Date: 4/20/2020 -enhancement: - 1. add stat64, or the rx/tx statics will become to 0 when data > 4G - 2. do not use skb_clone, will make QCOM's NSS and SFE 's cpu loading very high -fix: - -[V1.2.0.5] -Date: 4/8/2020 -enhancement: - 1. add attrite link_state, change carrier state accoring link_state - quectel-CM will set link_state to 1 when QMI setup call success. -fix: - -[V1.2.0.4] -Date: 4/8/2020 -enhancement: - 1. support X55's QMAP V5 -fix: diff --git a/quectel_QMI_WWAN/src/qmi_wwan_q.c b/quectel_QMI_WWAN/src/qmi_wwan_q.c deleted file mode 100644 index 6c81b039c..000000000 --- a/quectel_QMI_WWAN/src/qmi_wwan_q.c +++ /dev/null @@ -1,2626 +0,0 @@ -/* - * Copyright (c) 2012 Bjørn Mork - * - * The probing code is heavily inspired by cdc_ether, which is: - * Copyright (C) 2003-2005 by David Brownell - * Copyright (C) 2006 by Ole Andre Vadla Ravnas (ActiveSync) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#if LINUX_VERSION_CODE > KERNEL_VERSION(3,16,0) //8b094cd03b4a3793220d8d8d86a173bfea8c285b -#include -#else -#define timespec64 timespec -#define ktime_get_ts64 ktime_get_ts -#define timespec64_sub timespec_sub -#endif -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef ETH_P_MAP -#define ETH_P_MAP 0xDA1A -#endif - -#if (ETH_P_MAP == 0x00F9) -#undef ETH_P_MAP -#define ETH_P_MAP 0xDA1A -#endif - -#ifndef ARPHRD_RAWIP -#define ARPHRD_RAWIP ARPHRD_NONE -#endif - -#ifdef CONFIG_PINCTRL_IPQ807x -#define CONFIG_QCA_NSS_DRV -//#define CONFIG_QCA_NSS_PACKET_FILTER -#endif - -#define _RMNET_NSS_H_ -#define _RMENT_NSS_H_ -struct rmnet_nss_cb { - int (*nss_create)(struct net_device *dev); - int (*nss_free)(struct net_device *dev); - int (*nss_tx)(struct sk_buff *skb); -}; -static struct rmnet_nss_cb __read_mostly *nss_cb = NULL; -#if defined(CONFIG_PINCTRL_IPQ807x) || defined(CONFIG_PINCTRL_IPQ5018) -#ifdef CONFIG_RMNET_DATA -#define CONFIG_QCA_NSS_DRV -/* define at qsdk/qca/src/linux-4.4/net/rmnet_data/rmnet_data_main.c */ -/* set at qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c */ -extern struct rmnet_nss_cb *rmnet_nss_callbacks __rcu __read_mostly; -#endif -#endif - -/* This driver supports wwan (3G/LTE/?) devices using a vendor - * specific management protocol called Qualcomm MSM Interface (QMI) - - * in addition to the more common AT commands over serial interface - * management - * - * QMI is wrapped in CDC, using CDC encapsulated commands on the - * control ("master") interface of a two-interface CDC Union - * resembling standard CDC ECM. The devices do not use the control - * interface for any other CDC messages. Most likely because the - * management protocol is used in place of the standard CDC - * notifications NOTIFY_NETWORK_CONNECTION and NOTIFY_SPEED_CHANGE - * - * Alternatively, control and data functions can be combined in a - * single USB interface. - * - * Handling a protocol like QMI is out of the scope for any driver. - * It is exported as a character device using the cdc-wdm driver as - * a subdriver, enabling userspace applications ("modem managers") to - * handle it. - * - * These devices may alternatively/additionally be configured using AT - * commands on a serial interface - */ -#define VERSION_NUMBER "V1.2.2" -#define QUECTEL_WWAN_VERSION "Quectel_Linux&Android_QMI_WWAN_Driver_"VERSION_NUMBER -static const char driver_name[] = "qmi_wwan_q"; - -/* driver specific data */ -struct qmi_wwan_state { - struct usb_driver *subdriver; - atomic_t pmcount; - unsigned long unused; - struct usb_interface *control; - struct usb_interface *data; -}; - -/* default ethernet address used by the modem */ -static const u8 default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3}; - -#if 1 //Added by Quectel -/* - Quectel_WCDMA<E_Linux_USB_Driver_User_Guide_V1.9.pdf - 5.6. Test QMAP on GobiNet or QMI WWAN - 0 - no QMAP - 1 - QMAP (Aggregation protocol) - X - QMAP (Multiplexing and Aggregation protocol) -*/ -#define QUECTEL_WWAN_QMAP 4 //MAX is 7 - -#if defined(QUECTEL_WWAN_QMAP) -#define QUECTEL_QMAP_MUX_ID 0x81 - -static uint __read_mostly qmap_mode = 0; -module_param( qmap_mode, uint, S_IRUGO); -module_param_named( rx_qmap, qmap_mode, uint, S_IRUGO ); -#endif - -#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) || defined(CONFIG_BRIDGE_LAN) -#define QUECTEL_BRIDGE_MODE -#endif - -#ifdef QUECTEL_BRIDGE_MODE -static uint __read_mostly bridge_mode = 0/*|BIT(1)*/; -module_param( bridge_mode, uint, S_IRUGO ); -#endif - -#ifdef CONFIG_BRIDGE_LAN -static const u8 broadcast_mac_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; -#endif - -//#define QMI_NETDEV_ONE_CARD_MODE -static uint __read_mostly one_card_mode = 0; - -#if defined(QUECTEL_WWAN_QMAP) -#define QUECTEL_UL_DATA_AGG 1 - -#if defined(QUECTEL_UL_DATA_AGG) -struct tx_agg_ctx { - /* QMIWDS_ADMIN_SET_DATA_FORMAT_RESP TLV_0x17 and TLV_0x18 */ - uint ul_data_aggregation_max_datagrams; //UplinkDataAggregationMaxDatagramsTlv - uint ul_data_aggregation_max_size; //UplinkDataAggregationMaxSizeTlv - uint dl_minimum_padding; //0x1A -}; -#endif - -typedef struct { - unsigned int size; - unsigned int rx_urb_size; - unsigned int ep_type; - unsigned int iface_id; - unsigned int qmap_mode; - unsigned int qmap_version; - unsigned int dl_minimum_padding; - char ifname[8][16]; - unsigned char mux_id[8]; -} RMNET_INFO; - -typedef struct sQmiWwanQmap -{ - struct usbnet *mpNetDev; - struct driver_info driver_info; - atomic_t refcount; - struct net_device *mpQmapNetDev[QUECTEL_WWAN_QMAP]; - uint link_state; - uint qmap_mode; - uint qmap_size; - uint qmap_version; - -#if defined(QUECTEL_UL_DATA_AGG) - struct tx_agg_ctx tx_ctx; - struct tasklet_struct txq; - struct tasklet_struct usbnet_bh; -#endif - -#ifdef QUECTEL_BRIDGE_MODE - uint bridge_mode; - uint bridge_ipv4; - unsigned char bridge_mac[ETH_ALEN]; -#ifdef CONFIG_BRIDGE_LAN - unsigned char bridge_self_mac[ETH_ALEN]; -#endif -#endif - uint use_rmnet_usb; - RMNET_INFO rmnet_info; -} sQmiWwanQmap; - -#if LINUX_VERSION_CODE > KERNEL_VERSION(3,13,0) //8f84985fec10de64a6b4cdfea45f2b0ab8f07c78 -#define MHI_NETDEV_STATUS64 -#endif -struct qmap_priv { - struct usbnet *dev; - struct net_device *real_dev; - struct net_device *self_dev; - u8 offset_id; - u8 mux_id; - u8 qmap_version; // 5~v1, 9~v5 - u8 link_state; - -#if defined(MHI_NETDEV_STATUS64) - struct pcpu_sw_netstats __percpu *stats64; -#endif - - spinlock_t agg_lock; - struct sk_buff *agg_skb; - unsigned agg_count; - struct timespec64 agg_time; - struct hrtimer agg_hrtimer; - struct work_struct agg_wq; - -#ifdef QUECTEL_BRIDGE_MODE - uint bridge_mode; - uint bridge_ipv4; - unsigned char bridge_mac[ETH_ALEN]; -#ifdef CONFIG_BRIDGE_LAN - unsigned char bridge_self_mac[ETH_ALEN]; -#endif -#endif - uint use_qca_nss; -}; - -struct qmap_hdr { - u8 cd_rsvd_pad; - u8 mux_id; - u16 pkt_len; -} __packed; - -enum rmnet_map_v5_header_type { - RMNET_MAP_HEADER_TYPE_UNKNOWN, - RMNET_MAP_HEADER_TYPE_COALESCING = 0x1, - RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD = 0x2, - RMNET_MAP_HEADER_TYPE_ENUM_LENGTH -}; - -/* Main QMAP header */ -struct rmnet_map_header { -#if defined(__LITTLE_ENDIAN_BITFIELD) - u8 pad_len:6; - u8 next_hdr:1; - u8 cd_bit:1; -#elif defined (__BIG_ENDIAN_BITFIELD) - u8 cd_bit:1; - u8 next_hdr:1; - u8 pad_len:6; -#else -#error "Please fix " -#endif - u8 mux_id; - __be16 pkt_len; -} __aligned(1); - -/* QMAP v5 headers */ -struct rmnet_map_v5_csum_header { -#if defined(__LITTLE_ENDIAN_BITFIELD) - u8 next_hdr:1; - u8 header_type:7; - u8 hw_reserved:7; - u8 csum_valid_required:1; -#elif defined (__BIG_ENDIAN_BITFIELD) - u8 header_type:7; - u8 next_hdr:1; - u8 csum_valid_required:1; - u8 hw_reserved:7; -#else -#error "Please fix " -#endif - __be16 reserved; -} __aligned(1); - -#ifdef QUECTEL_BRIDGE_MODE -static int is_qmap_netdev(const struct net_device *netdev); -#endif -#endif - -static const struct driver_info rmnet_usb_info; - -#ifdef QUECTEL_BRIDGE_MODE -static int bridge_arp_reply(struct net_device *net, struct sk_buff *skb, uint bridge_ipv4) { - struct arphdr *parp; - u8 *arpptr, *sha; - u8 sip[4], tip[4], ipv4[4]; - struct sk_buff *reply = NULL; - - ipv4[0] = (bridge_ipv4 >> 24) & 0xFF; - ipv4[1] = (bridge_ipv4 >> 16) & 0xFF; - ipv4[2] = (bridge_ipv4 >> 8) & 0xFF; - ipv4[3] = (bridge_ipv4 >> 0) & 0xFF; - - parp = arp_hdr(skb); - - if (parp->ar_hrd == htons(ARPHRD_ETHER) && parp->ar_pro == htons(ETH_P_IP) - && parp->ar_op == htons(ARPOP_REQUEST) && parp->ar_hln == 6 && parp->ar_pln == 4) { - arpptr = (u8 *)parp + sizeof(struct arphdr); - sha = arpptr; - arpptr += net->addr_len; /* sha */ - memcpy(sip, arpptr, sizeof(sip)); - arpptr += sizeof(sip); - arpptr += net->addr_len; /* tha */ - memcpy(tip, arpptr, sizeof(tip)); - - pr_info("%s sip = %d.%d.%d.%d, tip=%d.%d.%d.%d, ipv4=%d.%d.%d.%d\n", netdev_name(net), - sip[0], sip[1], sip[2], sip[3], tip[0], tip[1], tip[2], tip[3], ipv4[0], ipv4[1], ipv4[2], ipv4[3]); - //wwan0 sip = 10.151.137.255, tip=10.151.138.0, ipv4=10.151.137.255 - if (tip[0] == ipv4[0] && tip[1] == ipv4[1] && (tip[2]&0xFC) == (ipv4[2]&0xFC) && tip[3] != ipv4[3]) - reply = arp_create(ARPOP_REPLY, ETH_P_ARP, *((__be32 *)sip), net, *((__be32 *)tip), sha, default_modem_addr, sha); - - if (reply) { - skb_reset_mac_header(reply); - __skb_pull(reply, skb_network_offset(reply)); - reply->ip_summed = CHECKSUM_UNNECESSARY; - reply->pkt_type = PACKET_HOST; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0) - netif_rx_ni(reply); -#else - netif_rx(reply); -#endif - } - return 1; - } - - return 0; -} - -static struct sk_buff *bridge_mode_tx_fixup(struct net_device *net, struct sk_buff *skb, uint bridge_ipv4, unsigned char *bridge_mac) { - struct ethhdr *ehdr; - const struct iphdr *iph; - - skb_reset_mac_header(skb); - ehdr = eth_hdr(skb); - - if (ehdr->h_proto == htons(ETH_P_ARP)) { - if (bridge_ipv4) - bridge_arp_reply(net, skb, bridge_ipv4); - return NULL; - } - - iph = ip_hdr(skb); - //DBG("iphdr: "); - //PrintHex((void *)iph, sizeof(struct iphdr)); - -// 1 0.000000000 0.0.0.0 255.255.255.255 DHCP 362 DHCP Request - Transaction ID 0xe7643ad7 - if (ehdr->h_proto == htons(ETH_P_IP) && iph->protocol == IPPROTO_UDP && iph->saddr == 0x00000000 && iph->daddr == 0xFFFFFFFF) { - //if (udp_hdr(skb)->dest == htons(67)) //DHCP Request - { - memcpy(bridge_mac, ehdr->h_source, ETH_ALEN); - pr_info("%s PC Mac Address: %02x:%02x:%02x:%02x:%02x:%02x\n", netdev_name(net), - bridge_mac[0], bridge_mac[1], bridge_mac[2], bridge_mac[3], bridge_mac[4], bridge_mac[5]); - } - } - -#ifdef CONFIG_BRIDGE_LAN - //bridge Lan IP 192.168.0.0 - if (ehdr->h_proto == htons(ETH_P_IP) && (iph->daddr & 0xFFFF) == 0xA8C0) - { - struct sk_buff *reply = skb_copy(skb, GFP_ATOMIC); - ehdr = eth_hdr(reply); - - memcpy(ehdr->h_source, default_modem_addr, ETH_ALEN); - if(is_qmap_netdev(net)) - { - struct qmap_priv *priv = netdev_priv(net); - memcpy(ehdr->h_dest, priv->bridge_self_mac, ETH_ALEN); - } - else - { - struct usbnet * usbnetdev = netdev_priv(net); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - memcpy(ehdr->h_dest, pQmapDev->bridge_self_mac, ETH_ALEN); - } - - //pr_info("%s br rx pkt addr: %02x:%02x:%02x:%02x:%02x:%02x -> %02x:%02x:%02x:%02x:%02x:%02x\n", netdev_name(net), - // ehdr->h_source[0], ehdr->h_source[1], ehdr->h_source[2], ehdr->h_source[3], ehdr->h_source[4], ehdr->h_source[5], - // ehdr->h_dest[0], ehdr->h_dest[1], ehdr->h_dest[2], ehdr->h_dest[3], ehdr->h_dest[4], ehdr->h_dest[5]); - - skb_reset_mac_header(reply); - __skb_pull(reply, skb_network_offset(reply)); - reply->ip_summed = CHECKSUM_UNNECESSARY; - reply->pkt_type = PACKET_HOST; -#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0) - netif_rx_ni(reply); -#else - netif_rx(reply); -#endif - return NULL; - } -#endif - - if (memcmp(ehdr->h_source, bridge_mac, ETH_ALEN)) { - return NULL; - } - - return skb; -} - -static void bridge_mode_rx_fixup(sQmiWwanQmap *pQmapDev, struct net_device *net, struct sk_buff *skb) { - uint bridge_mode = 0; - unsigned char *bridge_mac; - - if (pQmapDev->qmap_mode > 1 || pQmapDev->use_rmnet_usb == 1) { - struct qmap_priv *priv = netdev_priv(net); - bridge_mode = priv->bridge_mode; - bridge_mac = priv->bridge_mac; - } - else { - bridge_mode = pQmapDev->bridge_mode; - bridge_mac = pQmapDev->bridge_mac; - } - - if (bridge_mode) - memcpy(eth_hdr(skb)->h_dest, bridge_mac, ETH_ALEN); - else - memcpy(eth_hdr(skb)->h_dest, net->dev_addr, ETH_ALEN); -} -#endif - -#if defined(QUECTEL_WWAN_QMAP) -static ssize_t qmap_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *netdev = to_net_dev(dev); - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - - return snprintf(buf, PAGE_SIZE, "%d\n", pQmapDev->qmap_mode); -} - -static DEVICE_ATTR(qmap_mode, S_IRUGO, qmap_mode_show, NULL); - -static ssize_t qmap_size_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *netdev = to_net_dev(dev); - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - - return snprintf(buf, PAGE_SIZE, "%u\n", pQmapDev->qmap_size); -} - -static DEVICE_ATTR(qmap_size, S_IRUGO, qmap_size_show, NULL); - -static ssize_t link_state_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *netdev = to_net_dev(dev); - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - - return snprintf(buf, PAGE_SIZE, "0x%x\n", pQmapDev->link_state); -} - -static ssize_t link_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct net_device *netdev = to_net_dev(dev); - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - unsigned link_state = 0; - unsigned old_link = pQmapDev->link_state; - uint offset_id = 0; - - link_state = simple_strtoul(buf, NULL, 0); - - if (pQmapDev->qmap_mode == 1) { - pQmapDev->link_state = !!link_state; - } - else if (pQmapDev->qmap_mode > 1) { - offset_id = ((link_state&0x7F) - 1); - - if (offset_id >= pQmapDev->qmap_mode) { - dev_info(dev, "%s offset_id is %d. but qmap_mode is %d\n", __func__, offset_id, pQmapDev->qmap_mode); - return count; - } - - if (link_state&0x80) - pQmapDev->link_state &= ~(1 << offset_id); - else - pQmapDev->link_state |= (1 << offset_id); - } - - if (old_link != pQmapDev->link_state) { - struct net_device *qmap_net = pQmapDev->mpQmapNetDev[offset_id]; - - if (usbnetdev->net->flags & IFF_UP) { - if (pQmapDev->link_state) { - netif_carrier_on(usbnetdev->net); - } - } - - if (qmap_net && qmap_net != netdev) { - struct qmap_priv *priv = netdev_priv(qmap_net); - - priv->link_state = !!(pQmapDev->link_state & (1 << offset_id)); - - if (qmap_net->flags & IFF_UP) { - if (priv->link_state) { - netif_carrier_on(qmap_net); - if (netif_queue_stopped(qmap_net) && !netif_queue_stopped(usbnetdev->net)) - netif_wake_queue(qmap_net); - } - else { - netif_carrier_off(qmap_net); - } - } - } - - if (usbnetdev->net->flags & IFF_UP) { - if (!pQmapDev->link_state) { - netif_carrier_off(usbnetdev->net); - } - } - - dev_info(dev, "link_state 0x%x -> 0x%x\n", old_link, pQmapDev->link_state); - } - - return count; -} - -#ifdef QUECTEL_BRIDGE_MODE -static ssize_t bridge_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct net_device *netdev = to_net_dev(dev); - uint old_mode = 0; - uint bridge_mode = simple_strtoul(buf, NULL, 0); - - if (netdev->type != ARPHRD_ETHER) { - return count; - } - - if (is_qmap_netdev(netdev)) { - struct qmap_priv *priv = netdev_priv(netdev); - old_mode = priv->bridge_mode; - priv->bridge_mode = bridge_mode; - } - else { - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - old_mode = pQmapDev->bridge_mode; - pQmapDev->bridge_mode = bridge_mode; - } - - if (old_mode != bridge_mode) { - dev_info(dev, "bridge_mode change to 0x%x\n", bridge_mode); - } - - return count; -} - -static ssize_t bridge_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *netdev = to_net_dev(dev); - uint bridge_mode = 0; - - if (is_qmap_netdev(netdev)) { - struct qmap_priv *priv = netdev_priv(netdev); - bridge_mode = priv->bridge_mode; - } - else { - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - bridge_mode = pQmapDev->bridge_mode; - } - - return snprintf(buf, PAGE_SIZE, "%u\n", bridge_mode); -} - -static ssize_t bridge_ipv4_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *netdev = to_net_dev(dev); - unsigned int bridge_ipv4 = 0; - unsigned char ipv4[4]; - - if (is_qmap_netdev(netdev)) { - struct qmap_priv *priv = netdev_priv(netdev); - bridge_ipv4 = priv->bridge_ipv4; - } - else { - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - bridge_ipv4 = pQmapDev->bridge_ipv4; - } - - ipv4[0] = (bridge_ipv4 >> 24) & 0xFF; - ipv4[1] = (bridge_ipv4 >> 16) & 0xFF; - ipv4[2] = (bridge_ipv4 >> 8) & 0xFF; - ipv4[3] = (bridge_ipv4 >> 0) & 0xFF; - - return snprintf(buf, PAGE_SIZE, "%d.%d.%d.%d\n", ipv4[0], ipv4[1], ipv4[2], ipv4[3]); -} - -static ssize_t bridge_ipv4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct net_device *netdev = to_net_dev(dev); - - if (is_qmap_netdev(netdev)) { - struct qmap_priv *priv = netdev_priv(netdev); - priv->bridge_ipv4 = simple_strtoul(buf, NULL, 16); - } - else { - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - pQmapDev->bridge_ipv4 = simple_strtoul(buf, NULL, 16); - } - - return count; -} -#endif - -static DEVICE_ATTR(link_state, S_IWUSR | S_IRUGO, link_state_show, link_state_store); -#ifdef QUECTEL_BRIDGE_MODE -static DEVICE_ATTR(bridge_mode, S_IWUSR | S_IRUGO, bridge_mode_show, bridge_mode_store); -static DEVICE_ATTR(bridge_ipv4, S_IWUSR | S_IRUGO, bridge_ipv4_show, bridge_ipv4_store); -#endif - -static struct attribute *qmi_wwan_sysfs_attrs[] = { - &dev_attr_link_state.attr, - &dev_attr_qmap_mode.attr, - &dev_attr_qmap_size.attr, -#ifdef QUECTEL_BRIDGE_MODE - &dev_attr_bridge_mode.attr, - &dev_attr_bridge_ipv4.attr, -#endif - NULL, -}; - -static struct attribute_group qmi_wwan_sysfs_attr_group = { - .attrs = qmi_wwan_sysfs_attrs, -}; - -#ifdef QUECTEL_BRIDGE_MODE -static struct attribute *qmi_qmap_sysfs_attrs[] = { - &dev_attr_bridge_mode.attr, - &dev_attr_bridge_ipv4.attr, - NULL, -}; - -static struct attribute_group qmi_qmap_sysfs_attr_group = { - .attrs = qmi_qmap_sysfs_attrs, -}; -#endif - -static int qmap_open(struct net_device *qmap_net) -{ - struct qmap_priv *priv = netdev_priv(qmap_net); - struct net_device *real_dev = priv->real_dev; - - //printk("%s %s real_dev %d %d %d %d+++\n", __func__, dev->name, - // netif_carrier_ok(real_dev), netif_queue_stopped(real_dev), netif_carrier_ok(dev), netif_queue_stopped(dev)); - - if (!(priv->real_dev->flags & IFF_UP)) - return -ENETDOWN; - - if (priv->link_state) { - netif_carrier_on(real_dev); - netif_carrier_on(qmap_net); - if (netif_queue_stopped(qmap_net) && !netif_queue_stopped(real_dev)) - netif_wake_queue(qmap_net); - } - //printk("%s %s real_dev %d %d %d %d---\n", __func__, dev->name, - // netif_carrier_ok(real_dev), netif_queue_stopped(real_dev), netif_carrier_ok(dev), netif_queue_stopped(dev)); - - return 0; -} - -static int qmap_stop(struct net_device *qmap_net) -{ - //printk("%s %s %d %d+++\n", __func__, dev->name, - // netif_carrier_ok(dev), netif_queue_stopped(dev)); - - netif_carrier_off(qmap_net); - return 0; -} - -static void qmap_wake_queue(sQmiWwanQmap *pQmapDev) -{ - uint i = 0; - - if (!pQmapDev || !pQmapDev->use_rmnet_usb) - return; - - for (i = 0; i < pQmapDev->qmap_mode; i++) { - struct net_device *qmap_net = pQmapDev->mpQmapNetDev[i]; - - if (qmap_net && netif_carrier_ok(qmap_net) && netif_queue_stopped(qmap_net)) { - netif_wake_queue(qmap_net); - } - } -} - -static struct sk_buff * add_qhdr(struct sk_buff *skb, u8 mux_id) { - struct qmap_hdr *qhdr; - int pad = 0; - - pad = skb->len%4; - if (pad) { - pad = 4 - pad; - if (skb_tailroom(skb) < pad) { - printk("skb_tailroom small!\n"); - pad = 0; - } - if (pad) - __skb_put(skb, pad); - } - - qhdr = (struct qmap_hdr *)skb_push(skb, sizeof(struct qmap_hdr)); - qhdr->cd_rsvd_pad = pad; - qhdr->mux_id = mux_id; - qhdr->pkt_len = cpu_to_be16(skb->len - sizeof(struct qmap_hdr)); - - return skb; -} - -static struct sk_buff * add_qhdr_v5(struct sk_buff *skb, u8 mux_id) { - struct rmnet_map_header *map_header; - struct rmnet_map_v5_csum_header *ul_header; - u32 padding, map_datalen; - - map_datalen = skb->len; - padding = map_datalen%4; - if (padding) { - padding = 4 - padding; - if (skb_tailroom(skb) < padding) { - printk("skb_tailroom small!\n"); - padding = 0; - } - if (padding) - __skb_put(skb, padding); - } - - map_header = (struct rmnet_map_header *)skb_push(skb, (sizeof(struct rmnet_map_header) + sizeof(struct rmnet_map_v5_csum_header))); - map_header->cd_bit = 0; - map_header->next_hdr = 1; - map_header->pad_len = padding; - map_header->mux_id = mux_id; - map_header->pkt_len = htons(map_datalen + padding); - - ul_header = (struct rmnet_map_v5_csum_header *)(map_header + 1); - memset(ul_header, 0, sizeof(*ul_header)); - ul_header->header_type = RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD; - if (skb->ip_summed == CHECKSUM_PARTIAL) { -#if 0 //TODO - skb->ip_summed = CHECKSUM_NONE; - /* Ask for checksum offloading */ - ul_header->csum_valid_required = 1; -#endif - } - - return skb; -} - -static void rmnet_vnd_update_rx_stats(struct net_device *net, - unsigned rx_packets, unsigned rx_bytes) { -#if defined(MHI_NETDEV_STATUS64) - struct qmap_priv *dev = netdev_priv(net); - struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->stats64); - - u64_stats_update_begin(&stats64->syncp); -#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0) - stats64->rx_packets += rx_packets; - stats64->rx_bytes += rx_bytes; -#else - u64_stats_add(&stats64->rx_packets, rx_packets); - u64_stats_add(&stats64->rx_bytes, rx_bytes); -#endif - u64_stats_update_end(&stats64->syncp); -#else -#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0) - net->stats.rx_packets += rx_packets; - net->stats.rx_bytes += rx_bytes; -#else - u64_stats_add(&net->stats.rx_packets, rx_packets); - u64_stats_add(&net->stats.rx_bytes, rx_bytes); -#endif -#endif -} - -static void rmnet_vnd_update_tx_stats(struct net_device *net, - unsigned tx_packets, unsigned tx_bytes) { -#if defined(MHI_NETDEV_STATUS64) - struct qmap_priv *dev = netdev_priv(net); - struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->stats64); - - u64_stats_update_begin(&stats64->syncp); -#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0) - stats64->tx_packets += tx_packets; - stats64->tx_bytes += tx_bytes; -#else - u64_stats_add(&stats64->tx_packets, tx_packets); - u64_stats_add(&stats64->tx_bytes, tx_bytes); -#endif - u64_stats_update_end(&stats64->syncp); -#else -#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0) - net->stats.tx_packets += tx_packets; - net->stats.tx_bytes += tx_bytes; -#else - u64_stats_add(&net->stats.tx_packets, tx_packets); - u64_stats_add(&net->tx_bytes, tx_bytes); -#endif -#endif -} - -#if defined(MHI_NETDEV_STATUS64) -static struct rtnl_link_stats64 *_rmnet_vnd_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats) -{ - struct qmap_priv *dev = netdev_priv(net); - unsigned int start; - int cpu; - - netdev_stats_to_stats64(stats, &net->stats); - - if (nss_cb && dev->use_qca_nss) { // rmnet_nss.c:rmnet_nss_tx() will update rx stats - stats->rx_packets = 0; - stats->rx_bytes = 0; - } - - for_each_possible_cpu(cpu) { - struct pcpu_sw_netstats *stats64; - u64 rx_packets, rx_bytes; - u64 tx_packets, tx_bytes; - - stats64 = per_cpu_ptr(dev->stats64, cpu); - - do { - start = u64_stats_fetch_begin(&stats64->syncp); -#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0) - rx_packets = stats64->rx_packets; - rx_bytes = stats64->rx_bytes; - tx_packets = stats64->tx_packets; - tx_bytes = stats64->tx_bytes; -#else - rx_packets = u64_stats_read(&stats64->rx_packets); - rx_bytes = u64_stats_read(&stats64->rx_bytes); - tx_packets = u64_stats_read(&stats64->tx_packets); - tx_bytes = u64_stats_read(&stats64->tx_bytes); -#endif - } while (u64_stats_fetch_retry(&stats64->syncp, start)); - - - stats->rx_packets += rx_packets; - stats->rx_bytes += rx_bytes; - stats->tx_packets += tx_packets; - stats->tx_bytes += tx_bytes; - - } - - return stats; -} - -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 4,10,0 )) //bc1f44709cf27fb2a5766cadafe7e2ad5e9cb221 -static void rmnet_vnd_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats) { - _rmnet_vnd_get_stats64(net, stats); -} -#else -static struct rtnl_link_stats64 *rmnet_vnd_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats) { - return _rmnet_vnd_get_stats64(net, stats); -} -#endif -#endif - -#if defined(QUECTEL_UL_DATA_AGG) -static void usbnet_bh(unsigned long data) { - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)data; - struct tasklet_struct *t = &pQmapDev->usbnet_bh; - bool use_callback = false; - -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 5,8,0 )) //c955e329bb9d44fab75cf2116542fcc0de0473c5 - use_callback = t->use_callback; - if (use_callback) - t->callback(&pQmapDev->mpNetDev->bh); -#endif - - if (!use_callback) - t->func(t->data); - - if (!netif_queue_stopped(pQmapDev->mpNetDev->net)) { - qmap_wake_queue((sQmiWwanQmap *)data); - } -} - -static void rmnet_usb_tx_wake_queue(unsigned long data) { - qmap_wake_queue((sQmiWwanQmap *)data); -} - -#if 0 -static void rmnet_usb_tx_skb_destructor(struct sk_buff *skb) { - struct net_device *net = skb->dev; - struct usbnet * dev = netdev_priv( net ); - struct qmi_wwan_state *info = (void *)&dev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - - if (pQmapDev && pQmapDev->use_rmnet_usb) { - int i; - - for (i = 0; i < pQmapDev->qmap_mode; i++) { - struct net_device *qmap_net = pQmapDev->mpQmapNetDev[i]; - - if (qmap_net && netif_carrier_ok(qmap_net) && netif_queue_stopped(qmap_net)) { - tasklet_schedule(&pQmapDev->txq); - break; - } - } - } -} -#endif - -static int rmnet_usb_tx_agg_skip(struct sk_buff *skb, int offset) -{ - u8 *packet_start = skb->data + offset; - int ready2send = 0; - - if (skb->protocol == htons(ETH_P_IP)) { - struct iphdr *ip4h = (struct iphdr *)(packet_start); - - if (ip4h->protocol == IPPROTO_TCP) { - const struct tcphdr *th = (const struct tcphdr *)(packet_start + sizeof(struct iphdr)); - if (th->psh) { - ready2send = 1; - } - } - else if (ip4h->protocol == IPPROTO_ICMP) - ready2send = 1; - - } else if (skb->protocol == htons(ETH_P_IPV6)) { - struct ipv6hdr *ip6h = (struct ipv6hdr *)(packet_start); - - if (ip6h->nexthdr == NEXTHDR_TCP) { - const struct tcphdr *th = (const struct tcphdr *)(packet_start + sizeof(struct ipv6hdr)); - if (th->psh) { - ready2send = 1; - } - } else if (ip6h->nexthdr == NEXTHDR_ICMP) { - ready2send = 1; - } else if (ip6h->nexthdr == NEXTHDR_FRAGMENT) { - struct frag_hdr *frag; - - frag = (struct frag_hdr *)(packet_start - + sizeof(struct ipv6hdr)); - if (frag->nexthdr == IPPROTO_ICMPV6) - ready2send = 1; - } - } - - return ready2send; -} - -static void rmnet_usb_tx_agg_work(struct work_struct *work) -{ - struct qmap_priv *priv = - container_of(work, struct qmap_priv, agg_wq); - struct sk_buff *skb = NULL; - unsigned long flags; - - spin_lock_irqsave(&priv->agg_lock, flags); - if (likely(priv->agg_skb)) { - skb = priv->agg_skb; - priv->agg_skb = NULL; - priv->agg_count = 0; - skb->protocol = htons(ETH_P_MAP); - skb->dev = priv->real_dev; - ktime_get_ts64(&priv->agg_time); - } - spin_unlock_irqrestore(&priv->agg_lock, flags); - - if (skb) { - int err; -#if 0 - if (!skb->destructor) - skb->destructor = rmnet_usb_tx_skb_destructor; -#endif - err = dev_queue_xmit(skb); - if (err != NET_XMIT_SUCCESS) { - priv->self_dev->stats.tx_errors++; - } - } -} - -static enum hrtimer_restart rmnet_usb_tx_agg_timer_cb(struct hrtimer *timer) -{ - struct qmap_priv *priv = - container_of(timer, struct qmap_priv, agg_hrtimer); - - schedule_work(&priv->agg_wq); - return HRTIMER_NORESTART; -} - -static long agg_time_limit __read_mostly = 1000000L; //reduce this time, can get better TPUT performance, but will increase USB interrupts -module_param(agg_time_limit, long, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(agg_time_limit, "Maximum time packets sit in the agg buf"); - -static long agg_bypass_time __read_mostly = 10000000L; -module_param(agg_bypass_time, long, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(agg_bypass_time, "Skip agg when apart spaced more than this"); - -static int rmnet_usb_tx_agg(struct sk_buff *skb, struct qmap_priv *priv) { - struct qmi_wwan_state *info = (void *)&priv->dev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - struct tx_agg_ctx *ctx = &pQmapDev->tx_ctx; - int ready2send = 0; - int xmit_more = 0; - struct timespec64 diff, now; - struct sk_buff *agg_skb = NULL; - unsigned long flags; - int err; - struct net_device *pNet = priv->self_dev; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(5,1,0) //6b16f9ee89b8d5709f24bc3ac89ae8b5452c0d7c -#if LINUX_VERSION_CODE > KERNEL_VERSION(3,16,0) - xmit_more = skb->xmit_more; -#endif -#else - xmit_more = netdev_xmit_more(); -#endif - - rmnet_vnd_update_tx_stats(pNet, 1, skb->len); - - if (ctx->ul_data_aggregation_max_datagrams == 1) { - skb->protocol = htons(ETH_P_MAP); - skb->dev = priv->real_dev; -#if 0 - if (!skb->destructor) - skb->destructor = rmnet_usb_tx_skb_destructor; -#endif - err = dev_queue_xmit(skb); - if (err != NET_XMIT_SUCCESS) - pNet->stats.tx_errors++; - return NET_XMIT_SUCCESS; - } - -new_packet: - spin_lock_irqsave(&priv->agg_lock, flags); - agg_skb = NULL; - ready2send = 0; - ktime_get_ts64(&now); - diff = timespec64_sub(now, priv->agg_time); - - if (priv->agg_skb) { - if ((priv->agg_skb->len + skb->len) < ctx->ul_data_aggregation_max_size) { - memcpy(skb_put(priv->agg_skb, skb->len), skb->data, skb->len); - priv->agg_count++; - - if (diff.tv_sec > 0 || diff.tv_nsec > agg_time_limit) { - ready2send = 1; - } - else if (priv->agg_count == ctx->ul_data_aggregation_max_datagrams) { - ready2send = 1; - } - else if (xmit_more == 0) { - struct rmnet_map_header *map_header = (struct rmnet_map_header *)skb->data; - size_t offset = sizeof(struct rmnet_map_header); - if (map_header->next_hdr) - offset += sizeof(struct rmnet_map_v5_csum_header); - - ready2send = rmnet_usb_tx_agg_skip(skb, offset); - } - - dev_kfree_skb_any(skb); - skb = NULL; - } - else { - ready2send = 1; - } - - if (ready2send) { - agg_skb = priv->agg_skb; - priv->agg_skb = NULL; - priv->agg_count = 0; - } - } - else if (skb) { - if (diff.tv_sec > 0 || diff.tv_nsec > agg_bypass_time) { - ready2send = 1; - } - else if (xmit_more == 0) { - struct rmnet_map_header *map_header = (struct rmnet_map_header *)skb->data; - size_t offset = sizeof(struct rmnet_map_header); - if (map_header->next_hdr) - offset += sizeof(struct rmnet_map_v5_csum_header); - - ready2send = rmnet_usb_tx_agg_skip(skb, offset); - } - - if (ready2send == 0) { - priv->agg_skb = alloc_skb(ctx->ul_data_aggregation_max_size, GFP_ATOMIC); - if (priv->agg_skb) { - skb_reset_network_header(priv->agg_skb); //protocol da1a is buggy, dev wwan0 - memcpy(skb_put(priv->agg_skb, skb->len), skb->data, skb->len); - priv->agg_count++; - dev_kfree_skb_any(skb); - skb = NULL; - } - else { - ready2send = 1; - } - } - - if (ready2send) { - agg_skb = skb; - skb = NULL; - } - } - - if (ready2send) { - priv->agg_time = now; - } - spin_unlock_irqrestore(&priv->agg_lock, flags); - - if (agg_skb) { - agg_skb->protocol = htons(ETH_P_MAP); - agg_skb->dev = priv->real_dev; -#if 0 - if (!agg_skb->destructor) - agg_skb->destructor = rmnet_usb_tx_skb_destructor; -#endif - err = dev_queue_xmit(agg_skb); - if (err != NET_XMIT_SUCCESS) { - pNet->stats.tx_errors++; - } - } - - if (skb) { - goto new_packet; - } - - if (priv->agg_skb) { - if (!hrtimer_is_queued(&priv->agg_hrtimer)) - hrtimer_start(&priv->agg_hrtimer, ns_to_ktime(NSEC_PER_MSEC * 2), HRTIMER_MODE_REL); - } - - return NET_XMIT_SUCCESS; -} -#endif - -static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb, - struct net_device *pNet) -{ - int err; - struct qmap_priv *priv = netdev_priv(pNet); - - if (netif_queue_stopped(priv->real_dev)) { - netif_stop_queue(pNet); - return NETDEV_TX_BUSY; - } - - //printk("%s 1 skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - if (pNet->type == ARPHRD_ETHER) { - skb_reset_mac_header(skb); - -#ifdef QUECTEL_BRIDGE_MODE - if (priv->bridge_mode && bridge_mode_tx_fixup(pNet, skb, priv->bridge_ipv4, priv->bridge_mac) == NULL) { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } -#endif - - if (skb_pull(skb, ETH_HLEN) == NULL) { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } - } - //printk("%s 2 skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - - if (priv->qmap_version == 5) { - add_qhdr(skb, priv->mux_id); - } - else if (priv->qmap_version == 9) { - add_qhdr_v5(skb, priv->mux_id); - } - else { - dev_kfree_skb_any (skb); - return NETDEV_TX_OK; - } - //printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - - err = rmnet_usb_tx_agg(skb, priv); - - return err; -} - -static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu) -{ - if (new_mtu < 0) - return -EINVAL; - - if (new_mtu > 1500) - printk("warning, set mtu greater than 1500, %d\n", new_mtu); - - rmnet_dev->mtu = new_mtu; - return 0; -} - -/* drivers may override default ethtool_ops in their bind() routine */ -static const struct ethtool_ops rmnet_vnd_ethtool_ops = { - .get_link = ethtool_op_get_link, -}; - -static const struct net_device_ops rmnet_vnd_ops = { - .ndo_open = qmap_open, - .ndo_stop = qmap_stop, - .ndo_start_xmit = rmnet_vnd_start_xmit, - .ndo_change_mtu = rmnet_vnd_change_mtu, -#if defined(MHI_NETDEV_STATUS64) - .ndo_get_stats64 = rmnet_vnd_get_stats64, -#endif -}; - -static void rmnet_usb_ether_setup(struct net_device *rmnet_dev) -{ - ether_setup(rmnet_dev); - - rmnet_dev->flags |= IFF_NOARP; - rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); - -#if LINUX_VERSION_CODE > KERNEL_VERSION(4,10,0) - rmnet_dev->max_mtu = 65535; -#endif - - rmnet_dev->ethtool_ops = &rmnet_vnd_ethtool_ops; - rmnet_dev->netdev_ops = &rmnet_vnd_ops; -} - -static void rmnet_usb_rawip_setup(struct net_device *rmnet_dev) -{ - rmnet_dev->needed_headroom = 16; - - /* Raw IP mode */ - rmnet_dev->header_ops = NULL; /* No header */ - rmnet_dev->type = ARPHRD_RAWIP; - rmnet_dev->hard_header_len = 0; - rmnet_dev->flags |= IFF_NOARP; - rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); - - rmnet_dev->ethtool_ops = &rmnet_vnd_ethtool_ops; - rmnet_dev->netdev_ops = &rmnet_vnd_ops; -} - -static rx_handler_result_t qca_nss_rx_handler(struct sk_buff **pskb) -{ - struct sk_buff *skb = *pskb; - - if (!skb) - return RX_HANDLER_CONSUMED; - - //printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - - if (skb->pkt_type == PACKET_LOOPBACK) - return RX_HANDLER_PASS; - - /* Check this so that we dont loop around netif_receive_skb */ - if (skb->cb[0] == 1) { - skb->cb[0] = 0; - - return RX_HANDLER_PASS; - } - - if (nss_cb) { - nss_cb->nss_tx(skb); - return RX_HANDLER_CONSUMED; - } - - return RX_HANDLER_PASS; -} - -static int qmap_register_device(sQmiWwanQmap * pDev, u8 offset_id) -{ - struct net_device *real_dev = pDev->mpNetDev->net; - struct net_device *qmap_net; - struct qmap_priv *priv; - int err; - char name[IFNAMSIZ]; - int use_qca_nss = !!nss_cb; - - sprintf(name, "%s_%d", real_dev->name, offset_id + 1); -#ifdef NET_NAME_UNKNOWN - qmap_net = alloc_netdev(sizeof(struct qmap_priv), name, - NET_NAME_UNKNOWN, rmnet_usb_ether_setup); -#else - qmap_net = alloc_netdev(sizeof(struct qmap_priv), name, - rmnet_usb_ether_setup); -#endif - if (!qmap_net) - return -ENOBUFS; - - SET_NETDEV_DEV(qmap_net, &real_dev->dev); - priv = netdev_priv(qmap_net); - priv->offset_id = offset_id; - priv->real_dev = real_dev; - priv->self_dev = qmap_net; - priv->dev = pDev->mpNetDev; - priv->qmap_version = pDev->qmap_version; - priv->mux_id = QUECTEL_QMAP_MUX_ID + offset_id; -#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 17, 0) - memcpy (qmap_net->dev_addr, real_dev->dev_addr, ETH_ALEN); -#else - eth_hw_addr_set (real_dev, qmap_net->dev_addr); -#endif - -#ifdef QUECTEL_BRIDGE_MODE - priv->bridge_mode = !!(pDev->bridge_mode & BIT(offset_id)); - qmap_net->sysfs_groups[0] = &qmi_qmap_sysfs_attr_group; - if (priv->bridge_mode) - use_qca_nss = 0; -#ifdef CONFIG_BRIDGE_LAN - memcpy(priv->bridge_self_mac, broadcast_mac_addr, ETH_ALEN); -#endif -#endif - - if (nss_cb && use_qca_nss) { - rmnet_usb_rawip_setup(qmap_net); - } - - priv->agg_skb = NULL; - priv->agg_count = 0; - hrtimer_init(&priv->agg_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - priv->agg_hrtimer.function = rmnet_usb_tx_agg_timer_cb; - INIT_WORK(&priv->agg_wq, rmnet_usb_tx_agg_work); - ktime_get_ts64(&priv->agg_time); - spin_lock_init(&priv->agg_lock); - priv->use_qca_nss = 0; - -#if defined(MHI_NETDEV_STATUS64) - priv->stats64 = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); - if (!priv->stats64) { - err = -ENOBUFS; - goto out_free_newdev; - } -#endif - - err = register_netdev(qmap_net); - if (err) - dev_info(&real_dev->dev, "%s(%s)=%d\n", __func__, qmap_net->name, err); - if (err < 0) - goto out_free_newdev; - netif_device_attach (qmap_net); - netif_carrier_off(qmap_net); - - if (nss_cb && use_qca_nss) { - int rc = nss_cb->nss_create(qmap_net); - if (rc) { - /* Log, but don't fail the device creation */ - netdev_err(qmap_net, "Device will not use NSS path: %d\n", rc); - } else { - priv->use_qca_nss = 1; - netdev_info(qmap_net, "NSS context created\n"); - rtnl_lock(); - netdev_rx_handler_register(qmap_net, qca_nss_rx_handler, NULL); - rtnl_unlock(); - } - } - - strcpy(pDev->rmnet_info.ifname[offset_id], qmap_net->name); - pDev->rmnet_info.mux_id[offset_id] = priv->mux_id; - - pDev->mpQmapNetDev[offset_id] = qmap_net; - - dev_info(&real_dev->dev, "%s %s\n", __func__, qmap_net->name); - - return 0; - -out_free_newdev: - free_netdev(qmap_net); - return err; -} - -static void qmap_unregister_device(sQmiWwanQmap * pDev, u8 offset_id) { - struct net_device *qmap_net = pDev->mpQmapNetDev[offset_id]; - - if (qmap_net != NULL && qmap_net != pDev->mpNetDev->net) { - struct qmap_priv *priv = netdev_priv(qmap_net); - unsigned long flags; - - pr_info("qmap_unregister_device(%s)\n", qmap_net->name); - pDev->mpQmapNetDev[offset_id] = NULL; - netif_carrier_off( qmap_net ); - netif_stop_queue( qmap_net ); - - hrtimer_cancel(&priv->agg_hrtimer); - cancel_work_sync(&priv->agg_wq); - spin_lock_irqsave(&priv->agg_lock, flags); - if (priv->agg_skb) { - kfree_skb(priv->agg_skb); - } - spin_unlock_irqrestore(&priv->agg_lock, flags); - - if (nss_cb && priv->use_qca_nss) { - rtnl_lock(); - netdev_rx_handler_unregister(qmap_net); - rtnl_unlock(); - nss_cb->nss_free(qmap_net); - } - -#if defined(MHI_NETDEV_STATUS64) - free_percpu(priv->stats64); -#endif - unregister_netdev (qmap_net); - free_netdev(qmap_net); - } -} - -typedef struct { - unsigned int size; - unsigned int rx_urb_size; - unsigned int ep_type; - unsigned int iface_id; - unsigned int MuxId; - unsigned int ul_data_aggregation_max_datagrams; //0x17 - unsigned int ul_data_aggregation_max_size ;//0x18 - unsigned int dl_minimum_padding; //0x1A -} QMAP_SETTING; - -#ifdef CONFIG_BRIDGE_LAN -typedef struct { - u8 id; - u8 brmac[ETH_ALEN]; -} BRMAC_SETTING; -#endif - -int qma_setting_store(struct device *dev, QMAP_SETTING *qmap_settings, size_t size) { - struct net_device *netdev = to_net_dev(dev); - struct usbnet * usbnetdev = netdev_priv( netdev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - - if (qmap_settings->size != size) { - dev_err(dev, "ERROR: qmap_settings.size donot match!\n"); - return -EOPNOTSUPP; - } - -#ifdef QUECTEL_UL_DATA_AGG - netif_tx_lock_bh(netdev); - if (pQmapDev->tx_ctx.ul_data_aggregation_max_datagrams == 1 && qmap_settings->ul_data_aggregation_max_datagrams > 1) { - pQmapDev->tx_ctx.ul_data_aggregation_max_datagrams = qmap_settings->ul_data_aggregation_max_datagrams; - pQmapDev->tx_ctx.ul_data_aggregation_max_size = qmap_settings->ul_data_aggregation_max_size; - pQmapDev->tx_ctx.dl_minimum_padding = qmap_settings->dl_minimum_padding; - dev_info(dev, "ul_data_aggregation_max_datagrams=%d, ul_data_aggregation_max_size=%d, dl_minimum_padding=%d\n", - pQmapDev->tx_ctx.ul_data_aggregation_max_datagrams, - pQmapDev->tx_ctx.ul_data_aggregation_max_size, - pQmapDev->tx_ctx.dl_minimum_padding); - } - netif_tx_unlock_bh(netdev); - return 0; -#endif - - return -EOPNOTSUPP; -} - -static int qmap_ndo_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - struct usbnet * usbnetdev = netdev_priv( dev ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - int rc = -EOPNOTSUPP; - uint link_state = 0; - QMAP_SETTING qmap_settings = {0}; -#ifdef CONFIG_BRIDGE_LAN - BRMAC_SETTING brmac_settings = {0}; -#endif - - switch (cmd) { - case 0x89F1: //SIOCDEVPRIVATE - rc = copy_from_user(&link_state, ifr->ifr_ifru.ifru_data, sizeof(link_state)); - if (!rc) { - char buf[32]; - snprintf(buf, sizeof(buf), "%u", link_state); - link_state_store(&dev->dev, NULL, buf, strlen(buf)); - } - break; - - case 0x89F2: //SIOCDEVPRIVATE - rc = copy_from_user(&qmap_settings, ifr->ifr_ifru.ifru_data, sizeof(qmap_settings)); - if (!rc) { - rc = qma_setting_store(&dev->dev, &qmap_settings, sizeof(qmap_settings)); - } - break; - - case 0x89F3: //SIOCDEVPRIVATE - if (pQmapDev->use_rmnet_usb) { - uint i; - - for (i = 0; i < pQmapDev->qmap_mode; i++) { - struct net_device *qmap_net = pQmapDev->mpQmapNetDev[i]; - - if (!qmap_net) - break; - - strcpy(pQmapDev->rmnet_info.ifname[i], qmap_net->name); - } - rc = copy_to_user(ifr->ifr_ifru.ifru_data, &pQmapDev->rmnet_info, sizeof(pQmapDev->rmnet_info)); - } - break; - -#ifdef CONFIG_BRIDGE_LAN - case 0x89F4: //SIOCDEVPRIVATE - rc = copy_from_user(&brmac_settings, ifr->ifr_ifru.ifru_data, sizeof(brmac_settings)); - if (pQmapDev->use_rmnet_usb && brmac_settings.id < qmap_mode) { - struct net_device *qmap_net = pQmapDev->mpQmapNetDev[brmac_settings.id]; - struct qmap_priv *priv = netdev_priv(qmap_net); - memcpy(priv->bridge_self_mac, brmac_settings.brmac, ETH_ALEN); - pr_info("ioctl 0x89F4 change qmapnet bridge(%d) lan mac -> %02x:%02x:%02x:%02x:%02x:%02x\n", brmac_settings.id, priv->bridge_self_mac[0], - priv->bridge_self_mac[1], priv->bridge_self_mac[2], priv->bridge_self_mac[3], priv->bridge_self_mac[4], priv->bridge_self_mac[5]); - } - else if (!pQmapDev->use_rmnet_usb && brmac_settings.id == 0) { - memcpy(pQmapDev->bridge_self_mac, brmac_settings.brmac, ETH_ALEN); - pr_info("ioctl 0x89F4 change usbnet bridge(%d) lan mac -> %02x:%02x:%02x:%02x:%02x:%02x\n", brmac_settings.id, pQmapDev->bridge_self_mac[0], - pQmapDev->bridge_self_mac[1], pQmapDev->bridge_self_mac[2], pQmapDev->bridge_self_mac[3], pQmapDev->bridge_self_mac[4], pQmapDev->bridge_self_mac[5]); - } - else { - pr_info("ioctl 0x89F4 change bridge(%d) lan mac -> error id\n", brmac_settings.id); - rc = -1; - } - break; -#endif - - default: - break; - } - - return rc; -} - -#ifdef QUECTEL_BRIDGE_MODE -static int is_qmap_netdev(const struct net_device *netdev) { - return netdev->netdev_ops == &rmnet_vnd_ops; -} -#endif -#endif - -static struct sk_buff *qmi_wwan_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) { - //MDM9x07,MDM9628,MDM9x40,SDX20,SDX24 only work on RAW IP mode - if ((dev->driver_info->flags & FLAG_NOARP) == 0) - return skb; - - // Skip Ethernet header from message - if (dev->net->hard_header_len == 0) - return skb; - else - skb_reset_mac_header(skb); - -#ifdef QUECTEL_BRIDGE_MODE -{ - struct qmi_wwan_state *info = (void *)&dev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - - if (pQmapDev->bridge_mode && bridge_mode_tx_fixup(dev->net, skb, pQmapDev->bridge_ipv4, pQmapDev->bridge_mac) == NULL) { - dev_kfree_skb_any (skb); - return NULL; - } -} -#endif - - if (skb_pull(skb, ETH_HLEN)) { - return skb; - } else { - dev_err(&dev->intf->dev, "Packet Dropped "); - } - - // Filter the packet out, release it - dev_kfree_skb_any(skb); - return NULL; -} -#endif - -/* Make up an ethernet header if the packet doesn't have one. - * - * A firmware bug common among several devices cause them to send raw - * IP packets under some circumstances. There is no way for the - * driver/host to know when this will happen. And even when the bug - * hits, some packets will still arrive with an intact header. - * - * The supported devices are only capably of sending IPv4, IPv6 and - * ARP packets on a point-to-point link. Any packet with an ethernet - * header will have either our address or a broadcast/multicast - * address as destination. ARP packets will always have a header. - * - * This means that this function will reliably add the appropriate - * header iff necessary, provided our hardware address does not start - * with 4 or 6. - * - * Another common firmware bug results in all packets being addressed - * to 00:a0:c6:00:00:00 despite the host address being different. - * This function will also fixup such packets. - */ -static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -{ - __be16 proto; - - /* This check is no longer done by usbnet */ - if (skb->len < dev->net->hard_header_len) - return 0; - - switch (skb->data[0] & 0xf0) { - case 0x40: - proto = htons(ETH_P_IP); - break; - case 0x60: - proto = htons(ETH_P_IPV6); - break; - case 0x00: - if (is_multicast_ether_addr(skb->data)) - return 1; - /* possibly bogus destination - rewrite just in case */ - skb_reset_mac_header(skb); - goto fix_dest; - default: - /* pass along other packets without modifications */ - return 1; - } - if (skb_headroom(skb) < ETH_HLEN) - return 0; - skb_push(skb, ETH_HLEN); - skb_reset_mac_header(skb); - eth_hdr(skb)->h_proto = proto; - memset(eth_hdr(skb)->h_source, 0, ETH_ALEN); -#if 1 //Added by Quectel - //some kernel will drop ethernet packet which's souce mac is all zero - memcpy(eth_hdr(skb)->h_source, default_modem_addr, ETH_ALEN); -#endif - -fix_dest: -#ifdef QUECTEL_BRIDGE_MODE -{ - struct qmi_wwan_state *info = (void *)&dev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - bridge_mode_rx_fixup(pQmapDev, dev->net, skb); -} -#else - memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN); -#endif - - return 1; -} - -#if defined(QUECTEL_WWAN_QMAP) -static struct sk_buff *qmap_qmi_wwan_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) { - struct qmi_wwan_state *info = (void *)&dev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - - if (unlikely(pQmapDev == NULL)) { - goto drop_skb; - } else if (unlikely(pQmapDev->qmap_mode && !pQmapDev->link_state)) { - dev_dbg(&dev->net->dev, "link_state 0x%x, drop skb, len = %u\n", pQmapDev->link_state, skb->len); - goto drop_skb; - } else if (pQmapDev->qmap_mode == 0) { - skb = qmi_wwan_tx_fixup(dev, skb, flags); - } - else if (pQmapDev->qmap_mode > 1) { - WARN_ON(1); //never reach here. - } - else { - if (likely(skb)) { - skb = qmi_wwan_tx_fixup(dev, skb, flags); - - if (skb) { - if(pQmapDev->qmap_version == 5) - add_qhdr(skb, QUECTEL_QMAP_MUX_ID); - else - add_qhdr_v5(skb, QUECTEL_QMAP_MUX_ID); - } - else { - return NULL; - } - } - } - - return skb; -drop_skb: - dev_kfree_skb_any (skb); - return NULL; -} - -static void qmap_packet_decode(sQmiWwanQmap *pQmapDev, - struct sk_buff *skb_in, struct sk_buff_head *skb_chain) -{ - struct device *dev = &pQmapDev->mpNetDev->net->dev; - struct sk_buff *qmap_skb; - uint dl_minimum_padding = 0; - - if (pQmapDev->qmap_version == 9) - dl_minimum_padding = pQmapDev->tx_ctx.dl_minimum_padding; - - /* __skb_queue_head_init() do not call spin_lock_init(&list->lock), - so should not call skb_queue_tail/queue later. */ - __skb_queue_head_init(skb_chain); - - while (skb_in->len > sizeof(struct qmap_hdr)) { - struct rmnet_map_header *map_header = (struct rmnet_map_header *)skb_in->data; - struct rmnet_map_v5_csum_header *ul_header = NULL; - size_t hdr_size = sizeof(struct rmnet_map_header); - struct net_device *qmap_net; - int pkt_len = ntohs(map_header->pkt_len); - int skb_len; - __be16 protocol; - int mux_id; - int skip_nss = 0; - - if (map_header->next_hdr) { - ul_header = (struct rmnet_map_v5_csum_header *)(map_header + 1); - hdr_size += sizeof(struct rmnet_map_v5_csum_header); - } - - skb_len = pkt_len - (map_header->pad_len&0x3F); - skb_len -= dl_minimum_padding; - - mux_id = map_header->mux_id - QUECTEL_QMAP_MUX_ID; - if (mux_id >= pQmapDev->qmap_mode) { - dev_info(dev, "drop qmap unknow mux_id %x\n", map_header->mux_id); - goto error_pkt; - } - - qmap_net = pQmapDev->mpQmapNetDev[mux_id]; - if (qmap_net == NULL) { - dev_info(dev, "drop qmap unknow mux_id %x\n", map_header->mux_id); - goto skip_pkt; - } - - if (skb_len > qmap_net->mtu) { - dev_info(dev, "drop skb_len=%x larger than mtu=%d\n", skb_len, qmap_net->mtu); - goto error_pkt; - } - - if (skb_in->len < (pkt_len + hdr_size)) { - dev_info(dev, "drop qmap unknow pkt, len=%d, pkt_len=%d\n", skb_in->len, pkt_len); - goto error_pkt; - } - - if (map_header->cd_bit) { - dev_info(dev, "skip qmap command packet\n"); - goto skip_pkt; - } - - switch (skb_in->data[hdr_size] & 0xf0) { - case 0x40: -#ifdef CONFIG_QCA_NSS_PACKET_FILTER - { - struct iphdr *ip4h = (struct iphdr *)(&skb_in->data[hdr_size]); - if (ip4h->protocol == IPPROTO_ICMP) { - skip_nss = 1; - } - } -#endif - protocol = htons(ETH_P_IP); - break; - case 0x60: -#ifdef CONFIG_QCA_NSS_PACKET_FILTER - { - struct ipv6hdr *ip6h = (struct ipv6hdr *)(&skb_in->data[hdr_size]); - if (ip6h->nexthdr == NEXTHDR_ICMP) { - skip_nss = 1; - } - } -#endif - protocol = htons(ETH_P_IPV6); - break; - default: - dev_info(dev, "unknow skb->protocol %02x\n", skb_in->data[hdr_size]); - goto error_pkt; - } - - qmap_skb = netdev_alloc_skb(qmap_net, skb_len); - if (qmap_skb) { - skb_put(qmap_skb, skb_len); - memcpy(qmap_skb->data, skb_in->data + hdr_size, skb_len); - } - - if (qmap_skb == NULL) { - dev_info(dev, "fail to alloc skb, pkt_len = %d\n", skb_len); - goto error_pkt; - } - - skb_reset_transport_header(qmap_skb); - skb_reset_network_header(qmap_skb); - qmap_skb->pkt_type = PACKET_HOST; - skb_set_mac_header(qmap_skb, 0); - qmap_skb->protocol = protocol; - - if(skip_nss) - qmap_skb->cb[0] = 1; - - if (ul_header && ul_header->header_type == RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD - && ul_header->csum_valid_required) { -#if 0 //TODO - qmap_skb->ip_summed = CHECKSUM_UNNECESSARY; -#endif - } - - if (qmap_skb->dev->type == ARPHRD_ETHER) { - skb_push(qmap_skb, ETH_HLEN); - skb_reset_mac_header(qmap_skb); - memcpy(eth_hdr(qmap_skb)->h_source, default_modem_addr, ETH_ALEN); - memcpy(eth_hdr(qmap_skb)->h_dest, qmap_net->dev_addr, ETH_ALEN); - eth_hdr(qmap_skb)->h_proto = protocol; -#ifdef QUECTEL_BRIDGE_MODE - bridge_mode_rx_fixup(pQmapDev, qmap_net, qmap_skb); -#endif - } - - __skb_queue_tail(skb_chain, qmap_skb); - -skip_pkt: - skb_pull(skb_in, pkt_len + hdr_size); - } - -error_pkt: - return; -} - -static int qmap_qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) -{ - struct qmi_wwan_state *info = (void *)&dev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - struct sk_buff *qmap_skb; - struct sk_buff_head skb_chain; - - if (pQmapDev->qmap_mode == 0) - return qmi_wwan_rx_fixup(dev, skb_in); - - qmap_packet_decode(pQmapDev, skb_in, &skb_chain); - - while ((qmap_skb = __skb_dequeue (&skb_chain))) { - if (qmap_skb->dev != dev->net) { - WARN_ON(1); //never reach here. - } - else { - qmap_skb->protocol = 0; - usbnet_skb_return(dev, qmap_skb); - } - } - - return 0; -} -#endif - -/* very simplistic detection of IPv4 or IPv6 headers */ -static bool possibly_iphdr(const char *data) -{ - return (data[0] & 0xd0) == 0x40; -} - -/* disallow addresses which may be confused with IP headers */ -static int qmi_wwan_mac_addr(struct net_device *dev, void *p) -{ - int ret; - struct sockaddr *addr = p; - - ret = eth_prepare_mac_addr_change(dev, p); - if (ret < 0) - return ret; - if (possibly_iphdr(addr->sa_data)) - return -EADDRNOTAVAIL; - eth_commit_mac_addr_change(dev, p); - return 0; -} - -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 4,10,0 )) //bc1f44709cf27fb2a5766cadafe7e2ad5e9cb221 -static void (*_usbnet_get_stats64)(struct net_device *net, struct rtnl_link_stats64 *stats); - -static void qmi_wwan_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats) { - if (_usbnet_get_stats64) ////c8b5d129ee293bcf972e7279ac996bb8a138505c - return _usbnet_get_stats64(net, stats); - - netdev_stats_to_stats64(stats, &net->stats); -} -#else -static struct rtnl_link_stats64 * (*_usbnet_get_stats64)(struct net_device *net, struct rtnl_link_stats64 *stats); - -static struct rtnl_link_stats64 * qmi_wwan_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats) { - if (_usbnet_get_stats64) - return _usbnet_get_stats64(net, stats); - - netdev_stats_to_stats64(stats, &net->stats); - return stats; -} -#endif - -static int qmi_wwan_open (struct net_device *net) { - struct usbnet * usbnetdev = netdev_priv( net ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - int retval; - - retval = usbnet_open(net); - - if (!retval) { - if (pQmapDev && pQmapDev->qmap_mode == 1) { - if (pQmapDev->link_state) - netif_carrier_on(net); - } - } - - return retval; -} - -static netdev_tx_t qmi_wwan_start_xmit (struct sk_buff *skb, - struct net_device *net) -{ - struct usbnet * usbnetdev = netdev_priv( net ); - struct qmi_wwan_state *info = (void *)&usbnetdev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - int retval; - - retval = usbnet_start_xmit(skb, net); - - if (netif_queue_stopped(net) && pQmapDev && pQmapDev->use_rmnet_usb) { - int i; - - for (i = 0; i < pQmapDev->qmap_mode; i++) { - struct net_device *qmap_net = pQmapDev->mpQmapNetDev[i]; - if (qmap_net) { - netif_stop_queue(qmap_net); - } - } - } - - return retval; -} - -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 5,14,0 )) //b9067f5dc4a07c8e24e01a1b277c6722d91be39e -#define use_ndo_siocdevprivate -#endif -#ifdef use_ndo_siocdevprivate -static int qmap_ndo_siocdevprivate(struct net_device *dev, struct ifreq *ifr, void __user *data, int cmd) { - return qmap_ndo_do_ioctl(dev, ifr, cmd); -} -#endif - -static const struct net_device_ops qmi_wwan_netdev_ops = { - .ndo_open = qmi_wwan_open, - .ndo_stop = usbnet_stop, - .ndo_start_xmit = qmi_wwan_start_xmit, - .ndo_tx_timeout = usbnet_tx_timeout, - .ndo_change_mtu = usbnet_change_mtu, - .ndo_get_stats64 = qmi_wwan_get_stats64, - .ndo_set_mac_address = qmi_wwan_mac_addr, - .ndo_validate_addr = eth_validate_addr, -#if defined(QUECTEL_WWAN_QMAP)// && defined(CONFIG_ANDROID) - .ndo_do_ioctl = qmap_ndo_do_ioctl, -#ifdef use_ndo_siocdevprivate - .ndo_siocdevprivate = qmap_ndo_siocdevprivate, -#endif -#endif -}; - -static void ql_net_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) -{ - /* Inherit standard device info */ - usbnet_get_drvinfo(net, info); - strlcpy(info->driver, driver_name, sizeof(info->driver)); - strlcpy(info->version, VERSION_NUMBER, sizeof(info->version)); -} - -static struct ethtool_ops ql_net_ethtool_ops; - -/* using a counter to merge subdriver requests with our own into a - * combined state - */ -static int qmi_wwan_manage_power(struct usbnet *dev, int on) -{ - struct qmi_wwan_state *info = (void *)&dev->data; - int rv; - - dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, - atomic_read(&info->pmcount), on); - - if ((on && atomic_add_return(1, &info->pmcount) == 1) || - (!on && atomic_dec_and_test(&info->pmcount))) { - /* need autopm_get/put here to ensure the usbcore sees - * the new value - */ - rv = usb_autopm_get_interface(dev->intf); - dev->intf->needs_remote_wakeup = on; - if (!rv) - usb_autopm_put_interface(dev->intf); - } - return 0; -} - -static int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on) -{ - struct usbnet *dev = usb_get_intfdata(intf); - - /* can be called while disconnecting */ - if (!dev) - return 0; - return qmi_wwan_manage_power(dev, on); -} - -/* collect all three endpoints and register subdriver */ -static int qmi_wwan_register_subdriver(struct usbnet *dev) -{ - int rv; - struct usb_driver *subdriver = NULL; - struct qmi_wwan_state *info = (void *)&dev->data; - - /* collect bulk endpoints */ - rv = usbnet_get_endpoints(dev, info->data); - if (rv < 0) - goto err; - - /* update status endpoint if separate control interface */ - if (info->control != info->data) - dev->status = &info->control->cur_altsetting->endpoint[0]; - - /* require interrupt endpoint for subdriver */ - if (!dev->status) { - rv = -EINVAL; - goto err; - } - - /* for subdriver power management */ - atomic_set(&info->pmcount, 0); - - /* register subdriver */ -#if (LINUX_VERSION_CODE > KERNEL_VERSION( 5,12,0 )) //cac6fb015f719104e60b1c68c15ca5b734f57b9c - subdriver = usb_cdc_wdm_register(info->control, &dev->status->desc, - 4096, WWAN_PORT_QMI, &qmi_wwan_cdc_wdm_manage_power); -#else - subdriver = usb_cdc_wdm_register(info->control, &dev->status->desc, - 4096, &qmi_wwan_cdc_wdm_manage_power); - -#endif - if (IS_ERR(subdriver)) { - dev_err(&info->control->dev, "subdriver registration failed\n"); - rv = PTR_ERR(subdriver); - goto err; - } - - /* prevent usbnet from using status endpoint */ - dev->status = NULL; - - /* save subdriver struct for suspend/resume wrappers */ - info->subdriver = subdriver; - -err: - return rv; -} - -static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) -{ - int status = -1; - struct usb_driver *driver = driver_of(intf); - struct qmi_wwan_state *info = (void *)&dev->data; - - BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < - sizeof(struct qmi_wwan_state))); - - /* set up initial state */ - info->control = intf; - info->data = intf; - - status = qmi_wwan_register_subdriver(dev); - if (status < 0 && info->control != info->data) { - usb_set_intfdata(info->data, NULL); - usb_driver_release_interface(driver, info->data); - } - - /* Never use the same address on both ends of the link, even - * if the buggy firmware told us to. - */ - if (ether_addr_equal(dev->net->dev_addr, default_modem_addr)) - eth_hw_addr_random(dev->net); - - /* make MAC addr easily distinguishable from an IP header */ - if (possibly_iphdr(dev->net->dev_addr)) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0) - dev->net->dev_addr[0] |= 0x02; /* set local assignment bit */ - dev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */ -#else - u8 addr = dev->net->dev_addr[0]; - - addr |= 0x02; /* set local assignment bit */ - addr &= 0xbf; /* clear "IP" bit */ - dev_addr_mod(dev->net, 0, &addr, 1); -#endif - } - if (!_usbnet_get_stats64) - _usbnet_get_stats64 = dev->net->netdev_ops->ndo_get_stats64; - dev->net->netdev_ops = &qmi_wwan_netdev_ops; - - ql_net_ethtool_ops = *dev->net->ethtool_ops; - ql_net_ethtool_ops.get_drvinfo = ql_net_get_drvinfo; - dev->net->ethtool_ops = &ql_net_ethtool_ops; - -#if 1 //Added by Quectel - if (dev->driver_info->flags & FLAG_NOARP) { - int ret; - char buf[32] = "Module"; - - ret = usb_string(dev->udev, dev->udev->descriptor.iProduct, buf, sizeof(buf)); - if (ret > 0) { - buf[ret] = '\0'; - } - - dev_info(&intf->dev, "Quectel %s work on RawIP mode\n", buf); - dev->net->flags |= IFF_NOARP; - dev->net->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); - - usb_control_msg( - interface_to_usbdev(intf), - usb_sndctrlpipe(interface_to_usbdev(intf), 0), - 0x22, //USB_CDC_REQ_SET_CONTROL_LINE_STATE - 0x21, //USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE - 1, //active CDC DTR - intf->cur_altsetting->desc.bInterfaceNumber, - NULL, 0, 100); - } - - //to advoid module report mtu 1460, but rx 1500 bytes IP packets, and cause the customer's system crash - //next setting can make usbnet.c:usbnet_change_mtu() do not modify rx_urb_size according to hard mtu - dev->rx_urb_size = ETH_DATA_LEN + ETH_HLEN + 6; - -#if defined(QUECTEL_WWAN_QMAP) - if (qmap_mode > QUECTEL_WWAN_QMAP) - qmap_mode = QUECTEL_WWAN_QMAP; - - if (!status) - { - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)kzalloc(sizeof(sQmiWwanQmap), GFP_KERNEL); - - if (pQmapDev == NULL) - return -ENODEV; - -#ifdef QUECTEL_BRIDGE_MODE - pQmapDev->bridge_mode = bridge_mode; -#ifdef CONFIG_BRIDGE_LAN - memcpy(pQmapDev->bridge_self_mac, broadcast_mac_addr, ETH_ALEN); -#endif -#endif - pQmapDev->mpNetDev = dev; - pQmapDev->link_state = 1; - //on OpenWrt, if set rmnet_usb0.1 as WAN, '/sbin/netifd' will auto create VLAN for rmnet_usb0 - dev->net->features |= (NETIF_F_VLAN_CHALLENGED); - - if (dev->driver_info->flags & FLAG_NOARP) - { - int qmap_version = (dev->driver_info->data>>8)&0xFF; - int qmap_size = (dev->driver_info->data)&0xFF; - int idProduct = le16_to_cpu(dev->udev->descriptor.idProduct); - int lte_a = (idProduct == 0x0306 || idProduct == 0x030B || idProduct == 0x0512 || idProduct == 0x0620 || idProduct == 0x0800 || idProduct == 0x0801); - - if (qmap_size > 4096 || dev->udev->speed >= USB_SPEED_SUPER) { //if meet this requirements, must be LTE-A or 5G - lte_a = 1; - } - - pQmapDev->qmap_mode = qmap_mode; - if (lte_a && pQmapDev->qmap_mode == 0) { - pQmapDev->qmap_mode = 1; //force use QMAP - if(qmap_mode == 0) - qmap_mode = 1; //old quectel-CM only check sys/module/wwan0/parameters/qmap_mode - } - - if (pQmapDev->qmap_mode) { - pQmapDev->qmap_version = qmap_version; - pQmapDev->qmap_size = qmap_size*1024; - dev->rx_urb_size = pQmapDev->qmap_size; - //for these modules, if send packet before qmi_start_network, or cause host PC crash, or cause modules crash - pQmapDev->link_state = !lte_a; - - if (pQmapDev->qmap_mode > 1) - pQmapDev->use_rmnet_usb = 1; - else if (idProduct == 0x0800 || idProduct == 0x0801) - pQmapDev->use_rmnet_usb = 1; //benefit for ul data agg -#ifdef QMI_NETDEV_ONE_CARD_MODE - if(pQmapDev->use_rmnet_usb == 1 && pQmapDev->qmap_mode == 1) - one_card_mode = 1; - pQmapDev->rmnet_info.mux_id[0] = QUECTEL_QMAP_MUX_ID; -#endif - pQmapDev->rmnet_info.size = sizeof(RMNET_INFO); - pQmapDev->rmnet_info.rx_urb_size = pQmapDev->qmap_size; - pQmapDev->rmnet_info.ep_type = 2; //DATA_EP_TYPE_HSUSB - pQmapDev->rmnet_info.iface_id = 4; - pQmapDev->rmnet_info.qmap_mode = pQmapDev->qmap_mode; - pQmapDev->rmnet_info.qmap_version = pQmapDev->qmap_version; - pQmapDev->rmnet_info.dl_minimum_padding = 0; - -#if defined(QUECTEL_UL_DATA_AGG) - pQmapDev->tx_ctx.ul_data_aggregation_max_datagrams = 1; - pQmapDev->tx_ctx.ul_data_aggregation_max_size = 1500; -#endif - - if (pQmapDev->use_rmnet_usb && !one_card_mode) { - pQmapDev->driver_info = rmnet_usb_info; - pQmapDev->driver_info.data = dev->driver_info->data; - dev->driver_info = &pQmapDev->driver_info; - } - - if (pQmapDev->use_rmnet_usb && !one_card_mode) { - pQmapDev->usbnet_bh = dev->bh; - tasklet_init(&dev->bh, usbnet_bh, (unsigned long)pQmapDev); - } - } - } - - info->unused = (unsigned long)pQmapDev; - dev->net->sysfs_groups[0] = &qmi_wwan_sysfs_attr_group; - - dev_info(&intf->dev, "rx_urb_size = %zd\n", dev->rx_urb_size); - } -#endif -#endif - - return status; -} - -static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf) -{ - struct qmi_wwan_state *info = (void *)&dev->data; - struct usb_driver *driver = driver_of(intf); - struct usb_interface *other; - - if (dev->udev && dev->udev->state == USB_STATE_CONFIGURED) { - usb_control_msg( - interface_to_usbdev(intf), - usb_sndctrlpipe(interface_to_usbdev(intf), 0), - 0x22, //USB_CDC_REQ_SET_CONTROL_LINE_STATE - 0x21, //USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE - 0, //deactive CDC DTR - intf->cur_altsetting->desc.bInterfaceNumber, - NULL, 0, 100); - } - - if (info->subdriver && info->subdriver->disconnect) - info->subdriver->disconnect(info->control); - - /* allow user to unbind using either control or data */ - if (intf == info->control) - other = info->data; - else - other = info->control; - - /* only if not shared */ - if (other && intf != other) { - usb_set_intfdata(other, NULL); - usb_driver_release_interface(driver, other); - } - - info->subdriver = NULL; - info->data = NULL; - info->control = NULL; -} - -/* suspend/resume wrappers calling both usbnet and the cdc-wdm - * subdriver if present. - * - * NOTE: cdc-wdm also supports pre/post_reset, but we cannot provide - * wrappers for those without adding usbnet reset support first. - */ -static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct usbnet *dev = usb_get_intfdata(intf); - struct qmi_wwan_state *info = (void *)&dev->data; - int ret; - - /* Both usbnet_suspend() and subdriver->suspend() MUST return 0 - * in system sleep context, otherwise, the resume callback has - * to recover device from previous suspend failure. - */ - ret = usbnet_suspend(intf, message); - if (ret < 0) - goto err; - - if (intf == info->control && info->subdriver && - info->subdriver->suspend) - ret = info->subdriver->suspend(intf, message); - if (ret < 0) - usbnet_resume(intf); -err: - return ret; -} - -static int qmi_wwan_resume(struct usb_interface *intf) -{ - struct usbnet *dev = usb_get_intfdata(intf); - struct qmi_wwan_state *info = (void *)&dev->data; - int ret = 0; - bool callsub = (intf == info->control && info->subdriver && - info->subdriver->resume); - - if (callsub) - ret = info->subdriver->resume(intf); - if (ret < 0) - goto err; - ret = usbnet_resume(intf); - if (ret < 0 && callsub) - info->subdriver->suspend(intf, PMSG_SUSPEND); - -#if defined(QUECTEL_WWAN_QMAP) - if (!netif_queue_stopped(dev->net)) { - qmap_wake_queue((sQmiWwanQmap *)info->unused); - } -#endif - -err: - return ret; -} - -static int qmi_wwan_reset_resume(struct usb_interface *intf) -{ - dev_info(&intf->dev, "device do not support reset_resume\n"); - intf->needs_binding = 1; - return -EOPNOTSUPP; -} - -static struct sk_buff *rmnet_usb_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) -{ - //printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - if (skb->protocol != htons(ETH_P_MAP)) { - dev_kfree_skb_any(skb); - return NULL; - } - - return skb; -} - -static int rmnet_usb_rx_fixup(struct usbnet *dev, struct sk_buff *skb) -{ - struct net_device *net = dev->net; - unsigned headroom = skb_headroom(skb); - -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,3,1 )) //7bdd402706cf26bfef9050dfee3f229b7f33ee4f -//some customers port to v3.2 - if (net->type == ARPHRD_ETHER && headroom < ETH_HLEN) { - unsigned tailroom = skb_tailroom(skb); - - if ((tailroom + headroom) >= ETH_HLEN) { - unsigned moveroom = ETH_HLEN - headroom; - - memmove(skb->data + moveroom ,skb->data, skb->len); - skb->data += moveroom; - skb->tail += moveroom; - #ifdef WARN_ONCE - WARN_ONCE(1, "It is better reserve headroom in usbnet.c:rx_submit()!\n"); - #endif - } - } -#endif - - //printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - if (net->type == ARPHRD_ETHER && headroom >= ETH_HLEN) { - //usbnet.c rx_process() usbnet_skb_return() eth_type_trans() - skb_push(skb, ETH_HLEN); - skb_reset_mac_header(skb); - memcpy(eth_hdr(skb)->h_source, default_modem_addr, ETH_ALEN); - memcpy(eth_hdr(skb)->h_dest, net->dev_addr, ETH_ALEN); - eth_hdr(skb)->h_proto = htons(ETH_P_MAP); - - return 1; - } - - return 0; -} - -static rx_handler_result_t rmnet_usb_rx_handler(struct sk_buff **pskb) -{ - struct sk_buff *skb = *pskb; - struct usbnet *dev; - struct qmi_wwan_state *info; - sQmiWwanQmap *pQmapDev; - struct sk_buff *qmap_skb; - struct sk_buff_head skb_chain; - - if (!skb) - goto done; - - //printk("%s skb=%p, protocol=%x, len=%d\n", __func__, skb, skb->protocol, skb->len); - - if (skb->pkt_type == PACKET_LOOPBACK) - return RX_HANDLER_PASS; - - if (skb->protocol != htons(ETH_P_MAP)) { - WARN_ON(1); - return RX_HANDLER_PASS; - } - /* when open hyfi function, run cm will make system crash */ - //dev = rcu_dereference(skb->dev->rx_handler_data); - dev = netdev_priv(skb->dev); - - if (dev == NULL) { - WARN_ON(1); - return RX_HANDLER_PASS; - } - - info = (struct qmi_wwan_state *)&dev->data; - pQmapDev = (sQmiWwanQmap *)info->unused; - - qmap_packet_decode(pQmapDev, skb, &skb_chain); - while ((qmap_skb = __skb_dequeue (&skb_chain))) { - struct net_device *qmap_net = qmap_skb->dev; - - rmnet_vnd_update_rx_stats(qmap_net, 1, qmap_skb->len); - if (qmap_net->type == ARPHRD_ETHER) - __skb_pull(qmap_skb, ETH_HLEN); - netif_receive_skb(qmap_skb); - } - consume_skb(skb); - -done: - return RX_HANDLER_CONSUMED; -} - -static const struct driver_info qmi_wwan_info = { - .description = "WWAN/QMI device", - .flags = FLAG_WWAN, - .bind = qmi_wwan_bind, - .unbind = qmi_wwan_unbind, - .manage_power = qmi_wwan_manage_power, -}; - -#define qmi_wwan_raw_ip_info \ - .description = "WWAN/QMI device", \ - .flags = FLAG_WWAN | FLAG_RX_ASSEMBLE | FLAG_NOARP | FLAG_SEND_ZLP, \ - .bind = qmi_wwan_bind, \ - .unbind = qmi_wwan_unbind, \ - .manage_power = qmi_wwan_manage_power, \ - .tx_fixup = qmap_qmi_wwan_tx_fixup, \ - .rx_fixup = qmap_qmi_wwan_rx_fixup, \ - -static const struct driver_info rmnet_usb_info = { - .description = "RMNET/USB device", - .flags = FLAG_WWAN | FLAG_NOARP | FLAG_SEND_ZLP, - .bind = qmi_wwan_bind, - .unbind = qmi_wwan_unbind, - .manage_power = qmi_wwan_manage_power, - .tx_fixup = rmnet_usb_tx_fixup, - .rx_fixup = rmnet_usb_rx_fixup, -}; - -static const struct driver_info qmi_wwan_raw_ip_info_mdm9x07 = { - qmi_wwan_raw_ip_info - .data = (5<<8)|4, //QMAPV1 and 4KB -}; - -// mdm9x40/sdx12/sdx20/sdx24 share the same config -static const struct driver_info qmi_wwan_raw_ip_info_mdm9x40 = { - qmi_wwan_raw_ip_info - .data = (5<<8)|16, //QMAPV1 and 16KB -}; - -static const struct driver_info qmi_wwan_raw_ip_info_sdx55 = { - qmi_wwan_raw_ip_info - .data = (9<<8)|31, //QMAPV5 and 31KB -}; - -/* map QMI/wwan function by a fixed interface number */ -#define QMI_FIXED_INTF(vend, prod, num) \ - USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \ - .driver_info = (unsigned long)&qmi_wwan_info - -#define QMI_FIXED_RAWIP_INTF(vend, prod, num, chip) \ - USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \ - .driver_info = (unsigned long)&qmi_wwan_raw_ip_info_##chip - -static const struct usb_device_id products[] = { - { QMI_FIXED_INTF(0x05C6, 0x9003, 4) }, /* Quectel UC20 */ - { QMI_FIXED_INTF(0x05C6, 0x9215, 4) }, /* Quectel EC20 (MDM9215) */ - { QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0125, 4, mdm9x07) }, /* Quectel EC20 (MDM9X07)/EC25/EG25 */ - { QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0121, 4, mdm9x07) }, /* Quectel EC21 */ - { QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0191, 4, mdm9x07) }, /* Quectel EG91 */ - { QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0195, 4, mdm9x07) }, /* Quectel EG95 */ - { QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0700, 3, mdm9x07) }, /* Quectel BG95 (at+qcfgext="usbnet","rmnet") */ - { QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0306, 4, mdm9x40) }, /* Quectel EG06/EP06/EM06 */ - { QMI_FIXED_RAWIP_INTF(0x2C7C, 0x030B, 4, mdm9x40) }, /* Quectel EG065k/EG060K */ - { QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0512, 4, mdm9x40) }, /* Quectel EG12/EP12/EM12/EG16/EG18 */ - { QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0296, 4, mdm9x07) }, /* Quectel BG96 */ - { QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0435, 4, mdm9x07) }, /* Quectel AG35 */ - { QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0620, 4, mdm9x40) }, /* Quectel EG20 */ - { QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0800, 4, sdx55) }, /* Quectel RG500 */ - { QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0801, 4, sdx55) }, /* Quectel RG520 */ - { } /* END */ -}; -MODULE_DEVICE_TABLE(usb, products); - -static int qmi_wwan_probe(struct usb_interface *intf, - const struct usb_device_id *prod) -{ - struct usb_device_id *id = (struct usb_device_id *)prod; - - /* Workaround to enable dynamic IDs. This disables usbnet - * blacklisting functionality. Which, if required, can be - * reimplemented here by using a magic "blacklist" value - * instead of 0 in the static device id table - */ - if (!id->driver_info) { - dev_dbg(&intf->dev, "setting defaults for dynamic device id\n"); - id->driver_info = (unsigned long)&qmi_wwan_info; - } - - if (intf->cur_altsetting->desc.bInterfaceClass != 0xff) { - dev_info(&intf->dev, "Quectel module not qmi_wwan mode! please check 'at+qcfg=\"usbnet\"'\n"); - return -ENODEV; - } - - return usbnet_probe(intf, id); -} - -#if defined(QUECTEL_WWAN_QMAP) -static int qmap_qmi_wwan_probe(struct usb_interface *intf, - const struct usb_device_id *prod) -{ - int status = qmi_wwan_probe(intf, prod); - - if (!status) { - struct usbnet *dev = usb_get_intfdata(intf); - struct qmi_wwan_state *info = (void *)&dev->data; - sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused; - unsigned i; - - if (!pQmapDev) - return status; - - tasklet_init(&pQmapDev->txq, rmnet_usb_tx_wake_queue, (unsigned long)pQmapDev); - - if (pQmapDev->qmap_mode == 1) { - pQmapDev->mpQmapNetDev[0] = dev->net; - if (pQmapDev->use_rmnet_usb && !one_card_mode) { - pQmapDev->mpQmapNetDev[0] = NULL; - qmap_register_device(pQmapDev, 0); - } - } - else if (pQmapDev->qmap_mode > 1) { - for (i = 0; i < pQmapDev->qmap_mode; i++) { - qmap_register_device(pQmapDev, i); - } - } - - if (pQmapDev->use_rmnet_usb && !one_card_mode) { - rtnl_lock(); - /* when open hyfi function, run cm will make system crash */ - //netdev_rx_handler_register(dev->net, rmnet_usb_rx_handler, dev); - netdev_rx_handler_register(dev->net, rmnet_usb_rx_handler, NULL); - rtnl_unlock(); - } - - if (pQmapDev->link_state == 0) { - netif_carrier_off(dev->net); - } - } - - return status; -} - -static void qmap_qmi_wwan_disconnect(struct usb_interface *intf) -{ - struct usbnet *dev = usb_get_intfdata(intf); - struct qmi_wwan_state *info; - sQmiWwanQmap *pQmapDev; - uint i; - - if (!dev) - return; - - info = (void *)&dev->data; - pQmapDev = (sQmiWwanQmap *)info->unused; - - if (!pQmapDev) { - return usbnet_disconnect(intf); - } - - pQmapDev->link_state = 0; - - if (pQmapDev->qmap_mode > 1) { - for (i = 0; i < pQmapDev->qmap_mode; i++) { - qmap_unregister_device(pQmapDev, i); - } - } - - if (pQmapDev->use_rmnet_usb && !one_card_mode) { - qmap_unregister_device(pQmapDev, 0); - rtnl_lock(); - netdev_rx_handler_unregister(dev->net); - rtnl_unlock(); - } - - tasklet_kill(&pQmapDev->txq); - - usbnet_disconnect(intf); - /* struct usbnet *dev had free by usbnet_disconnect()->free_netdev(). - so we should access info. */ - //info->unused = 0; - kfree(pQmapDev); -} -#endif - -static struct usb_driver qmi_wwan_driver = { - .name = "qmi_wwan_q", - .id_table = products, - .probe = qmi_wwan_probe, -#if defined(QUECTEL_WWAN_QMAP) - .probe = qmap_qmi_wwan_probe, - .disconnect = qmap_qmi_wwan_disconnect, -#else - .probe = qmi_wwan_probe, - .disconnect = usbnet_disconnect, -#endif - .suspend = qmi_wwan_suspend, - .resume = qmi_wwan_resume, - .reset_resume = qmi_wwan_reset_resume, - .supports_autosuspend = 1, - .disable_hub_initiated_lpm = 1, -}; - -static int __init qmi_wwan_driver_init(void) -{ -#ifdef CONFIG_QCA_NSS_DRV - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (!nss_cb) { - printk(KERN_ERR "qmi_wwan_driver_init: driver load must after '/etc/modules.d/42-rmnet-nss'\n"); - } -#endif - return usb_register(&qmi_wwan_driver); -} -module_init(qmi_wwan_driver_init); -static void __exit qmi_wwan_driver_exit(void) -{ - usb_deregister(&qmi_wwan_driver); -} -module_exit(qmi_wwan_driver_exit); - -MODULE_AUTHOR("Bjørn Mork "); -MODULE_DESCRIPTION("Qualcomm MSM Interface (QMI) WWAN driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(QUECTEL_WWAN_VERSION); diff --git a/quectel_QMI_WWAN/src/rmnet_nss.c b/quectel_QMI_WWAN/src/rmnet_nss.c deleted file mode 100644 index e6e841468..000000000 --- a/quectel_QMI_WWAN/src/rmnet_nss.c +++ /dev/null @@ -1,424 +0,0 @@ -/* Copyright (c) 2019, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define RMNET_NSS_HASH_BITS 8 -#define hash_add_ptr(table, node, key) \ - hlist_add_head(node, &table[hash_ptr(key, HASH_BITS(table))]) - -static DEFINE_HASHTABLE(rmnet_nss_ctx_hashtable, RMNET_NSS_HASH_BITS); - -struct rmnet_nss_ctx { - struct hlist_node hnode; - struct net_device *rmnet_dev; - struct nss_rmnet_rx_handle *nss_ctx; -}; - -enum __rmnet_nss_stat { - RMNET_NSS_RX_ETH, - RMNET_NSS_RX_FAIL, - RMNET_NSS_RX_NON_ETH, - RMNET_NSS_RX_BUSY, - RMNET_NSS_TX_NO_CTX, - RMNET_NSS_TX_SUCCESS, - RMNET_NSS_TX_FAIL, - RMNET_NSS_TX_NONLINEAR, - RMNET_NSS_TX_BAD_IP, - RMNET_NSS_EXCEPTIONS, - RMNET_NSS_EX_BAD_HDR, - RMNET_NSS_EX_BAD_IP, - RMNET_NSS_EX_SUCCESS, - RMNET_NSS_TX_BAD_FRAGS, - RMNET_NSS_TX_LINEARIZE_FAILS, - RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS, - RMNET_NSS_TX_BUSY_LOOP, - RMNET_NSS_NUM_STATS, -}; - -static unsigned long rmnet_nss_stats[RMNET_NSS_NUM_STATS]; - -#define RMNET_NSS_STAT(name, counter, desc) \ - module_param_named(name, rmnet_nss_stats[counter], ulong, 0444); \ - MODULE_PARM_DESC(name, desc) - -RMNET_NSS_STAT(rmnet_nss_rx_ethernet, RMNET_NSS_RX_ETH, - "Number of Ethernet headers successfully removed"); -RMNET_NSS_STAT(rmnet_nss_rx_fail, RMNET_NSS_RX_FAIL, - "Number of Ethernet headers that could not be removed"); -RMNET_NSS_STAT(rmnet_nss_rx_non_ethernet, RMNET_NSS_RX_NON_ETH, - "Number of non-Ethernet packets received"); -RMNET_NSS_STAT(rmnet_nss_rx_busy, RMNET_NSS_RX_BUSY, - "Number of packets dropped decause rmnet_data device was busy"); -RMNET_NSS_STAT(rmnet_nss_tx_slow, RMNET_NSS_TX_NO_CTX, - "Number of packets sent over non-NSS-accelerated rmnet device"); -RMNET_NSS_STAT(rmnet_nss_tx_fast, RMNET_NSS_TX_SUCCESS, - "Number of packets sent over NSS-accelerated rmnet device"); -RMNET_NSS_STAT(rmnet_nss_tx_fail, RMNET_NSS_TX_FAIL, - "Number of packets that NSS could not transmit"); -RMNET_NSS_STAT(rmnet_nss_tx_nonlinear, RMNET_NSS_TX_NONLINEAR, - "Number of non linear sent over NSS-accelerated rmnet device"); -RMNET_NSS_STAT(rmnet_nss_tx_invalid_ip, RMNET_NSS_TX_BAD_IP, - "Number of ingress packets with invalid IP headers"); -RMNET_NSS_STAT(rmnet_nss_tx_invalid_frags, RMNET_NSS_TX_BAD_FRAGS, - "Number of ingress packets with invalid frag format"); -RMNET_NSS_STAT(rmnet_nss_tx_linearize_fail, RMNET_NSS_TX_LINEARIZE_FAILS, - "Number of ingress packets where linearize in tx fails"); -RMNET_NSS_STAT(rmnet_nss_tx_exceptions, RMNET_NSS_EXCEPTIONS, - "Number of times our DL exception handler was invoked"); -RMNET_NSS_STAT(rmnet_nss_exception_non_ethernet, RMNET_NSS_EX_BAD_HDR, - "Number of non-Ethernet exception packets"); -RMNET_NSS_STAT(rmnet_nss_exception_invalid_ip, RMNET_NSS_EX_BAD_IP, - "Number of exception packets with invalid IP headers"); -RMNET_NSS_STAT(rmnet_nss_exception_success, RMNET_NSS_EX_SUCCESS, - "Number of exception packets handled successfully"); -RMNET_NSS_STAT(rmnet_nss_tx_non_zero_headlen_frags, RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS, - "Number of packets with non zero headlen"); -RMNET_NSS_STAT(rmnet_nss_tx_busy_loop, RMNET_NSS_TX_BUSY_LOOP, - "Number of times tx packets busy looped"); - -static void rmnet_nss_inc_stat(enum __rmnet_nss_stat stat) -{ - if (stat >= 0 && stat < RMNET_NSS_NUM_STATS) - rmnet_nss_stats[stat]++; -} - -static struct rmnet_nss_ctx *rmnet_nss_find_ctx(struct net_device *dev) -{ - struct rmnet_nss_ctx *ctx; - struct hlist_head *bucket; - u32 hash; - - hash = hash_ptr(dev, HASH_BITS(rmnet_nss_ctx_hashtable)); - bucket = &rmnet_nss_ctx_hashtable[hash]; - hlist_for_each_entry(ctx, bucket, hnode) { - if (ctx->rmnet_dev == dev) - return ctx; - } - - return NULL; -} - -static void rmnet_nss_free_ctx(struct rmnet_nss_ctx *ctx) -{ - if (ctx) { - hash_del(&ctx->hnode); - nss_rmnet_rx_xmit_callback_unregister(ctx->nss_ctx); - nss_rmnet_rx_destroy_sync(ctx->nss_ctx); - kfree(ctx); - } -} - -/* Pull off an ethernet header, if possible */ -static int rmnet_nss_ethhdr_pull(struct sk_buff *skb) -{ - if (!skb->protocol || skb->protocol == htons(ETH_P_802_3)) { - void *ret = skb_pull(skb, sizeof(struct ethhdr)); - - rmnet_nss_inc_stat((ret) ? RMNET_NSS_RX_ETH : - RMNET_NSS_RX_FAIL); - return !ret; - } - - rmnet_nss_inc_stat(RMNET_NSS_RX_NON_ETH); - return -1; -} - -/* Copy headers to linear section for non linear packets */ -static int rmnet_nss_adjust_header(struct sk_buff *skb) -{ - struct iphdr *iph; - skb_frag_t *frag; - int bytes = 0; - u8 transport; - - if (skb_shinfo(skb)->nr_frags != 1) { - rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_FRAGS); - return -EINVAL; - } - - if (skb_headlen(skb)) { - rmnet_nss_inc_stat(RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS); - return 0; - } - - frag = &skb_shinfo(skb)->frags[0]; - - iph = (struct iphdr *)(skb_frag_address(frag)); - - if (iph->version == 4) { - bytes = iph->ihl*4; - transport = iph->protocol; - } else if (iph->version == 6) { - struct ipv6hdr *ip6h = (struct ipv6hdr *)iph; - - bytes = sizeof(struct ipv6hdr); - /* Dont have to account for extension headers yet */ - transport = ip6h->nexthdr; - } else { - rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_IP); - return -EINVAL; - } - - if (transport == IPPROTO_TCP) { - struct tcphdr *th; - - th = (struct tcphdr *)((u8 *)iph + bytes); - bytes += th->doff * 4; - } else if (transport == IPPROTO_UDP) { - bytes += sizeof(struct udphdr); - } else { - /* cant do anything else here unfortunately so linearize */ - if (skb_linearize(skb)) { - rmnet_nss_inc_stat(RMNET_NSS_TX_LINEARIZE_FAILS); - return -EINVAL; - } else { - return 0; - } - } - - if (bytes > skb_frag_size(frag)) { - rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_FRAGS); - return -EINVAL; - } - - skb_push(skb, bytes); - memcpy(skb->data, iph, bytes); - - /* subtract to account for skb_push */ - skb->len -= bytes; - - frag->page_offset += bytes; - skb_frag_size_sub(frag, bytes); - - /* subtract to account for skb_frag_size_sub */ - skb->data_len -= bytes; - - return 0; -} - -/* Main downlink handler - * Looks up NSS contex associated with the device. If the context is found, - * we add a dummy ethernet header with the approriate protocol field set, - * the pass the packet off to NSS for hardware acceleration. - */ -int rmnet_nss_tx(struct sk_buff *skb) -{ - struct ethhdr *eth; - struct rmnet_nss_ctx *ctx; - struct net_device *dev = skb->dev; - nss_tx_status_t rc; - unsigned int len; - u8 version; - - if (skb_is_nonlinear(skb)) { - if (rmnet_nss_adjust_header(skb)) - goto fail; - else - rmnet_nss_inc_stat(RMNET_NSS_TX_NONLINEAR); - } - - version = ((struct iphdr *)skb->data)->version; - - ctx = rmnet_nss_find_ctx(dev); - if (!ctx) { - rmnet_nss_inc_stat(RMNET_NSS_TX_NO_CTX); - return -EINVAL; - } - - eth = (struct ethhdr *)skb_push(skb, sizeof(*eth)); - memset(ð->h_dest, 0, ETH_ALEN * 2); - if (version == 4) { - eth->h_proto = htons(ETH_P_IP); - } else if (version == 6) { - eth->h_proto = htons(ETH_P_IPV6); - } else { - rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_IP); - goto fail; - } - - skb->protocol = htons(ETH_P_802_3); - /* Get length including ethhdr */ - len = skb->len; - -transmit: - rc = nss_rmnet_rx_tx_buf(ctx->nss_ctx, skb); - if (rc == NSS_TX_SUCCESS) { - /* Increment rmnet_data device stats. - * Don't call rmnet_data_vnd_rx_fixup() to do this, as - * there's no guarantee the skb pointer is still valid. - */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += len; - rmnet_nss_inc_stat(RMNET_NSS_TX_SUCCESS); - return 0; - } else if (rc == NSS_TX_FAILURE_QUEUE) { - rmnet_nss_inc_stat(RMNET_NSS_TX_BUSY_LOOP); - goto transmit; - } - -fail: - rmnet_nss_inc_stat(RMNET_NSS_TX_FAIL); - kfree_skb(skb); - return 1; -} - -/* Called by NSS in the DL exception case. - * Since the packet cannot be sent over the accelerated path, we need to - * handle it. Remove the ethernet header and pass it onward to the stack - * if possible. - */ -void rmnet_nss_receive(struct net_device *dev, struct sk_buff *skb, - struct napi_struct *napi) -{ - rmnet_nss_inc_stat(RMNET_NSS_EXCEPTIONS); - - if (!skb) - return; - - if (rmnet_nss_ethhdr_pull(skb)) { - rmnet_nss_inc_stat(RMNET_NSS_EX_BAD_HDR); - goto drop; - } - - /* reset header pointers */ - skb_reset_transport_header(skb); - skb_reset_network_header(skb); - skb_reset_mac_header(skb); - - /* reset packet type */ - skb->pkt_type = PACKET_HOST; - - skb->dev = dev; - - /* reset protocol type */ - switch (skb->data[0] & 0xF0) { - case 0x40: - skb->protocol = htons(ETH_P_IP); - break; - case 0x60: - skb->protocol = htons(ETH_P_IPV6); - break; - default: - rmnet_nss_inc_stat(RMNET_NSS_EX_BAD_IP); - goto drop; - } - - rmnet_nss_inc_stat(RMNET_NSS_EX_SUCCESS); - - /* Set this so that we dont loop around netif_receive_skb */ - - skb->cb[0] = 1; - - netif_receive_skb(skb); - return; - -drop: - kfree_skb(skb); -} - -/* Called by NSS in the UL acceleration case. - * We are guaranteed to have an ethernet packet here from the NSS hardware, - * We need to pull the header off and invoke our ndo_start_xmit function - * to handle transmitting the packet to the network stack. - */ -void rmnet_nss_xmit(struct net_device *dev, struct sk_buff *skb) -{ - netdev_tx_t ret; - - skb_pull(skb, sizeof(struct ethhdr)); - rmnet_nss_inc_stat(RMNET_NSS_RX_ETH); - - /* NSS takes care of shaping, so bypassing Qdiscs like this is OK */ - ret = dev->netdev_ops->ndo_start_xmit(skb, dev); - if (unlikely(ret == NETDEV_TX_BUSY)) { - dev_kfree_skb_any(skb); - rmnet_nss_inc_stat(RMNET_NSS_RX_BUSY); - } -} - -/* Create and register an NSS context for an rmnet_data device */ -int rmnet_nss_create_vnd(struct net_device *dev) -{ - struct rmnet_nss_ctx *ctx; - - ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC); - if (!ctx) - return -ENOMEM; - - ctx->rmnet_dev = dev; - ctx->nss_ctx = nss_rmnet_rx_create_sync_nexthop(dev, NSS_N2H_INTERFACE, - NSS_C2C_TX_INTERFACE); - if (!ctx->nss_ctx) { - kfree(ctx); - return -1; - } - - nss_rmnet_rx_register(ctx->nss_ctx, rmnet_nss_receive, dev); - nss_rmnet_rx_xmit_callback_register(ctx->nss_ctx, rmnet_nss_xmit); - hash_add_ptr(rmnet_nss_ctx_hashtable, &ctx->hnode, dev); - return 0; -} - -/* Unregister and destroy the NSS context for an rmnet_data device */ -int rmnet_nss_free_vnd(struct net_device *dev) -{ - struct rmnet_nss_ctx *ctx; - - ctx = rmnet_nss_find_ctx(dev); - rmnet_nss_free_ctx(ctx); - - return 0; -} - -static const struct rmnet_nss_cb rmnet_nss = { - .nss_create = rmnet_nss_create_vnd, - .nss_free = rmnet_nss_free_vnd, - .nss_tx = rmnet_nss_tx, -}; - -int __init rmnet_nss_init(void) -{ - pr_err("%s(): initializing rmnet_nss\n", __func__); - RCU_INIT_POINTER(rmnet_nss_callbacks, &rmnet_nss); - return 0; -} - -void __exit rmnet_nss_exit(void) -{ - struct hlist_node *tmp; - struct rmnet_nss_ctx *ctx; - int bkt; - - pr_err("%s(): exiting rmnet_nss\n", __func__); - RCU_INIT_POINTER(rmnet_nss_callbacks, NULL); - - /* Tear down all NSS contexts */ - hash_for_each_safe(rmnet_nss_ctx_hashtable, bkt, tmp, ctx, hnode) - rmnet_nss_free_ctx(ctx); -} - -#if 0 -MODULE_LICENSE("GPL v2"); -module_init(rmnet_nss_init); -module_exit(rmnet_nss_exit); -#endif