diff --git a/application/quectel_CM_5G/Makefile b/application/quectel_CM_5G/Makefile new file mode 100644 index 0000000..fc18da0 --- /dev/null +++ b/application/quectel_CM_5G/Makefile @@ -0,0 +1,41 @@ +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/kernel.mk + +PKG_NAME:= quectel-CM-5G-M +PKG_RELEASE:=5 +PKG_VERSION:=1.6 + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) + SECTION:=utils + CATEGORY:=Utilities + TITLE:=quectel-CM-5G app building test with metric option ( -M ) +endef + +define Package/$(PKG_NAME)/description + quectel-CM-5G app building test +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + $(CP) src/* $(PKG_BUILD_DIR)/ +endef + +define Build/Compile + $(MAKE) -C $(PKG_BUILD_DIR) \ + $(TARGET_CONFIGURE_OPTS) CFLAGS="$(TARGET_CFLAGS)" +endef + +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/usr/bin $(1)/lib/netifd/proto $(1)/lib/netifd + $(INSTALL_BIN) $(PKG_BUILD_DIR)/quectel-CM $(1)/usr/bin/quectel-CM-M + $(INSTALL_BIN) $(PKG_BUILD_DIR)/quectel-qmi-proxy $(1)/usr/bin/ + $(INSTALL_BIN) ./files/rmnet.sh $(1)/lib/netifd/proto + $(INSTALL_BIN) ./files/rmnet6.sh $(1)/lib/netifd/proto + $(INSTALL_BIN) ./files/rmnet6.script $(1)/lib/netifd + $(INSTALL_BIN) ./files/rmnet.script $(1)/lib/netifd + $(INSTALL_BIN) ./files/rmnet_init.sh $(1)/usr/bin +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/application/quectel_CM_5G/files/dhcp b/application/quectel_CM_5G/files/dhcp new file mode 100755 index 0000000..7673927 --- /dev/null +++ b/application/quectel_CM_5G/files/dhcp @@ -0,0 +1,48 @@ + +config dnsmasq + option domainneeded '1' + option boguspriv '1' + option filterwin2k '0' + option localise_queries '1' + option rebind_protection '1' + option rebind_localhost '1' + option local '/lan/' + option domain 'lan' + option expandhosts '1' + option nonegcache '0' + option authoritative '1' + option readethers '1' + option leasefile '/tmp/dhcp.leases' + option resolvfile '/tmp/resolv.conf.auto' + option nonwildcard '1' + option localservice '1' + +config dhcp 'lan' + option interface 'lan' + option start '100' + option limit '150' + option leasetime '12h' + option ra 'relay' + option dhcpv6 'disabled' + option ndp 'relay' + +config dhcp 'wan' + option interface 'wan' + option ignore '1' + option ra 'relay' + option dhcpv6 'disabled' + option ndp 'relay' + option ndproxy_routing '0' + option master '1' + +config dhcp 'wan6' + option ra 'relay' + option dhcpv6 'disabled' + option ndp 'relay' + option ndproxy_routing '0' + option master '1' + option interface 'wan6' + +config odhcpd 'odhcpd' + option loglevel '7' + diff --git a/application/quectel_CM_5G/files/rmnet.script b/application/quectel_CM_5G/files/rmnet.script new file mode 100755 index 0000000..93d2cf0 --- /dev/null +++ b/application/quectel_CM_5G/files/rmnet.script @@ -0,0 +1,65 @@ +#!/bin/sh +# Copyright (c) 2019 Qualcomm Technologies, Inc. +# All Rights Reserved. +# Confidential and Proprietary - Qualcomm Technologies, Inc. + + +[ -z "$1" ] && echo "Error: should be run by rmnet" && exit 1 +[ -z "$2" ] && echo "Error: should be run by rmnet" && exit 1 + +. /lib/functions.sh +. /lib/functions/network.sh +. /lib/netifd/netifd-proto.sh +setup_interface() { + INTERFACE=$1 + CONFIG=/tmp/rmnet_$2_ipv4config + logger "rmnet setup_interface $1 $2 here" + #Fetch information from lower. + [ -f ${CONFIG} ] || { + proto_notify_error "$INTERFACE" "RMNET data call Not ready" + proto_block_restart "$INTERFACE" + return + } + . ${CONFIG} + ip=$PUBLIC_IP + DNS=$DNSSERVERS + router=$GATEWAY + subnet=$NETMASK + interface=$IFNAME + #Send the information to the netifd + proto_init_update "$interface" 1 1 + #ip and subnet + proto_add_ipv4_address "$ip" "${subnet:-255.255.255.0}" + + #Any router? if not, remove below scripts + #router format should be separated by space + for i in $router; do + proto_add_ipv4_route "$i" 32 "" "$ip" + proto_add_ipv4_route 0.0.0.0 0 "$i" "$ip" + done + + #dns information tell the netifd. + for dns in $DNS; do + proto_add_dns_server "$dns" + done + + #Domain information tell the netifd + for domain in $domain; do + proto_add_dns_search "$domain" + done + + #proto_add_data + [ -n "$ZONE" ] && json_add_string zone "$ZONE" + proto_close_data + + proto_send_update "$INTERFACE" + +} + +case "$1" in + renew) + setup_interface $2 $3 + ;; +esac + +exit 0 diff --git a/application/quectel_CM_5G/files/rmnet.sh b/application/quectel_CM_5G/files/rmnet.sh new file mode 100755 index 0000000..f783561 --- /dev/null +++ b/application/quectel_CM_5G/files/rmnet.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# Copyright (c) 2019 Qualcomm Technologies, Inc. +# All Rights Reserved. +# Confidential and Proprietary - Qualcomm Technologies, Inc. + +. /lib/functions.sh +. /lib/functions/network.sh +. ../netifd-proto.sh +init_proto "$@" + +proto_rmnet_setup() { + local cfg="$1" + local iface="$2" + + logger "rmnet started" + #Call rmnet management script below!! + logger "rmnet updated ${cfg} ${iface}" + /lib/netifd/rmnet.script renew $cfg $iface +} + +proto_rmnet_teardown() { + local cfg="$1" + #Tear down rmnet manager script here.*/ +} + +proto_rmnet_init_config() { + #ddno_device=1 + available=1 +} + +add_protocol rmnet diff --git a/application/quectel_CM_5G/files/rmnet6.script b/application/quectel_CM_5G/files/rmnet6.script new file mode 100755 index 0000000..d252c2c --- /dev/null +++ b/application/quectel_CM_5G/files/rmnet6.script @@ -0,0 +1,60 @@ +#!/bin/sh +# Copyright (c) 2019 Qualcomm Technologies, Inc. +# All Rights Reserved. +# Confidential and Proprietary - Qualcomm Technologies, Inc. + + +[ -z "$1" ] && echo "Error: should be run by rmnet" && exit 1 +[ -z "$2" ] && echo "Error: should be run by rmnet" && exit 1 + +. /lib/functions.sh +. /lib/functions/network.sh +. /lib/netifd/netifd-proto.sh +setup_interface() { + INTERFACE=$1 + CONFIG=/tmp/rmnet_$2_ipv6config + logger "rmnet setup_interface $1 $2 here" + #Fetch information from lower. + [ -f ${CONFIG} ] || { + proto_notify_error "$INTERFACE" "RMNET data call NOT ready" + proto_block_restart "$INTERFACE" + return + } + . ${CONFIG} + ADDRESSES=$PUBLIC_IP + interface=$IFNAME + #Send the information to the netifd + proto_init_update "$interface" 1 1 + + #ip and subnet + proto_add_ipv6_address "${PUBLIC_IP}" "128" + proto_add_ipv6_prefix "${PUBLIC_IP}/${PrefixLength}" + + #router format should be separated by space + proto_add_ipv6_route "$GATEWAY" 128 + proto_add_ipv6_route "::0" 0 "$GATEWAY" "" "" "${PUBLIC_IP}/${PrefixLength}" + + #dns information tell the netifd. + for dns in $DNSSERVERS; do + proto_add_dns_server "$dns" + done + + #Domain information tell the netifd + for domain in $domain; do + proto_add_dns_search "$domain" + done + + #proto_add_data + [ -n "$ZONE" ] && json_add_string zone "$ZONE" + proto_close_data + + proto_send_update "$INTERFACE" +} + +case "$1" in + renew|bound) + setup_interface $2 $3 + ;; +esac + +exit 0 diff --git a/application/quectel_CM_5G/files/rmnet6.sh b/application/quectel_CM_5G/files/rmnet6.sh new file mode 100755 index 0000000..94a9598 --- /dev/null +++ b/application/quectel_CM_5G/files/rmnet6.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# Copyright (c) 2019 Qualcomm Technologies, Inc. +# All Rights Reserved. +# Confidential and Proprietary - Qualcomm Technologies, Inc. + +. /lib/functions.sh +. /lib/functions/network.sh +. ../netifd-proto.sh +init_proto "$@" + +proto_rmnet6_setup() { + local cfg="$1" + local iface="$2" + + logger "rmnet6 started" + #Call rmnet management script below!! + /lib/netifd/rmnet6.script renew $cfg $iface + logger "rmnet6 updated" +} + +proto_rmnet6_teardown() { + local cfg="$1" + #Tear down rmnet manager script here.*/ +} + +proto_rmnet6_init_config() { + #ddno_device=1 + available=1 +} + +add_protocol rmnet6 diff --git a/application/quectel_CM_5G/files/rmnet_init.sh b/application/quectel_CM_5G/files/rmnet_init.sh new file mode 100755 index 0000000..3d55438 --- /dev/null +++ b/application/quectel_CM_5G/files/rmnet_init.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +uci set network.wan='interface' +uci set network.wan.ifname='wwan0' +uci set network.wan.proto='rmnet' + +uci set network.wan6='interface' +uci set network.wan6.ifname='wwan0' +uci set network.wan6.proto='rmnet6' + +uci set dhcp.lan.ra='relay' +uci set dhcp.lan.dhcpv6='disabled' +uci set dhcp.lan.ndp='relay' + +uci set dhcp.wan.ra='relay' +uci set dhcp.wan.dhcpv6='disabled' +uci set dhcp.wan.ndp='relay' +uci set dhcp.wan.ndproxy_routing='0' + +uci set dhcp.wan6=dhcp +uci set dhcp.wan6.interface='wan6' +uci set dhcp.wan6.ra='relay' +uci set dhcp.wan6.dhcpv6='disabled' +uci set dhcp.wan6.ndp='relay' +uci set dhcp.wan6.ndproxy_routing='0' +uci set dhcp.wan6.master='1' + +uci set dhcp.odhcpd=odhcpd +uci set dhcp.odhcpd.loglevel='7' + +uci commit diff --git a/application/quectel_CM_5G/src/CMakeLists.txt b/application/quectel_CM_5G/src/CMakeLists.txt new file mode 100644 index 0000000..8ce3f5a --- /dev/null +++ b/application/quectel_CM_5G/src/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 2.4) + +project(quectel-CM) +add_definitions(-Wall -Wextra -Werror -O1) +option(USE_QRTR "Enable QRTR" OFF) + +set( QL_CM_SRC + QmiWwanCM.c GobiNetCM.c main.c MPQMUX.c QMIThread.c util.c qmap_bridge_mode.c mbim-cm.c device.c + atc.c atchannel.c at_tok.c + udhcpc.c + ) + +if(USE_QRTR) +add_definitions(-DCONFIG_QRTR) +set( QRTR_SRC qrtr.c rmnetctl.c) +endif() + +add_executable(quectel-CM ${QL_CM_SRC} ${QRTR_SRC}) +target_link_libraries(quectel-CM PUBLIC pthread) +install (TARGETS quectel-CM DESTINATION bin) + +add_executable(quectel-qmi-proxy quectel-qmi-proxy.c) +target_link_libraries(quectel-qmi-proxy PUBLIC pthread) +install (TARGETS quectel-qmi-proxy DESTINATION bin) + +add_executable(quectel-mbim-proxy quectel-mbim-proxy.c) +target_link_libraries(quectel-mbim-proxy PUBLIC pthread) +install (TARGETS quectel-mbim-proxy DESTINATION bin) + +add_executable(quectel-atc-proxy quectel-atc-proxy.c atchannel.c at_tok.c util.c) +target_link_libraries(quectel-atc-proxy PUBLIC pthread) +install (TARGETS quectel-atc-proxy DESTINATION bin) + +add_executable(quectel-qrtr-proxy quectel-qrtr-proxy.c) +target_link_libraries(quectel-qrtr-proxy PUBLIC pthread) +install (TARGETS quectel-qrtr-proxy DESTINATION bin) diff --git a/application/quectel_CM_5G/src/GobiNetCM.c b/application/quectel_CM_5G/src/GobiNetCM.c new file mode 100644 index 0000000..a57249f --- /dev/null +++ b/application/quectel_CM_5G/src/GobiNetCM.c @@ -0,0 +1,246 @@ +/****************************************************************************** + @file GobiNetCM.c + @brief GobiNet driver. + + DESCRIPTION + Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ +#include +#include +#include +#include +#include +#include "QMIThread.h" + +#ifdef CONFIG_GOBINET +static int qmiclientId[QMUX_TYPE_ALL]; + +// 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; + + fd = qmiclientId[pRequest->QMIHdr.QMIType]; + pRequest->QMIHdr.ClientId = (fd&0xFF) ? fd&0xFF : pRequest->QMIHdr.QMIType; + + if (fd <= 0) { + dbg_time("%s QMIType: %d has no clientID", __func__, pRequest->QMIHdr.QMIType); + return -ENODEV; + } + + // Always ready to 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; + } else { + dbg_time("%s write=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno)); + } + } 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 = cm_open_dev(qcqmi); + 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; + } + + 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_COEX: dbg_time("Get clientCOEX = %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; + + qmiclientId[QMUX_TYPE_WDS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS); + if (profile->enable_ipv6) + 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); +#ifdef CONFIG_COEX_WWAN_STATE + qmiclientId[QMUX_TYPE_COEX] = GobiNetGetClientID(qcqmi, QMUX_TYPE_COEX); +#endif + if (profile->qmap_mode == 0 || profile->loopback_state) {//when QMAP enabled, set data format in GobiNet Driver + qmiclientId[QMUX_TYPE_WDS_ADMIN] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS_ADMIN); + profile->wda_client = qmiclientId[QMUX_TYPE_WDS_ADMIN]; + } + + //donot check clientWDA, there is only one client for WDA, if quectel-CM is killed by SIGKILL, i cannot get client ID for WDA again! + if (qmiclientId[QMUX_TYPE_WDS] == 0) /*|| (clientWDA == -1)*/ { + 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 SIG_EVENT_STOP: + wait_for_request_quit = 1; + break; + default: + break; + } + } + continue; + } + + { + ssize_t nreads; + PQCQMIMSG pResponse = (PQCQMIMSG)cm_recv_buf; + + nreads = read(fd, &pResponse->MUXMsg, sizeof(cm_recv_buf) - 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) ? fd&0xFF : pResponse->QMIHdr.QMIType;; + + 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; +} + +const struct qmi_device_ops gobi_qmidev_ops = { + .deinit = GobiNetDeInit, + .send = GobiNetSendQMI, + .read = GobiNetThread, +}; +#endif + diff --git a/application/quectel_CM_5G/src/Makefile b/application/quectel_CM_5G/src/Makefile new file mode 100644 index 0000000..1468508 --- /dev/null +++ b/application/quectel_CM_5G/src/Makefile @@ -0,0 +1,46 @@ +ifneq ($(CROSS_COMPILE),) +CROSS-COMPILE:=$(CROSS_COMPILE) +endif +#CROSS-COMPILE:=/workspace/buildroot/buildroot-qemu_mips_malta_defconfig/output/host/usr/bin/mips-buildroot-linux-uclibc- +#CROSS-COMPILE:=/workspace/buildroot/buildroot-qemu_arm_vexpress_defconfig/output/host/usr/bin/arm-buildroot-linux-uclibcgnueabi- +#CROSS-COMPILE:=/workspace/buildroot-git/qemu_mips64_malta/output/host/usr/bin/mips-gnu-linux- +ifeq ($(CC),cc) +CC:=$(CROSS-COMPILE)gcc +endif +LD:=$(CROSS-COMPILE)ld + +QL_CM_SRC=QmiWwanCM.c GobiNetCM.c main.c QCQMUX.c QMIThread.c util.c qmap_bridge_mode.c mbim-cm.c device.c +QL_CM_SRC+=atc.c atchannel.c at_tok.c +#QL_CM_SRC+=qrtr.c rmnetctl.c +ifeq (1,1) +QL_CM_DHCP=udhcpc.c +else +LIBMNL=libmnl/ifutils.c libmnl/attr.c libmnl/callback.c libmnl/nlmsg.c libmnl/socket.c +DHCP=libmnl/dhcp/dhcpclient.c libmnl/dhcp/dhcpmsg.c libmnl/dhcp/packet.c +QL_CM_DHCP=udhcpc_netlink.c +QL_CM_DHCP+=${LIBMNL} +endif + +CFLAGS += -Wall -Wextra -Werror -O1 #-s +LDFLAGS += -lpthread -ldl -lrt + +release: clean qmi-proxy mbim-proxy atc-proxy #qrtr-proxy + $(CC) ${CFLAGS} ${QL_CM_SRC} ${QL_CM_DHCP} -o quectel-CM ${LDFLAGS} + +debug: clean + $(CC) ${CFLAGS} -g -DCM_DEBUG ${QL_CM_SRC} ${QL_CM_DHCP} -o quectel-CM -lpthread -ldl -lrt + +qmi-proxy: + $(CC) ${CFLAGS} quectel-qmi-proxy.c -o quectel-qmi-proxy ${LDFLAGS} + +mbim-proxy: + $(CC) ${CFLAGS} quectel-mbim-proxy.c -o quectel-mbim-proxy ${LDFLAGS} + +qrtr-proxy: + $(CC) ${CFLAGS} quectel-qrtr-proxy.c -o quectel-qrtr-proxy ${LDFLAGS} + +atc-proxy: + $(CC) ${CFLAGS} quectel-atc-proxy.c atchannel.c at_tok.c util.c -o quectel-atc-proxy ${LDFLAGS} + +clean: + rm -rf *.o libmnl/*.o quectel-CM quectel-qmi-proxy quectel-mbim-proxy quectel-atc-proxy diff --git a/application/quectel_CM_5G/src/Makefile.am b/application/quectel_CM_5G/src/Makefile.am new file mode 100644 index 0000000..87e5266 --- /dev/null +++ b/application/quectel_CM_5G/src/Makefile.am @@ -0,0 +1,22 @@ +bin_PROGRAMS = quectel-CM +QL_CM_SRC=QmiWwanCM.c GobiNetCM.c main.c MPQMUX.c QMIThread.c util.c qmap_bridge_mode.c mbim-cm.c device.c +QL_CM_SRC+=atc.c atchannel.c at_tok.c +#QL_CM_SRC+=qrtr.c rmnetctl.c +QL_CM_DHCP=udhcpc.c +if USE_QRTR +quectel_CM_CFLAGS = -DCONFIG_QRTR +QL_CM_SRC += qrtr.c rmnetctl.c +if USE_MSM_IPC +quectel_CM_CFLAGS += -DUSE_LINUX_MSM_IPC +endif +endif + +quectel_CM_SOURCES = ${QL_CM_SRC} ${QL_CM_DHCP} + +bin_PROGRAMS += quectel-qmi-proxy +quectel_qmi_proxy_SOURCES = quectel-qmi-proxy.c + +bin_PROGRAMS += quectel-mbim-proxy +quectel_mbim_proxy_SOURCES = quectel-mbim-proxy.c +LIBS = -l pthread +CFLAGS = -Wall -Wextra -Werror -O1 diff --git a/application/quectel_CM_5G/src/NOTICE b/application/quectel_CM_5G/src/NOTICE new file mode 100644 index 0000000..898aac4 --- /dev/null +++ b/application/quectel_CM_5G/src/NOTICE @@ -0,0 +1,7 @@ +This program is totally open souce code, and public domain software for customers of Quectel company. + +The APIs of QMI WWAMN interfaces are defined by Qualcomm. And this program complies with Qualcomm QMI WWAN interfaces specification. + +Customers are free to modify the source codes and redistribute them. + +For those who is not Quectel's customer, all rights are closed, and any copying and commercial development over this progrma is not allowed. diff --git a/application/quectel_CM_5G/src/QCQCTL.h b/application/quectel_CM_5G/src/QCQCTL.h new file mode 100644 index 0000000..6111614 --- /dev/null +++ b/application/quectel_CM_5G/src/QCQCTL.h @@ -0,0 +1,394 @@ +/****************************************************************************** + @file QCQCTL.h + + DESCRIPTION + This module contains QMI QCTL module. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ + + +#ifndef QCQCTL_H +#define QCQCTL_H + +#include "QCQMI.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 QMI_MESSAGE_CTL_INTERNAL_PROXY_OPEN 0xFF00 + +#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_LIBQMI_PROXY_OPEN_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 + char device_path[0]; // result code +} __attribute__ ((packed)) QMICTL_LIBQMI_PROXY_OPEN_MSG, *PQMICTL_LIBQMI_PROXY_OPEN_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; + QMICTL_LIBQMI_PROXY_OPEN_MSG LibQmiProxyOpenReq; + }; +} __attribute__ ((packed)) QMICTL_MSG, *PQMICTL_MSG; +#pragma pack(pop) + +#endif //QCQCTL_H \ No newline at end of file diff --git a/application/quectel_CM_5G/src/QCQMI.h b/application/quectel_CM_5G/src/QCQMI.h new file mode 100644 index 0000000..e9cab6b --- /dev/null +++ b/application/quectel_CM_5G/src/QCQMI.h @@ -0,0 +1,320 @@ +/****************************************************************************** + @file QCQMI.h + + DESCRIPTION + This module contains QMI module. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ + + +#ifndef USBQMI_H +#define USBQMI_H + +typedef uint8_t uint8; +typedef int8_t int8; +typedef uint16_t uint16; +typedef int16_t int16; +typedef uint32_t uint32; +typedef uint64_t uint64; + +typedef signed char CHAR; +typedef unsigned char UCHAR; +typedef short SHORT; +typedef unsigned short USHORT; +typedef int INT; +typedef unsigned int UINT; +typedef long LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef signed 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_COEX = 0x22, + 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; + +typedef struct _QMI_TLV +{ + UCHAR TLVType; + USHORT TLVLength; + union { + int8_t s8; + uint8_t u8; + int16_t s16; + uint16_t u16; + int32_t s32; + uint32_t u32; + uint64_t u64; + }; +} __attribute__ ((packed)) QMI_TLV, *PQMI_TLV; + +// 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/application/quectel_CM_5G/src/QCQMUX.c b/application/quectel_CM_5G/src/QCQMUX.c new file mode 100644 index 0000000..97f2d1a --- /dev/null +++ b/application/quectel_CM_5G/src/QCQMUX.c @@ -0,0 +1,477 @@ +/****************************************************************************** + @file MPQMUX.c + @brief QMI mux. + + DESCRIPTION + Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ + +#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} + +#if 0 +static const QMI_NAME_T qmi_IFType[] = { +{USB_CTL_MSG_TYPE_QMI, "USB_CTL_MSG_TYPE_QMI"}, +}; + +static const QMI_NAME_T qmi_CtlFlags[] = { +qmi_name_item(QMICTL_CTL_FLAG_CMD), +qmi_name_item(QCQMI_CTL_FLAG_SERVICE), +}; + +static const QMI_NAME_T qmi_QMIType[] = { +qmi_name_item(QMUX_TYPE_CTL), +qmi_name_item(QMUX_TYPE_WDS), +qmi_name_item(QMUX_TYPE_DMS), +qmi_name_item(QMUX_TYPE_NAS), +qmi_name_item(QMUX_TYPE_QOS), +qmi_name_item(QMUX_TYPE_WMS), +qmi_name_item(QMUX_TYPE_PDS), +qmi_name_item(QMUX_TYPE_WDS_ADMIN), +qmi_name_item(QMUX_TYPE_COEX), +}; + +static const QMI_NAME_T qmi_ctl_CtlFlags[] = { +qmi_name_item(QMICTL_FLAG_REQUEST), +qmi_name_item(QMICTL_FLAG_RESPONSE), +qmi_name_item(QMICTL_FLAG_INDICATION), +}; +#endif + +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 +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_qos_Type[] = { +qmi_name_item( QMI_QOS_SET_EVENT_REPORT_REQ), // 0x0001 +qmi_name_item( QMI_QOS_SET_EVENT_REPORT_RESP), // 0x0001 +qmi_name_item( QMI_QOS_SET_EVENT_REPORT_IND), // 0x0001 +qmi_name_item( QMI_QOS_BIND_DATA_PORT_REQ), // 0x002B +qmi_name_item( QMI_QOS_BIND_DATA_PORT_RESP), // 0x002B +qmi_name_item( QMI_QOS_INDICATION_REGISTER_REQ), // 0x002F +qmi_name_item( QMI_QOS_INDICATION_REGISTER_RESP), // 0x002F +qmi_name_item( QMI_QOS_GLOBAL_QOS_FLOW_IND), // 0x0031 +qmi_name_item( QMI_QOS_GET_QOS_INFO_REQ), // 0x0033 +qmi_name_item( QMI_QOS_GET_QOS_INFO_RESP), // 0x0033 +}; + +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_CELL_LOCATION_INFO_REQ), +qmi_name_item(QMINAS_GET_CELL_LOCATION_INFO_RESP), +qmi_name_item(QMINAS_GET_PLMN_NAME_REQ), // 0x0044 +qmi_name_item(QMINAS_GET_PLMN_NAME_RESP), // 0x0044 +qmi_name_item(QUECTEL_PACKET_TRANSFER_START_IND), // 0X100 +qmi_name_item(QUECTEL_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 +qmi_name_item(QMINAS_GET_SIG_INFO_REQ), +qmi_name_item(QMINAS_GET_SIG_INFO_RESP), + +}; + +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 +qmi_name_item(QMI_WDA_SET_LOOPBACK_CONFIG_REQ), // 0x002F +qmi_name_item(QMI_WDA_SET_LOOPBACK_CONFIG_RESP), // 0x002F +qmi_name_item(QMI_WDA_SET_LOOPBACK_CONFIG_IND), // 0x002F +}; + +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 QMI_NAME_T qmux_coex_Type[] = { +qmi_name_item(QMI_COEX_GET_WWAN_STATE_REQ), // 0x0022 +qmi_name_item(QMI_COEX_GET_WWAN_STATE_RESP), // 0x0022 +}; + +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); + const 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: + case QMUX_TYPE_WDS_IPV6: + 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: + dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), + QMUX_NAME(qmux_qos_Type, le16_to_cpu(QMUXMsgHdr->Type), tag)); + break; + case QMUX_TYPE_COEX: + dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), + QMUX_NAME(qmux_coex_Type, le16_to_cpu(QMUXMsgHdr->Type), tag)); + break; + 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/application/quectel_CM_5G/src/QCQMUX.h b/application/quectel_CM_5G/src/QCQMUX.h new file mode 100644 index 0000000..eb42dd8 --- /dev/null +++ b/application/quectel_CM_5G/src/QCQMUX.h @@ -0,0 +1,4310 @@ +/****************************************************************************** + @file QCQMUX.h + + DESCRIPTION + This module contains QMI QMUX module. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ + + +#ifndef QCQMUX_H +#define QCQMUX_H + +#include "QCQMI.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 +#define QMIWDS_CREATE_PROFILE_REQ 0x0027 +#define QMIWDS_CREATE_PROFILE_RESP 0x0027 +#define QMIWDS_MODIFY_PROFILE_SETTINGS_REQ 0x0028 +#define QMIWDS_MODIFY_PROFILE_SETTINGS_RESP 0x0028 +#define QMIWDS_GET_PROFILE_LIST_REQ 0x002A +#define QMIWDS_GET_PROFILE_LIST_RESP 0x002A +#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_UNKNOW 0x00 +#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 QMI_WDA_SET_LOOPBACK_CONFIG_REQ 0x002F +#define QMI_WDA_SET_LOOPBACK_CONFIG_RESP 0x002F +#define QMI_WDA_SET_LOOPBACK_CONFIG_IND 0x002F + +#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; + QMIWDS_ENDPOINT_TLV epTlv; +#ifdef QUECTEL_UL_DATA_AGG + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DlMinimumPassingTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationMaxDatagramsTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationMaxSizeTlv; +#endif +} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG, *PQMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG; + +typedef struct _QMI_U8_TLV +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR TLVVaule; +} __attribute__ ((packed)) QMI_U8_TLV, *PQMI_U8_TLV; + +typedef struct _QMI_U32_TLV +{ + UCHAR TLVType; + USHORT TLVLength; + ULONG TLVVaule; +} __attribute__ ((packed)) QMI_U32_TLV, *PQMI_U32_TLV; + +typedef struct _QMI_WDA_SET_LOOPBACK_CONFIG_REQ_MSG { + USHORT Type; + USHORT Length; + QMI_U8_TLV loopback_state; //0x01 + QMI_U32_TLV replication_factor; //0x10 +} __attribute__ ((packed)) QMI_WDA_SET_LOOPBACK_CONFIG_REQ_MSG, *PQMI_WDA_SET_LOOPBACK_CONFIG_REQ_MSG; + +typedef struct _QMI_WDA_SET_LOOPBACK_CONFIG_IND_MSG +{ + USHORT Type; + USHORT Length; + QMI_U8_TLV loopback_state; //0x01 + QMI_U32_TLV replication_factor; //0x10 +} __attribute__ ((packed)) QMI_WDA_SET_LOOPBACK_CONFIG_IND_MSG, *PQMI_WDA_SET_LOOPBACK_CONFIG_IND_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 + +/* + �Bit 0 �Profile identifier + �Bit 1 �Profile name + �Bit 2 �PDP type + �Bit 3 �APN name + �Bit 4 �DNS address + �Bit 5 �UMTS/GPRS granted QoS + �Bit 6 �Username + �Bit 7 �Authentication Protocol + �Bit 8 �IP address + �Bit 9 �Gateway information (address and subnet mask) + �Bit 10 �PCSCF address using a PCO flag + �Bit 11 �PCSCF server address list + �Bit 12 �PCSCF domain name list + �Bit 13 �MTU + �Bit 14 �Domain name list + �Bit 15 �IP family + �Bit 16 �IM_CM flag + �Bit 17 �Technology name + �Bit 18 �Operator reserved PCO +*/ +#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4DNS_ADDR (1 << 4) +#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4_ADDR (1 << 8) +#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4GATEWAY_ADDR (1 << 9) +#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_MTU (1 << 13) +#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_PCSCF_SV_ADDR (1 << 11) +#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_PCSCF_DOM_NAME (1 << 14) + +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_RUNNING_SETTINGS_PCSCF_IPV6_ADDR +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR PCSCFNumber; +} __attribute__ ((packed)) QMIWDS_GET_RUNNING_SETTINGS_PCSCF_IPV6_ADDR, *PQMIWDS_GET_RUNNING_SETTINGS_PCSCF_IPV6_ADDR; + +typedef struct _QMIWDS_GET_RUNNING_SETTINGS_PCSCF_IPV4_ADDR +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR PCSCFNumber; +} __attribute__ ((packed)) QMIWDS_GET_RUNNING_SETTINGS_PCSCF_IPV4_ADDR, *PQMIWDS_GET_RUNNING_SETTINGS_PCSCF_IPV4_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_PDPCONTEXT +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR pdp_context; +} __attribute__ ((packed)) QMIWDS_PDPCONTEXT, *PQMIWDS_PDPCONTEXT; + +typedef struct _QMIWDS_PROFILELIST +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR ProfileList[1024]; +} __attribute__ ((packed)) QMIWDS_PROFILELIST, *PQMIWDS_PROFILELIST; + +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_IPTYPE +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR IPType; +} __attribute__ ((packed)) QMIWDS_IPTYPE, *PQMIWDS_IPTYPE; + +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; + +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; + +typedef struct _QMIWDS_CREATE_PROFILE_SETTINGS_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR ProfileType; + UCHAR TLV2Type; //0x25 + USHORT TLV2Length; + UCHAR pdp_context; +} __attribute__ ((packed)) QMIWDS_CREATE_PROFILE_SETTINGS_REQ_MSG, *PQMIWDS_CREATE_PROFILE_SETTINGS_REQ_MSG; + +typedef struct _QMIWDS_GET_PROFILE_LIST_REQ_MSG +{ + USHORT Type; + USHORT Length; +} __attribute__ ((packed)) QMIWDS_GET_PROFILE_LIST_REQ_MSG, *PQMIWDS_GET_PROFILE_LIST_REQ_MSG; + +typedef struct _QMIWDS_GET_PROFILE_LIST_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR ProfileList[1024]; +} __attribute__ ((packed)) QMIWDS_GET_PROFILE_LIST_RESP_MSG, *PQMIWDS_GET_PROFILE_LIST_RESP_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_SET_EVENT_REPORT_IND 0x0001 +#define QMI_QOS_BIND_DATA_PORT_REQ 0x002B +#define QMI_QOS_BIND_DATA_PORT_RESP 0x002B +#define QMI_QOS_INDICATION_REGISTER_REQ 0x002F +#define QMI_QOS_INDICATION_REGISTER_RESP 0x002F +#define QMI_QOS_GLOBAL_QOS_FLOW_IND 0x0031 +#define QMI_QOS_GET_QOS_INFO_REQ 0x0033 +#define QMI_QOS_GET_QOS_INFO_RESP 0x0033 + + +#if 1 +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_SET_EVENT_REPORT_IND_MSG +{ + USHORT Type; // QMUX type 0x0001 + USHORT Length; + UCHAR TLVs; +} QMI_QOS_SET_EVENT_REPORT_IND_MSG, *PQMI_QOS_SET_EVENT_REPORT_IND_MSG; + + +typedef struct _QMI_QOS_BIND_DATA_PORT_TLV_EP_ID +{ + UCHAR TLVType; //0x10 + USHORT TLVLength; + ULONG ep_type; + ULONG iface_id; +} __attribute__ ((packed)) QMI_QOS_BIND_DATA_PORT_TLV_EP_ID, *PQMI_QOS_BIND_DATA_PORT_TLV_EP_ID; + +typedef struct _QMI_QOS_BIND_DATA_PORT_TLV_MUX_ID +{ + UCHAR TLVType; //0x11 + USHORT TLVLength; + UCHAR mux_id; +} __attribute__ ((packed)) QMI_QOS_BIND_DATA_PORT_TLV_MUX_ID, *PQMI_QOS_BIND_DATA_PORT_TLV_MUX_ID; + +typedef struct _QMI_QOS_BIND_DATA_PORT_TLV_DATA_PORT +{ + UCHAR TLVType; //0x12 + USHORT TLVLength; + USHORT data_port; +} __attribute__ ((packed)) QMI_QOS_BIND_DATA_PORT_TLV_DATA_PORT, *PQMI_QOS_BIND_DATA_PORT_TLV_DATA_PORT; + +typedef struct _QMI_QOS_BIND_DATA_PORT_REQ_MSG +{ + USHORT Type; + USHORT Length; + QMI_QOS_BIND_DATA_PORT_TLV_EP_ID EpIdTlv; + QMI_QOS_BIND_DATA_PORT_TLV_MUX_ID MuxIdTlv; + //QMI_QOS_BIND_DATA_PORT_TLV_DATA_PORT DataPortTlv; +} __attribute__ ((packed)) QMI_QOS_BIND_DATA_PORT_REQ_MSG, *PQMI_QOS_BIND_DATA_PORT_REQ_MSG; + +typedef struct _QMI_QOS_BIND_DATA_PORT_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; //0x02 + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} __attribute__ ((packed)) QMI_QOS_BIND_DATA_PORT_RESP_MSG, *PQMI_QOS_BIND_DATA_PORT_RESP_MSG; + +typedef struct _QMI_QOS_INDICATION_REGISTER_TLV_REPORT_GLOBAL_QOS_FLOW +{ + UCHAR TLVType; //0x10 + USHORT TLVLength; + UCHAR report_global_qos_flows; +} __attribute__ ((packed)) QMI_QOS_INDICATION_REGISTER_TLV_REPORT_GLOBAL_QOS_FLOW, *PQMI_QOS_INDICATION_REGISTER_TLV_REPORT_GLOBAL_QOS_FLOW; + +typedef struct _QMI_QOS_INDICATION_REGISTER_TLV_SUPPRESS_REPORT_FLOW_CTL +{ + UCHAR TLVType; //0x11 + USHORT TLVLength; + UCHAR suppress_report_flow_control; +} __attribute__ ((packed)) QMI_QOS_INDICATION_REGISTER_TLV_SUPPRESS_REPORT_FLOW_CTL, *PQMI_QOS_INDICATION_REGISTER_TLV_SUPPRESS_REPORT_FLOW_CTL; + +typedef struct _QMI_QOS_INDICATION_REGISTER_TLV_SUPPRESS_NW_STATUS_IND +{ + UCHAR TLVType; //0x12 + USHORT TLVLength; + UCHAR suppress_network_status_ind; +} __attribute__ ((packed)) QMI_QOS_INDICATION_REGISTER_TLV_SUPPRESS_NW_STATUS_IND, *PQMI_QOS_INDICATION_REGISTER_TLV_SUPPRESS_NW_STATUS_IND; + +typedef struct _QMI_QOS_INDICATION_REGISTER_REQ_MSG +{ + USHORT Type; + USHORT Length; + QMI_QOS_INDICATION_REGISTER_TLV_REPORT_GLOBAL_QOS_FLOW ReportGlobalQosFlowTlv; + //QMI_QOS_INDICATION_REGISTER_TLV_SUPPRESS_REPORT_FLOW_CTL SuppressReportFlowCtlTlv; + //QMI_QOS_INDICATION_REGISTER_TLV_SUPPRESS_NW_STATUS_IND SuppressNWStatusIndTlv; +} __attribute__ ((packed)) QMI_QOS_INDICATION_REGISTER_REQ_MSG, *PQMI_QOS_INDICATION_REGISTER_REQ_MSG; + +typedef struct _QMI_QOS_INDICATION_REGISTER_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; //0x02 + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} __attribute__ ((packed)) QMI_QOS_INDICATION_REGISTER_RESP_MSG, *PQMI_QOS_INDICATION_REGISTER_RESP_MSG; + +typedef struct _QMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_STATE +{ + UCHAR TLVType; //0x01 + USHORT TLVLength; + ULONG qos_id; + UCHAR new_flow; + ULONG state_change; +} __attribute__ ((packed)) QMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_STATE, *PQMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_STATE; + +typedef struct _QMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_GRANTED +{ + UCHAR TLVType; //0x10 0x11 + USHORT TLVLength; + ULONG64 flow_valid_params; + ULONG ip_flow_trf_cls; + ULONG64 data_rate_max; + ULONG64 guaranteed_rate; + ULONG peak_rate; + ULONG token_rate; + ULONG bucket_size; + ULONG ip_flow_latency; + ULONG ip_flow_jitter; + USHORT ip_flow_pkt_error_rate_multiplier; + USHORT ip_flow_pkt_error_rate_exponent; + ULONG ip_flow_min_policed_packet_size; + ULONG ip_flow_max_allowed_packet_size; + ULONG ip_flow_3gpp_residual_bit_error_rate; + ULONG ip_flow_3gpp_traffic_handling_priority; + USHORT ip_flow_3gpp2_profile_id; + UCHAR ip_flow_3gpp2_flow_priority; + UCHAR ip_flow_3gpp_im_cn_flag; + UCHAR ip_flow_3gpp_sig_ind; + ULONG ip_flow_lte_qci; +} __attribute__ ((packed)) QMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_GRANTED, *PQMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_GRANTED; + +typedef struct _QMI_QOS_GLOBAL_QOS_FLOW_TLV_FILTER +{ + UCHAR TLVType; //0x12 0x13 + USHORT TLVLength; + UCHAR tx_rx_qos_filter_len; + UCHAR ip_version; + ULONG64 valid_params0; + ULONG ipv4_addr0; + ULONG subnet_mask0; + ULONG ipv4_addr1; + ULONG subnet_mask1; + UCHAR val4; + UCHAR mask4; + ULONG64 valid_params01; + UCHAR ipv6_address00; + UCHAR ipv6_address01; + UCHAR ipv6_address02; + UCHAR ipv6_address03; + UCHAR ipv6_address04; + UCHAR ipv6_address05; + UCHAR ipv6_address06; + UCHAR ipv6_address07; + UCHAR ipv6_address08; + UCHAR ipv6_address09; + UCHAR ipv6_address010; + UCHAR ipv6_address011; + UCHAR ipv6_address012; + UCHAR ipv6_address013; + UCHAR ipv6_address014; + ULONG ipv6_address015; + UCHAR prefix_len0; + UCHAR ipv6_address10; + UCHAR ipv6_address11; + UCHAR ipv6_address12; + UCHAR ipv6_address13; + UCHAR ipv6_address14; + UCHAR ipv6_address15; + UCHAR ipv6_address16; + UCHAR ipv6_address17; + UCHAR ipv6_address18; + UCHAR ipv6_address19; + UCHAR ipv6_address110; + UCHAR ipv6_address111; + UCHAR ipv6_address112; + UCHAR ipv6_address113; + UCHAR ipv6_address114; + ULONG ipv6_address115; + UCHAR prefix_len1; + UCHAR val6; + UCHAR mask6; + ULONG flow_label; + ULONG xport_protocol; + ULONG64 valid_params2; + USHORT port0; + USHORT range0; + USHORT port1; + USHORT range1; + ULONG64 valid_params3; + USHORT port2; + USHORT range2; + USHORT port3; + USHORT range3; + ULONG64 valid_params4; + UCHAR type; + UCHAR code; + ULONG64 valid_params5; + ULONG spi0; + ULONG64 valid_params6; + ULONG spi1; + USHORT filter_id; + USHORT filter_precedence; +} __attribute__ ((packed)) QMI_QOS_GLOBAL_QOS_FLOW_TLV_FILTER, *PQMI_QOS_GLOBAL_QOS_FLOW_TLV_FILTER; + +typedef struct _QMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_TYPE +{ + UCHAR TLVType; //0x14 + USHORT TLVLength; + ULONG flow_type; +} __attribute__ ((packed)) QMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_TYPE, *PQMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_TYPE; + +typedef struct _QMI_QOS_GLOBAL_QOS_FLOW_TLV_BEARER_ID +{ + UCHAR TLVType; //0x15 + USHORT TLVLength; + UCHAR bearer_id; +} __attribute__ ((packed)) QMI_QOS_GLOBAL_QOS_FLOW_TLV_BEARER_ID, *PQMI_QOS_GLOBAL_QOS_FLOW_TLV_BEARER_ID; + +typedef struct _QMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_CTL_SEQ_NUM +{ + UCHAR TLVType; //0x16 + USHORT TLVLength; + USHORT fc_seq_num; +} __attribute__ ((packed)) QMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_CTL_SEQ_NUM, *PQMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_CTL_SEQ_NUM; + +typedef struct _QMI_QOS_GLOBAL_QOS_FLOW_TLV_5G_QCI +{ + UCHAR TLVType; //0x17 0x18 + USHORT TLVLength; + ULONG tx_rx_5g_qci; +} __attribute__ ((packed)) QMI_QOS_GLOBAL_QOS_FLOW_TLV_5G_QCI, *PQMI_QOS_GLOBAL_QOS_FLOW_TLV_5G_QCI; + +typedef struct _QMI_QOS_GLOBAL_QOS_FLOW_TLV_AVG_WINDOW +{ + UCHAR TLVType; //0x19 0x1A + USHORT TLVLength; + USHORT tx_rx_avg_window; +} __attribute__ ((packed)) QMI_QOS_GLOBAL_QOS_FLOW_TLV_AVG_WINDOW, *PQMI_QOS_GLOBAL_QOS_FLOW_TLV_AVG_WINDOW; + +typedef struct _QMI_QOS_GLOBAL_QOS_FLOW_TLV_TX_FILTER_MATCH_ALL +{ + UCHAR TLVType; //0x1B + USHORT TLVLength; + UCHAR tx_filter_match_all_len; + USHORT filter_id; +} __attribute__ ((packed)) QMI_QOS_GLOBAL_QOS_FLOW_TLV_TX_FILTER_MATCH_ALL, *PQMI_QOS_GLOBAL_QOS_FLOW_TLV_TX_FILTER_MATCH_ALL; + +typedef struct _QMI_QOS_GLOBAL_QOS_FLOW_IND_MSG +{ + USHORT Type; + USHORT Length; + QMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_STATE FlowStateTlv; + //QMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_GRANTED TxFlowGrantedTlv; + //QMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_GRANTED RxFlowGrantedTlv; + //QMI_QOS_GLOBAL_QOS_FLOW_TLV_FILTER TxFilterTlv; + //QMI_QOS_GLOBAL_QOS_FLOW_TLV_FILTER RxFilterTlv; + //QMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_TYPE FlowTypeTlv; + //QMI_QOS_GLOBAL_QOS_FLOW_TLV_BEARER_ID BearerIdTlv; + //QMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_CTL_SEQ_NUM FlowCtlSeqNumTlv; + //QMI_QOS_GLOBAL_QOS_FLOW_TLV_5G_QCI Tx5GQciTlv; + //QMI_QOS_GLOBAL_QOS_FLOW_TLV_5G_QCI Rx5GQciTlv; + //QMI_QOS_GLOBAL_QOS_FLOW_TLV_AVG_WINDOW AvgWindowTlv; + //QMI_QOS_GLOBAL_QOS_FLOW_TLV_TX_FILTER_MATCH_ALL TxFilterMatchAllTlv; +} __attribute__ ((packed)) QMI_QOS_GLOBAL_QOS_FLOW_IND_MSG, *PQMI_QOS_GLOBAL_QOS_FLOW_IND_MSG; + +typedef struct _QMI_QOS_GET_QOS_INFO_TLV_QOS_ID +{ + UCHAR TLVType; //0x01 + USHORT TLVLength; + ULONG qos_id; +} __attribute__ ((packed)) QMI_QOS_GET_QOS_INFO_TLV_QOS_ID, *PQMI_QOS_GET_QOS_INFO_TLV_QOS_ID; + +typedef struct _QMI_QOS_GET_QOS_INFO_TLV_FLOW_STATUS +{ + UCHAR TLVType; //0x10 + USHORT TLVLength; + UCHAR flow_status; +} __attribute__ ((packed)) QMI_QOS_GET_QOS_INFO_TLV_FLOW_STATUS, *PQMI_QOS_GET_QOS_INFO_TLV_FLOW_STATUS; + +typedef struct _QMI_QOS_GET_QOS_INFO_TLV_GRANTED_FLOW +{ + UCHAR TLVType; //0x11 0x12 + USHORT TLVLength; + ULONG64 flow_valid_params; + ULONG ip_flow_trf_cls; + ULONG64 data_rate_max; + ULONG64 guaranteed_rate; + ULONG peak_rate; + ULONG token_rate; + ULONG bucket_size; + ULONG ip_flow_latency; + ULONG ip_flow_jitter; + USHORT ip_flow_pkt_error_rate_multiplier; + USHORT ip_flow_pkt_error_rate_exponent; + ULONG ip_flow_min_policed_packet_size; + ULONG ip_flow_max_allowed_packet_size; + ULONG ip_flow_3gpp_residual_bit_error_rate; + ULONG ip_flow_3gpp_traffic_handling_priority; + USHORT ip_flow_3gpp2_profile_id; + UCHAR ip_flow_3gpp2_flow_priority; + UCHAR ip_flow_3gpp_im_cn_flag; + UCHAR ip_flow_3gpp_sig_ind; + ULONG ip_flow_lte_qci; +} __attribute__ ((packed)) QMI_QOS_GET_QOS_INFO_TLV_GRANTED_FLOW, *PQMI_QOS_GET_QOS_INFO_TLV_GRANTED_FLOW; + +typedef struct _QMI_QOS_GET_QOS_INFO_TLV_FILTER_SPECS +{ + UCHAR TLVType; //0x13 0x14 + USHORT TLVLength; + UCHAR tx_rx_qos_filter_len; + UCHAR ip_version; + ULONG64 valid_params0; + ULONG ipv4_addr0; + ULONG subnet_mask0; + ULONG ipv4_addr1; + ULONG subnet_mask1; + UCHAR val4; + UCHAR mask4; + ULONG64 valid_params01; + UCHAR ipv6_address00; + UCHAR ipv6_address01; + UCHAR ipv6_address02; + UCHAR ipv6_address03; + UCHAR ipv6_address04; + UCHAR ipv6_address05; + UCHAR ipv6_address06; + UCHAR ipv6_address07; + UCHAR ipv6_address08; + UCHAR ipv6_address09; + UCHAR ipv6_address010; + UCHAR ipv6_address011; + UCHAR ipv6_address012; + UCHAR ipv6_address013; + UCHAR ipv6_address014; + ULONG ipv6_address015; + UCHAR prefix_len0; + UCHAR ipv6_address10; + UCHAR ipv6_address11; + UCHAR ipv6_address12; + UCHAR ipv6_address13; + UCHAR ipv6_address14; + UCHAR ipv6_address15; + UCHAR ipv6_address16; + UCHAR ipv6_address17; + UCHAR ipv6_address18; + UCHAR ipv6_address19; + UCHAR ipv6_address110; + UCHAR ipv6_address111; + UCHAR ipv6_address112; + UCHAR ipv6_address113; + UCHAR ipv6_address114; + ULONG ipv6_address115; + UCHAR prefix_len1; + UCHAR val6; + UCHAR mask6; + ULONG flow_label; + ULONG xport_protocol; + ULONG64 valid_params2; + USHORT port0; + USHORT range0; + USHORT port1; + USHORT range1; + ULONG64 valid_params3; + USHORT port2; + USHORT range2; + USHORT port3; + USHORT range3; + ULONG64 valid_params4; + UCHAR type; + UCHAR code; + ULONG64 valid_params5; + ULONG spi0; + ULONG64 valid_params6; + ULONG spi1; + USHORT filter_id; + USHORT filter_precedence; +} __attribute__ ((packed)) QMI_QOS_GET_QOS_INFO_TLV_FILTER_SPECS, *PQMI_QOS_GET_QOS_INFO_TLV_FILTER_SPECS; + +typedef struct _QMI_QOS_GET_QOS_INFO_TLV_EXT_ERROR_INFO +{ + UCHAR TLVType; //0x15 + USHORT TLVLength; + USHORT ext_error_info; +} __attribute__ ((packed)) QMI_QOS_GET_QOS_INFO_TLV_EXT_ERROR_INFO, *PQMI_QOS_GET_QOS_INFO_TLV_EXT_ERROR_INFO; + +typedef struct _QMI_QOS_GET_QOS_INFO_TLV_5G_QCI +{ + UCHAR TLVType; //0x16 0x17 + USHORT TLVLength; + ULONG tx_rx_5g_qci; +} __attribute__ ((packed)) QMI_QOS_GET_QOS_INFO_TLV_5G_QCI, *PQMI_QOS_GET_QOS_INFO_TLV_5G_QCI; + +typedef struct _QMI_QOS_GET_QOS_INFO_TLV_AVG_WINDOW +{ + UCHAR TLVType; //0x18 0x19 + USHORT TLVLength; + USHORT tx_rx_averaging_window; +} __attribute__ ((packed)) QMI_QOS_GET_QOS_INFO_TLV_AVG_WINDOW, *PQMI_QOS_GET_QOS_INFO_TLV_AVG_WINDOW; + +typedef struct _QMI_QOS_GET_QOS_INFO_TLV_TX_FILTER_MATCH_ALL +{ + UCHAR TLVType; //0x1A + USHORT TLVLength; + UCHAR tx_filter_match_all_len; + USHORT filter_id; +} __attribute__ ((packed)) QMI_QOS_GET_QOS_INFO_TLV_TX_FILTER_MATCH_ALL, *PQMI_QOS_GET_QOS_INFO_TLV_TX_FILTER_MATCH_ALL; + +typedef struct _QMI_QOS_GET_QOS_INFO_REQ_MSG +{ + USHORT Type; + USHORT Length; + QMI_QOS_GET_QOS_INFO_TLV_QOS_ID QosIdTlv; +} __attribute__ ((packed)) QMI_QOS_GET_QOS_INFO_REQ_MSG, *PQMI_QOS_GET_QOS_INFO_REQ_MSG; + +typedef struct _QMI_QOS_GET_QOS_INFO_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; //0x02 + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + //QMI_QOS_GET_QOS_INFO_TLV_FLOW_STATUS FlowStatusTlv; + //QMI_QOS_GET_QOS_INFO_TLV_GRANTED_FLOW TxGrantedFlowTlv; + //QMI_QOS_GET_QOS_INFO_TLV_GRANTED_FLOW RxGrantedFlowTlv; + //QMI_QOS_GET_QOS_INFO_TLV_FILTER_SPECS TxFilterSpecsTlv; + //QMI_QOS_GET_QOS_INFO_TLV_FILTER_SPECS RxFilterSpecsTlv; + //QMI_QOS_GET_QOS_INFO_TLV_EXT_ERROR_INFO ExtErrorInfoTlv; + //QMI_QOS_GET_QOS_INFO_TLV_5G_QCI Tx5GQciTlv; + //QMI_QOS_GET_QOS_INFO_TLV_5G_QCI Rx5GQciTlv; + //QMI_QOS_GET_QOS_INFO_TLV_AVG_WINDOW TxAvgWindowTlv; + //QMI_QOS_GET_QOS_INFO_TLV_AVG_WINDOW RxAvgWindowTlv; + //QMI_QOS_GET_QOS_INFO_TLV_TX_FILTER_MATCH_ALL TxFilterMatchAllTlv; +} __attribute__ ((packed)) QMI_QOS_GET_QOS_INFO_RESP_MSG, *PQMI_QOS_GET_QOS_INFO_RESP_MSG; + +#define QOS_IND_FLOW_STATE_ACTIVATED 0x00 +#define QOS_IND_FLOW_STATE_MODIFIED 0x01 +#define QOS_IND_FLOW_STATE_DELETED 0x02 +#define QOS_IND_FLOW_STATE_SUSPENDED 0x03 +#define QOS_IND_FLOW_STATE_ENABLED 0x04 +#define QOS_IND_FLOW_STATE_DISABLED 0x05 +#define QOS_IND_FLOW_STATE_INVALID 0x06 + +#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_CELL_LOCATION_INFO_REQ 0x0043 +#define QMINAS_GET_CELL_LOCATION_INFO_RESP 0x0043 +#define QMINAS_GET_PLMN_NAME_REQ 0x0044 +#define QMINAS_GET_PLMN_NAME_RESP 0x0044 +#define QUECTEL_PACKET_TRANSFER_START_IND 0X100 +#define QUECTEL_PACKET_TRANSFER_END_IND 0X101 +#define QMINAS_GET_SYS_INFO_REQ 0x004D +#define QMINAS_GET_SYS_INFO_RESP 0x004D +#define QMINAS_SYS_INFO_IND 0x004E +#define QMINAS_GET_SIG_INFO_REQ 0x004F +#define QMINAS_GET_SIG_INFO_RESP 0x004F + +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; + +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; + +typedef enum { + NAS_SYS_SRV_STATUS_NO_SRV_V01 = 0, + NAS_SYS_SRV_STATUS_LIMITED_V01 = 1, + NAS_SYS_SRV_STATUS_SRV_V01 = 2, + NAS_SYS_SRV_STATUS_LIMITED_REGIONAL_V01 = 3, + NAS_SYS_SRV_STATUS_PWR_SAVE_V01 = 4, +}nas_service_status_enum_type_v01; + +typedef enum { + SYS_SRV_DOMAIN_NO_SRV_V01 = 0, + SYS_SRV_DOMAIN_CS_ONLY_V01 = 1, + SYS_SRV_DOMAIN_PS_ONLY_V01 = 2, + SYS_SRV_DOMAIN_CS_PS_V01 = 3, + SYS_SRV_DOMAIN_CAMPED_V01 = 4, +}nas_service_domain_enum_type_v01; + +typedef enum { + QMI_NAS_RADIO_INTERFACE_UNKNOWN = -1, + QMI_NAS_RADIO_INTERFACE_NONE = 0x00, + QMI_NAS_RADIO_INTERFACE_CDMA_1X = 0x01, + QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO = 0x02, + QMI_NAS_RADIO_INTERFACE_AMPS = 0x03, + QMI_NAS_RADIO_INTERFACE_GSM = 0x04, + QMI_NAS_RADIO_INTERFACE_UMTS = 0x05, + QMI_NAS_RADIO_INTERFACE_LTE = 0x08, + QMI_NAS_RADIO_INTERFACE_TD_SCDMA = 0x09, + QMI_NAS_RADIO_INTERFACE_5GNR = 0x0C, +} QMI_NAS_RADIO_INTERFACE_E; + +typedef enum { + QMI_NAS_ACTIVE_BAND_BC_0 = 0, + QMI_NAS_ACTIVE_BAND_BC_1 = 1, + QMI_NAS_ACTIVE_BAND_BC_2 = 2, + QMI_NAS_ACTIVE_BAND_BC_3 = 3, + QMI_NAS_ACTIVE_BAND_BC_4 = 4, + QMI_NAS_ACTIVE_BAND_BC_5 = 5, + QMI_NAS_ACTIVE_BAND_BC_6 = 6, + QMI_NAS_ACTIVE_BAND_BC_7 = 7, + QMI_NAS_ACTIVE_BAND_BC_8 = 8, + QMI_NAS_ACTIVE_BAND_BC_9 = 9, + QMI_NAS_ACTIVE_BAND_BC_10 = 10, + QMI_NAS_ACTIVE_BAND_BC_11 = 11, + QMI_NAS_ACTIVE_BAND_BC_12 = 12, + QMI_NAS_ACTIVE_BAND_BC_13 = 13, + QMI_NAS_ACTIVE_BAND_BC_14 = 14, + QMI_NAS_ACTIVE_BAND_BC_15 = 15, + QMI_NAS_ACTIVE_BAND_BC_16 = 16, + QMI_NAS_ACTIVE_BAND_BC_17 = 17, + QMI_NAS_ACTIVE_BAND_BC_18 = 18, + QMI_NAS_ACTIVE_BAND_BC_19 = 19, + QMI_NAS_ACTIVE_BAND_GSM_450 = 40, + QMI_NAS_ACTIVE_BAND_GSM_480 = 41, + QMI_NAS_ACTIVE_BAND_GSM_750 = 42, + QMI_NAS_ACTIVE_BAND_GSM_850 = 43, + QMI_NAS_ACTIVE_BAND_GSM_900_EXTENDED = 44, + QMI_NAS_ACTIVE_BAND_GSM_900_PRIMARY = 45, + QMI_NAS_ACTIVE_BAND_GSM_900_RAILWAYS = 46, + QMI_NAS_ACTIVE_BAND_GSM_DCS_1800 = 47, + QMI_NAS_ACTIVE_BAND_GSM_PCS_1900 = 48, + QMI_NAS_ACTIVE_BAND_WCDMA_2100 = 80, + QMI_NAS_ACTIVE_BAND_WCDMA_PCS_1900 = 81, + QMI_NAS_ACTIVE_BAND_WCDMA_DCS_1800 = 82, + QMI_NAS_ACTIVE_BAND_WCDMA_1700_US = 83, + QMI_NAS_ACTIVE_BAND_WCDMA_850 = 84, + QMI_NAS_ACTIVE_BAND_WCDMA_800 = 85, + QMI_NAS_ACTIVE_BAND_WCDMA_2600 = 86, + QMI_NAS_ACTIVE_BAND_WCDMA_900 = 87, + QMI_NAS_ACTIVE_BAND_WCDMA_1700_JAPAN = 88, + QMI_NAS_ACTIVE_BAND_WCDMA_1500_JAPAN = 90, + QMI_NAS_ACTIVE_BAND_WCDMA_850_JAPAN = 91, + QMI_NAS_ACTIVE_BAND_EUTRAN_1 = 120, + QMI_NAS_ACTIVE_BAND_EUTRAN_2 = 121, + QMI_NAS_ACTIVE_BAND_EUTRAN_3 = 122, + QMI_NAS_ACTIVE_BAND_EUTRAN_4 = 123, + QMI_NAS_ACTIVE_BAND_EUTRAN_5 = 124, + QMI_NAS_ACTIVE_BAND_EUTRAN_6 = 125, + QMI_NAS_ACTIVE_BAND_EUTRAN_7 = 126, + QMI_NAS_ACTIVE_BAND_EUTRAN_8 = 127, + QMI_NAS_ACTIVE_BAND_EUTRAN_9 = 128, + QMI_NAS_ACTIVE_BAND_EUTRAN_10 = 129, + QMI_NAS_ACTIVE_BAND_EUTRAN_11 = 130, + QMI_NAS_ACTIVE_BAND_EUTRAN_12 = 131, + QMI_NAS_ACTIVE_BAND_EUTRAN_13 = 132, + QMI_NAS_ACTIVE_BAND_EUTRAN_14 = 133, + QMI_NAS_ACTIVE_BAND_EUTRAN_17 = 134, + QMI_NAS_ACTIVE_BAND_EUTRAN_18 = 143, + QMI_NAS_ACTIVE_BAND_EUTRAN_19 = 144, + QMI_NAS_ACTIVE_BAND_EUTRAN_20 = 145, + QMI_NAS_ACTIVE_BAND_EUTRAN_21 = 146, + QMI_NAS_ACTIVE_BAND_EUTRAN_23 = 152, + QMI_NAS_ACTIVE_BAND_EUTRAN_24 = 147, + QMI_NAS_ACTIVE_BAND_EUTRAN_25 = 148, + QMI_NAS_ACTIVE_BAND_EUTRAN_26 = 153, + QMI_NAS_ACTIVE_BAND_EUTRAN_27 = 164, + QMI_NAS_ACTIVE_BAND_EUTRAN_28 = 158, + QMI_NAS_ACTIVE_BAND_EUTRAN_29 = 159, + QMI_NAS_ACTIVE_BAND_EUTRAN_30 = 160, + QMI_NAS_ACTIVE_BAND_EUTRAN_31 = 165, + QMI_NAS_ACTIVE_BAND_EUTRAN_32 = 154, + QMI_NAS_ACTIVE_BAND_EUTRAN_33 = 135, + QMI_NAS_ACTIVE_BAND_EUTRAN_34 = 136, + QMI_NAS_ACTIVE_BAND_EUTRAN_35 = 137, + QMI_NAS_ACTIVE_BAND_EUTRAN_36 = 138, + QMI_NAS_ACTIVE_BAND_EUTRAN_37 = 139, + QMI_NAS_ACTIVE_BAND_EUTRAN_38 = 140, + QMI_NAS_ACTIVE_BAND_EUTRAN_39 = 141, + QMI_NAS_ACTIVE_BAND_EUTRAN_40 = 142, + QMI_NAS_ACTIVE_BAND_EUTRAN_41 = 149, + QMI_NAS_ACTIVE_BAND_EUTRAN_42 = 150, + QMI_NAS_ACTIVE_BAND_EUTRAN_43 = 151, + QMI_NAS_ACTIVE_BAND_EUTRAN_46 = 163, + QMI_NAS_ACTIVE_BAND_EUTRAN_47 = 166, + QMI_NAS_ACTIVE_BAND_EUTRAN_48 = 167, + QMI_NAS_ACTIVE_BAND_EUTRAN_66 = 161, + QMI_NAS_ACTIVE_BAND_EUTRAN_71 = 168, + QMI_NAS_ACTIVE_BAND_EUTRAN_125 = 155, + QMI_NAS_ACTIVE_BAND_EUTRAN_126 = 156, + QMI_NAS_ACTIVE_BAND_EUTRAN_127 = 157, + QMI_NAS_ACTIVE_BAND_EUTRAN_250 = 162, + QMI_NAS_ACTIVE_BAND_TDSCDMA_A = 200, + QMI_NAS_ACTIVE_BAND_TDSCDMA_B = 201, + QMI_NAS_ACTIVE_BAND_TDSCDMA_C = 202, + QMI_NAS_ACTIVE_BAND_TDSCDMA_D = 203, + QMI_NAS_ACTIVE_BAND_TDSCDMA_E = 204, + QMI_NAS_ACTIVE_BAND_TDSCDMA_F = 205, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_1 = 250, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_2 = 251, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_3 = 252, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_5 = 253, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_7 = 254, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_8 = 255, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_20 = 256, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_28 = 257, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_38 = 258, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_41 = 259, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_50 = 260, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_51 = 261, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_66 = 262, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_70 = 263, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_71 = 264, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_74 = 265, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_75 = 266, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_76 = 267, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_77 = 268, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_78 = 269, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_79 = 270, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_80 = 271, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_81 = 272, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_82 = 273, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_83 = 274, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_84 = 275, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_85 = 276, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_257= 277, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_258= 278, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_259= 279, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_260= 280, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_261= 281, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_12 = 282, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_25 = 283, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_34 = 284, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_39 = 285, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_40 = 286, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_65 = 287, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_86 = 288, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_48 = 289, + QMI_NAS_ACTIVE_BAND_NR5G_BAND_14 = 290 +} QMI_NAS_ACTIVE_BAND_E; + +typedef struct { + UCHAR TLVType; + USHORT TLVLength; + + uint8_t srv_domain_valid; + uint8_t srv_domain; + uint8_t srv_capability_valid; + uint8_t srv_capability; + uint8_t roam_status_valid; + uint8_t roam_status; + uint8_t is_sys_forbidden_valid; + uint8_t is_sys_forbidden; + + uint8_t lac_valid; + uint16_t lac; + uint8_t cell_id_valid; + uint32_t cell_id; + uint8_t reg_reject_info_valid; + uint8_t reject_srv_domain; + uint8_t rej_cause; + uint8_t network_id_valid; + UCHAR MCC[3]; + UCHAR MNC[3]; + + uint8_t tac_valid; + uint16_t tac; +} __attribute__ ((packed)) NR5G_SYSTEM_INFO, *PNR5G_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 +typedef struct { + UCHAR TLVType; + USHORT TLVLength; + CHAR rssi; + SHORT ecio; +} __attribute__ ((packed)) QMINAS_SIG_INFO_CDMA_TLV_MSG, *PQMINAS_SIG_INFO_CDMA_TLV_MSG; + +typedef struct { + UCHAR TLVType; + USHORT TLVLength; + CHAR rssi; + SHORT ecio; + CHAR sinr; + INT io; +} __attribute__ ((packed)) QMINAS_SIG_INFO_HDR_TLV_MSG, *PQMINAS_SIG_INFO_HDR_TLV_MSG; + +typedef struct { + UCHAR TLVType; + USHORT TLVLength; + CHAR rssi; +} __attribute__ ((packed)) QMINAS_SIG_INFO_GSM_TLV_MSG, *PQMINAS_SIG_INFO_GSM_TLV_MSG; + +typedef struct { + UCHAR TLVType; + USHORT TLVLength; + CHAR rssi; + SHORT ecio; +} __attribute__ ((packed)) QMINAS_SIG_INFO_WCDMA_TLV_MSG, *PQMINAS_SIG_INFO_WCDMA_TLV_MSG; + +typedef struct { + UCHAR TLVType; + USHORT TLVLength; + CHAR rssi; + CHAR rsrq; + SHORT rsrp; + SHORT snr; +} __attribute__ ((packed)) QMINAS_SIG_INFO_LTE_TLV_MSG, *PQMINAS_SIG_INFO_LTE_TLV_MSG; + +typedef struct { + UCHAR TLVType; + USHORT TLVLength; + CHAR rscp; +} __attribute__ ((packed)) QMINAS_SIG_INFO_TDSCDMA_TLV_MSG, *PQMINAS_SIG_INFO_TDSCDMA_TLV_MSG; + +typedef struct { + UCHAR TLVType; + USHORT TLVLength; + SHORT rsrp; + SHORT snr; +} __attribute__ ((packed)) QMINAS_SIG_INFO_5G_NSA_TLV_MSG, *PQMINAS_SIG_INFO_5G_NSA_TLV_MSG; + +typedef struct { + UCHAR TLVType; + USHORT TLVLength; + SHORT nr5g_rsrq; +} __attribute__ ((packed)) QMINAS_SIG_INFO_5G_SA_TLV_MSG, *PQMINAS_SIG_INFO_5G_SA_TLV_MSG; + +typedef struct { + uint8 radio_if; + uint16 active_band; + uint16 active_channel; +} __attribute__ ((packed)) NasGetRfBandInfo; + +typedef struct { + UCHAR TLVType; + USHORT TLVLength; + + uint8 num_instances; + NasGetRfBandInfo bands_array[0]; +} __attribute__ ((packed)) NasGetRfBandInfoList; + +typedef struct { + uint8 radio_if; + uint16 dedicated_band; +} __attribute__ ((packed)) NasGetRfBandInfoDedicated; + +typedef struct { + UCHAR TLVType; + USHORT TLVLength; + + uint8 num_instances; + NasGetRfBandInfoDedicated bands_array[0]; +} __attribute__ ((packed)) NasGetRfBandInfoDedicatedList; + +typedef struct { + uint8 radio_if; + uint16 active_band; + uint32 active_channel; +} __attribute__ ((packed)) NasGetRfBandInfoExtended; + +typedef struct { + UCHAR TLVType; + USHORT TLVLength; + + uint8 num_instances; + NasGetRfBandInfoExtended bands_array[0]; +} __attribute__ ((packed)) NasGetRfBandInfoExtendedList; + +typedef struct { + uint8 radio_if; + uint32 bandwidth; +} __attribute__ ((packed)) NasGetRfBandInfoBandWidth; + +typedef struct { + UCHAR TLVType; + USHORT TLVLength; + + uint8 num_instances; + NasGetRfBandInfoBandWidth bands_array[0]; +} __attribute__ ((packed)) NasGetRfBandInfoBandWidthList; + +typedef struct { + UCHAR TLVType; + USHORT TLVLength; + + uint8 plmn[3]; + uint8 tac[3]; + uint64 global_cell_id; + uint16 physical_cell_id; + int16 rsrq; + int16 rsrp; + int16 snr; +} __attribute__ ((packed)) NasGetCellLocationNr5gServingCell; + +typedef struct { + uint16 physical_cell_id; + int16 rsrq; + int16 rsrp; + int16 rssi; + int16 cell_selection_rx_level; +} __attribute__ ((packed)) NasGetCellLocationLteInfoCell; + +typedef struct { + UCHAR TLVType; + USHORT TLVLength; + + uint8 ue_in_idle; + uint8 plmn[3]; + uint16 tracking_area_code; + uint32 global_cell_id; + uint16 absolute_rf_channel_number; + uint16 serving_cell_id; + uint8 cell_reselection_priority; + uint8 s_non_intra_search_threshold; + uint8 serving_cell_low_threshold; + uint8 s_intra_search_threshold; + uint8 cells_len; + NasGetCellLocationLteInfoCell cells_array[0]; +} __attribute__ ((packed)) NasGetCellLocationLteInfoIntrafrequency; + +typedef struct _QmiMessageNasGetCellLocationInfoOutputInterfrequencyLteInfoFrequencyElement { + uint16 eutra_absolute_rf_channel_number; + uint8 cell_selection_rx_level_low_threshold; + uint8 cell_selection_rx_level_high_threshold; + uint8 cell_reselection_priority; + uint8 cells_len; + NasGetCellLocationLteInfoCell cells_array[0]; +} __attribute__ ((packed)) NasGetCellLocationLteInfoInterfrequencyFrequencyElement; + +typedef struct { + UCHAR TLVType; + USHORT TLVLength; + + uint8 ue_in_idle; + uint8 freqs_len; + NasGetCellLocationLteInfoInterfrequencyFrequencyElement freqs[0]; +} __attribute__ ((packed)) NasGetCellLocationLteInfoInterfrequency; + +// ======================= 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 +#define QMIUIM_POWER_DOWN 0x0030 +#define QMIUIM_POWER_UP 0x0031 + + +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; + +#define UIM_CARD_STATE_ABSENT 0x00 +#define UIM_CARD_STATE_PRESENT 0x01 +#define UIM_CARD_STATE_ERROR 0x02 + +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; + UCHAR AppType; + UCHAR AppState; + UCHAR PersoState; + UCHAR PersoFeature; + UCHAR PersoRetries; + UCHAR PersoUnblockRetries; + UCHAR AIDLength; +} __attribute__ ((packed)) QMIUIM_CARD_STATUS, *PQMIUIM_CARD_STATUS; + +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_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; + +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 _QMIUIM_SET_CARD_SLOT_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR slot; +} __attribute__ ((packed)) QMIUIM_SET_CARD_SLOT_REQ_MSG, *PQMIUIM_SET_CARD_SLOT_REQ_MSG; + +// ======================= COEX ============================== +#define QMI_COEX_GET_WWAN_STATE_REQ 0x22 +#define QMI_COEX_GET_WWAN_STATE_RESP 0x22 + +typedef struct { + + uint32_t freq; + /**< Band center frequency in MHz. */ + + uint32_t bandwidth; + /**< Bandwidth in MHz. */ +}coex_band_type_v01; /* Type */ + +typedef struct _QMI_COEX_GET_WWAN_STATE_RESP_MSG_LTE_BAND +{ + UCHAR TLVType; + USHORT TLVLength; + coex_band_type_v01 ul_band; + coex_band_type_v01 dl_band; +} __attribute__ ((packed)) QMI_COEX_GET_WWAN_STATE_RESP_MSG_LTE_BAND, *PQMI_COEX_GET_WWAN_STATE_RESP_MSG_LTE_BAND; + + +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; + QMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG ModifyProfileSettingsReq; + QMIWDS_MODIFY_PROFILE_SETTINGS_RESP_MSG ModifyProfileSettingsResp; + QMIWDS_GET_PROFILE_SETTINGS_REQ_MSG GetProfileSettingsReq; + QMIWDS_CREATE_PROFILE_SETTINGS_REQ_MSG CreatetProfileSettingsReq; + QMIWDS_GET_PROFILE_LIST_REQ_MSG GetProfileListReq; + QMIWDS_GET_PROFILE_LIST_RESP_MSG GetProfileListResp; +#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 1 + QMI_QOS_SET_EVENT_REPORT_REQ_MSG QosSetEventReportReq; + QMI_QOS_SET_EVENT_REPORT_RESP_MSG QosSetEventReportRsp; + QMI_QOS_SET_EVENT_REPORT_IND_MSG QosSetEventReportInd; + QMI_QOS_BIND_DATA_PORT_REQ_MSG QosBindDataPortReq; + QMI_QOS_BIND_DATA_PORT_RESP_MSG QosBindDataPortRsp; + QMI_QOS_INDICATION_REGISTER_REQ_MSG QosIndRegReq; + QMI_QOS_INDICATION_REGISTER_RESP_MSG QosIndRegRsp; + QMI_QOS_GLOBAL_QOS_FLOW_IND_MSG QosGlobalQosFlowInd; + QMI_QOS_GET_QOS_INFO_REQ_MSG QosGetQosInfoReq; + QMI_QOS_GET_QOS_INFO_RESP_MSG QosGetQosInfoRsp; +#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; +#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; + QMIUIM_SET_CARD_SLOT_REQ_MSG UIMSetCardSlotReq; + + QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG SetDataFormatReq; + QMI_WDA_SET_LOOPBACK_CONFIG_REQ_MSG SetLoopBackReq; + QMI_WDA_SET_LOOPBACK_CONFIG_IND_MSG SetLoopBackInd; + }; +} __attribute__ ((packed)) QMUX_MSG, *PQMUX_MSG; + +typedef struct _QCQMIMSG { + QCQMI_HDR QMIHdr; + union { + QMICTL_MSG CTLMsg; + QMUX_MSG MUXMsg; + }; +} __attribute__ ((packed)) QCQMIMSG, *PQCQMIMSG; + +#pragma pack(pop) + +#endif // QCQMUX_H + diff --git a/application/quectel_CM_5G/src/QMIThread.c b/application/quectel_CM_5G/src/QMIThread.c new file mode 100644 index 0000000..8f7e866 --- /dev/null +++ b/application/quectel_CM_5G/src/QMIThread.c @@ -0,0 +1,3037 @@ +/****************************************************************************** + @file QMIThread.c + @brief QMI WWAN connectivity manager. + + DESCRIPTION + Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ +#include "QMIThread.h" +#include + +#ifndef MIN +#define MIN(a, b) ((a) < (b)? (a): (b)) +#endif + +#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) + +static uint32_t WdsConnectionIPv4Handle = 0; +static uint32_t WdsConnectionIPv6Handle = 0; +static int s_is_cdma = 0; +static int s_5g_type = WWAN_DATA_CLASS_NONE; +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); +} + +static void uchar2char(char *dst_ptr, size_t dst_len, const UCHAR *src_ptr, size_t src_len) { + size_t copy = MIN(dst_len-1, src_len); + + if (copy) + memcpy(dst_ptr, src_ptr, copy); + dst_ptr[copy] = 0; +} + +static int s_9x07 = 1; + +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->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; +} + +#if 0 +static USHORT NasSetEventReportReq(PQMUX_MSG pMUXMsg, void *arg) { + pMUXMsg->SetEventReportReq.TLVType = 0x10; + pMUXMsg->SetEventReportReq.TLVLength = 0x04; + pMUXMsg->SetEventReportReq.ReportSigStrength = 0x00; + pMUXMsg->SetEventReportReq.NumTresholds = 2; + pMUXMsg->SetEventReportReq.TresholdList[0] = -113; + pMUXMsg->SetEventReportReq.TresholdList[1] = -50; + return sizeof(QMINAS_SET_EVENT_REPORT_REQ_MSG); +} + +static USHORT WdsSetEventReportReq(PQMUX_MSG pMUXMsg, void *arg) { + pMUXMsg->EventReportReq.TLVType = 0x10; // 0x10 -- current channel rate indicator + pMUXMsg->EventReportReq.TLVLength = 0x0001; // 1 + pMUXMsg->EventReportReq.Mode = 0x00; // 0-do not report; 1-report when rate changes + + pMUXMsg->EventReportReq.TLV2Type = 0x11; // 0x11 + pMUXMsg->EventReportReq.TLV2Length = 0x0005; // 5 + pMUXMsg->EventReportReq.StatsPeriod = 0x00; // seconds between reports; 0-do not report + pMUXMsg->EventReportReq.StatsMask = 0x000000ff; // + + pMUXMsg->EventReportReq.TLV3Type = 0x12; // 0x12 -- current data bearer indicator + pMUXMsg->EventReportReq.TLV3Length = 0x0001; // 1 + pMUXMsg->EventReportReq.Mode3 = 0x01; // 0-do not report; 1-report when changes + + pMUXMsg->EventReportReq.TLV4Type = 0x13; // 0x13 -- dormancy status indicator + pMUXMsg->EventReportReq.TLV4Length = 0x0001; // 1 + pMUXMsg->EventReportReq.DormancyStatus = 0x00; // 0-do not report; 1-report when changes + return sizeof(QMIWDS_SET_EVENT_REPORT_REQ_MSG); +} + +static USHORT DmsSetEventReportReq(PQMUX_MSG pMUXMsg) { + PPIN_STATUS pPinState = (PPIN_STATUS)(&pMUXMsg->DmsSetEventReportReq + 1); + PUIM_STATE pUimState = (PUIM_STATE)(pPinState + 1); + // Pin State + pPinState->TLVType = 0x12; + pPinState->TLVLength = 0x01; + pPinState->ReportPinState = 0x01; + // UIM State + pUimState->TLVType = 0x15; + pUimState->TLVLength = 0x01; + pUimState->UIMState = 0x01; + return sizeof(QMIDMS_SET_EVENT_REPORT_REQ_MSG) + sizeof(PIN_STATUS) + sizeof(UIM_STATE); +} +#endif + +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->profile_index && !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->profile_index; + 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, +}; + +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 int qmap_version = 0x05; +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) = %zu, 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(qmap_version); //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(qmap_version); //DL 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); + +#ifdef QUECTEL_UL_DATA_AGG + if (!qmap_settings->ul_data_aggregation_max_datagrams) { + return ((size_t)&((QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG *)0)->DlMinimumPassingTlv); + } + + //Maximum number of datagrams in a single aggregated packet on uplink + pMUXMsg->SetDataFormatReq.DlMinimumPassingTlv.TLVType = 0x19; + pMUXMsg->SetDataFormatReq.DlMinimumPassingTlv.TLVLength = cpu_to_le16(4); + pMUXMsg->SetDataFormatReq.DlMinimumPassingTlv.Value = cpu_to_le32(qmap_settings->dl_minimum_padding); + + //Maximum number of datagrams in a single aggregated packet on uplink + pMUXMsg->SetDataFormatReq.UplinkDataAggregationMaxDatagramsTlv.TLVType = 0x1B; + pMUXMsg->SetDataFormatReq.UplinkDataAggregationMaxDatagramsTlv.TLVLength = cpu_to_le16(4); + pMUXMsg->SetDataFormatReq.UplinkDataAggregationMaxDatagramsTlv.Value = cpu_to_le32(qmap_settings->ul_data_aggregation_max_datagrams); + + //Maximum size in bytes of a single aggregated packet allowed on downlink + pMUXMsg->SetDataFormatReq.UplinkDataAggregationMaxSizeTlv.TLVType = 0x1C; + pMUXMsg->SetDataFormatReq.UplinkDataAggregationMaxSizeTlv.TLVLength = cpu_to_le16(4); + pMUXMsg->SetDataFormatReq.UplinkDataAggregationMaxSizeTlv.Value = cpu_to_le32(qmap_settings->ul_data_aggregation_max_size); +#endif + + return sizeof(QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG); + } +} + +#ifdef CONFIG_SIM +static USHORT DmsUIMVerifyPinReqSend(PQMUX_MSG pMUXMsg, void *arg) { + pMUXMsg->UIMVerifyPinReq.TLVType = 0x01; + pMUXMsg->UIMVerifyPinReq.PINID = 0x01; //Pin1, not Puk + pMUXMsg->UIMVerifyPinReq.PINLen = strlen((const char *)arg); + qstrcpy((char *)&pMUXMsg->UIMVerifyPinReq.PINValue, ((const char *)arg)); + pMUXMsg->UIMVerifyPinReq.TLVLength = cpu_to_le16(2 + strlen((const char *)arg)); + return sizeof(QMIDMS_UIM_VERIFY_PIN_REQ_MSG) + (strlen((const char *)arg) - 1); +} + +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((char *)&pMUXMsg->UIMUIMVerifyPinReq.PINValue, ((const char *)arg)); + return sizeof(QMIUIM_VERIFY_PIN_REQ_MSG) + (strlen((const char *)arg) - 1); +} + +#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 WdsGetProfileListReqSend(PQMUX_MSG pMUXMsg, void *arg) { + (void)(arg); + pMUXMsg->GetProfileListReq.Length = cpu_to_le16(sizeof(QMIWDS_GET_PROFILE_LIST_REQ_MSG) - 4); + return sizeof(QMIWDS_GET_PROFILE_LIST_REQ_MSG); +} + +static USHORT WdsCreateProfileSettingsReqSend(PQMUX_MSG pMUXMsg, void *arg) { + PROFILE_T *profile = (PROFILE_T *)arg; + pMUXMsg->CreatetProfileSettingsReq.Length = cpu_to_le16(sizeof(QMIWDS_CREATE_PROFILE_SETTINGS_REQ_MSG) - 4); + pMUXMsg->CreatetProfileSettingsReq.TLVType = 0x01; + pMUXMsg->CreatetProfileSettingsReq.TLVLength = cpu_to_le16(0x01); + pMUXMsg->CreatetProfileSettingsReq.ProfileType = 0x00; // 0 ~ 3GPP, 1 ~ 3GPP2 + pMUXMsg->CreatetProfileSettingsReq.TLV2Type = 0x25; + pMUXMsg->CreatetProfileSettingsReq.TLV2Length = cpu_to_le16(0x01); + pMUXMsg->CreatetProfileSettingsReq.pdp_context = profile->pdp; // 0 ~ 3GPP, 1 ~ 3GPP2 + return sizeof(QMIWDS_CREATE_PROFILE_SETTINGS_REQ_MSG); +} + +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->profile_index; + return sizeof(QMIWDS_GET_PROFILE_SETTINGS_REQ_MSG); +} + +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->profile_index; + + pTLV = (UCHAR *)(&pMUXMsg->ModifyProfileSettingsReq + 1); + + pPdpType = (PQMIWDS_PDPTYPE)(pTLV + TLVLength); + pPdpType->TLVType = 0x11; + pPdpType->TLVLength = cpu_to_le16(0x01); + pPdpType->PdpType = profile->iptype; + 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) +{ + (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 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, const char *funcname) { + int ret; + + if (!pRequest) + return -EINVAL; + + pthread_mutex_lock(&cm_command_mutex); + + 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(&cm_command_cond, &cm_command_mutex, msecs); + if (!ret) { + if (s_pResponse && ppResponse) { + *ppResponse = s_pResponse; + } else { + if (s_pResponse) { + free(s_pResponse); + s_pResponse = NULL; + } + } + } else { + dbg_time("%s message timeout", funcname); + } + } + + pthread_mutex_unlock(&cm_command_mutex); + + return ret; +} + +void QmiThreadRecvQMI(PQCQMIMSG pResponse) { + pthread_mutex_lock(&cm_command_mutex); + if (pResponse == NULL) { + if (s_pRequest) { + free(s_pRequest); + s_pRequest = NULL; + s_pResponse = NULL; + pthread_cond_signal(&cm_command_cond); + } + pthread_mutex_unlock(&cm_command_mutex); + 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(&cm_command_cond); + } else if ((pResponse->QMIHdr.QMIType == QMUX_TYPE_CTL) + && (le16_to_cpu(pResponse->CTLMsg.QMICTLMsgHdrRsp.QMICTLType == QMICTL_REVOKE_CLIENT_ID_IND))) { + qmidevice_send_event_to_main(MODEM_REPORT_RESET_EVENT); + } 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_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 ((pResponse->QMIHdr.QMIType == QMUX_TYPE_WDS_ADMIN) + && (le16_to_cpu(pResponse->MUXMsg.QMUXMsgHdrResp.Type) == QMI_WDA_SET_LOOPBACK_CONFIG_IND)) { + qmidevice_send_event_to_main_ext(RIL_UNSOL_LOOPBACK_CONFIG_IND, + &pResponse->MUXMsg.SetLoopBackInd, sizeof(pResponse->MUXMsg.SetLoopBackInd)); + } +#ifdef CONFIG_REG_QOS_IND + else if ((pResponse->QMIHdr.QMIType == QMUX_TYPE_QOS) + && (le16_to_cpu(pResponse->MUXMsg.QMUXMsgHdrResp.Type) == QMI_QOS_GLOBAL_QOS_FLOW_IND)) { + UINT qos_id = 0; + UCHAR new_flow = ql_get_global_qos_flow_ind_qos_id(pResponse, &qos_id); + if (qos_id != 0 && new_flow == 1) + qmidevice_send_event_to_main_ext(RIL_UNSOL_GLOBAL_QOS_FLOW_IND_QOS_ID, &qos_id, sizeof(qos_id)); +#ifdef CONFIG_GET_QOS_DATA_RATE + if (new_flow) { + ULONG64 max_data_rate[2] = {0}; + if (ql_get_global_qos_flow_ind_data_rate(pResponse, (void *)max_data_rate) == 0){} + } +#endif + } +#endif + else { + if (debug_qmi) + dbg_time("nobody care this qmi msg!!"); + } + pthread_mutex_unlock(&cm_command_mutex); +} + +#ifdef CONFIG_COEX_WWAN_STATE +static int requestGetCoexWWANState(void) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + PQMI_COEX_GET_WWAN_STATE_RESP_MSG_LTE_BAND pLteBand; + static QMI_COEX_GET_WWAN_STATE_RESP_MSG_LTE_BAND oldLteBand = {-1, -1}; + int err; + + pRequest = ComposeQMUXMsg(QMUX_TYPE_COEX, QMI_COEX_GET_WWAN_STATE_REQ, NULL, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + + 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)) { + dbg_time("%s QMUXResult = 0x%x, QMUXError = 0x%x", __func__, le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXResult), le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError)); + err = le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError); + free(pResponse); + return err; + } + pLteBand = (PQMI_COEX_GET_WWAN_STATE_RESP_MSG_LTE_BAND)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x10); + + if (pLteBand && memcmp(pLteBand, &oldLteBand, sizeof(oldLteBand))) { + oldLteBand = *pLteBand; + dbg_time("%s ul_freq %d ul_bandwidth %d", __func__, le32_to_cpu(pLteBand->ul_band.freq), le32_to_cpu(pLteBand->ul_band.bandwidth)); + dbg_time("%s dl_freq %d dl_bandwidth %d", __func__, le32_to_cpu(pLteBand->dl_band.freq), le32_to_cpu(pLteBand->dl_band.bandwidth)); + } + free(pResponse); + return 0; +} +#endif + +static 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}; + + qmap_settings.size = sizeof(qmap_settings); + + if (profile->qmap_mode) { + profile->rawIP = 1; + s_9x07 = profile->rawIP; + + qmap_settings.MuxId = profile->muxid; + + if (profile->hardware_interface == HARDWARE_PCIE) { //SDX20_PCIE + qmap_settings.rx_urb_size = profile->qmap_size; //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; //SDX24&SDX55 support 32KB + qmap_settings.ep_type = DATA_EP_TYPE_HSUSB; + qmap_settings.iface_id = 0x04; + } + + qmap_settings.ul_data_aggregation_max_datagrams = 11; //by test result, 11 can get best TPUT + qmap_settings.ul_data_aggregation_max_size = 8*1024; + qmap_settings.dl_minimum_padding = 0; //no effect when register to real netowrk + if(profile->qmap_version != 0x09) + profile->qmap_version = 0x05; + + qmap_version = profile->qmap_version; + if (profile->rmnet_info.size) { + qmap_settings.rx_urb_size = profile->rmnet_info.rx_urb_size; + qmap_settings.ep_type = profile->rmnet_info.ep_type; + qmap_settings.iface_id = profile->rmnet_info.iface_id; + qmap_settings.dl_minimum_padding = profile->rmnet_info.dl_minimum_padding; + qmap_version = profile->rmnet_info.qmap_version; + } + + if (!profile->wda_client) { + if (qmidev_is_gobinet(profile->qmichannel)) { + //when QMAP enabled, set data format in GobiNet driver + } + else if (profile->proxy[0]) { + /* the first running 'quectel-cm' had alloc wda client and set data format, + so we can ingore to set data format here. */ + } + goto skip_WdaSetDataFormat; + } + } + + 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); + s_9x07 = profile->rawIP; //MDM90x7 only support RAW IP, do not support Eth + } + + 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 = %u", qmap_settings.rx_urb_size); //must same as rx_urb_size defined in GobiNet&qmi_wwan driver + } + +#ifdef QUECTEL_UL_DATA_AGG + if (qmap_settings.ul_data_aggregation_max_datagrams) + { + linkProto = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x17); + if (linkProto != NULL) { + qmap_settings.ul_data_aggregation_max_datagrams = MIN(qmap_settings.ul_data_aggregation_max_datagrams, le32_to_cpu(linkProto->Value)); + dbg_time("qmap_settings.ul_data_aggregation_max_datagrams = %u", qmap_settings.ul_data_aggregation_max_datagrams); + } + + linkProto = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x18); + if (linkProto != NULL) { + qmap_settings.ul_data_aggregation_max_size = MIN(qmap_settings.ul_data_aggregation_max_size, le32_to_cpu(linkProto->Value)); + dbg_time("qmap_settings.ul_data_aggregation_max_size = %u", qmap_settings.ul_data_aggregation_max_size); + } + + linkProto = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x1A); + if (linkProto != NULL) { + qmap_settings.dl_minimum_padding = le32_to_cpu(linkProto->Value); + dbg_time("qmap_settings.dl_minimum_padding = %u", qmap_settings.dl_minimum_padding); + } + + if (qmap_settings.ul_data_aggregation_max_datagrams > 1) { + ql_set_driver_qmap_setting(profile, &qmap_settings); + } + } +#endif + + free(pResponse); + +skip_WdaSetDataFormat: + if (profile->enable_ipv4) { + if (profile->qmapnet_adapter[0]) { + // 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 ipv4 + 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->enable_ipv6) { + if (profile->qmapnet_adapter[0]) { + // bind wds ipv6 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); + } + + // set ipv6 + 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); + } + + pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_SET_AUTO_CONNECT_REQ , WdsSetAutoConnect, (void *)&autoconnect_setting); + QmiThreadSendQMI(pRequest, &pResponse); + if (pResponse) free(pResponse); + + return 0; +} + +#ifdef CONFIG_SIM +static 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; + + if (s_9x07) + pRequest = ComposeQMUXMsg(QMUX_TYPE_UIM, QMIUIM_GET_CARD_STATUS_REQ, NULL, NULL); + else + pRequest = ComposeQMUXMsg(QMUX_TYPE_DMS, QMIDMS_UIM_GET_PIN_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 requestGetSIMStatus(SIM_Status *pSIMStatus) { //RIL_REQUEST_GET_SIM_STATUS + 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" + }; + + if (s_9x07) + pRequest = ComposeQMUXMsg(QMUX_TYPE_UIM, QMIUIM_GET_CARD_STATUS_REQ, NULL, NULL); + else + pRequest = ComposeQMUXMsg(QMUX_TYPE_DMS, QMIDMS_UIM_GET_STATE_REQ, NULL, NULL); + + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + *pSIMStatus = SIM_ABSENT; + if (s_9x07) + { + 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; + + pCardStatus = (PQMIUIM_CARD_STATUS)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x10); + if (pCardStatus != NULL) + { + pPINState = (PQMIUIM_PIN_STATE)((PUCHAR)pCardStatus + sizeof(QMIUIM_CARD_STATUS) + pCardStatus->AIDLength); + CardState = pCardStatus->CardState; + if (CardState == UIM_CARD_STATE_PRESENT) { + if (pPINState->UnivPIN == 1) + { + PIN1State = pCardStatus->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; + } + } + + *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 + { + } + } + else + { + //UIM state. Values: + // 0x00 UIM initialization completed + // 0x01 UIM is locked or the UIM failed + // 0x02 UIM is not present + // 0x03 Reserved + // 0xFF UIM state is currently + //unavailable + if (pResponse->MUXMsg.UIMGetStateResp.UIMState == 0x00) { + *pSIMStatus = SIM_READY; + } else if (pResponse->MUXMsg.UIMGetStateResp.UIMState == 0x01) { + *pSIMStatus = SIM_ABSENT; + err = requestGetPINStatus(pSIMStatus); + } else if ((pResponse->MUXMsg.UIMGetStateResp.UIMState == 0x02) || (pResponse->MUXMsg.UIMGetStateResp.UIMState == 0xFF)) { + *pSIMStatus = SIM_ABSENT; + } else { + *pSIMStatus = SIM_ABSENT; + } + } + dbg_time("%s SIMStatus: %s", __func__, SIM_Status_String[*pSIMStatus]); + + free(pResponse); + + return 0; +} + +static int requestEnterSimPin(const char *pPinCode) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + + if (s_9x07) + pRequest = ComposeQMUXMsg(QMUX_TYPE_UIM, QMIUIM_VERIFY_PIN_REQ, UimVerifyPinReqSend, (void *)pPinCode); + else + pRequest = ComposeQMUXMsg(QMUX_TYPE_DMS, QMIDMS_UIM_VERIFY_PIN_REQ, DmsUIMVerifyPinReqSend, (void *)pPinCode); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + free(pResponse); + return 0; +} +#endif + +#ifdef CONFIG_IMSI_ICCID +static int requestGetICCID(void) { //RIL_REQUEST_GET_IMSI + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + PQMIUIM_CONTENT pUimContent; + int err; + + if (s_9x07) { + pRequest = ComposeQMUXMsg(QMUX_TYPE_UIM, QMIUIM_READ_TRANSPARENT_REQ, UimReadTransparentIMSIReqSend, (void *)"EF_ICCID"); + err = QmiThreadSendQMI(pRequest, &pResponse); + } else { + return 0; + } + 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; +} + +static int requestGetIMSI(void) { //RIL_REQUEST_GET_IMSI + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + PQMIUIM_CONTENT pUimContent; + int err; + + if (s_9x07) { + pRequest = ComposeQMUXMsg(QMUX_TYPE_UIM, QMIUIM_READ_TRANSPARENT_REQ, UimReadTransparentIMSIReqSend, (void *)"EF_IMSI"); + err = QmiThreadSendQMI(pRequest, &pResponse); + } else { + return 0; + } + 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 + +#if 1 +static void quectel_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; +#if 0 + *(p_mcc+0) = '0' + d1; + *(p_mcc+1) = '0' + d2; + *(p_mcc+2) = '0' + d3; +#endif + } + else + { + //dbg_time( "invalid digits %d %d %d", d1, d2, d3 ); + *p_mcc = 0; + } + } +} + +static void quectel_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; + } + } +} + +static 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; +} +#endif + +#if 0 +// Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length. +static const char * MCCMNC_CODES_HAVING_3DIGITS_MNC[] = { + "302370", "302720", "310260", + "405025", "405026", "405027", "405028", "405029", "405030", "405031", "405032", + "405033", "405034", "405035", "405036", "405037", "405038", "405039", "405040", + "405041", "405042", "405043", "405044", "405045", "405046", "405047", "405750", + "405751", "405752", "405753", "405754", "405755", "405756", "405799", "405800", + "405801", "405802", "405803", "405804", "405805", "405806", "405807", "405808", + "405809", "405810", "405811", "405812", "405813", "405814", "405815", "405816", + "405817", "405818", "405819", "405820", "405821", "405822", "405823", "405824", + "405825", "405826", "405827", "405828", "405829", "405830", "405831", "405832", + "405833", "405834", "405835", "405836", "405837", "405838", "405839", "405840", + "405841", "405842", "405843", "405844", "405845", "405846", "405847", "405848", + "405849", "405850", "405851", "405852", "405853", "405875", "405876", "405877", + "405878", "405879", "405880", "405881", "405882", "405883", "405884", "405885", + "405886", "405908", "405909", "405910", "405911", "405912", "405913", "405914", + "405915", "405916", "405917", "405918", "405919", "405920", "405921", "405922", + "405923", "405924", "405925", "405926", "405927", "405928", "405929", "405930", + "405931", "405932", "502142", "502143", "502145", "502146", "502147", "502148" +}; + +static const char * MCC_CODES_HAVING_3DIGITS_MNC[] = { + "302", //Canada + "310", //United States of America + "311", //United States of America + "312", //United States of America + "313", //United States of America + "314", //United States of America + "315", //United States of America + "316", //United States of America + "334", //Mexico + "338", //Jamaica + "342", //Barbados + "344", //Antigua and Barbuda + "346", //Cayman Islands + "348", //British Virgin Islands + "365", //Anguilla + "708", //Honduras (Republic of) + "722", //Argentine Republic + "732" //Colombia (Republic of) +}; + +int requestGetIMSI(const char **pp_imsi, USHORT *pMobileCountryCode, USHORT *pMobileNetworkCode) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + + if (pp_imsi) *pp_imsi = NULL; + if (pMobileCountryCode) *pMobileCountryCode = 0; + if (pMobileNetworkCode) *pMobileNetworkCode = 0; + + pRequest = ComposeQMUXMsg(QMUX_TYPE_DMS, QMIDMS_UIM_GET_IMSI_REQ, NULL, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + if (pMUXMsg->UIMGetIMSIResp.TLV2Type == 0x01 && le16_to_cpu(pMUXMsg->UIMGetIMSIResp.TLV2Length) >= 5) { + int mnc_len = 2; + unsigned i; + char tmp[4]; + + if (pp_imsi) *pp_imsi = strndup((const char *)(&pMUXMsg->UIMGetIMSIResp.IMSI), le16_to_cpu(pMUXMsg->UIMGetIMSIResp.TLV2Length)); + + for (i = 0; i < sizeof(MCCMNC_CODES_HAVING_3DIGITS_MNC)/sizeof(MCCMNC_CODES_HAVING_3DIGITS_MNC[0]); i++) { + if (!strncmp((const char *)(&pMUXMsg->UIMGetIMSIResp.IMSI), MCCMNC_CODES_HAVING_3DIGITS_MNC[i], 6)) { + mnc_len = 3; + break; + } + } + if (mnc_len == 2) { + for (i = 0; i < sizeof(MCC_CODES_HAVING_3DIGITS_MNC)/sizeof(MCC_CODES_HAVING_3DIGITS_MNC[0]); i++) { + if (!strncmp((const char *)(&pMUXMsg->UIMGetIMSIResp.IMSI), MCC_CODES_HAVING_3DIGITS_MNC[i], 3)) { + mnc_len = 3; + break; + } + } + } + + tmp[0] = (&pMUXMsg->UIMGetIMSIResp.IMSI)[0]; + tmp[1] = (&pMUXMsg->UIMGetIMSIResp.IMSI)[1]; + tmp[2] = (&pMUXMsg->UIMGetIMSIResp.IMSI)[2]; + tmp[3] = 0; + if (pMobileCountryCode) *pMobileCountryCode = atoi(tmp); + tmp[0] = (&pMUXMsg->UIMGetIMSIResp.IMSI)[3]; + tmp[1] = (&pMUXMsg->UIMGetIMSIResp.IMSI)[4]; + tmp[2] = 0; + if (mnc_len == 3) { + tmp[2] = (&pMUXMsg->UIMGetIMSIResp.IMSI)[6]; + } + if (pMobileNetworkCode) *pMobileNetworkCode = atoi(tmp); + } + + free(pResponse); + + return 0; +} +#endif + +static 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_5G_NSA, "5G_NSA"}, + {WWAN_DATA_CLASS_5G_SA, "5G_SA"}, + {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"}, +}; + +static const 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"; +} + +static USHORT char2ushort(UCHAR str[3]) { + int i; + char temp[4]; + USHORT ret= 0; + + memcpy(temp, str, 3); + temp[3] = '\0'; + + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + ret = (USHORT)atoi(temp); + + return ret; +} + +static 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; + PTDSCDMA_SYSTEM_INFO pTdscdmaSystemInfo; + PNR5G_SYSTEM_INFO pNr5gSystemInfo; + UCHAR DeviceClass = 0; + ULONG DataCapList = 0; + + /* Additional LTE System Info - Availability of Dual connectivity of E-UTRA with NR5G */ + uint8_t endc_available_valid = 0; /**< Must be set to true if endc_available is being passed */ + uint8_t endc_available = 0x00; + /**< + Upper layer indication in LTE SIB2. Values: \n + - 0x00 -- 5G Not available \n + - 0x01 -- 5G Available + + */ + /* Additional LTE System Info - DCNR restriction Info */ + uint8_t restrict_dcnr_valid = 0; /**< Must be set to true if restrict_dcnr is being passed */ + uint8_t restrict_dcnr = 0x01; + /**< + DCNR restriction in NAS attach/TAU accept. Values: \n + - 0x00 -- Not restricted \n + - 0x01 -- Restricted + */ + + *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_5g_type = WWAN_DATA_CLASS_NONE; + 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; + 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 0x4A: // NR5G Service Status Info + if (pServiceStatusInfo->SrvStatus == NAS_SYS_SRV_STATUS_SRV_V01) { + DataCapList |= WWAN_DATA_CLASS_5G_SA; + DeviceClass = DEVICE_CLASS_GSM; + is_lte = 1; + s_is_cdma = 0; + } + break; + case 0x4B: // NR5G System Info + pNr5gSystemInfo = (PNR5G_SYSTEM_INFO)pServiceStatusInfo; + if (pNr5gSystemInfo->srv_domain_valid == 0x01) { + if (pNr5gSystemInfo->srv_domain & SYS_SRV_DOMAIN_PS_ONLY_V01) { + *pPSAttachedState = 1; + } + } + + if (pNr5gSystemInfo->network_id_valid == 0x01) { + MobileCountryCode = (USHORT)char2ushort(pNr5gSystemInfo->MCC); + MobileNetworkCode = (USHORT)char2ushort(pNr5gSystemInfo->MNC); + } + break; + case 0x4E: //Additional LTE System Info - Availability of Dual Connectivity of E-UTRA with NR5G + endc_available_valid = 1; + endc_available = pServiceStatusInfo->SrvStatus; + break; + + case 0x4F: //Additional LTE System Info - DCNR restriction Info + restrict_dcnr_valid = 1; + restrict_dcnr = pServiceStatusInfo->SrvStatus; + 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) { + 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) { + MobileCountryCode = (USHORT)char2ushort(pCdmaSystemInfo->MCC); + MobileNetworkCode = (USHORT)char2ushort(pCdmaSystemInfo->MNC); + } + break; + case 0x16: // HDR + // HDR_SYSTEM_INFO + pHdrSystemInfo = (PHDR_SYSTEM_INFO)pServiceStatusInfo; + if (pHdrSystemInfo->SrvDomainValid == 0x01) { + 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) { + quectel_convert_cdma_mcc_2_ascii_mcc(&MobileCountryCode, cmda_mcc); + quectel_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) { + 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) { + MobileCountryCode = (USHORT)char2ushort(pGsmSystemInfo->MCC); + MobileNetworkCode = (USHORT)char2ushort(pGsmSystemInfo->MNC); + } + break; + case 0x18: // WCDMA + // WCDMA_SYSTEM_INFO + pWcdmaSystemInfo = (PWCDMA_SYSTEM_INFO)pServiceStatusInfo; + if (pWcdmaSystemInfo->SrvDomainValid == 0x01) { + 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) { + MobileCountryCode = (USHORT)char2ushort(pWcdmaSystemInfo->MCC); + MobileNetworkCode = (USHORT)char2ushort(pWcdmaSystemInfo->MNC); + } + break; + case 0x19: // LTE_SYSTEM_INFO + // LTE_SYSTEM_INFO + pLteSystemInfo = (PLTE_SYSTEM_INFO)pServiceStatusInfo; + if (pLteSystemInfo->SrvDomainValid == 0x01) { + 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) { + MobileCountryCode = (USHORT)char2ushort(pLteSystemInfo->MCC); + MobileNetworkCode = (USHORT)char2ushort(pLteSystemInfo->MNC); + } + break; + case 0x25: // TDSCDMA + // TDSCDMA_SYSTEM_INFO + pTdscdmaSystemInfo = (PTDSCDMA_SYSTEM_INFO)pServiceStatusInfo; + if (pTdscdmaSystemInfo->SrvDomainValid == 0x01) { + 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) { + MobileCountryCode = (USHORT)char2ushort(pTdscdmaSystemInfo->MCC); + MobileNetworkCode = (USHORT)char2ushort(pTdscdmaSystemInfo->MNC); + } + 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 (DataCapList & WWAN_DATA_CLASS_LTE) { + if (endc_available_valid && restrict_dcnr_valid) { + if (endc_available && !restrict_dcnr) { + DataCapList |= WWAN_DATA_CLASS_5G_NSA; + } + } + } + + 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_5G_SA) { + s_5g_type = WWAN_DATA_CLASS_5G_SA; + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_5G_SA); + } else if (DataCapList & WWAN_DATA_CLASS_5G_NSA) { + s_5g_type = WWAN_DATA_CLASS_5G_NSA; + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_5G_NSA); + } 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); + } + } + + dbg_time("%s MCC: %d, MNC: %d, PS: %s, DataCap: %s", __func__, + MobileCountryCode, MobileNetworkCode, (*pPSAttachedState == 1) ? "Attached" : "Detached" , pDataCapStr); + + free(pResponse); + + return 0; +} + +static 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"; + + if (s_9x07) { + return requestRegistrationState2(pPSAttachedState); + } + + 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) { + quectel_convert_cdma_mcc_2_ascii_mcc(&MobileCountryCode, cmda_mcc); + quectel_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; +} + +static 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; +} + +static 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, __func__); + 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); + } + + err = le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError); + free(pResponse); + return err; + } + + 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; +} + +static 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; + + (void)profile; + if (curIpFamily == IpFamilyV4 && WdsConnectionIPv4Handle == 0) + return 0; + if (curIpFamily == IpFamilyV6 && WdsConnectionIPv6Handle == 0) + return 0; + + dbg_time("%s WdsConnectionIPv%dHandle", __func__, curIpFamily == IpFamilyV4 ? 4 : 6); + + 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; +} + +static 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_MTU pMtu; + IPV4_T *pIpv4 = &profile->ipv4; + IPV6_T *pIpv6 = &profile->ipv6; + UCHAR QMIType = (curIpFamily == 0x04) ? QMUX_TYPE_WDS : QMUX_TYPE_WDS_IPV6; + PQMIWDS_GET_RUNNING_SETTINGS_PCSCF_IPV6_ADDR pPCSCFIpv6Addr; + PQMIWDS_GET_RUNNING_SETTINGS_PCSCF_IPV4_ADDR pPCSCFIpv4Addr; + + 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(); + + pPCSCFIpv6Addr = (PQMIWDS_GET_RUNNING_SETTINGS_PCSCF_IPV6_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x2e); // 0x2e - pcscf ipv6 address + if (pPCSCFIpv6Addr) { + if (pPCSCFIpv6Addr->PCSCFNumber == 1) { + UCHAR *PCSCFIpv6Addr1 = (UCHAR *)(pPCSCFIpv6Addr + 1); + memcpy(profile->PCSCFIpv6Addr1, PCSCFIpv6Addr1, 16); + }else if (pPCSCFIpv6Addr->PCSCFNumber == 2) { + UCHAR *PCSCFIpv6Addr1 = (UCHAR *)(pPCSCFIpv6Addr + 1); + UCHAR *PCSCFIpv6Addr2 = PCSCFIpv6Addr1 + 16; + memcpy(profile->PCSCFIpv6Addr1, PCSCFIpv6Addr1, 16); + memcpy(profile->PCSCFIpv6Addr2, PCSCFIpv6Addr2, 16); + } + } + + pPCSCFIpv4Addr = (PQMIWDS_GET_RUNNING_SETTINGS_PCSCF_IPV4_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x23); // 0x23 - pcscf ipv4 address + if (pPCSCFIpv4Addr) { + if (pPCSCFIpv4Addr->PCSCFNumber == 1) { + UCHAR *PCSCFIpv4Addr1 = (UCHAR *)(pPCSCFIpv4Addr + 1); + memcpy(&profile->PCSCFIpv4Addr1, PCSCFIpv4Addr1, 4); + }else if (pPCSCFIpv4Addr->PCSCFNumber == 2) { + UCHAR *PCSCFIpv4Addr1 = (UCHAR *)(pPCSCFIpv4Addr + 1); + UCHAR *PCSCFIpv4Addr2 = PCSCFIpv4Addr1 + 4; + memcpy(&profile->PCSCFIpv4Addr1, PCSCFIpv4Addr1, 4); + memcpy(&profile->PCSCFIpv4Addr2, PCSCFIpv4Addr2, 4); + } + } + + 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; + } + + pIpv6Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6PRIMARYDNS); + if (pIpv6Addr) { + memcpy(pIpv6->DnsPrimary, pIpv6Addr->IPV6Address, 16); + } + + pIpv6Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6SECONDARYDNS); + if (pIpv6Addr) { + memcpy(pIpv6->DnsSecondary, pIpv6Addr->IPV6Address, 16); + } + + 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; + } + + 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) { + if (curIpFamily == IpFamilyV4) + pIpv4->Mtu = le32_to_cpu(pMtu->Mtu); + else + pIpv6->Mtu = le32_to_cpu(pMtu->Mtu); + } + + free(pResponse); + return 0; +} + +#ifdef CONFIG_APN +static int requestSetProfile(PROFILE_T *profile) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + const char *new_apn = profile->apn ? profile->apn : ""; + const char *new_user = profile->user ? profile->user : ""; + const char *new_password = profile->password ? profile->password : ""; + const char *ipStr[] = {"IPV4", "NULL", "IPV6", "IPV4V6"}; + + dbg_time("%s[pdp:%d index:%d] %s/%s/%s/%d/%s", __func__, profile->pdp, profile->profile_index, profile->apn, profile->user, profile->password, profile->auth,ipStr[profile->iptype]); + if (!profile->profile_index) + return -1; + + if ( !strcmp(profile->old_apn, new_apn) && !strcmp(profile->old_user, new_user) + && !strcmp(profile->old_password, new_password) + && profile->old_iptype == profile->iptype + && profile->old_auth == profile->auth) + { + dbg_time("no need to set skip the rest"); + return 0; + } + + pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_MODIFY_PROFILE_SETTINGS_REQ, WdsModifyProfileSettingsReq, profile); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + free(pResponse); + return 1; +} + +static int requestGetProfile(PROFILE_T *profile) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + PQMIWDS_APNNAME pApnName; + PQMIWDS_USERNAME pUserName; + PQMIWDS_PASSWD pPassWd; + PQMIWDS_AUTH_PREFERENCE pAuthPref; + PQMIWDS_IPTYPE pIpType; + PQMIWDS_PDPCONTEXT pPdpContext; + PQMIWDS_PROFILELIST pProfileList; + + const char *ipStr[] = {"IPV4", "NULL", "IPV6", "IPV4V6"}; + + profile->old_apn[0] = profile->old_user[0] = profile->old_password[0] = '\0'; + profile->old_auth = 0; + profile->old_iptype = 0; + if (profile->enable_ipv4 && profile->enable_ipv6) + profile->iptype = 3; + else if (profile->enable_ipv6) + profile->iptype = 2; + else + profile->iptype = 0; + + if (!profile->pdp) + return 0; + +_re_check: + pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_GET_PROFILE_LIST_REQ, WdsGetProfileListReqSend, profile); + err = QmiThreadSendQMI(pRequest, &pResponse);s_pResponse = malloc(le16_to_cpu(pResponse->QMIHdr.Length) + 1); + qmi_rsp_check_and_return(); + + pProfileList = (PQMIWDS_PROFILELIST)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x01); + uint8 profile_indexs[42] = {0}; + uint8 profile_num = pProfileList->ProfileList[0]; + if(profile_num >= 1) + { + uint8 j = 0; + uint8 k = 2; + for(int i=0; iProfileList[k]; + if(pProfileList->ProfileList[++k] == 0) + k+=2; + else + k+=2+pProfileList->ProfileList[k]; + } + } + free(pResponse); + + for(int i=0; iprofile_index = profile_indexs[i]; + + pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_GET_PROFILE_SETTINGS_REQ, WdsGetProfileSettingsReqSend, profile); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + pPdpContext = (PQMIWDS_PDPCONTEXT)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x25); + if(pPdpContext->pdp_context == profile->pdp) + break; + else + free(pResponse); + + if(i == profile_num-1) + { + pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_CREATE_PROFILE_REQ, WdsCreateProfileSettingsReqSend, profile); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + free(pResponse); + goto _re_check; + } + } + + + 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); + pIpType = (PQMIWDS_IPTYPE)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11); + + if (pApnName/* && le16_to_cpu(pApnName->TLVLength)*/) + uchar2char(profile->old_apn, sizeof(profile->old_apn), &pApnName->ApnName, le16_to_cpu(pApnName->TLVLength)); + if (pUserName/* && pUserName->UserName*/) + uchar2char(profile->old_user, sizeof(profile->old_user), &pUserName->UserName, le16_to_cpu(pUserName->TLVLength)); + if (pPassWd/* && le16_to_cpu(pPassWd->TLVLength)*/) + uchar2char(profile->old_password, sizeof(profile->old_password), &pPassWd->Passwd, le16_to_cpu(pPassWd->TLVLength)); + if (pAuthPref/* && le16_to_cpu(pAuthPref->TLVLength)*/) { + profile->old_auth = pAuthPref->AuthPreference; + } + if (pIpType) { + profile->old_iptype = pIpType->IPType; + } + + dbg_time("%s[pdp:%d index:%d] %s/%s/%s/%d/%s", __func__, profile->pdp, profile->profile_index, profile->old_apn, profile->old_user, profile->old_password, profile->old_auth, ipStr[profile->old_iptype]); + + free(pResponse); + return 0; +} +#endif + +#ifdef CONFIG_SIGNALINFO +static int requestGetSignalInfo(void) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + + pRequest = ComposeQMUXMsg(QMUX_TYPE_NAS, QMINAS_GET_SIG_INFO_REQ, NULL, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + + // CDMA + { + PQMINAS_SIG_INFO_CDMA_TLV_MSG ptlv = (PQMINAS_SIG_INFO_CDMA_TLV_MSG)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x10); + if (ptlv && ptlv->TLVLength) + { + dbg_time("%s CDMA: RSSI %d dBm, ECIO %.1lf dBm", __func__, + ptlv->rssi, (-0.5) * (double)ptlv->ecio); + } + } + + // HDR + { + PQMINAS_SIG_INFO_HDR_TLV_MSG ptlv = (PQMINAS_SIG_INFO_HDR_TLV_MSG)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11); + if (ptlv && ptlv->TLVLength) + { + dbg_time("%s HDR: RSSI %d dBm, ECIO %.1lf dBm, IO %d dBm", __func__, + ptlv->rssi, (-0.5) * (double)ptlv->ecio, ptlv->io); + } + } + + // GSM + { + PQMINAS_SIG_INFO_GSM_TLV_MSG ptlv = (PQMINAS_SIG_INFO_GSM_TLV_MSG)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x12); + if (ptlv && ptlv->TLVLength) + { + dbg_time("%s GSM: RSSI %d dBm", __func__, ptlv->rssi); + } + } + + // WCDMA + { + PQMINAS_SIG_INFO_WCDMA_TLV_MSG ptlv = (PQMINAS_SIG_INFO_WCDMA_TLV_MSG)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x13); + if (ptlv && ptlv->TLVLength) + { + dbg_time("%s WCDMA: RSSI %d dBm, ECIO %.1lf dBm", __func__, + ptlv->rssi, (-0.5) * (double)ptlv->ecio); + } + } + + // LTE + { + PQMINAS_SIG_INFO_LTE_TLV_MSG ptlv = (PQMINAS_SIG_INFO_LTE_TLV_MSG)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x14); + if (ptlv && ptlv->TLVLength) + { + dbg_time("%s LTE: RSSI %d dBm, RSRQ %d dB, RSRP %d dBm, SNR %.1lf dB", __func__, + ptlv->rssi, ptlv->rsrq, ptlv->rsrp, (0.1) * (double)ptlv->snr); + } + } + + // TDSCDMA + { + PQMINAS_SIG_INFO_TDSCDMA_TLV_MSG ptlv = (PQMINAS_SIG_INFO_TDSCDMA_TLV_MSG)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x15); + if (ptlv && ptlv->TLVLength) + { + dbg_time("%s LTE: RSCP %d dBm", __func__, ptlv->rscp); + } + } + + // 5G_NSA + if (s_5g_type == WWAN_DATA_CLASS_5G_NSA) + { + PQMINAS_SIG_INFO_5G_NSA_TLV_MSG ptlv = (PQMINAS_SIG_INFO_5G_NSA_TLV_MSG)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x17); + if (ptlv && ptlv->TLVLength) + { + dbg_time("%s 5G_NSA: RSRP %d dBm, SNR %.1lf dB", __func__, ptlv->rsrp, (0.1) * (double)ptlv->snr); + } + } + + // 5G_SA + if (s_5g_type == WWAN_DATA_CLASS_5G_SA) + { + PQMINAS_SIG_INFO_5G_SA_TLV_MSG ptlv = (PQMINAS_SIG_INFO_5G_SA_TLV_MSG)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x18); + if (ptlv && ptlv->TLVLength) + { + dbg_time("%s 5G_SA: NR5G_RSRQ %d dB", __func__, ptlv->nr5g_rsrq); + } + } + + free(pResponse); + return 0; +} +#endif + +#ifdef CONFIG_VERSION +static int requestBaseBandVersion(PROFILE_T *profile) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + PDEVICE_REV_ID revId; + int err; + + 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)) + { + uchar2char(profile->BaseBandVersion, sizeof(profile->BaseBandVersion), &revId->RevisionID, le16_to_cpu(revId->TLVLength)); + dbg_time("%s %s", __func__, profile->BaseBandVersion); + } + + free(pResponse); + return 0; +} +#endif + +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); +} + +static USHORT UimSetCardSlotReq(PQMUX_MSG pMUXMsg, void *arg) { + pMUXMsg->UIMSetCardSlotReq.TLVType = 0x01; + pMUXMsg->UIMSetCardSlotReq.TLVLength = cpu_to_le16(1); + pMUXMsg->UIMSetCardSlotReq.slot = *((UCHAR *)arg); + + return sizeof(QMIUIM_SET_CARD_SLOT_REQ_MSG); +} + +static int requestRadioPower(int state) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + UCHAR OperatingMode = (!!state) ? DMS_OP_MODE_ONLINE : DMS_OP_MODE_LOW_POWER; + USHORT SimOp = (!!state) ? QMIUIM_POWER_UP : QMIUIM_POWER_DOWN; + UCHAR cardSlot = 0x01; + + dbg_time("%s(%d)", __func__, state); + + pRequest = ComposeQMUXMsg(QMUX_TYPE_DMS, QMIDMS_SET_OPERATING_MODE_REQ, DmsSetOperatingModeReq, &OperatingMode); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + free(pResponse); + + pRequest = ComposeQMUXMsg(QMUX_TYPE_UIM, SimOp, UimSetCardSlotReq, &cardSlot); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + free(pResponse); + + return 0; +} + +static USHORT WdaSetLoopBackReq(PQMUX_MSG pMUXMsg, void *arg) { + (void)arg; + pMUXMsg->SetLoopBackReq.loopback_state.TLVType = 0x01; + pMUXMsg->SetLoopBackReq.loopback_state.TLVLength = cpu_to_le16(1); + + pMUXMsg->SetLoopBackReq.replication_factor.TLVType = 0x10; + pMUXMsg->SetLoopBackReq.replication_factor.TLVLength = cpu_to_le16(4); + + return sizeof(QMI_WDA_SET_LOOPBACK_CONFIG_REQ_MSG); +} + +static int requestSetLoopBackState(UCHAR loopback_state, ULONG replication_factor) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + + dbg_time("%s(loopback_state=%d, replication_factor=%u)", __func__, loopback_state, replication_factor); + + pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS_ADMIN, QMI_WDA_SET_LOOPBACK_CONFIG_REQ, WdaSetLoopBackReq, NULL); + pRequest->MUXMsg.SetLoopBackReq.loopback_state.TLVVaule = loopback_state; + pRequest->MUXMsg.SetLoopBackReq.replication_factor.TLVVaule = cpu_to_le16(replication_factor); + + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + free(pResponse); + return 0; +} + +#ifdef CONFIG_ENABLE_QOS +static USHORT QosSetBindMuxDataPort(PQMUX_MSG pMUXMsg, void *arg) { + PROFILE_T *profile = (PROFILE_T *)arg; + pMUXMsg->QosBindDataPortReq.EpIdTlv.TLVType = 0x10; + pMUXMsg->QosBindDataPortReq.EpIdTlv.TLVLength = cpu_to_le16(8); + pMUXMsg->QosBindDataPortReq.EpIdTlv.ep_type = cpu_to_le32(profile->rmnet_info.ep_type); + pMUXMsg->QosBindDataPortReq.EpIdTlv.iface_id = cpu_to_le32(profile->rmnet_info.iface_id); + pMUXMsg->QosBindDataPortReq.MuxIdTlv.TLVType = 0x11; + pMUXMsg->QosBindDataPortReq.MuxIdTlv.TLVLength = cpu_to_le16(1); + pMUXMsg->QosBindDataPortReq.MuxIdTlv.mux_id = profile->muxid; + return sizeof(QMI_QOS_BIND_DATA_PORT_REQ_MSG); +} + +#ifdef CONFIG_REG_QOS_IND +static USHORT QosIndRegReq(PQMUX_MSG pMUXMsg, void *arg) { + pMUXMsg->QosIndRegReq.ReportGlobalQosFlowTlv.TLVType = 0x10; + pMUXMsg->QosIndRegReq.ReportGlobalQosFlowTlv.TLVLength = cpu_to_le16(1); + pMUXMsg->QosIndRegReq.ReportGlobalQosFlowTlv.report_global_qos_flows = 1; + return sizeof(QMI_QOS_INDICATION_REGISTER_REQ_MSG); +} +#endif + +static int requestRegisterQos(PROFILE_T *profile) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse = NULL; + PQMUX_MSG pMUXMsg; + int err; + + pRequest = ComposeQMUXMsg(QMUX_TYPE_QOS, QMI_QOS_BIND_DATA_PORT_REQ , QosSetBindMuxDataPort, (void *)profile); + err = QmiThreadSendQMI(pRequest, &pResponse); + dbg_time("%s QosSetBindMuxDataPort", __func__); + qmi_rsp_check_and_return(); + if (pResponse) free(pResponse); + +#ifdef CONFIG_REG_QOS_IND + pRequest = ComposeQMUXMsg(QMUX_TYPE_QOS, QMI_QOS_INDICATION_REGISTER_REQ , QosIndRegReq, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + dbg_time("%s QosIndRegReq", __func__); + qmi_rsp_check_and_return(); + if (pResponse) free(pResponse); +#endif + return 0; +} + +#ifdef CONFIG_GET_QOS_INFO +UCHAR ql_get_qos_info_data_rate(PQCQMIMSG pResponse, void *max_data_rate) +{ + PQMI_QOS_GET_QOS_INFO_TLV_GRANTED_FLOW qos_tx_granted_flow = NULL; + PQMI_QOS_GET_QOS_INFO_TLV_GRANTED_FLOW qos_rx_granted_flow = NULL; + qos_tx_granted_flow = (PQMI_QOS_GET_QOS_INFO_TLV_GRANTED_FLOW)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11); + if(qos_tx_granted_flow != NULL) + { + *(ULONG64 *)(max_data_rate) = le64_to_cpu(qos_tx_granted_flow->data_rate_max); + dbg_time("GET_QOS_INFO: tx_data_rate_max=%llu", *(ULONG64 *)(max_data_rate+0)); + } + else + dbg_time("GET_QOS_INFO: No qos_tx_granted_flow"); + qos_rx_granted_flow = (PQMI_QOS_GET_QOS_INFO_TLV_GRANTED_FLOW)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x12); + if(qos_rx_granted_flow != NULL) + { + *(ULONG64 *)(max_data_rate+sizeof(ULONG64)) = le64_to_cpu(qos_rx_granted_flow->data_rate_max); + dbg_time("GET_QOS_INFO: rx_data_rate_max=%llu", *(ULONG64 *)(max_data_rate+sizeof(ULONG64))); + } + else + dbg_time("GET_QOS_INFO: No qos_rx_granted_flow"); + if(qos_tx_granted_flow != NULL || qos_rx_granted_flow != NULL) + return 0; + else + return 1; +} + +static USHORT QosGetQosInfoReq(PQMUX_MSG pMUXMsg, void *arg) { + PROFILE_T *profile = (PROFILE_T *)arg; + pMUXMsg->QosGetQosInfoReq.QosIdTlv.TLVType = 0x01; + pMUXMsg->QosGetQosInfoReq.QosIdTlv.TLVLength = cpu_to_le16(4); + pMUXMsg->QosGetQosInfoReq.QosIdTlv.qos_id = cpu_to_le32(profile->qos_id); + return sizeof(QMI_QOS_GET_QOS_INFO_REQ_MSG); +} + +static int requestGetQosInfo(PROFILE_T *profile) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse = NULL; + PQMUX_MSG pMUXMsg; + int err; + + if(profile->qos_id == 0) + { + dbg_time("%s request not send: invalid qos_id", __func__); + return 0; + } + pRequest = ComposeQMUXMsg(QMUX_TYPE_QOS, QMI_QOS_GET_QOS_INFO_REQ , QosGetQosInfoReq, (void *)profile); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + if (pResponse) + { +#ifdef CONFIG_GET_QOS_DATA_RATE + ULONG64 max_data_rate[2] = {0}; + if(ql_get_qos_info_data_rate(pResponse, (void *)max_data_rate) == 0){} +#endif + free(pResponse); + } + return 0; +} +#endif //#ifdef CONFIG_GET_QOS_INFO + +#ifdef CONFIG_REG_QOS_IND +UCHAR ql_get_global_qos_flow_ind_qos_id(PQCQMIMSG pResponse, UINT *qos_id) +{ + PQMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_STATE qos_flow_state = NULL; + qos_flow_state = (PQMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_STATE)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x01); + if(qos_flow_state != NULL) + { + if(le32_to_cpu(qos_flow_state->state_change) == QOS_IND_FLOW_STATE_ACTIVATED && qos_flow_state->new_flow == 1) + { + *qos_id = le32_to_cpu(qos_flow_state->qos_id); + dbg_time("QMI_QOS_GLOBAL_QOS_FLOW_IND: qos_id=%u state=QOS_IND_FLOW_STATE_ACTIVATED", *qos_id); + } + return (qos_flow_state->new_flow); + } + return (0); +} + +#ifdef CONFIG_GET_QOS_DATA_RATE +UCHAR ql_get_global_qos_flow_ind_data_rate(PQCQMIMSG pResponse, void *max_data_rate) +{ + PQMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_GRANTED qos_tx_flow_granted = NULL; + PQMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_GRANTED qos_rx_flow_granted = NULL; + qos_tx_flow_granted = (PQMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_GRANTED)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x10); + if(qos_tx_flow_granted != NULL) + { + *(ULONG64 *)(max_data_rate) = le64_to_cpu(qos_tx_flow_granted->data_rate_max); + dbg_time("QMI_QOS_GLOBAL_QOS_FLOW_IND: tx_data_rate_max=%llu", *(ULONG64 *)(max_data_rate+0)); + } + else + dbg_time("QMI_QOS_GLOBAL_QOS_FLOW_IND: No qos_tx_flow_granted"); + qos_rx_flow_granted = (PQMI_QOS_GLOBAL_QOS_FLOW_TLV_FLOW_GRANTED)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11); + if(qos_rx_flow_granted != NULL) + { + *(ULONG64 *)(max_data_rate+sizeof(ULONG64)) = le64_to_cpu(qos_rx_flow_granted->data_rate_max); + dbg_time("QMI_QOS_GLOBAL_QOS_FLOW_IND: rx_data_rate_max=%llu", *(ULONG64 *)(max_data_rate+sizeof(ULONG64))); + } + else + dbg_time("QMI_QOS_GLOBAL_QOS_FLOW_IND: No qos_rx_flow_granted"); + if(qos_tx_flow_granted != NULL || qos_rx_flow_granted != NULL) + return 0; + else + return 1; +} +#endif +#endif //#ifdef CONFIG_REG_QOS_IND +#endif //#ifdef CONFIG_ENABLE_QOS + +#ifdef CONFIG_CELLINFO +/* + at+qeng="servingcell" and at+qeng="neighbourcell" + https://gitlab.freedesktop.org/mobile-broadband/libqmi/-/blob/master/src/qmicli/qmicli-nas.c +*/ +static int nas_get_cell_location_info(void); +static int nas_get_rf_band_information(void); + +static int requestGetCellInfoList(void) { + dbg_time("%s", __func__); + nas_get_cell_location_info(); + nas_get_rf_band_information(); + return 0; +} +#endif + +const struct request_ops qmi_request_ops = { +#ifdef CONFIG_VERSION + .requestBaseBandVersion = requestBaseBandVersion, +#endif + .requestSetEthMode = requestSetEthMode, +#ifdef CONFIG_SIM + .requestGetSIMStatus = requestGetSIMStatus, + .requestEnterSimPin = requestEnterSimPin, +#endif +#ifdef CONFIG_IMSI_ICCID + .requestGetICCID = requestGetICCID, + .requestGetIMSI = requestGetIMSI, +#endif +#ifdef CONFIG_APN + .requestSetProfile = requestSetProfile, + .requestGetProfile = requestGetProfile, +#endif + .requestRegistrationState = requestRegistrationState, + .requestSetupDataCall = requestSetupDataCall, + .requestQueryDataCall = requestQueryDataCall, + .requestDeactivateDefaultPDP = requestDeactivateDefaultPDP, + .requestGetIPAddress = requestGetIPAddress, +#ifdef CONFIG_SIGNALINFO + .requestGetSignalInfo = requestGetSignalInfo, +#endif +#ifdef CONFIG_CELLINFO + .requestGetCellInfoList = requestGetCellInfoList, +#endif + .requestSetLoopBackState = requestSetLoopBackState, + .requestRadioPower = requestRadioPower, +#ifdef CONFIG_ENABLE_QOS + .requestRegisterQos = requestRegisterQos, +#endif +#ifdef CONFIG_GET_QOS_INFO + .requestGetQosInfo = requestGetQosInfo, +#endif +#ifdef CONFIG_COEX_WWAN_STATE + .requestGetCoexWWANState = requestGetCoexWWANState, +#endif +}; + +#ifdef CONFIG_CELLINFO +static char *str_from_bcd_plmn (uint8 plmn[3]) +{ + const char bcd_chars[] = "0123456789*#abc\0\0"; + static char str[12]; + int i; + int j = 0; + + for (i = 0; i < 3; i++) { + str[j] = bcd_chars[plmn[i]&0xF]; + if (str[j]) j++; + str[j] = bcd_chars[plmn[i]>>4]; + if (str[j]) j++; + } + + str[j++] = 0; + + return str; +} + +typedef struct { + UINT type; + const char *name; +} ENUM_NAME_T; + +#define enum_name(type) {type, #type} +#define N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0])) + +static const ENUM_NAME_T QMI_NAS_ACTIVE_BAND_NAME[] = { + enum_name(QMI_NAS_ACTIVE_BAND_BC_0), + enum_name(QMI_NAS_ACTIVE_BAND_BC_1), + enum_name(QMI_NAS_ACTIVE_BAND_BC_2), + enum_name(QMI_NAS_ACTIVE_BAND_BC_3), + enum_name(QMI_NAS_ACTIVE_BAND_BC_4), + enum_name(QMI_NAS_ACTIVE_BAND_BC_5), + enum_name(QMI_NAS_ACTIVE_BAND_BC_6), + enum_name(QMI_NAS_ACTIVE_BAND_BC_7), + enum_name(QMI_NAS_ACTIVE_BAND_BC_8), + enum_name(QMI_NAS_ACTIVE_BAND_BC_9), + enum_name(QMI_NAS_ACTIVE_BAND_BC_10), + enum_name(QMI_NAS_ACTIVE_BAND_BC_11), + enum_name(QMI_NAS_ACTIVE_BAND_BC_12), + enum_name(QMI_NAS_ACTIVE_BAND_BC_13), + enum_name(QMI_NAS_ACTIVE_BAND_BC_14), + enum_name(QMI_NAS_ACTIVE_BAND_BC_15), + enum_name(QMI_NAS_ACTIVE_BAND_BC_16), + enum_name(QMI_NAS_ACTIVE_BAND_BC_17), + enum_name(QMI_NAS_ACTIVE_BAND_BC_18), + enum_name(QMI_NAS_ACTIVE_BAND_BC_19), + enum_name(QMI_NAS_ACTIVE_BAND_GSM_450), + enum_name(QMI_NAS_ACTIVE_BAND_GSM_480), + enum_name(QMI_NAS_ACTIVE_BAND_GSM_750), + enum_name(QMI_NAS_ACTIVE_BAND_GSM_850), + enum_name(QMI_NAS_ACTIVE_BAND_GSM_900_EXTENDED), + enum_name(QMI_NAS_ACTIVE_BAND_GSM_900_PRIMARY), + enum_name(QMI_NAS_ACTIVE_BAND_GSM_900_RAILWAYS), + enum_name(QMI_NAS_ACTIVE_BAND_GSM_DCS_1800), + enum_name(QMI_NAS_ACTIVE_BAND_GSM_PCS_1900), + enum_name(QMI_NAS_ACTIVE_BAND_WCDMA_2100), + enum_name(QMI_NAS_ACTIVE_BAND_WCDMA_PCS_1900), + enum_name(QMI_NAS_ACTIVE_BAND_WCDMA_DCS_1800), + enum_name(QMI_NAS_ACTIVE_BAND_WCDMA_1700_US), + enum_name(QMI_NAS_ACTIVE_BAND_WCDMA_850), + enum_name(QMI_NAS_ACTIVE_BAND_WCDMA_800), + enum_name(QMI_NAS_ACTIVE_BAND_WCDMA_2600), + enum_name(QMI_NAS_ACTIVE_BAND_WCDMA_900), + enum_name(QMI_NAS_ACTIVE_BAND_WCDMA_1700_JAPAN), + enum_name(QMI_NAS_ACTIVE_BAND_WCDMA_1500_JAPAN), + enum_name(QMI_NAS_ACTIVE_BAND_WCDMA_850_JAPAN), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_1), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_2), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_3), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_4), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_5), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_6), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_7), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_8), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_9), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_10), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_11), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_12), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_13), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_14), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_17), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_18), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_19), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_20), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_21), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_23), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_24), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_25), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_26), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_27), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_28), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_29), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_30), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_31), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_32), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_33), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_34), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_35), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_36), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_37), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_38), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_39), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_40), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_41), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_42), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_43), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_46), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_47), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_48), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_66), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_71), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_125), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_126), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_127), + enum_name(QMI_NAS_ACTIVE_BAND_EUTRAN_250), + enum_name(QMI_NAS_ACTIVE_BAND_TDSCDMA_A), + enum_name(QMI_NAS_ACTIVE_BAND_TDSCDMA_B), + enum_name(QMI_NAS_ACTIVE_BAND_TDSCDMA_C), + enum_name(QMI_NAS_ACTIVE_BAND_TDSCDMA_D), + enum_name(QMI_NAS_ACTIVE_BAND_TDSCDMA_E), + enum_name(QMI_NAS_ACTIVE_BAND_TDSCDMA_F), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_1 ), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_2 ), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_3 ), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_5 ), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_7 ), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_8 ), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_20), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_28), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_38), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_41), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_50), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_51), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_66), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_70), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_71), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_74), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_75), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_76), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_77), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_78), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_79), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_80), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_81), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_82), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_83), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_84), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_85), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_257), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_258), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_259), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_260), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_261), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_12), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_25), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_34), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_39), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_40), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_65), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_86), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_48), + enum_name(QMI_NAS_ACTIVE_BAND_NR5G_BAND_14), +}; + +static const char *qmi_nas_radio_interface_get_string(uint8 radio_if) +{ + const char *str = NULL; + + switch (radio_if) { + case QMI_NAS_RADIO_INTERFACE_CDMA_1X: str = "cdma-1x"; break; + case QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO: str = "cdma-1xevdo"; break; + case QMI_NAS_RADIO_INTERFACE_AMPS: str = "amps"; break; + case QMI_NAS_RADIO_INTERFACE_GSM: str = "gsm"; break; + case QMI_NAS_RADIO_INTERFACE_UMTS: str = "umts"; break; + case QMI_NAS_RADIO_INTERFACE_LTE: str = "lte"; break; + case QMI_NAS_RADIO_INTERFACE_TD_SCDMA: str = "td-scdma"; break; + case QMI_NAS_RADIO_INTERFACE_5GNR: str = "5gnr"; break; + default: str = NULL; break; + } + + return str ? str : "unknown"; +} + +static const char *qmi_nas_active_band_get_string(uint32 active_band) +{ + size_t i; + + for (i = 0; i < N_ELEMENTS(QMI_NAS_ACTIVE_BAND_NAME); i++) { + if (active_band == QMI_NAS_ACTIVE_BAND_NAME[i].type) + return QMI_NAS_ACTIVE_BAND_NAME[i].name + strlen("QMI_NAS_ACTIVE_BAND_"); + } + + return "unknown"; +} + +typedef struct { + uint16 min; + uint16 max; + const char *name; +} EarfcnRange; + +/* http://niviuk.free.fr/lte_band.php */ +static const EarfcnRange earfcn_ranges[] = { + { 0, 599, "E-UTRA band 1: 2100" }, + { 600, 1199, "E-UTRA band 2: 1900 PCS" }, + { 1200, 1949, "E-UTRA band 3: 1800+" }, + { 1950, 2399, "E-UTRA band 4: AWS-1" }, + { 2400, 2649, "E-UTRA band 5: 850" }, + { 2650, 2749, "E-UTRA band 6: UMTS only" }, + { 2750, 3449, "E-UTRA band 7: 2600" }, + { 3450, 3799, "E-UTRA band 8: 900" }, + { 3800, 4149, "E-UTRA band 9: 1800" }, + { 4150, 4749, "E-UTRA band 10: AWS-1+" }, + { 4750, 4999, "E-UTRA band 11: 1500 Lower" }, + { 5000, 5179, "E-UTRA band 12: 700 a" }, + { 5180, 5279, "E-UTRA band 13: 700 c" }, + { 5280, 5379, "E-UTRA band 14: 700 PS" }, + { 5730, 5849, "E-UTRA band 17: 700 b" }, + { 5850, 5999, "E-UTRA band 18: 800 Lower" }, + { 6000, 6149, "E-UTRA band 19: 800 Upper" }, + { 6150, 6449, "E-UTRA band 20: 800 DD" }, + { 6450, 6599, "E-UTRA band 21: 1500 Upper" }, + { 6600, 7399, "E-UTRA band 22: 3500" }, + { 7500, 7699, "E-UTRA band 23: 2000 S-band" }, + { 7700, 8039, "E-UTRA band 24: 1600 L-band" }, + { 8040, 8689, "E-UTRA band 25: 1900+" }, + { 8690, 9039, "E-UTRA band 26: 850+" }, + { 9040, 9209, "E-UTRA band 27: 800 SMR" }, + { 9210, 9659, "E-UTRA band 28: 700 APT" }, + { 9660, 9769, "E-UTRA band 29: 700 d" }, + { 9770, 9869, "E-UTRA band 30: 2300 WCS" }, + { 9870, 9919, "E-UTRA band 31: 450" }, + { 9920, 10359, "E-UTRA band 32: 1500 L-band" }, + { 36000, 36199, "E-UTRA band 33: TD 1900" }, + { 36200, 36349, "E-UTRA band 34: TD 2000" }, + { 36350, 36949, "E-UTRA band 35: TD PCS Lower" }, + { 36950, 37549, "E-UTRA band 36: TD PCS Upper" }, + { 37550, 37749, "E-UTRA band 37: TD PCS Center" }, + { 37750, 38249, "E-UTRA band 38: TD 2600" }, + { 38250, 38649, "E-UTRA band 39: TD 1900+" }, + { 38650, 39649, "E-UTRA band 40: TD 2300" }, + { 39650, 41589, "E-UTRA band 41: TD 2500" }, + { 41590, 43589, "E-UTRA band 42: TD 3500" }, + { 43590, 45589, "E-UTRA band 43: TD 3700" }, + { 45590, 46589, "E-UTRA band 44: TD 700" }, +}; + +static const char * earfcn_to_eutra_band_string (uint16 earfcn) +{ + size_t i; + + for (i = 0; i < N_ELEMENTS (earfcn_ranges); i++) { + if (earfcn <= earfcn_ranges[i].max && earfcn >= earfcn_ranges[i].min) + return earfcn_ranges[i].name; + } + + return "unknown"; +} + +static int nas_get_cell_location_info(void) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + PQMI_TLV pV; + int err; + int i, j; + + pRequest = ComposeQMUXMsg(QMUX_TYPE_NAS, QMINAS_GET_CELL_LOCATION_INFO_REQ, NULL, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + pV = (PQMI_TLV)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x2E); + if (pV && pV->TLVLength) { + printf ("5GNR ARFCN: '%u'\n", pV->u32); + } + + { + NasGetCellLocationNr5gServingCell *ptlv = (NasGetCellLocationNr5gServingCell *)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x2F); + if (ptlv && ptlv->TLVLength) + { + printf ("5GNR cell information:\n" + "\tPLMN: '%s'\n" + "\tTracking Area Code: '%u'\n" + "\tGlobal Cell ID: '%" PRIu64 "'\n" + "\tPhysical Cell ID: '%u'\n" + "\tRSRQ: '%.1lf dB'\n" + "\tRSRP: '%.1lf dBm'\n" + "\tSNR: '%.1lf dB'\n", + str_from_bcd_plmn(ptlv->plmn), + ptlv->tac[0]<<16 | ptlv->tac[1]<<8 | ptlv->tac[2] , + ptlv->global_cell_id, + ptlv->physical_cell_id, + (0.1) * ((double)ptlv->rsrq), + (0.1) * ((double)ptlv->rsrp), + (0.1) * ((double)ptlv->snr)); + } + } + + { + NasGetCellLocationLteInfoIntrafrequency *ptlv = (NasGetCellLocationLteInfoIntrafrequency *)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x13); + if (ptlv && ptlv->TLVLength) + { + printf ("Intrafrequency LTE Info:\n" + "\tUE In Idle: '%s'\n" + "\tPLMN: '%s'\n" + "\tTracking Area Code: '%u'\n" + "\tGlobal Cell ID: '%u'\n" + "\tEUTRA Absolute RF Channel Number: '%u' (%s)\n" + "\tServing Cell ID: '%u'\n", + ptlv->ue_in_idle ? "yes" : "no", + str_from_bcd_plmn(ptlv->plmn), + ptlv->tracking_area_code, + ptlv->global_cell_id, + ptlv->absolute_rf_channel_number, earfcn_to_eutra_band_string(ptlv->absolute_rf_channel_number), + ptlv->serving_cell_id); + + if (ptlv->ue_in_idle) + printf ("\tCell Reselection Priority: '%u'\n" + "\tS Non Intra Search Threshold: '%u'\n" + "\tServing Cell Low Threshold: '%u'\n" + "\tS Intra Search Threshold: '%u'\n", + ptlv->cell_reselection_priority, + ptlv->s_non_intra_search_threshold, + ptlv->serving_cell_low_threshold, + ptlv->s_intra_search_threshold); + + + for (i = 0; i < ptlv->cells_len; i++) { + NasGetCellLocationLteInfoCell *cell = &ptlv->cells_array[i]; + + printf ("\tCell [%u]:\n" + "\t\tPhysical Cell ID: '%u'\n" + "\t\tRSRQ: '%.1lf' dB\n" + "\t\tRSRP: '%.1lf' dBm\n" + "\t\tRSSI: '%.1lf' dBm\n", + i, + cell->physical_cell_id, + (double) cell->rsrq * 0.1, + (double) cell->rsrp * 0.1, + (double) cell->rssi * 0.1); + + if (ptlv->ue_in_idle) + printf ("\t\tCell Selection RX Level: '%d'\n", + cell->cell_selection_rx_level); + } + } + } + + { + NasGetCellLocationLteInfoInterfrequency *ptlv = (NasGetCellLocationLteInfoInterfrequency *)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x14); + if (ptlv && ptlv->TLVLength) + { + int off = offsetof(NasGetCellLocationLteInfoInterfrequency, freqs[0]); + printf ("Interfrequency LTE Info:\n" + "\tUE In Idle: '%s'\n", ptlv->ue_in_idle ? "yes" : "no"); + + for (i = 0; i < ptlv->freqs_len; i++) { + NasGetCellLocationLteInfoInterfrequencyFrequencyElement *freq = (((void *)ptlv) + off); + + off += sizeof(*freq); + printf ("\tFrequency [%u]:\n" + "\t\tEUTRA Absolute RF Channel Number: '%u' (%s)\n" + "\t\tSelection RX Level Low Threshold: '%u'\n" + "\t\tCell Selection RX Level High Threshold: '%u'\n", + i, + freq->eutra_absolute_rf_channel_number, earfcn_to_eutra_band_string(freq->eutra_absolute_rf_channel_number), + freq->cell_selection_rx_level_low_threshold, + freq->cell_selection_rx_level_high_threshold); + if (ptlv->ue_in_idle) + printf ("\t\tCell Reselection Priority: '%u'\n", + freq->cell_reselection_priority); + + + for (j = 0; j < freq->cells_len; j++) { + NasGetCellLocationLteInfoCell *cell = &freq->cells_array[j]; + + off += sizeof(*cell); + printf ("\t\tCell [%u]:\n" + "\t\t\tPhysical Cell ID: '%u'\n" + "\t\t\tRSRQ: '%.1lf' dB\n" + "\t\t\tRSRP: '%.1lf' dBm\n" + "\t\t\tRSSI: '%.1lf' dBm\n" + "\t\t\tCell Selection RX Level: '%u'\n", + j, + cell->physical_cell_id, + (double) cell->rsrq * 0.1, + (double) cell->rsrp * 0.1, + (double) cell->rssi * 0.1, + cell->cell_selection_rx_level); + } + } + } + } + + pV = (PQMI_TLV)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x1E); + if (pV && pV->TLVLength) { + if (pV->u32 == 0xFFFFFFFF) + printf ("LTE Timing Advance: 'unavailable'\n"); + else + printf ("LTE Timing Advance: '%u'\n", pV->u32); + } + + free(pResponse); + return 0; +} + +static int nas_get_rf_band_information(void) +{ + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + int i; + + pRequest = ComposeQMUXMsg(QMUX_TYPE_NAS, QMINAS_GET_RF_BAND_INFO_REQ, NULL, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + { + NasGetRfBandInfoList *ptlv = (NasGetRfBandInfoList *)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x01); + if (ptlv && ptlv->TLVLength) + { + printf ("Band Information:\n"); + for (i = 0; i < ptlv->num_instances; i++) { + NasGetRfBandInfo *band = &ptlv->bands_array[i]; + + printf ("\tRadio Interface: '%s'\n" + "\tActive Band Class: '%s'\n" + "\tActive Channel: '%u'\n", + qmi_nas_radio_interface_get_string (band->radio_if), + qmi_nas_active_band_get_string (band->active_band), + band->active_channel); + } + } + } + + { + NasGetRfBandInfoExtendedList *ptlv = (NasGetRfBandInfoExtendedList *)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11); + if (ptlv && ptlv->TLVLength) + { + printf ("Band Information (Extended):\n"); + for (i = 0; i < ptlv->num_instances; i++) { + NasGetRfBandInfoExtended *band = &ptlv->bands_array[i]; + + printf ("\tRadio Interface: '%s'\n" + "\tActive Band Class: '%s'\n" + "\tActive Channel: '%u'\n", + qmi_nas_radio_interface_get_string (band->radio_if), + qmi_nas_active_band_get_string (band->active_band), + band->active_channel); + } + } + } + + { + NasGetRfBandInfoBandWidthList *ptlv = (NasGetRfBandInfoBandWidthList *)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x12); + if (ptlv && ptlv->TLVLength) + { + printf ("Bandwidth:\n"); + for (i = 0; i < ptlv->num_instances; i++) { + NasGetRfBandInfoBandWidth *band = &ptlv->bands_array[i]; + + printf ("\tRadio Interface: '%s'\n" + "\tBandwidth: '%u'\n", + qmi_nas_radio_interface_get_string (band->radio_if), + (band->bandwidth)); + } + } + } + + free(pResponse); + return 0; +} +#endif diff --git a/application/quectel_CM_5G/src/QMIThread.h b/application/quectel_CM_5G/src/QMIThread.h new file mode 100644 index 0000000..6d65053 --- /dev/null +++ b/application/quectel_CM_5G/src/QMIThread.h @@ -0,0 +1,424 @@ +#ifndef __QMI_THREAD_H__ +#define __QMI_THREAD_H__ + +#define CONFIG_GOBINET +#define CONFIG_QMIWWAN +#define CONFIG_SIM +#define CONFIG_APN +#define CONFIG_VERSION +//#define CONFIG_SIGNALINFO +//#define CONFIG_CELLINFO +//#define CONFIG_COEX_WWAN_STATE +#define CONFIG_DEFAULT_PDP 1 +//#define CONFIG_IMSI_ICCID +#define QUECTEL_UL_DATA_AGG +//#define QUECTEL_QMI_MERGE +//#define REBOOT_SIM_CARD_WHEN_APN_CHANGE +//#define REBOOT_SIM_CARD_WHEN_LONG_TIME_NO_PS 60 //unit is seconds +//#define CONFIG_QRTR +//#define CONFIG_ENABLE_QOS +//#define CONFIG_REG_QOS_IND +//#define CONFIG_GET_QOS_INFO +//#define CONFIG_GET_QOS_DATA_RATE + +#if (defined(CONFIG_REG_QOS_IND) || defined(CONFIG_GET_QOS_INFO) || defined(CONFIG_GET_QOS_DATA_RATE)) +#ifndef CONFIG_REG_QOS_IND +#define CONFIG_REG_QOS_IND +#endif +#ifndef CONFIG_ENABLE_QOS +#define CONFIG_ENABLE_QOS +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qendian.h" +#include "QCQMI.h" +#include "QCQCTL.h" +#include "QCQMUX.h" +#include "util.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 +#define WWAN_DATA_CLASS_5G_NSA 0x00000040 +#define WWAN_DATA_CLASS_5G_SA 0x00000080 +#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; + const char *str; +}; + +#pragma pack(push, 1) +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; + +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; + +//Configured downlink data aggregationprotocol +#define WDA_DL_DATA_AGG_DISABLED (0x00) //DL data aggregation is disabled (default) +#define WDA_DL_DATA_AGG_TLP_ENABLED (0x01) // DL TLP is enabled +#define WDA_DL_DATA_AGG_QC_NCM_ENABLED (0x02) // DL QC_NCM isenabled +#define WDA_DL_DATA_AGG_MBIM_ENABLED (0x03) // DL MBIM isenabled +#define WDA_DL_DATA_AGG_RNDIS_ENABLED (0x04) // DL RNDIS is enabled +#define WDA_DL_DATA_AGG_QMAP_ENABLED (0x05) // DL QMAP isenabled +#define WDA_DL_DATA_AGG_QMAP_V2_ENABLED (0x06) // DL QMAP V2 is enabled +#define WDA_DL_DATA_AGG_QMAP_V3_ENABLED (0x07) // DL QMAP V3 is enabled +#define WDA_DL_DATA_AGG_QMAP_V4_ENABLED (0x08) // DL QMAP V4 is enabled +#define WDA_DL_DATA_AGG_QMAP_V5_ENABLED (0x09) // DL QMAP V5 is enabled + +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; + +#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); +}; +#ifdef CONFIG_QRTR +extern const struct qmi_device_ops qrtr_qmidev_ops; +#endif +extern const struct qmi_device_ops gobi_qmidev_ops; +extern const struct qmi_device_ops qmiwwan_qmidev_ops; +extern const struct qmi_device_ops mbim_dev_ops; +extern const struct qmi_device_ops atc_dev_ops; +extern int (*qmidev_send)(PQCQMIMSG pRequest); + +struct usb_device_info { + int idVendor; + int idProduct; + int busnum; + int devnum; + int bNumInterfaces; +}; + +struct usb_interface_info { + int bNumEndpoints; + int bInterfaceClass; + int bInterfaceSubClass; + int bInterfaceProtocol; + char driver[32]; +}; + +#define LIBQMI_PROXY "qmi-proxy" //src/libqmi-glib/qmi-proxy.h +#define LIBMBIM_PROXY "mbim-proxy" +#define QUECTEL_QMI_PROXY "quectel-qmi-proxy" +#define QUECTEL_MBIM_PROXY "quectel-mbim-proxy" +#define QUECTEL_ATC_PROXY "quectel-atc-proxy" +#define QUECTEL_QRTR_PROXY "quectel-qrtr-proxy" + +#ifndef bool +#define bool uint8_t +#endif +struct request_ops; +typedef struct __PROFILE { + //user input start + const char *apn; + const char *user; + const char *password; + int auth; + int iptype; + const char *pincode; + char proxy[32]; + int pdp;//pdp_context + int profile_index;//profile_index + int enable_bridge; + bool enable_ipv4; + bool enable_ipv6; + bool no_dhcp; + const char *logfile; + const char *usblogfile; + char expect_adapter[32]; + int kill_pdp; + int replication_factor; + //user input end + + char qmichannel[32]; + char usbnet_adapter[32]; + char qmapnet_adapter[32]; + char driver_name[32]; + int qmap_mode; + int qmap_size; + int qmap_version; + int curIpFamily; + int rawIP; + int muxid; +#ifdef CONFIG_ENABLE_QOS + UINT qos_id; +#endif + int wda_client; + uint32_t udhcpc_ip; + IPV4_T ipv4; + IPV6_T ipv6; + UINT PCSCFIpv4Addr1; + UINT PCSCFIpv4Addr2; + UCHAR PCSCFIpv6Addr1[16]; + UCHAR PCSCFIpv6Addr2[16]; + bool reattach_flag; + int hardware_interface; + int software_interface; + + struct usb_device_info usb_dev; + struct usb_interface_info usb_intf; + + int usbmon_fd; + FILE *usbmon_logfile_fp; + bool loopback_state; + + char BaseBandVersion[64]; + char old_apn[64]; + char old_user[64]; + char old_password[64]; + int old_auth; + int old_iptype; + int metric; + + const struct qmi_device_ops *qmi_ops; + const struct request_ops *request_ops; + RMNET_INFO rmnet_info; +} PROFILE_T; + +#ifdef QUECTEL_QMI_MERGE +#define MERGE_PACKET_IDENTITY 0x2c7c +#define MERGE_PACKET_VERSION 0x0001 +#define MERGE_PACKET_MAX_PAYLOAD_SIZE 56 +typedef struct __QMI_MSG_HEADER { + uint16_t idenity; + uint16_t version; + uint16_t cur_len; + uint16_t total_len; +} QMI_MSG_HEADER; + +typedef struct __QMI_MSG_PACKET { + QMI_MSG_HEADER header; + uint16_t len; + char buf[4096]; +} QMI_MSG_PACKET; +#endif + +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; + +#pragma pack(pop) + +#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 +#define MODEM_REPORT_RESET_EVENT 0x1006 +#define RIL_UNSOL_LOOPBACK_CONFIG_IND 0x1007 +#ifdef CONFIG_REG_QOS_IND +#define RIL_UNSOL_GLOBAL_QOS_FLOW_IND_QOS_ID 0x1008 +#endif + +extern pthread_mutex_t cm_command_mutex; +extern pthread_cond_t cm_command_cond; +extern unsigned int cm_recv_buf[1024]; +extern int cm_open_dev(const char *dev); +extern int cm_open_proxy(const char *name); +extern int pthread_cond_timeout_np(pthread_cond_t *cond, pthread_mutex_t * mutex, unsigned msecs); +extern int QmiThreadSendQMITimeout(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse, unsigned msecs, const char *funcname); +#define QmiThreadSendQMI(pRequest, ppResponse) QmiThreadSendQMITimeout(pRequest, ppResponse, 30 * 1000, __func__) +extern void QmiThreadRecvQMI(PQCQMIMSG pResponse); +extern void udhcpc_start(PROFILE_T *profile); +extern void udhcpc_stop(PROFILE_T *profile); +extern void ql_set_driver_link_state(PROFILE_T *profile, int link_state); +extern void ql_set_driver_qmap_setting(PROFILE_T *profile, QMAP_SETTING *qmap_settings); +extern void ql_get_driver_rmnet_info(PROFILE_T *profile, RMNET_INFO *rmnet_info); +extern void dump_qmi(void *dataBuffer, int dataLen); +extern void qmidevice_send_event_to_main(int triger_event); +extern void qmidevice_send_event_to_main_ext(int triger_event, void *data, unsigned len); +extern uint8_t qmi_over_mbim_get_client_id(uint8_t QMIType); +extern uint8_t qmi_over_mbim_release_client_id(uint8_t QMIType, uint8_t ClientId); +#ifdef CONFIG_REG_QOS_IND +extern UCHAR ql_get_global_qos_flow_ind_qos_id(PQCQMIMSG pResponse, UINT *qos_id); +#endif +#ifdef CONFIG_GET_QOS_DATA_RATE +extern UCHAR ql_get_global_qos_flow_ind_data_rate(PQCQMIMSG pResponse, void *max_data_rate); +#endif + +struct request_ops { + int (*requestBaseBandVersion)(PROFILE_T *profile); + int (*requestSetEthMode)(PROFILE_T *profile); + int (*requestSetLoopBackState)(UCHAR loopback_state, ULONG replication_factor); + int (*requestGetSIMStatus)(SIM_Status *pSIMStatus); + int (*requestEnterSimPin)(const char *pPinCode); + int (*requestSetProfile)(PROFILE_T *profile); // 1 ~ success and apn change, 0 ~ success and no apn change, -1 ~ fail + int (*requestGetProfile)(PROFILE_T *profile); + int (*requestRegistrationState)(UCHAR *pPSAttachedState); + int (*requestSetupDataCall)(PROFILE_T *profile, int curIpFamily); + int (*requestQueryDataCall)(UCHAR *pConnectionStatus, int curIpFamily); + int (*requestDeactivateDefaultPDP)(PROFILE_T *profile, int curIpFamily); + int (*requestGetIPAddress)(PROFILE_T *profile, int curIpFamily); + int (*requestGetSignalInfo)(void); + int (*requestGetCellInfoList)(void); + int (*requestGetICCID)(void); + int (*requestGetIMSI)(void); + int (*requestRadioPower)(int state); + int (*requestRegisterQos)(PROFILE_T *profile); + int (*requestGetQosInfo)(PROFILE_T *profile); + int (*requestGetCoexWWANState)(void); +}; +extern const struct request_ops qmi_request_ops; +extern const struct request_ops mbim_request_ops; +extern const struct request_ops atc_request_ops; + +extern int get_driver_type(PROFILE_T *profile); +extern BOOL qmidevice_detect(char *qmichannel, char *usbnet_adapter, unsigned bufsize, PROFILE_T *profile); +int mhidevice_detect(char *qmichannel, char *usbnet_adapter, PROFILE_T *profile); +int atdevice_detect(char *atchannel, char *usbnet_adapter, PROFILE_T *profile); +extern int ql_bridge_mode_detect(PROFILE_T *profile); +extern int ql_enable_qmi_wwan_rawip_mode(PROFILE_T *profile); +extern int ql_qmap_mode_detect(PROFILE_T *profile); +#ifdef CONFIG_QRTR +extern int rtrmnet_ctl_create_vnd(char *devname, char *vndname, uint8_t muxid, + uint32_t qmap_version, uint32_t ul_agg_cnt, uint32_t ul_agg_size); +#endif + +#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 int qmidevice_control_fd[2]; +extern int g_donot_exit_when_modem_hangup; +extern void update_resolv_conf(int iptype, const char *ifname, const char *dns1, const char *dns2); +void update_ipv4_address(const char *ifname, const char *ip, const char *gw, unsigned prefix); +void update_ipv6_address(const char *ifname, const char *ip, const char *gw, unsigned prefix); +int reattach_driver(PROFILE_T *profile); +extern void no_trunc_strncpy(char *dest, const char *src, size_t dest_size); + +enum +{ + DRV_INVALID, + SOFTWARE_QMI, + SOFTWARE_MBIM, + SOFTWARE_ECM_RNDIS_NCM, + SOFTWARE_QRTR, + HARDWARE_PCIE, + HARDWARE_USB, +}; + +enum +{ + SIG_EVENT_START, + SIG_EVENT_CHECK, + SIG_EVENT_STOP, +}; + +typedef enum +{ + DMS_OP_MODE_ONLINE, + DMS_OP_MODE_LOW_POWER, + DMS_OP_MODE_FACTORY_TEST_MODE, + DMS_OP_MODE_OFFLINE, + DMS_OP_MODE_RESETTING, + DMS_OP_MODE_SHUTTING_DOWN, + DMS_OP_MODE_PERSISTENT_LOW_POWER, + DMS_OP_MODE_MODE_ONLY_LOW_POWER, + DMS_OP_MODE_NET_TEST_GW, +}Device_operating_mode; + +#ifdef CM_DEBUG +#define dbg_time(fmt, args...) do { \ + fprintf(stdout, "[%15s-%04d: %s] " fmt "\n", __FILE__, __LINE__, get_time(), ##args); \ + fflush(stdout);\ + if (logfilefp) fprintf(logfilefp, "[%s-%04d: %s] " fmt "\n", __FILE__, __LINE__, get_time(), ##args); \ +} while(0) +#else +#define dbg_time(fmt, args...) do { \ + fprintf(stdout, "[%s] " fmt "\n", get_time(), ##args); \ + fflush(stdout);\ + if (logfilefp) fprintf(logfilefp, "[%s] " fmt "\n", get_time(), ##args); \ +} while(0) +#endif +#endif diff --git a/application/quectel_CM_5G/src/QmiWwanCM.c b/application/quectel_CM_5G/src/QmiWwanCM.c new file mode 100644 index 0000000..1800f64 --- /dev/null +++ b/application/quectel_CM_5G/src/QmiWwanCM.c @@ -0,0 +1,459 @@ +/****************************************************************************** + @file QmiWwanCM.c + @brief QMI WWAN connectivity manager. + + DESCRIPTION + Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ + +#include +#include +#include +#include +#include +#include "QMIThread.h" + +#ifdef CONFIG_QMIWWAN +static int cdc_wdm_fd = -1; +static UCHAR qmiclientId[QMUX_TYPE_ALL]; + +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) +{ + (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 USHORT CtlLibQmiProxyOpenReq(PQMICTL_MSG QCTLMsg, void *arg) +{ + (void)arg; + const char *device_path = (const char *)(arg); + QCTLMsg->LibQmiProxyOpenReq.TLVType = 0x01; + QCTLMsg->LibQmiProxyOpenReq.TLVLength = cpu_to_le16(strlen(device_path)); + //strcpy(QCTLMsg->LibQmiProxyOpenReq.device_path, device_path); + //__builtin___strcpy_chk + memcpy(QCTLMsg->LibQmiProxyOpenReq.device_path, device_path, strlen(device_path)); + return sizeof(QMICTL_LIBQMI_PROXY_OPEN_MSG) + (strlen(device_path)); +} + +static int libqmi_proxy_open(const char *cdc_wdm) { + int ret; + PQCQMIMSG pResponse; + + ret = QmiThreadSendQMI(ComposeQCTLMsg(QMI_MESSAGE_CTL_INTERNAL_PROXY_OPEN, + CtlLibQmiProxyOpenReq, (void *)cdc_wdm), &pResponse); + if (!ret && pResponse + && pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXResult == 0 + && pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXError == 0) { + ret = 0; + } + else { + return -1; + } + + if (pResponse) + free(pResponse); + + return ret; +} + +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_CTL) { + pRequest->QMIHdr.ClientId = qmiclientId[pRequest->QMIHdr.QMIType]; + if (pRequest->QMIHdr.ClientId == 0) { + dbg_time("QMIType %d has no clientID", pRequest->QMIHdr.QMIType); + 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 UCHAR 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; + + 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_COEX: dbg_time("Get clientCOEX = %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->proxy[0] && !strcmp(profile->proxy, LIBQMI_PROXY)) { + ret = libqmi_proxy_open(profile->qmichannel); + if (ret) + return ret; + } + + if (!profile->proxy[0]) { + for (i = 0; i < 10; i++) { + ret = QmiThreadSendQMITimeout(ComposeQCTLMsg(QMICTL_SYNC_REQ, NULL, NULL), NULL, 1 * 1000, __func__); + 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 0 + dbg_time("QMUXType = %02x Version = %d.%d", + pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].QMUXType, + pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MajorVersion, + pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MinorVersion); +#endif + 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); + qmiclientId[QMUX_TYPE_WDS] = QmiWwanGetClientID(QMUX_TYPE_WDS); + if (profile->enable_ipv6) + 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); + qmiclientId[QMUX_TYPE_WDS_ADMIN] = QmiWwanGetClientID(QMUX_TYPE_WDS_ADMIN); +#ifdef CONFIG_COEX_WWAN_STATE + qmiclientId[QMUX_TYPE_COEX] = QmiWwanGetClientID(QMUX_TYPE_COEX); +#endif +#ifdef CONFIG_ENABLE_QOS + qmiclientId[QMUX_TYPE_QOS] = QmiWwanGetClientID(QMUX_TYPE_QOS); +#endif + profile->wda_client = qmiclientId[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((QMUX_TYPE_WDS_IPV6 == i ? QMUX_TYPE_WDS : i), qmiclientId[i]); + qmiclientId[i] = 0; + } + } + + return 0; +} + +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) && le16_to_cpu(pHdr->Length) < size) { + nreads += read(fd, pHdr+1, le16_to_cpu(pHdr->Length) + 1 - sizeof(QCQMI_HDR)); + } + + return nreads; +} + +#ifdef QUECTEL_QMI_MERGE +static int QmiWwanMergeQmiRsp(void *buf, ssize_t *src_size) { + static QMI_MSG_PACKET s_QMIPacket; + QMI_MSG_HEADER *header = NULL; + ssize_t size = *src_size; + + if((uint16_t)size < sizeof(QMI_MSG_HEADER)) + return -1; + + header = (QMI_MSG_HEADER *)buf; + if(le16_to_cpu(header->idenity) != MERGE_PACKET_IDENTITY || le16_to_cpu(header->version) != MERGE_PACKET_VERSION || le16_to_cpu(header->cur_len) > le16_to_cpu(header->total_len)) + return -1; + + if(le16_to_cpu(header->cur_len) == le16_to_cpu(header->total_len)) { + *src_size = le16_to_cpu(header->total_len); + memcpy(buf, buf + sizeof(QMI_MSG_HEADER), *src_size); + s_QMIPacket.len = 0; + return 0; + } + + memcpy(s_QMIPacket.buf + s_QMIPacket.len, buf + sizeof(QMI_MSG_HEADER), le16_to_cpu(header->cur_len)); + s_QMIPacket.len += le16_to_cpu(header->cur_len); + + if (le16_to_cpu(header->cur_len) < MERGE_PACKET_MAX_PAYLOAD_SIZE || s_QMIPacket.len >= le16_to_cpu(header->total_len)) { + memcpy(buf, s_QMIPacket.buf, s_QMIPacket.len); + *src_size = s_QMIPacket.len; + s_QMIPacket.len = 0; + return 0; + } + + return -1; +} +#endif + +static void * QmiWwanThread(void *pData) { + PROFILE_T *profile = (PROFILE_T *)pData; + const char *cdc_wdm = (const char *)profile->qmichannel; + int wait_for_request_quit = 0; + char num = cdc_wdm[strlen(cdc_wdm)-1]; + + if (profile->proxy[0]) { + if (!strncmp(profile->proxy, QUECTEL_QMI_PROXY, strlen(QUECTEL_QMI_PROXY))) { + snprintf(profile->proxy, sizeof(profile->proxy), "%s%c", QUECTEL_QMI_PROXY, num); + } + } + else if (!strncmp(cdc_wdm, "/dev/mhi_IPCR", strlen("/dev/mhi_IPCR"))) { + snprintf(profile->proxy, sizeof(profile->proxy), "%s%c", QUECTEL_QRTR_PROXY, num); + } + else if (profile->qmap_mode > 1) { + snprintf(profile->proxy, sizeof(profile->proxy), "%s%c", QUECTEL_QMI_PROXY, num); + } + + if (profile->proxy[0]) + cdc_wdm_fd = cm_open_proxy(profile->proxy); + else + cdc_wdm_fd = cm_open_dev(cdc_wdm); + + 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; + } + + 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, wait_for_request_quit ? 1000 : -1); + } while ((ret < 0) && (errno == EINTR)); + + if (ret == 0 && wait_for_request_quit) { + QmiThreadRecvQMI(NULL); + 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; + + //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 SIG_EVENT_STOP: + wait_for_request_quit = 1; + break; + default: + break; + } + } + } + + if (fd == cdc_wdm_fd) { + ssize_t nreads; + PQCQMIMSG pResponse = (PQCQMIMSG)cm_recv_buf; + + if (!profile->proxy[0]) + nreads = read(fd, cm_recv_buf, sizeof(cm_recv_buf)); + else + nreads = qmi_proxy_read(fd, cm_recv_buf, sizeof(cm_recv_buf)); + //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; + } +#ifdef QUECTEL_QMI_MERGE + if((profile->qmap_mode == 0 || profile->qmap_mode == 1) && QmiWwanMergeQmiRsp(cm_recv_buf, &nreads)) + continue; +#endif + 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; +} + +const struct qmi_device_ops qmiwwan_qmidev_ops = { + .init = QmiWwanInit, + .deinit = QmiWwanDeInit, + .send = QmiWwanSendQMI, + .read = QmiWwanThread, +}; + +uint8_t qmi_over_mbim_get_client_id(uint8_t QMIType) { + return QmiWwanGetClientID(QMIType); +} + +uint8_t qmi_over_mbim_release_client_id(uint8_t QMIType, uint8_t ClientId) { + return QmiWwanReleaseClientID(QMIType, ClientId); +} +#endif + diff --git a/application/quectel_CM_5G/src/ReleaseNote.txt b/application/quectel_CM_5G/src/ReleaseNote.txt new file mode 100644 index 0000000..4ac3c68 --- /dev/null +++ b/application/quectel_CM_5G/src/ReleaseNote.txt @@ -0,0 +1,339 @@ +Release Notes + +[V1.6.5] +Date: 7/3/2023 +enhancement: + 1. Fix the issue of qmi client id leakage caused by kill 9 killing the client of quectel-qmi-proxy + 2. Fix wds_ipv6 client ID can't be released issue + 3. Fix wds_ipv6 client ID can't be released issue + 4. Resolve PDP_ Context&Profile_ The issue of index mixing + 5. Add parameter - d to obtain IP and DNS information through qmi + 6. Fix mbim dialing. When the user does not specify apn through - s, prompt the user and exit the dialing program + 7. Prioritize the use of IP commands for optimization, and use ifconfig if not available + 8. Optimize and add/remove copyright +fix: + + +[V1.6.4] +Date: 9/7/2022 +enhancement: + 1. set cflags as -Wall -Wextra -Werror -O1, and fix compile errors + 2. some code refactoring + 3. add quectel-qrtr-proxy +fix: + 1. netmask error when use ifconfig on little endian cpu + +[V1.6.2] +Date: 11/18/2021 +enhancement: + 1. support 'LTE && WiFi Coexistence Solution via QMI'. + If customer want use this feature, need enable CONFIG_COEX_WWAN_STATE in QMIThread.h + +[V1.6.1] +Date: 7/20/2021 +enhancement: + 1. add requestGetCellInfoList requestRadioPower + 2. add QMI OVER MBIM + 3. support qrtr and rmnet + 4. support RG500U PCIE + 5. add qos service && get qos flow data_rate_max func +fix: + 1. mbim: increase mbim open timeout to 3 seconds. some modem take long time for open cmd. + 2. support MsChapV2 + 3. mbim: invalid memory access when only get one DNS + 4. some bug fix for use AT Command to setup data call + +[V1.6.0.26] +Date: 4/22/2021 +enhancement: + 1. add lots of log file to show how to use this tool + 2. support pcie mhi multiple call + 3. at command: support EC200U/EC200T/EC200S/RG801H/RG500U/ +fix: + 1. mbim-proxy: fix errors on big endian cpu, ignore mbim open/close cmd from quectel-cm + +[V1.6.0.25] +Date: 4/8/2021 +enhancement: +fix: + 1. fix compile error when use gcc 9.3.0 + 2. fix yocto 'QA Issue: No GNU_HASH in the ELF binary' + +[V1.6.0.24] +Date: 3/9/2021 +enhancement: + 1. '-p [quectel-][qmi|mbim]-proxy', can connect to quectel/libqmi/libmbim's proxy, even only one data + 2. set variable s_9x07 as 1 (from 0), most of modems are base on MDM90x7 and later QCOM chip. +fix: + 1. define CHAR as signed char + 2. mofidy Makefile to generate more compile warnnings and fix them + +[V1.6.0.23] +Date: 2/26/2021 +enhancement: + 1. support 'AT+QNETDEVCTL' (not release) +fix: + 1. modify help/usage + 2. fix some memroy access error in mbim-cm.c + +[V1.6.0.22] +Date: 2/4/2021 +enhancement: + 1. support connect to libqmi's qmi-proxy + 2. only allow ' 0/1/2/none/pap/chap' for auth of '-s' + 3. '-m iface-idx' bind QMAP data call to wwan0_ +fix: + +[V1.6.0.21] +Date: 1/28/2021 +enhancement: + 1. print 5G signal +fix: + 1. fix compile errors: -Werror=format-truncation= + +[V1.6.0.20] +Date: 12/29/2020 +enhancement: + 1. Code refactoring + 2. support 'AT+QNETDEVCTL' (not release) +fix: + +[V1.6.0.19] +Date: 12/4/2020 +enhancement: + 1. if 'udhcpc's default.script' missed, directy set ip/dns/route by 'ip' co,mand +fix: + +[V1.6.0.18] +Date: 12/4/2020 +enhancement: + 1. Code refactoring +fix: + +[V1.6.0.17] +Date: 8/25/2020 +enhancement: + 1. support MBIM multi-call + 2. support unisoc RG500U mbim + 3. QUECTEL_QMI_MERGE: some SOC can not read more then 64 bytes (QMI)data via USB Endpoint 0 +fix: + +[V1.6.0.15] +Date: 7/24/2020 +enhancement: +fix: + 1. QMAP multi-call, AT+CFUN=4 then AT+CFUN=1, only one call can obtain IP by DHCP + +[V1.6.0.14] +Date: 6/10/2020 +enhancement: + 1. support X55's GobiNet LOOPBACK +fix: + 1. very old uclib do not support htole32 and pthread_condattr_setclock + 2. pthread_cond_wait tv_nsec >= 1000000000U is wrong + 3. do not close socket in udhcpc.c ifc_get_addr() + +[V1.6.0.13] +Date: 6/9/2020 +enhancement: + 1. add some example for openwrt, marco 'QL_OPENWER_NETWORK_SETUP' +fix: + +[V1.6.0.12] +Date: 5/29/2020 +enhancement: +fix: + 1. some EM12's usb-net-qmi/mbim interface is at 8 (not 4) + +[V1.6.0.11] +Date: 5/28/2020 +enhancement: +fix: + 1. fix mbim debug on Big Endian CPU + +[V1.6.0.10] +Date: 5/25/2020 +enhancement: +fix: + 1. set QMAP .ul_data_aggregation_max_datagrams to 11 (from 16) + +[V1.6.0.9] +Date: 5/22/2020 +enhancement: +fix: + 1. dial fail when register to 5G-SA + +[V1.6.0.8] +Date: 4/30/2020 +enhancement: + 1. support '-b' to seletc brige mode +fix: + +[V1.6.0.7] +Date: 4/29/2020 +enhancement: + 1. support QMAP multi-call for qmi_wwan_q and pcie_mhi 's rmnet driver +fix: + +[V1.6.0.6] +Date: 4/20/2020 +enhancement: + 1. support '-k pdn_idx' to hangup call '-n pdn_idx' +fix: + 1. fix set dl_minimum_padding as 0, modems do not support this featrue + +[V1.6.0.5] +Date: 4/10/2020 +enhancement: + 1. support X55's QMAPV5 for PCIE +fix: + +[V1.6.0.3] +Date: 4/8/2020 +enhancement: + 1. support multi-modems all use multi-data-calls +fix: + +[V1.6.0.2] +Date: 4/7/2020 +enhancement: + 1. support X55's QMAPV5 for USB +fix: + +[V1.6.0.1] +Date: 4/1/2020 +enhancement: + 1. support QMAP UL AGG (multi data call) +fix: + 1. some EM12's usb-net-qmi/mbim interface is at 8 (not 4) + +[V1.5.9] +Date: 3/4/2020 +enhancement: + 1. support pcie mhi multi-APN data call + 3. support QMAP UL AGG (single data call) +fix: + 1. set 4 bytes aligned for mbim parameters, or cause dial mbim call fail + +[V1.5.8] +Date: 2/18/2020 +enhancement: + 1. support '-l 14' X55's loopback function +fix: + +[V1.5.7] +Date: 2/6/2020 +enhancement: + 1. support '-u usbmon_log_file' to catch usbmon log +fix: + +[V1.5.6] +Date: 1/20/202 +enhancement: + 1. show driver name and version + 2. support PCSCF + 3. support bridge in mbim +fix: + +[V1.5.5] +Date: 12/31/2019 +enhancement: +fix: + 1. fix some memory access bug in mbim-cm.c + +[WCDMA<E_QConnectManager_Linux&Android_V1.5.4] +Date: 12/17/2019 +enhancement: + 1. Add copyright + 2. auto detect pcie mhi /dev/mhi* +fix: + +[WCDMA<E_QConnectManager_Linux&Android_V1.5.3] +Date: 2019/12/11 +enhancement: +1. support show SignalInfo, controlled by macro CONFIG_SIGNALINFO +2. support show 5G_NSA/5G_NA +3. support Microsoft Extend MBIM message +fix: +1. quectel-qmi-proxy bugs on Big-Endian CPU + +[WCDMA<E_QConnectManager_Linux&Android_V1.5.2] +Date: 12/2/2019 +enhancement: + 1. support requestGetSignalInfo() +fix: + +[WCDMA<E_QConnectManager_Linux&Android_V1.4.1] +Date: 10/23/2019 +enhancement: + 1. support QMI_CTL_REVOKE_CLIENT_ID_IND (Quectel define QMI) + 2. add copyright +fix: + 1. remove SIGUSR + +[WCDMA<E_QConnectManager_Linux&Android_V1.3.10] +Date: 10/14/2019 +enhancement: + 1. increase retry interval +fix: + +[WCDMA<E_QConnectManager_Linux&Android_V1.2.1] +Date: 2019/02/26 +enhancement: +1. Implement help message. + +root@ubuntu:# ./quectel-CM -h +[02-26_10:39:21:353] Usage: ./quectel-CM [options] +[02-26_10:39:21:353] -s [apn [user password auth]] Set apn/user/password/auth get from your network provider +[02-26_10:39:21:353] -p pincode Verify sim card pin if sim card is locked +[02-26_10:39:21:353] -f logfilename Save log message of this program to file +[02-26_10:39:21:353] -i interface Specify network interface(default auto-detect) +[02-26_10:39:21:353] -4 IPv4 protocol +[02-26_10:39:21:353] -6 IPv6 protocol +[02-26_10:39:21:353] -m muxID Specify muxid when set multi-pdn data connection. +[02-26_10:39:21:353] -n channelID Specify channelID when set multi-pdn data connection(default 1). +[02-26_10:39:21:353] [Examples] +[02-26_10:39:21:353] Example 1: ./quectel-CM +[02-26_10:39:21:353] Example 2: ./quectel-CM -s 3gnet +[02-26_10:39:21:353] Example 3: ./quectel-CM -s 3gnet carl 1234 0 -p 1234 -f gobinet_log.txt +root@ubuntu:# +2. Support bridge mode when set multi-pdn data connections. +3. Host device can access network in bridge mode. + +[WCDMA<E_QConnectManager_Linux&Android_V1.1.46] +Date: 2019/02/18 +enhancement: +1. support only IPV6 data call. quectel-CM now support three dialing methods: IPV4 only, IPV6 only, IPV4V6. + ./quectel-CM -4(or no argument) only IPV4 + -6 only IPV6 + -4 -6 IPV4 && IPV6 + +[WCDMA<E_QConnectManager_Linux&Android_V1.1.45] +Date: 2018/09/13 +enhancement: +1. support EG12 PCIE interface + +[WCDMA<E_QConnectManager_Linux&Android_V1.1.44] +Date: 2018/09/10 +enhancement: +1. support setup IPV4&IPV6 data call. + +[WCDMA<E_QConnectManager_Linux&Android_V1.1.43] +[WCDMA<E_QConnectManager_Linux&Android_V1.1.42] +Date: 2018/08/29 +enhancement: +1. support QMI_WWAN's QMAP fucntion and bridge mode, please contact Quectel FAE to get qmi_wwan.c patch. + when enable QMI_WWAN's QMAP IP Mux function, must run 'quectel-qmi-proxy -d /dev/cdc-wdmX' before quectel-CM + +[WCDMA<E_QConnectManager_Linux&Android_V1.1.41] +Date: 2018/05/24 +enhancement: +1. fix a cdma data call error + +[WCDMA<E_QConnectManager_Linux&Android_V1.1.40] +Date: 2018/05/12 +enhancement: +1. support GobiNet's QMAP fucntion and bridge mode. + 'Quectel_WCDMA<E_Linux&Android_GobiNet_Driver_V1.3.5' and later version is required to use QMAP and bridge mode. + for detail, please refer to GobiNet Driver + diff --git a/application/quectel_CM_5G/src/at_tok.c b/application/quectel_CM_5G/src/at_tok.c new file mode 100644 index 0000000..6736cc8 --- /dev/null +++ b/application/quectel_CM_5G/src/at_tok.c @@ -0,0 +1,283 @@ +/* //device/system/reference-ril/at_tok.c +** +** Copyright 2006, 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 "at_tok.h" +#include +#include +#include +#include +#include + +/** + * Starts tokenizing an AT response string + * returns -1 if this is not a valid response string, 0 on success. + * updates *p_cur with current position + */ +int at_tok_start(char **p_cur) +{ + if (*p_cur == NULL) { + return -1; + } + + // skip prefix + // consume "^[^:]:" + + *p_cur = strchr(*p_cur, ':'); + + if (*p_cur == NULL) { + return -1; + } + + (*p_cur)++; + + return 0; +} + +static void skipWhiteSpace(char **p_cur) +{ + if (*p_cur == NULL) return; + + while (**p_cur != '\0' && isspace(**p_cur)) { + (*p_cur)++; + } +} + +static void skipNextComma(char **p_cur) +{ + if (*p_cur == NULL) return; + + while (**p_cur != '\0' && **p_cur != ',') { + (*p_cur)++; + } + + if (**p_cur == ',') { + (*p_cur)++; + } +} + +static char * nextTok(char **p_cur) +{ + char *ret = NULL; + + skipWhiteSpace(p_cur); + + if (*p_cur == NULL) { + ret = NULL; + } else if (**p_cur == '"') { + (*p_cur)++; + ret = strsep(p_cur, "\""); + skipNextComma(p_cur); + } else { + ret = strsep(p_cur, ","); + } + + return ret; +} + + +/** + * Parses the next integer in the AT response line and places it in *p_out + * returns 0 on success and -1 on fail + * updates *p_cur + * "base" is the same as the base param in strtol + */ + +static int at_tok_nextint_base(char **p_cur, int *p_out, int base, int uns) +{ + char *ret; + + if (*p_cur == NULL) { + return -1; + } + + ret = nextTok(p_cur); + + if (ret == NULL) { + return -1; + } else { + long l; + char *end; + + if (uns) + l = strtoul(ret, &end, base); + else + l = strtol(ret, &end, base); + + *p_out = (int)l; + + if (end == ret) { + return -1; + } + } + + return 0; +} + +/** + * Parses the next base 10 integer in the AT response line + * and places it in *p_out + * returns 0 on success and -1 on fail + * updates *p_cur + */ +int at_tok_nextint(char **p_cur, int *p_out) +{ + return at_tok_nextint_base(p_cur, p_out, 10, 0); +} + +/** + * Parses the next base 16 integer in the AT response line + * and places it in *p_out + * returns 0 on success and -1 on fail + * updates *p_cur + */ +int at_tok_nexthexint(char **p_cur, int *p_out) +{ + return at_tok_nextint_base(p_cur, p_out, 16, 1); +} + +int at_tok_nextbool(char **p_cur, char *p_out) +{ + int ret; + int result; + + ret = at_tok_nextint(p_cur, &result); + + if (ret < 0) { + return -1; + } + + // booleans should be 0 or 1 + if (!(result == 0 || result == 1)) { + return -1; + } + + if (p_out != NULL) { + *p_out = (char)result; + } + + return ret; +} + +int at_tok_nextstr(char **p_cur, char **p_out) +{ + if (*p_cur == NULL) { + return -1; + } + + *p_out = nextTok(p_cur); + + return 0; +} + +/** returns 1 on "has more tokens" and 0 if no */ +int at_tok_hasmore(char **p_cur) +{ + return ! (*p_cur == NULL || **p_cur == '\0'); +} + +int at_tok_count(const char *in_line) +{ + int commas = 0; + const char *p; + + if (!in_line) + return 0; + + for (p = in_line; *p != '\0' ; p++) { + if (*p == ',') commas++; + } + + return commas; +} + +//fmt: d ~ int, x ~ hexint, b ~ bool, s ~ str +int at_tok_scanf(const char *in_line, const char *fmt, ...) +{ + int n = 0; + int err; + va_list ap; + const char *p = fmt; + void *d; + void *dump; + static char s_line[1024]; + char *line = s_line; + + if (!in_line) + return 0; + + strncpy(s_line, in_line, sizeof(s_line) - 1); + + va_start(ap, fmt); + + err = at_tok_start(&line); + if (err < 0) goto error; + + for (; *p; p++) { + if (*p == ',' || *p == ' ') + continue; + + if (*p != '%') { + goto error; + } + p++; + + d = va_arg(ap, void *); + if (!d) + d = &dump; + + if (!at_tok_hasmore(&line)) + break; + + if (*line == '-' && *(line + 1) == ',') { + line += 2; + n++; + if (*p == 'd') + *(int *)d = -1; + continue; + } + + switch(*p) { + case 'd': + err = at_tok_nextint(&line, (int *)d); + if (err < 0) goto error; + break; + case 'x': + err = at_tok_nexthexint(&line, (int *)d); + if (err < 0) goto error; + break; + case 'b': + err = at_tok_nextbool(&line, (char *)d); + if (err < 0) goto error; + break; + case 's': + err = at_tok_nextstr(&line, (char **)d); //if strdup(line), here return free memory to caller + if (err < 0) goto error; + break; + default: + goto error; + break; + } + + n++; + } + + va_end(ap); + +error: + //free(line); + return n; +} diff --git a/application/quectel_CM_5G/src/at_tok.h b/application/quectel_CM_5G/src/at_tok.h new file mode 100644 index 0000000..2fcb683 --- /dev/null +++ b/application/quectel_CM_5G/src/at_tok.h @@ -0,0 +1,33 @@ +/* //device/system/reference-ril/at_tok.h +** +** Copyright 2006, 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 AT_TOK_H +#define AT_TOK_H 1 + +int at_tok_start(char **p_cur); +int at_tok_nextint(char **p_cur, int *p_out); +int at_tok_nexthexint(char **p_cur, int *p_out); + +int at_tok_nextbool(char **p_cur, char *p_out); +int at_tok_nextstr(char **p_cur, char **out); + +int at_tok_hasmore(char **p_cur); +int at_tok_count(const char *in_line); +int at_tok_scanf(const char *line, const char *fmt, ...); + +#endif /*AT_TOK_H */ + diff --git a/application/quectel_CM_5G/src/atc.c b/application/quectel_CM_5G/src/atc.c new file mode 100644 index 0000000..55497ee --- /dev/null +++ b/application/quectel_CM_5G/src/atc.c @@ -0,0 +1,1055 @@ +/****************************************************************************** + @file atc.c + @brief at command. + + DESCRIPTION + Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int asprintf(char **s, const char *fmt, ...); + +#include "QMIThread.h" + +#include "atchannel.h" +#include "at_tok.h" + +static int asr_style_atc = 0; +static int s_pdp; +#define safe_free(__x) do { if (__x) { free((void *)__x); __x = NULL;}} while(0) +#define safe_at_response_free(__x) { if (__x) { at_response_free(__x); __x = NULL;}} + +#define at_response_error(err, p_response) \ + (err \ + || p_response == NULL \ + || p_response->finalResponse == NULL \ + || p_response->success == 0) + +static int atc_init(PROFILE_T *profile) { + int err; + char *cmd; + ATResponse *p_response = NULL; + + if (profile->proxy[0]) { + s_pdp = profile->pdp; + err = at_send_command_singleline("AT+QNETDEVSTATUS=?", "+QNETDEVSTATUS:", &p_response); + if (at_response_error(err, p_response)) + asr_style_atc = 1; //EC200T/EC100Y do not support this AT, but RG801/RG500U support + safe_at_response_free(p_response); + + return err; + } + + err = at_handshake(); + if (err) { + dbg_time("handshake fail, TODO ... "); + goto exit; + } + + s_pdp = profile->pdp; + at_send_command_singleline("AT+QCFG=\"usbnet\"", "+QCFG:", NULL); + at_send_command_multiline("AT+QNETDEVCTL=?", "+QNETDEVCTL:", NULL); + at_send_command("AT+CGREG=2", NULL); //GPRS Network Registration Status + at_send_command("AT+CEREG=2", NULL); //EPS Network Registration Status + at_send_command("AT+C5GREG=2", NULL); //5GS Network Registration Status + + err = at_send_command_singleline("AT+QNETDEVSTATUS=?", "+QNETDEVSTATUS:", &p_response); + if (at_response_error(err, p_response)) + asr_style_atc = 1; //EC200T/EC100Y do not support this AT, but RG801/RG500U support + safe_at_response_free(p_response); + + err = at_send_command_singleline("AT+QCFG=\"NAT\"", "+QCFG:", &p_response); + if (!at_response_error(err, p_response)) { + int old_nat, new_nat = asr_style_atc ? 1 : 0; + + err = at_tok_scanf(p_response->p_intermediates->line, "%s%d", NULL, &old_nat); + if (err == 2 && old_nat != new_nat) { + safe_at_response_free(p_response); + asprintf(&cmd, "AT+QCFG=\"NAT\",%d", new_nat); + err = at_send_command(cmd, &p_response); + safe_free(cmd); + if (!at_response_error(err, p_response)) { + err = at_send_command("at+cfun=1,1",NULL); + if (!err) + g_donot_exit_when_modem_hangup = 1; + //reboot to take effect + } + safe_at_response_free(p_response); + } + err = 0; + } + safe_at_response_free(p_response); + +exit: + return err; +} + +static int atc_deinit(void) { + return 0; +} + +/** + * Called by atchannel when an unsolicited line appears + * This is called on atchannel's reader thread. AT commands may + * not be issued here + */ +static void onUnsolicited (const char *s, const char *sms_pdu) +{ + (void)sms_pdu; + + if (strStartsWith(s, "+QNETDEVSTATUS:")) { + qmidevice_send_event_to_main(RIL_UNSOL_DATA_CALL_LIST_CHANGED); + } + else if (strStartsWith(s, "+CGREG:") + || strStartsWith(s, "+CEREG:") + || strStartsWith(s, "+C5GREG:")) { + qmidevice_send_event_to_main(RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED); + } +} + +static void onTimeout(void) { + dbg_time("%s", __func__); + //TODO +} + +static void onClose(void) { + dbg_time("%s", __func__); +} + +static void * atc_read_thread(void *param) { + PROFILE_T *profile = (PROFILE_T *)param; + const char *cdc_wdm = (const char *)profile->qmichannel; + int wait_for_request_quit = 0; + int atc_fd; + + atc_fd = cm_open_dev(cdc_wdm); + if (atc_fd <= 0) { + dbg_time("fail to open (%s), errno: %d (%s)", cdc_wdm, errno, strerror(errno)); + goto __quit; + } + + dbg_time("atc_fd = %d", atc_fd); + + if (at_open(atc_fd, onUnsolicited, 0)) + goto __quit; + + at_set_on_timeout(onTimeout); + at_set_on_reader_closed(onClose); + qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED); + + while (atc_fd > 0) { + struct pollfd pollfds[] = {{atc_fd, POLLIN, 0}, {qmidevice_control_fd[1], POLLIN, 0}}; + int ne, ret, nevents = 2; + + ret = poll(pollfds, nevents, wait_for_request_quit ? 1000 : -1); + + if (ret == 0 && wait_for_request_quit) { + break; + } + + 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 (revents & (POLLERR | POLLHUP | POLLNVAL)) + goto __quit; + } + + if ((revents & POLLIN) == 0) + continue; + + if (atc_fd == fd) { + usleep(10*1000); //let atchannel.c read at response. + } + else if (fd == qmidevice_control_fd[1]) { + int triger_event; + if (read(fd, &triger_event, sizeof(triger_event)) == sizeof(triger_event)) { + //dbg_time("triger_event = 0x%x", triger_event); + switch (triger_event) { + case RIL_REQUEST_QUIT: + goto __quit; + break; + case SIG_EVENT_STOP: + wait_for_request_quit = 1; + break; + default: + break; + } + } + } + } + } + +__quit: + at_close(); + qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED); + dbg_time("%s exit", __func__); + + return NULL; +} + +const struct qmi_device_ops atc_dev_ops = { + .init = atc_init, + .deinit = atc_deinit, + .read = atc_read_thread, +}; + +static int requestBaseBandVersion(PROFILE_T *profile) { + int retVal = -1; + int err; + ATResponse *p_response = NULL; + + (void)profile; + + err = at_send_command_multiline("AT+CGMR", "\0", &p_response); + if (at_response_error(err, p_response)) + goto exit; + + if (p_response->p_intermediates && p_response->p_intermediates->line) { + strncpy(profile->BaseBandVersion, p_response->p_intermediates->line, sizeof(profile->BaseBandVersion) - 1); + retVal = 0; + } + +exit: + safe_at_response_free(p_response); + return retVal; +} + +static int requestGetSIMStatus(SIM_Status *pSIMStatus) +{ + int err; + ATResponse *p_response = NULL; + char *cpinLine; + char *cpinResult; + int ret = SIM_NOT_READY; + + err = at_send_command_singleline("AT+CPIN?", "+CPIN:", &p_response); + if (at_response_error(err, p_response)) + goto done; + + switch (at_get_cme_error(p_response)) + { + case CME_SUCCESS: + break; + + case CME_SIM_NOT_INSERTED: + case CME_OPERATION_NOT_ALLOWED: + case CME_FAILURE: + ret = SIM_ABSENT; + goto done; + + default: + ret = SIM_NOT_READY; + goto done; + } + + cpinLine = p_response->p_intermediates->line; + err = at_tok_start (&cpinLine); + + if (err < 0) + { + ret = SIM_NOT_READY; + goto done; + } + + err = at_tok_nextstr(&cpinLine, &cpinResult); + + if (err < 0) + { + ret = SIM_NOT_READY; + goto done; + } + + if (0 == strcmp (cpinResult, "SIM PIN")) + { + ret = SIM_PIN; + goto done; + } + else if (0 == strcmp (cpinResult, "SIM PUK")) + { + ret = SIM_PUK; + goto done; + } + else if (0 == strcmp (cpinResult, "PH-NET PIN")) + { + return SIM_NETWORK_PERSONALIZATION; + } + else if (0 != strcmp (cpinResult, "READY")) + { + /* we're treating unsupported lock types as "sim absent" */ + ret = SIM_ABSENT; + goto done; + } + + ret = SIM_READY; + +done: + safe_at_response_free(p_response); + *pSIMStatus = ret; + return err; +} + +static int requestEnterSimPin(const char *pPinCode) { + int retVal = -1; + int err; + ATResponse *p_response = NULL; + char *cmd = NULL; + + asprintf(&cmd, "AT+CPIN=%s", pPinCode); + err = at_send_command(cmd, NULL); + safe_free(cmd); + + if (!at_response_error(err, p_response)) { + retVal = 0; + } + + safe_at_response_free(p_response); + return retVal; +} + +static int requestSetProfile(PROFILE_T *profile) { + int err; + ATResponse *p_response = NULL; + char *cmd = NULL; + const char *new_apn = profile->apn ? profile->apn : ""; + const char *new_user = profile->user ? profile->user : ""; + const char *new_password = profile->password ? profile->password : ""; + const char *ipStr[] = {"NULL", "IPV4", "IPV6", "IPV4V6"}; + + dbg_time("%s[%d] %s/%s/%s/%d/%s", __func__, + profile->pdp, profile->apn, profile->user, profile->password, + profile->auth,ipStr[profile->iptype]); + + if ( !strcmp(profile->old_apn, new_apn) && !strcmp(profile->old_user, new_user) + && !strcmp(profile->old_password, new_password) + && profile->old_iptype == profile->iptype + && profile->old_auth == profile->auth) + { + dbg_time("no need to set skip the rest"); + return 0; + } + + asprintf(&cmd, "AT+QICSGP=%d,%d,\"%s\",\"%s\",\"%s\",%d", + profile->pdp, profile->iptype, new_apn, new_user, new_password, profile->auth); + err = at_send_command(cmd, &p_response); + safe_free(cmd); + if (at_response_error(err, p_response)) { + safe_at_response_free(p_response); + asprintf(&cmd, "AT+CGDCONT=%d,\"%s\",\"%s\"", profile->pdp, ipStr[profile->iptype], new_apn); + err = at_send_command(cmd, &p_response); + safe_free(cmd); + } + + safe_at_response_free(p_response); + return 1; +} + +static int requestGetProfile(PROFILE_T *profile) { + int retVal = -1; + int err; + ATResponse *p_response = NULL; + char *cmd = NULL; + int pdp; + int old_iptype = 1; // 1 ~ IPV4, 2 ~ IPV6, 3 ~ IPV4V6 + char *old_apn = "", *old_user = "", *old_password = ""; + int old_auth = 0; + const char *ipStr[] = {"NULL", "IPV4", "IPV6", "IPV4V6"}; + + if (profile->enable_ipv4 && profile->enable_ipv6) + profile->iptype = 3; + else if (profile->enable_ipv6) + profile->iptype = 2; + else + profile->iptype = 1; + +_re_check: + asprintf(&cmd, "AT+QICSGP=%d", profile->pdp); + err = at_send_command_singleline(cmd, "+QICSGP:", &p_response); + safe_free(cmd); + if (err == AT_ERROR_INVALID_RESPONSE && p_response == NULL) { + //bug of RG801H + safe_at_response_free(p_response); + asprintf(&cmd, "AT+QICSGP=%d,%d,\"\",\"\",\"\",0", profile->pdp, profile->iptype); + err = at_send_command(cmd, &p_response); + safe_free(cmd); + if (!at_response_error(err, p_response)) { + safe_at_response_free(p_response); + goto _re_check; + } + } + + if (!at_response_error(err, p_response)) { + err = at_tok_scanf(p_response->p_intermediates->line, + "%d%s%s%s%d", &old_iptype, &old_apn, &old_user, &old_password, &old_auth); + + if (err != 4 || pdp != profile->pdp) + goto _error; + } + else { + ATLine *atLine = NULL; + char *cgdcont_iptype = NULL; + + safe_at_response_free(p_response); + err = at_send_command_multiline("AT+CGDCONT?", "+CGDCONT:", &p_response); + if (at_response_error(err, p_response)) + goto _error; + + atLine = p_response->p_intermediates; + while (atLine) { + err = at_tok_scanf(atLine->line, "%d%s%s", &pdp, &cgdcont_iptype, &old_apn); + if (err == 3 && pdp == profile->pdp) { + if (!strcasecmp(cgdcont_iptype, ipStr[3])) + old_iptype = 3; + else if (!strcasecmp(cgdcont_iptype, ipStr[2])) + old_iptype = 2; + else + old_iptype = 1; + break; + } + old_apn = NULL; + atLine = atLine->p_next; + } + } + + retVal = 0; + +_error: + if (!old_apn) old_apn = ""; + if (!old_user) old_user = ""; + if (!old_password) old_password = ""; + + strncpy(profile->old_apn, old_apn, sizeof(profile->old_apn)); + strncpy(profile->old_user, old_user, sizeof(profile->old_user)); + strncpy(profile->old_password, old_password, sizeof(profile->old_password)); + profile->old_auth = old_auth; + profile->old_iptype = old_iptype; + + dbg_time("%s[%d] %s/%s/%s/%d/%s", __func__, + profile->pdp, profile->old_apn, profile->old_user, profile->old_password, + profile->old_auth, ipStr[profile->old_iptype]); + + safe_at_response_free(p_response); + + return retVal; +} + +static int requestRegistrationState(UCHAR *pPSAttachedState) { + int retVal = -1; + int err; + ATResponse *p_response = NULL; + ATLine *p_cur; + int i; + int cops_act = -1; + int state = 0, lac = 0, cid = 0, act = 0; + int commas; + char *line; + + *pPSAttachedState = 0; + + err = at_send_command_multiline( + "AT+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS?", + "+COPS:", &p_response); + if (at_response_error(err, p_response)) + goto error; + +/* +AT< +COPS: 0,0,"CHINA MOBILE",13 +AT< +COPS: 0,1,"CMCC",13 +AT< +COPS: 0,2,"46000",13 +AT< OK +*/ + retVal = 0; + for (i = 0, p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next, i++) { + err = at_tok_scanf(p_cur->line, "%d%d%s%d", NULL, NULL, NULL, &cops_act); + if (err != 4) goto error; + + break; + } + + safe_at_response_free(p_response); + switch (cops_act) { + case 2: //UTRAN + case 3: //GSM W/EGPRS + case 4: //UTRAN W/HSDPA + case 5: //UTRAN W/HSUPA + case 6: //UTRAN W/HSDPA and HSUPA + //AT+CGREG GPRS Network Registration Status + err = at_send_command_singleline("AT+CGREG?", "+CGREG:", &p_response); + break; + + case 7: //E-UTRAN + case 13: //E-UTRAN-NR dual connectivity + //AT+CEREG EPS Network Registration Status + err = at_send_command_singleline("AT+CEREG?", "+CEREG:", &p_response); + break; + + case 10: //E-UTRAN connected to a 5GCN + case 11: //NR connected to a 5GCN + case 12: //NG-RAN + //AT+C5GREG 5GS Network Registration Status + err = at_send_command_singleline("AT+C5GREG?", "+C5GREG:", &p_response); + break; + + default: + goto error; + break; + } + + if (at_response_error(err, p_response)) + goto error; + if (!p_response->p_intermediates || !p_response->p_intermediates->line) goto error; + + line = p_response->p_intermediates->line; + commas = at_tok_count(line); + switch (commas) { + case 0: /* +CREG: */ + err = at_tok_nextint(&line, &state); + if (err < 0) goto error; + break; + + case 1: /* +CREG: , */ + err = at_tok_scanf(line, "%d%d", NULL, &state); + if (err != 2) goto error; + break; + + case 2: /* +CREG: , , */ + err = at_tok_scanf(line, "%d%x%x", NULL, &state, &lac, &cid); + if (err != 3) goto error; + break; + + case 3: /* +CREG: , , , */ + err = at_tok_scanf(line, "%d%d%x%x", NULL, &state, &lac, &cid); + if (err != 4) goto error; + break; + + case 4: //, , , , */ + case 5: + case 6: + case 7: + err = at_tok_scanf(line, "%d%d%x%x%d", NULL, &state, &lac, &cid, &act); + if (err != 5) goto error; + break; + + default: + goto error; + } + + //dbg_time("state=%d", state); + + if (state == 1 || state == 5) { //Registered, home network / roaming + *pPSAttachedState = 1; + } + +error: + safe_at_response_free(p_response); + return retVal; +} + +static int requestSetupDataCall(PROFILE_T *profile, int curIpFamily) { + int err; + ATResponse *p_response = NULL; + char *cmd = NULL; + ATLine *p_cur = NULL; + int pdp = profile->pdp; + int state = 0; + + (void)curIpFamily; + + if (strStartsWith(profile->BaseBandVersion, "RG801H") || strStartsWith(profile->BaseBandVersion, "EC200H")) { + //RG801H will miss USB_CDC_NOTIFY_NETWORK_CONNECTION + asprintf(&cmd, "ifconfig %s up", profile->usbnet_adapter); + if (system(cmd)) {}; + safe_free(cmd); + } + + if (asr_style_atc) { + err = at_send_command_multiline("AT+CGACT?", "+CGACT:", &p_response); + if (at_response_error(err, p_response)) + goto _error; + + for (p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next) { + int cid = 0; + state = 0; + + err = at_tok_scanf(p_cur->line, "%d%d", &cid, &state); + if (cid == pdp) + break; + else if(state) + state = 0; + } + safe_at_response_free(p_response); + + if (state == 0) { + asprintf(&cmd, "AT+CGACT=1,%d", pdp); + err = at_send_command(cmd, &p_response); + safe_free(cmd); + if (at_response_error(err, p_response)) + goto _error; + } + } + + if(asr_style_atc) + asprintf(&cmd, "AT+QNETDEVCTL=1,%d,%d", pdp, 1); + else + asprintf(&cmd, "AT+QNETDEVCTL=%d,1,%d", pdp, 1); + err = at_send_command(cmd, &p_response); + safe_free(cmd); + + if (at_response_error(err, p_response)) + goto _error; + + if (!asr_style_atc) { //TODO some modems do not sync return setup call resule + int t = 0; + + while (t++ < 15) { + asprintf(&cmd, "AT+QNETDEVSTATUS=%d", pdp); + err = at_send_command_singleline(cmd, "+QNETDEVSTATUS", &p_response); + safe_free(cmd); + if (err) goto _error; + + if (!at_response_error(err, p_response)) { + break; + } + safe_at_response_free(p_response); + sleep(1); + } + } + + //some modem do not report URC + qmidevice_send_event_to_main(RIL_UNSOL_DATA_CALL_LIST_CHANGED); + +_error: + safe_at_response_free(p_response); + //dbg_time("%s err=%d", __func__, err); + return err; +} + +static int at_netdevstatus(int pdp, unsigned int *pV4Addr) { + int err; + ATResponse *p_response = NULL; + char *cmd = NULL; + char *ipv4_address = NULL; + char *ipv4_gate = NULL; + char *ipv4_DHCP = NULL; + char *ipv4_pDNS = NULL; + char *ipv4_sDNS = NULL; + char *ipv6_address = NULL; + char *ipv6_gate = NULL; + char *ipv6_DHCP = NULL; + char *ipv6_pDNS = NULL; + char *ipv6_sDNS = NULL; + + *pV4Addr = 0; + + asprintf(&cmd, "AT+QNETDEVSTATUS=%d", pdp); + err = at_send_command_singleline(cmd, "+QNETDEVSTATUS", &p_response); + safe_free(cmd); + if (at_response_error(err, p_response)) goto _error; + if (!p_response->p_intermediates || !p_response->p_intermediates->line) goto _error; + + err = at_tok_scanf(p_response->p_intermediates->line, "%s%s%s%s%s%s%s%s%s%s", + &ipv4_address, &ipv4_gate, &ipv4_DHCP, &ipv4_pDNS, &ipv4_sDNS, + &ipv6_address, &ipv6_gate, &ipv6_DHCP, &ipv6_pDNS, &ipv6_sDNS); + if (err > 0) { +#if 0 + dbg_time("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", + ipv4_address, ipv4_gate, ipv4_DHCP, ipv4_pDNS, ipv4_sDNS, + ipv6_address, ipv6_gate, ipv6_DHCP, ipv6_pDNS, ipv6_sDNS); +#endif + + if (ipv4_address && ipv4_address[0]) { + int addr[4] = {0, 0, 0, 0}; + + if (strstr(ipv4_address, ".")) { + sscanf(ipv4_address, "%d.%d.%d.%d", &addr[0], &addr[1], &addr[2], &addr[3]); + } + else { + sscanf(ipv4_address, "%02X%02X%02X%02X", &addr[3], &addr[2], &addr[1], &addr[0]); + } + *pV4Addr = (addr[0]) | (addr[1]<<8) | (addr[2]<<16) | (addr[3]<<24); + } + } + +_error: + safe_at_response_free(p_response); + return 0; +} + +static int requestQueryDataCall(UCHAR *pConnectionStatus, int curIpFamily) { + int err; + ATResponse *p_response = NULL; + ATLine *p_cur = NULL; + int state = 0; + int bind = 0; + int cid; + int pdp = s_pdp; + unsigned int v4Addr = 0; + + (void)curIpFamily; + + *pConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; + + if (!asr_style_atc) { + err = at_netdevstatus(pdp, &v4Addr); + if (!err && v4Addr) { + *pConnectionStatus = QWDS_PKT_DATA_CONNECTED; + //if (profile->ipv4.Address == v4Addr) {} //TODO + } + return err; + } + + err = at_send_command_multiline("AT+QNETDEVCTL?", "+QNETDEVCTL:", &p_response); + if (at_response_error(err, p_response)) + goto _error; + + for (p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next) + { + //+QNETDECTL:,,, + err = at_tok_scanf(p_cur->line, "%d%d%d%d", &bind, &cid, NULL, &state); + if (err != 4 || cid != pdp) + continue; + if (bind != 1) + bind = 0; + } + safe_at_response_free(p_response); + + if (bind == 0 || state == 0) + goto _error; + + err = at_send_command_multiline("AT+CGACT?", "+CGACT:", &p_response); + if (at_response_error(err, p_response)) + goto _error; + + for (p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next) + { + state = 0; + err = at_tok_scanf(p_cur->line, "%d%d", &cid, &state); + if (cid == pdp) + break; + else if(state) + state = 0; + } + safe_at_response_free(p_response); + + if (bind && state) + *pConnectionStatus = QWDS_PKT_DATA_CONNECTED; + +_error: + safe_at_response_free(p_response); + //dbg_time("%s err=%d, call_state=%d", __func__, err, *pConnectionStatus); + return 0; +} + +static int requestDeactivateDefaultPDP(PROFILE_T *profile, int curIpFamily) { + char *cmd = NULL; + int pdp = profile->pdp; + + (void)curIpFamily; + + if (asr_style_atc) + asprintf(&cmd, "AT+QNETDEVCTL=0,%d,%d", pdp, 0); + else + asprintf(&cmd, "AT+QNETDEVCTL=%d,0,%d", pdp, 0); + at_send_command(cmd, NULL); + safe_free(cmd); + + //dbg_time("%s err=%d", __func__, err); + return 0; +} + +static int requestGetIPAddress(PROFILE_T *profile, int curIpFamily) { + int err; + ATResponse *p_response = NULL; + char *cmd = NULL; + ATLine *p_cur = NULL; + int pdp = profile->pdp; + unsigned int v4Addr = 0; + + (void)curIpFamily; + + if (!asr_style_atc) { + err = at_netdevstatus(pdp, &v4Addr); + goto _error; + } + + asprintf(&cmd, "AT+CGPADDR=%d", profile->pdp); + err = at_send_command_singleline(cmd, "+CGPADDR:", &p_response); + safe_free(cmd); + if (at_response_error(err, p_response)) + goto _error; + + //+CGPADDR: 1,"10.201.80.91","2409:8930:4B3:41C7:F9B8:3D9B:A2F7:CA96" + for (p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next) + { + char *ipv4 = NULL; + char *ipv6 = NULL; + + err = at_tok_scanf(p_cur->line, "%d%s%s", &pdp, &ipv4, &ipv6); + if (err < 2 || pdp != profile->pdp) + continue; + + if (ipv4) { + int addr[4] = {0, 0, 0, 0}; + + sscanf(ipv4, "%d.%d.%d.%d", &addr[0], &addr[1], &addr[2], &addr[3]); + v4Addr = (addr[0]) | (addr[1]<<8) | (addr[2]<<16) | (addr[3]<<24); + break; + } + } + +_error: + if (v4Addr && profile->ipv4.Address != v4Addr) { + unsigned char *v4 = (unsigned char *)&v4Addr; + + profile->ipv4.Address = v4Addr; + dbg_time("%s %d.%d.%d.%d", __func__, v4[0], v4[1], v4[2], v4[3]); + } + + //dbg_time("%s err=%d", __func__, err); + return v4Addr ? 0 : -1; +} + +static int requestGetSignalInfo(void) { + int retVal = -1; + int err; + ATResponse *p_response = NULL; + int i; + ATLine *p_cur = NULL; + char *rat = NULL; + int cops_act = 0; + int is_nr5g_nsa = 0, nr5g_sa = 0; + int verbose = 0; + + err = at_send_command_singleline("at+cops?", "+COPS:", &p_response); + if (at_response_error(err, p_response)) goto _error; + if (!p_response->p_intermediates || !p_response->p_intermediates->line) goto _error; + + retVal = 0; + err = at_tok_scanf(p_response->p_intermediates->line, "%d%d%s%d", NULL, NULL, NULL, &cops_act); + if (err != 4) goto _error; + + nr5g_sa = (cops_act == 11); + + safe_at_response_free(p_response); + err = at_send_command_multiline("at+qeng=\"servingcell\"", "+QENG:", &p_response); + if (at_response_error(err, p_response)) + goto _error; + + for (i = 0, p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next, i++) { + char *type, *state; + + err = at_tok_scanf(p_cur->line, "%s%s", &type, &state); + if (err != 2 || strcmp(type, "servingcell")) + continue; + + if (!strcmp(state, "SEARCH") || !strcmp(state, "LIMSRV")) + continue; + + if (!strcmp(state, "NOCONN") || !strcmp(state, "CONNECT")) { + err = at_tok_scanf(p_cur->line, "%s%s%s", &type, &state, &rat); + if (err != 3) + continue; + } + else { + rat = state; + } + + if (!strcmp(rat, "NR5G-SA")) + { + //+QENG: "servingcell",,"NR5G-SA",,,,,,,,,,,,,, + //+QENG: "servingcell","NOCONN","NR5G-SA","TDD", 454,12,0,21,4ED,636576,78,3,-85,-11,32,0,5184 + struct qeng_servingcell_nr5g_sa { + char *cell_type, *state, *rat, *is_tdd; + int MCC, MNC, cellID/*hex*/; + int PCID, TAC/*hex*/, ARFCN; + int band, NR_DL_bandwidth; + int RSRP, RSRQ, RSSI, SINR; + }; + struct qeng_servingcell_nr5g_sa nr5g_sa; + + memset(&nr5g_sa, 0, sizeof(nr5g_sa)); + err = at_tok_scanf(p_cur->line, "%s,%s,%s,%s,%d,%d,%x,%d,%x,%d,%d,%d,%d,%d,%d,%d", + &nr5g_sa.cell_type, &nr5g_sa.state, &nr5g_sa.rat, &nr5g_sa.is_tdd, + &nr5g_sa.MCC, &nr5g_sa.MNC, &nr5g_sa.cellID, &nr5g_sa.PCID, &nr5g_sa.TAC, + &nr5g_sa.ARFCN, &nr5g_sa.band, &nr5g_sa.NR_DL_bandwidth, + &nr5g_sa.RSRP, &nr5g_sa.RSRQ, &nr5g_sa.RSSI, &nr5g_sa.SINR); + + if (err >= 13 && verbose) { + dbg_time("%s,%s,%s,%s,%d,%d,%x,%d,%x,%d,%d,%d,%d,%d,%d,%d", + nr5g_sa.cell_type, nr5g_sa.state, nr5g_sa.rat, nr5g_sa.is_tdd, + nr5g_sa.MCC, nr5g_sa.MNC, nr5g_sa.cellID, nr5g_sa.PCID, nr5g_sa.TAC, + nr5g_sa.ARFCN, nr5g_sa.band, nr5g_sa.NR_DL_bandwidth, + nr5g_sa.RSRP, nr5g_sa.RSRQ, nr5g_sa.RSSI, nr5g_sa.SINR); + } + } + else if (!strcmp(rat, "NR5G-NSA")) + { + //+QENG: "NR5G-NSA",,,,,< SINR>,,, + struct qeng_servingcell_nr5g_nsa { + char *mcc, *mnc; + int pcid, rsrp, sinr, rsrq; + }; + struct qeng_servingcell_nr5g_nsa nr5g_nsa; + + memset(&nr5g_nsa, 0, sizeof(nr5g_nsa)); + err = at_tok_scanf(p_cur->line, "%s%s%s%s%d%d%d%dd", + NULL, NULL, &nr5g_nsa.mcc, &nr5g_nsa.mnc, &nr5g_nsa.pcid, &nr5g_nsa.rsrp, &nr5g_nsa.sinr, &nr5g_nsa.rsrq); + if (err == 8 && verbose) + { + dbg_time("mcc=%s, mnc=%s, pcid=%d, rsrp=%d, sinr=%d, rsrq=%d", + nr5g_nsa.mcc, nr5g_nsa.mnc, nr5g_nsa.pcid, nr5g_nsa.rsrp, nr5g_nsa.sinr, nr5g_nsa.rsrq); + } + + is_nr5g_nsa = 1; + } + else if (!strcmp(rat, "LTE")) + { + //+QENG: "LTE",,,,,,,,,,,,,,,,, + struct qeng_servingcell_lte { + char *is_tdd, *mcc, *mnc; + int cellID/*hex*/, pcid, earfcn, freq_band_ind, ul_bandwidth, dl_bandwidth; + int tac/*hex*/, rsrp, rsrq, rssi, sinr, cqi,tx_power,srxlev; + }; + struct qeng_servingcell_lte lte; + + memset(<e, 0, sizeof(lte)); + if (!strcmp(rat, state)) + err = at_tok_scanf(p_cur->line, "%s%s%s%s%s%x%d%d%d%d%d%x%d%d%d%d%d%d%d", + NULL, NULL, <e.is_tdd, <e.mcc, <e.mnc, + <e.cellID, <e.pcid, <e.earfcn, <e.freq_band_ind, <e.ul_bandwidth, <e.dl_bandwidth, + <e.tac, <e.rsrp, <e.rsrq, <e.rssi, <e.sinr, <e.cqi, <e.tx_power, <e.srxlev); + else + err = at_tok_scanf(p_cur->line, "%s%s%s%s%s%s%x%d%d%d%d%d%x%d%d%d%d%d%d%d", + NULL, NULL, NULL, <e.is_tdd, <e.mcc, <e.mnc, + <e.cellID, <e.pcid, <e.earfcn, <e.freq_band_ind, <e.ul_bandwidth, <e.dl_bandwidth, + <e.tac, <e.rsrp, <e.rsrq, <e.rssi, <e.sinr, <e.cqi, <e.tx_power, <e.srxlev); + + if (err >= 18 && verbose) + { + dbg_time("is_tdd=%s, mcc=%s, mnc=%s", lte.is_tdd, lte.mcc, lte.mnc); + dbg_time("cellID=%x, pcid=%d, earfcn=%d", lte.cellID, lte.pcid, lte.earfcn); + dbg_time("freq_band_ind=%d, ul_bandwidth=%d, dl_bandwidth=%d", lte.freq_band_ind, lte.ul_bandwidth, lte.dl_bandwidth); + dbg_time("tac=%x, rsrp=%d, rsrq=%d, rssi=%d, sinr=%d", lte.tac, lte.rsrp, lte.rsrq, lte.rssi, lte.sinr); + dbg_time("cqi=%d, tx_power=%d, earfcn=%d", lte.cqi, lte.tx_power, lte.srxlev); + } + } + } + + if (is_nr5g_nsa) { + int endc_avl, plmn_info_list_r15_avl, endc_rstr, nr5g_basic; + + is_nr5g_nsa = 0; + safe_at_response_free(p_response); + err = at_send_command_multiline("at+qendc", "+QENDC:", &p_response); + if (at_response_error(err, p_response)) goto _error; + if (!p_response->p_intermediates || !p_response->p_intermediates->line) goto _error; + + err = at_tok_scanf(p_response->p_intermediates->line, "%d%d%d%d", + &endc_avl, &plmn_info_list_r15_avl, &endc_rstr, &nr5g_basic); + if (err == 4 && nr5g_basic) { + is_nr5g_nsa = 1; + } + } + + if (verbose) + dbg_time("cops_act=%d, nr5g_nsa=%d, nr5g_sa=%d", cops_act, is_nr5g_nsa, nr5g_sa); + +_error: + safe_at_response_free(p_response); + return retVal; +} + +static int requestGetICCID(void) { + int retVal = -1; + int err; + ATResponse *p_response = NULL; + char *iccid; + + err = at_send_command_singleline("AT+QCCID", "+QCCID:", &p_response); + if (at_response_error(err, p_response)) goto _error; + if (!p_response->p_intermediates || !p_response->p_intermediates->line) goto _error; + + err = at_tok_scanf(p_response->p_intermediates->line, "%s", &iccid); + if (err != 1) goto _error; + + if (iccid && iccid[0]) { + dbg_time("%s %s", __func__, iccid); + retVal = 0; + } + +_error: + safe_at_response_free(p_response); + return retVal; +} + +static int requestGetIMSI(void) { + int retVal = -1; + int err; + ATResponse *p_response = NULL; + char *imsi; + + err = at_send_command_numeric("AT+CIMI", &p_response); + if (at_response_error(err, p_response)) goto _error; + if (!p_response->p_intermediates || !p_response->p_intermediates->line) goto _error; + + imsi = p_response->p_intermediates->line; + if (imsi) { + dbg_time("%s %s", __func__, imsi); + retVal = 0; + } + +_error: + safe_at_response_free(p_response); + return retVal; +} + +const struct request_ops atc_request_ops = { + .requestBaseBandVersion = requestBaseBandVersion, + .requestGetSIMStatus = requestGetSIMStatus, + .requestEnterSimPin = requestEnterSimPin, + .requestSetProfile = requestSetProfile, + .requestGetProfile = requestGetProfile, + .requestRegistrationState = requestRegistrationState, + .requestSetupDataCall = requestSetupDataCall, + .requestQueryDataCall = requestQueryDataCall, + .requestDeactivateDefaultPDP = requestDeactivateDefaultPDP, + .requestGetIPAddress = requestGetIPAddress, + .requestGetSignalInfo = requestGetSignalInfo, + .requestGetICCID = requestGetICCID, + .requestGetIMSI = requestGetIMSI, +}; + diff --git a/application/quectel_CM_5G/src/atchannel.c b/application/quectel_CM_5G/src/atchannel.c new file mode 100644 index 0000000..90aa1c4 --- /dev/null +++ b/application/quectel_CM_5G/src/atchannel.c @@ -0,0 +1,1037 @@ +/* //device/system/reference-ril/atchannel.c +** +** Copyright 2006, 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 "atchannel.h" +#include "at_tok.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "QMIThread.h" +#define LOGE dbg_time +#define LOGD dbg_time + +#define NUM_ELEMS(x) (sizeof(x)/sizeof(x[0])) + +#define MAX_AT_RESPONSE sizeof(cm_recv_buf) +#define HANDSHAKE_RETRY_COUNT 8 +#define HANDSHAKE_TIMEOUT_MSEC 1000 + +static pthread_t s_tid_reader; +static int s_fd = -1; /* fd of the AT channel */ +static ATUnsolHandler s_unsolHandler; +static int s_atc_proxy = 0; /* fd of the AT channel */ + +/* for input buffering */ + +static char *s_ATBuffer = (char *)cm_recv_buf; +static char *s_ATBufferCur = (char *)cm_recv_buf; + +static int s_readCount = 0; + +#if AT_DEBUG +void AT_DUMP(const char* prefix, const char* buff, int len) +{ + if (len < 0) + len = strlen(buff); + LOGD("%.*s", len, buff); +} +#endif + +/* + * for current pending command + * these are protected by s_commandmutex + */ +static ATCommandType s_type; +static const char *s_responsePrefix = NULL; +static const char *s_smsPDU = NULL; +static const char *s_raw_data = NULL; +static size_t s_raw_len; +static ATResponse *sp_response = NULL; + +static void (*s_onTimeout)(void) = NULL; +static void (*s_onReaderClosed)(void) = NULL; +static int s_readerClosed; + +static void onReaderClosed(); +static int writeCtrlZ (const char *s); +static int writeline (const char *s); +static int writeraw (const char *s, size_t len); + +static void sleepMsec(long long msec) +{ + struct timespec ts; + int err; + + ts.tv_sec = (msec / 1000); + ts.tv_nsec = (msec % 1000) * 1000 * 1000; + + do { + err = nanosleep (&ts, &ts); + } while (err < 0 && errno == EINTR); +} + +/** returns 1 if line starts with prefix, 0 if it does not */ +int strStartsWith(const char *line, const char *prefix) +{ + for ( ; *line != '\0' && *prefix != '\0' ; line++, prefix++) { + if (*line != *prefix) { + return 0; + } + } + + return *prefix == '\0'; +} + +/** add an intermediate response to sp_response*/ +static void addIntermediate(const char *line) +{ + ATLine *p_new; + + p_new = (ATLine *) malloc(sizeof(ATLine)); + + p_new->line = strdup(line); + + /* note: this adds to the head of the list, so the list + will be in reverse order of lines received. the order is flipped + again before passing on to the command issuer */ + p_new->p_next = sp_response->p_intermediates; + sp_response->p_intermediates = p_new; +} + + +/** + * returns 1 if line is a final response indicating error + * See 27.007 annex B + * WARNING: NO CARRIER and others are sometimes unsolicited + */ +static const char * s_finalResponsesError[] = { + "ERROR", + "+CMS ERROR:", + "+CME ERROR:", + "NO CARRIER", /* sometimes! */ + "NO ANSWER", + "NO DIALTONE", + "COMMAND NOT SUPPORT", +}; +static int isFinalResponseError(const char *line) +{ + size_t i; + + for (i = 0 ; i < NUM_ELEMS(s_finalResponsesError) ; i++) { + if (strStartsWith(line, s_finalResponsesError[i])) { + return 1; + } + } + + return 0; +} + +/** + * returns 1 if line is a final response indicating success + * See 27.007 annex B + * WARNING: NO CARRIER and others are sometimes unsolicited + */ +static const char * s_finalResponsesSuccess[] = { + "OK", + "+QIND: \"FOTA\",\"END\",0", + "CONNECT" /* some stacks start up data on another channel */ +}; + +static int isFinalResponseSuccess(const char *line) +{ + size_t i; + + for (i = 0 ; i < NUM_ELEMS(s_finalResponsesSuccess) ; i++) { + if (strStartsWith(line, s_finalResponsesSuccess[i])) { + return 1; + } + } + + return 0; +} + +#if 0 +/** + * returns 1 if line is a final response, either error or success + * See 27.007 annex B + * WARNING: NO CARRIER and others are sometimes unsolicited + */ +static int isFinalResponse(const char *line) +{ + return isFinalResponseSuccess(line) || isFinalResponseError(line); +} +#endif + +/** + * returns 1 if line is the first line in (what will be) a two-line + * SMS unsolicited response + */ +static const char * s_smsUnsoliciteds[] = { + "+CMT:", + "+CDS:", + "+CBM:", + "+CMTI:" +}; +static int isSMSUnsolicited(const char *line) +{ + size_t i; + + for (i = 0 ; i < NUM_ELEMS(s_smsUnsoliciteds) ; i++) { + if (strStartsWith(line, s_smsUnsoliciteds[i])) { + return 1; + } + } + + return 0; +} + + +/** assumes s_commandmutex is held */ +static void handleFinalResponse(const char *line) +{ + sp_response->finalResponse = strdup(line); + + pthread_cond_signal(&cm_command_cond); +} + +static void handleUnsolicited(const char *line) +{ + if (s_unsolHandler != NULL) { + s_unsolHandler(line, NULL); + } +} + +static void processLine(const char *line) +{ + pthread_mutex_lock(&cm_command_mutex); + + if (sp_response == NULL) { + /* no command pending */ + handleUnsolicited(line); + } else if (s_raw_data != NULL && 0 == strcmp(line, "CONNECT")) { + usleep(500*1000); //for EC20 + writeraw(s_raw_data, s_raw_len); + s_raw_data = NULL; + } else if (isFinalResponseSuccess(line)) { + if(s_atc_proxy) + handleUnsolicited(line); + sp_response->success = 1; + handleFinalResponse(line); + } else if (isFinalResponseError(line)) { + if(s_atc_proxy) + handleUnsolicited(line); + sp_response->success = 0; + handleFinalResponse(line); + } else if (s_smsPDU != NULL && 0 == strcmp(line, "> ")) { + // See eg. TS 27.005 4.3 + // Commands like AT+CMGS have a "> " prompt + writeCtrlZ(s_smsPDU); + s_smsPDU = NULL; + } else switch (s_type) { + case NO_RESULT: + handleUnsolicited(line); + break; + case NUMERIC: + if (sp_response->p_intermediates == NULL + && isdigit(line[0]) + ) { + addIntermediate(line); + } else { + /* either we already have an intermediate response or + the line doesn't begin with a digit */ + handleUnsolicited(line); + } + break; + case SINGLELINE: + if (sp_response->p_intermediates == NULL + && strStartsWith (line, s_responsePrefix) + ) { + addIntermediate(line); + } else { + /* we already have an intermediate response */ + handleUnsolicited(line); + } + break; + case MULTILINE: + if (strStartsWith (line, s_responsePrefix)) { + addIntermediate(line); + } else { + handleUnsolicited(line); + } + break; + + default: /* this should never be reached */ + LOGE("Unsupported AT command type %d\n", s_type); + handleUnsolicited(line); + break; + } + + pthread_mutex_unlock(&cm_command_mutex); +} + + +/** + * Returns a pointer to the end of the next line + * special-cases the "> " SMS prompt + * + * returns NULL if there is no complete line + */ +static char * findNextEOL(char *cur) +{ + if (cur[0] == '>' && cur[1] == ' ' && cur[2] == '\0') { + /* SMS prompt character...not \r terminated */ + return cur+2; + } + + // Find next newline + while (*cur != '\0' && *cur != '\r' && *cur != '\n') cur++; + + return *cur == '\0' ? NULL : cur; +} + + +/** + * Reads a line from the AT channel, returns NULL on timeout. + * Assumes it has exclusive read access to the FD + * + * This line is valid only until the next call to readline + * + * This function exists because as of writing, android libc does not + * have buffered stdio. + */ + +static const char *readline() +{ + ssize_t count; + + char *p_read = NULL; + char *p_eol = NULL; + char *ret; + + /* this is a little odd. I use *s_ATBufferCur == 0 to + * mean "buffer consumed completely". If it points to a character, than + * the buffer continues until a \0 + */ + if (*s_ATBufferCur == '\0') { + /* empty buffer */ + s_ATBufferCur = s_ATBuffer; + *s_ATBufferCur = '\0'; + p_read = s_ATBuffer; + } else { /* *s_ATBufferCur != '\0' */ + /* there's data in the buffer from the last read */ + + // skip over leading newlines + while (*s_ATBufferCur == '\r' || *s_ATBufferCur == '\n') + s_ATBufferCur++; + + p_eol = findNextEOL(s_ATBufferCur); + + if (p_eol == NULL) { + /* a partial line. move it up and prepare to read more */ + size_t len; + + len = strlen(s_ATBufferCur); + + memmove(s_ATBuffer, s_ATBufferCur, len + 1); + p_read = s_ATBuffer + len; + s_ATBufferCur = s_ATBuffer; + } + /* Otherwise, (p_eol !- NULL) there is a complete line */ + /* that will be returned the while () loop below */ + } + + while (p_eol == NULL) { + if (0 == MAX_AT_RESPONSE - (p_read - s_ATBuffer)) { + LOGE("ERROR: Input line exceeded buffer\n"); + /* ditch buffer and start over again */ + s_ATBufferCur = s_ATBuffer; + *s_ATBufferCur = '\0'; + p_read = s_ATBuffer; + } + + do { + while (s_fd > 0) { + struct pollfd pollfds[1] = {{s_fd, POLLIN, 0}}; + int ret; + + do { + ret = poll(pollfds, 1, -1); + } while ((ret < 0) && (errno == EINTR)); + + if (pollfds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { + break; + } else if (pollfds[0].revents & (POLLIN)) { + break; + } + }; + + count = (s_fd == -1) ? 0 : read(s_fd, p_read, + MAX_AT_RESPONSE - (p_read - s_ATBuffer)); + } while (count < 0 && errno == EINTR); + + if (count > 0) { + AT_DUMP( "<< ", p_read, count ); + s_readCount += count; + + p_read[count] = '\0'; + + // skip over leading newlines + while (*s_ATBufferCur == '\r' || *s_ATBufferCur == '\n') + s_ATBufferCur++; + + p_eol = findNextEOL(s_ATBufferCur); + p_read += count; + } else if (count <= 0) { + /* read error encountered or EOF reached */ + if(count == 0) { + LOGD("atchannel: EOF reached"); + } else { + LOGD("atchannel: read error %s", strerror(errno)); + } + return NULL; + } + } + + /* a full line in the buffer. Place a \0 over the \r and return */ + + ret = s_ATBufferCur; + *p_eol = '\0'; + s_ATBufferCur = p_eol + 1; /* this will always be <= p_read, */ + /* and there will be a \0 at *p_read */ + + LOGD("AT< %s", ret); + return ret; +} + + +static void onReaderClosed() +{ + LOGE("%s", __func__); + if (s_onReaderClosed != NULL && s_readerClosed == 0) { + + pthread_mutex_lock(&cm_command_mutex); + + s_readerClosed = 1; + + pthread_cond_signal(&cm_command_cond); + + pthread_mutex_unlock(&cm_command_mutex); + + s_onReaderClosed(); + } +} + + +static void *readerLoop(void *arg) +{ + (void)arg; + + for (;;) { + const char * line; + + line = readline(); + + if (line == NULL) { + break; + } + + if(isSMSUnsolicited(line)) { + char *line1; + const char *line2; + + // The scope of string returned by 'readline()' is valid only + // till next call to 'readline()' hence making a copy of line + // before calling readline again. + line1 = strdup(line); + line2 = readline(); + + if (line2 == NULL) { + break; + } + + if (s_unsolHandler != NULL) { + s_unsolHandler (line1, line2); + } + free(line1); + } else { + processLine(line); + } + } + + onReaderClosed(); + + return NULL; +} + +/** + * Sends string s to the radio with a \r appended. + * Returns AT_ERROR_* on error, 0 on success + * + * This function exists because as of writing, android libc does not + * have buffered stdio. + */ +static int writeline (const char *s) +{ + size_t cur = 0; + size_t len = strlen(s); + ssize_t written; + static char at_command[64]; + + if (s_fd < 0 || s_readerClosed > 0) { + return AT_ERROR_CHANNEL_CLOSED; + } + + LOGD("AT> %s", s); + + AT_DUMP( ">> ", s, strlen(s) ); + +#if 1 //send '\r' maybe fail via USB controller: Intel Corporation 7 Series/C210 Series Chipset Family USB xHCI Host Controller (rev 04) + if (len < (sizeof(at_command) - 1)) { + strcpy(at_command, s); + at_command[len++] = '\r'; + s = (const char *)at_command; + } +#endif + + /* the main string */ + while (cur < len) { + do { + written = write (s_fd, s + cur, len - cur); + } while (written < 0 && errno == EINTR); + + if (written < 0) { + return AT_ERROR_GENERIC; + } + + cur += written; + } + +#if 1 //Quectel send '\r' maybe fail via USB controller: Intel Corporation 7 Series/C210 Series Chipset Family USB xHCI Host Controller (rev 04) + if (s == (const char *)at_command) { + return 0; + } +#endif + + /* the \r */ + + do { + written = write (s_fd, "\r" , 1); + } while ((written < 0 && errno == EINTR) || (written == 0)); + + if (written < 0) { + return AT_ERROR_GENERIC; + } + + return 0; +} +static int writeCtrlZ (const char *s) +{ + size_t cur = 0; + size_t len = strlen(s); + ssize_t written; + + if (s_fd < 0 || s_readerClosed > 0) { + return AT_ERROR_CHANNEL_CLOSED; + } + + LOGD("AT> %s^Z", s); + + AT_DUMP( ">* ", s, strlen(s) ); + + /* the main string */ + while (cur < len) { + do { + written = write (s_fd, s + cur, len - cur); + } while (written < 0 && errno == EINTR); + + if (written < 0) { + return AT_ERROR_GENERIC; + } + + cur += written; + } + + /* the ^Z */ + + do { + written = write (s_fd, "\032" , 1); + } while ((written < 0 && errno == EINTR) || (written == 0)); + + if (written < 0) { + return AT_ERROR_GENERIC; + } + + return 0; +} + +static int writeraw (const char *s, size_t len) { + size_t cur = 0; + ssize_t written; + + if (s_fd < 0 || s_readerClosed > 0) { + return AT_ERROR_CHANNEL_CLOSED; + } + + /* the main string */ + while (cur < len) { + struct pollfd pollfds[1] = {{s_fd, POLLOUT, 0}}; + int ret; + + ret = poll(pollfds, 1, -1); + if (ret <= 0) + break; + + do { + written = write (s_fd, s + cur, len - cur); + } while (written < 0 && errno == EINTR); + + if (written < 0) { + return AT_ERROR_GENERIC; + } + + cur += written; + } + + if (written < 0) { + return AT_ERROR_GENERIC; + } + + return cur; +} + +static void clearPendingCommand() +{ + if (sp_response != NULL) { + at_response_free(sp_response); + } + + sp_response = NULL; + s_responsePrefix = NULL; + s_smsPDU = NULL; +} + + +/** + * Starts AT handler on stream "fd' + * returns 0 on success, -1 on error + */ +int at_open(int fd, ATUnsolHandler h, int proxy) +{ + int ret; + pthread_attr_t attr; + + s_fd = fd; + s_unsolHandler = h; + s_readerClosed = 0; + s_atc_proxy = proxy; + + s_responsePrefix = NULL; + s_smsPDU = NULL; + sp_response = NULL; + + pthread_attr_init (&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + ret = pthread_create(&s_tid_reader, &attr, readerLoop, NULL); + + if (ret < 0) { + LOGE("readerLoop create fail!"); + perror ("pthread_create\n"); + return -1; + } + + return 0; +} + +/* FIXME is it ok to call this from the reader and the command thread? */ +void at_close() +{ + dbg_time("at_close"); + if (s_fd >= 0) { + close(s_fd); + } + s_fd = -1; + + pthread_mutex_lock(&cm_command_mutex); + + s_readerClosed = 1; + + pthread_cond_signal(&cm_command_cond); + + pthread_mutex_unlock(&cm_command_mutex); + + /* the reader thread should eventually die */ +} + +static ATResponse * at_response_new() +{ + return (ATResponse *) calloc(1, sizeof(ATResponse)); +} + +void at_response_free(ATResponse *p_response) +{ + ATLine *p_line; + + if (p_response == NULL) return; + + p_line = p_response->p_intermediates; + + while (p_line != NULL) { + ATLine *p_toFree; + + p_toFree = p_line; + p_line = p_line->p_next; + + free(p_toFree->line); + free(p_toFree); + } + + free (p_response->finalResponse); + free (p_response); +} + +/** + * The line reader places the intermediate responses in reverse order + * here we flip them back + */ +static void reverseIntermediates(ATResponse *p_response) +{ + ATLine *pcur,*pnext; + + pcur = p_response->p_intermediates; + p_response->p_intermediates = NULL; + + while (pcur != NULL) { + pnext = pcur->p_next; + pcur->p_next = p_response->p_intermediates; + p_response->p_intermediates = pcur; + pcur = pnext; + } +} + +/** + * Internal send_command implementation + * Doesn't lock or call the timeout callback + * + * timeoutMsec == 0 means infinite timeout + */ +static int at_send_command_full_nolock (const char *command, ATCommandType type, + const char *responsePrefix, const char *smspdu, + long long timeoutMsec, ATResponse **pp_outResponse) +{ + int err = 0; + + if (!timeoutMsec) + timeoutMsec = 15000; + + if(sp_response != NULL) { + err = AT_ERROR_COMMAND_PENDING; + goto error; + } + + if (command != NULL) + err = writeline (command); + + if (err < 0) { + printf("%s errno: %d (%s)\n", __func__, errno, strerror(errno)); + goto error; + } + + s_type = type; + s_responsePrefix = responsePrefix; + s_smsPDU = smspdu; + sp_response = at_response_new(); + + while (sp_response->finalResponse == NULL && s_readerClosed == 0) { + err = pthread_cond_timeout_np(&cm_command_cond, &cm_command_mutex, timeoutMsec); + + if (err == ETIMEDOUT) { + err = AT_ERROR_TIMEOUT; + goto error; + } + } + + if (pp_outResponse == NULL) { + at_response_free(sp_response); + } else { + /* line reader stores intermediate responses in reverse order */ + reverseIntermediates(sp_response); + *pp_outResponse = sp_response; + } + + sp_response = NULL; + + if(s_readerClosed > 0) { + err = AT_ERROR_CHANNEL_CLOSED; + goto error; + } + + err = 0; +error: + clearPendingCommand(); + + return err; +} + +/** + * Internal send_command implementation + * + * timeoutMsec == 0 means infinite timeout + */ +static int at_send_command_full (const char *command, ATCommandType type, + const char *responsePrefix, const char *smspdu, + long long timeoutMsec, ATResponse **pp_outResponse) +{ + int err; + + if (0 != pthread_equal(s_tid_reader, pthread_self())) { + /* cannot be called from reader thread */ + return AT_ERROR_INVALID_THREAD; + } + + pthread_mutex_lock(&cm_command_mutex); + + err = at_send_command_full_nolock(command, type, + responsePrefix, smspdu, + timeoutMsec, pp_outResponse); + + pthread_mutex_unlock(&cm_command_mutex); + + if (err == AT_ERROR_TIMEOUT && s_onTimeout != NULL) { + s_onTimeout(); + } + + return err; +} + + +/** + * Issue a single normal AT command with no intermediate response expected + * + * "command" should not include \r + * pp_outResponse can be NULL + * + * if non-NULL, the resulting ATResponse * must be eventually freed with + * at_response_free + */ +int at_send_command (const char *command, ATResponse **pp_outResponse) +{ + int err; + + err = at_send_command_full (command, NO_RESULT, NULL, + NULL, 0, pp_outResponse); + + return err; +} + + +int at_send_command_singleline (const char *command, + const char *responsePrefix, + ATResponse **pp_outResponse) +{ + int err; + + err = at_send_command_full (command, SINGLELINE, responsePrefix, + NULL, 0, pp_outResponse); + + if (err == 0 && pp_outResponse != NULL + && (*pp_outResponse)->success > 0 + && (*pp_outResponse)->p_intermediates == NULL + ) { + /* successful command must have an intermediate response */ + at_response_free(*pp_outResponse); + *pp_outResponse = NULL; + return AT_ERROR_INVALID_RESPONSE; + } + + return err; +} + + +int at_send_command_numeric (const char *command, + ATResponse **pp_outResponse) +{ + int err; + + err = at_send_command_full (command, NUMERIC, NULL, + NULL, 0, pp_outResponse); + + if (err == 0 && pp_outResponse != NULL + && (*pp_outResponse)->success > 0 + && (*pp_outResponse)->p_intermediates == NULL + ) { + /* successful command must have an intermediate response */ + at_response_free(*pp_outResponse); + *pp_outResponse = NULL; + return AT_ERROR_INVALID_RESPONSE; + } + + return err; +} + + +int at_send_command_sms (const char *command, + const char *pdu, + const char *responsePrefix, + ATResponse **pp_outResponse) +{ + int err; + + err = at_send_command_full (command, SINGLELINE, responsePrefix, + pdu, 0, pp_outResponse); + + if (err == 0 && pp_outResponse != NULL + && (*pp_outResponse)->success > 0 + && (*pp_outResponse)->p_intermediates == NULL + ) { + /* successful command must have an intermediate response */ + at_response_free(*pp_outResponse); + *pp_outResponse = NULL; + return AT_ERROR_INVALID_RESPONSE; + } + + return err; +} + +int at_send_command_multiline (const char *command, + const char *responsePrefix, + ATResponse **pp_outResponse) +{ + int err; + + err = at_send_command_full (command, MULTILINE, responsePrefix, + NULL, 0, pp_outResponse); + + return err; +} + +int at_send_command_raw (const char *command, + const char *raw_data, unsigned int raw_len, + const char *responsePrefix, + ATResponse **pp_outResponse) +{ + int err; + + s_raw_data = raw_data; + s_raw_len = raw_len; + err = at_send_command_full (command, SINGLELINE, responsePrefix, + NULL, 0, pp_outResponse); + + return err; +} + +/** + * Periodically issue an AT command and wait for a response. + * Used to ensure channel has start up and is active + */ + +int at_handshake() +{ + int i; + int err = 0; + + if (0 != pthread_equal(s_tid_reader, pthread_self())) { + /* cannot be called from reader thread */ + return AT_ERROR_INVALID_THREAD; + } + + pthread_mutex_lock(&cm_command_mutex); + + for (i = 0 ; i < HANDSHAKE_RETRY_COUNT ; i++) { + /* some stacks start with verbose off */ + err = at_send_command_full_nolock ("ATE0Q0V1", NO_RESULT, + NULL, NULL, HANDSHAKE_TIMEOUT_MSEC, NULL); + + if (err == 0) { + break; + } + } + + pthread_mutex_unlock(&cm_command_mutex); + + if (err == 0) { + /* pause for a bit to let the input buffer drain any unmatched OK's + (they will appear as extraneous unsolicited responses) */ + + sleepMsec(HANDSHAKE_TIMEOUT_MSEC); + } + + return err; +} + +AT_CME_Error at_get_cme_error(const ATResponse *p_response) +{ + int ret; + int err; + char *p_cur; + + if (p_response == NULL) + return CME_ERROR_NON_CME; + + if (p_response->success > 0) { + return CME_SUCCESS; + } + + if (p_response->finalResponse == NULL + || !strStartsWith(p_response->finalResponse, "+CME ERROR:") + ) { + return CME_ERROR_NON_CME; + } + + p_cur = p_response->finalResponse; + err = at_tok_start(&p_cur); + + if (err < 0) { + return CME_ERROR_NON_CME; + } + + err = at_tok_nextint(&p_cur, &ret); + + if (err < 0) { + return CME_ERROR_NON_CME; + } + + return (AT_CME_Error) ret; +} + +/** This callback is invoked on the command thread */ +void at_set_on_timeout(void (*onTimeout)(void)) +{ + s_onTimeout = onTimeout; +} + +/** + * This callback is invoked on the reader thread (like ATUnsolHandler) + * when the input stream closes before you call at_close + * (not when you call at_close()) + * You should still call at_close() + */ +void at_set_on_reader_closed(void (*onClose)(void)) +{ + s_onReaderClosed = onClose; +} diff --git a/application/quectel_CM_5G/src/atchannel.h b/application/quectel_CM_5G/src/atchannel.h new file mode 100644 index 0000000..cce28b1 --- /dev/null +++ b/application/quectel_CM_5G/src/atchannel.h @@ -0,0 +1,152 @@ +/* //device/system/reference-ril/atchannel.h +** +** Copyright 2006, 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 ATCHANNEL_H +#define ATCHANNEL_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* define AT_DEBUG to send AT traffic to /tmp/radio-at.log" */ +#define AT_DEBUG 0 + +#if AT_DEBUG +extern void AT_DUMP(const char* prefix, const char* buff, int len); +#else +#define AT_DUMP(prefix,buff,len) do{}while(0) +#endif + +#define AT_ERROR_GENERIC -1 +#define AT_ERROR_COMMAND_PENDING -2 +#define AT_ERROR_CHANNEL_CLOSED -3 +#define AT_ERROR_TIMEOUT -4 +#define AT_ERROR_INVALID_THREAD -5 /* AT commands may not be issued from + reader thread (or unsolicited response + callback */ +#define AT_ERROR_INVALID_RESPONSE -6 /* eg an at_send_command_singleline that + did not get back an intermediate + response */ + + +typedef enum { + NO_RESULT, /* no intermediate response expected */ + NUMERIC, /* a single intermediate response starting with a 0-9 */ + SINGLELINE, /* a single intermediate response starting with a prefix */ + MULTILINE /* multiple line intermediate response + starting with a prefix */ +} ATCommandType; + +/** a singly-lined list of intermediate responses */ +typedef struct ATLine { + struct ATLine *p_next; + char *line; +} ATLine; + +/** Free this with at_response_free() */ +typedef struct { + int success; /* true if final response indicates + success (eg "OK") */ + char *finalResponse; /* eg OK, ERROR */ + ATLine *p_intermediates; /* any intermediate responses */ +} ATResponse; + +/** + * a user-provided unsolicited response handler function + * this will be called from the reader thread, so do not block + * "s" is the line, and "sms_pdu" is either NULL or the PDU response + * for multi-line TS 27.005 SMS PDU responses (eg +CMT:) + */ +typedef void (*ATUnsolHandler)(const char *s, const char *sms_pdu); + +int at_open(int fd, ATUnsolHandler h, int proxy); +void at_close(); + +/* This callback is invoked on the command thread. + You should reset or handshake here to avoid getting out of sync */ +void at_set_on_timeout(void (*onTimeout)(void)); +/* This callback is invoked on the reader thread (like ATUnsolHandler) + when the input stream closes before you call at_close + (not when you call at_close()) + You should still call at_close() + It may also be invoked immediately from the current thread if the read + channel is already closed */ +void at_set_on_reader_closed(void (*onClose)(void)); + +int at_send_command_singleline (const char *command, + const char *responsePrefix, + ATResponse **pp_outResponse); + +int at_send_command_numeric (const char *command, + ATResponse **pp_outResponse); + +int at_send_command_multiline (const char *command, + const char *responsePrefix, + ATResponse **pp_outResponse); + +int at_send_command_raw (const char *command, + const char *raw_data, unsigned int raw_len, + const char *responsePrefix, + ATResponse **pp_outResponse); + +int at_handshake(); + +int at_send_command (const char *command, ATResponse **pp_outResponse); + +int at_send_command_sms (const char *command, const char *pdu, + const char *responsePrefix, + ATResponse **pp_outResponse); + +void at_response_free(ATResponse *p_response); + +int strStartsWith(const char *line, const char *prefix); + +typedef enum { + CME_ERROR_NON_CME = -1, + CME_SUCCESS = 0, + + CME_OPERATION_NOT_ALLOWED = 3, + CME_OPERATION_NOT_SUPPORTED = 4, + CME_PH_SIM_PIN= 5, + CME_PH_FSIM_PIN = 6, + CME_PH_FSIM_PUK = 7, + CME_SIM_NOT_INSERTED =10, + CME_SIM_PIN_REQUIRED = 11, + CME_SIM_PUK_REQUIRED = 12, + CME_FAILURE = 13, + CME_SIM_BUSY = 14, + CME_SIM_WRONG = 15, + CME_INCORRECT_PASSWORD = 16, + CME_SIM_PIN2_REQUIRED = 17, + CME_SIM_PUK2_REQUIRED = 18, + CME_MEMORY_FULL = 20, + CME_INVALID_INDEX = 21, + CME_NOT_FOUND = 22, + CME_MEMORY_FAILURE = 23, + CME_STRING_TO_LONG = 24, + CME_INVALID_CHAR = 25, + CME_DIALSTR_TO_LONG = 26, + CME_INVALID_DIALCHAR = 27, +} AT_CME_Error; + +AT_CME_Error at_get_cme_error(const ATResponse *p_response); + +#ifdef __cplusplus +} +#endif + +#endif /*ATCHANNEL_H*/ diff --git a/application/quectel_CM_5G/src/configure.ac b/application/quectel_CM_5G/src/configure.ac new file mode 100644 index 0000000..f4c60ea --- /dev/null +++ b/application/quectel_CM_5G/src/configure.ac @@ -0,0 +1,48 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.61]) +AC_INIT([quectel-CM], [1.0], [fae-support@quectel.com]) +AC_CONFIG_HEADERS([config.h]) + +# Checks for programs. +AC_PROG_CC + +# Checks for libraries. + +# Checks for header files. + +# Checks for typedefs, structures, and compiler characteristics. +AC_ARG_WITH(sanitized-headers, + AS_HELP_STRING([--with-sanitized-headers=DIR], + [Specify the location of the sanitized Linux headers]), + [CPPFLAGS="$CPPFLAGS -idirafter $withval"]) + +AC_ARG_WITH([qrtr], + AC_HELP_STRING([--with-qrtr], + [enable qrtr, building which use qrtr])) + +if (test "x${with_qrtr}" = "xyes"); then + #AC_DEFINE(ENABLE_USEQTRT, 1, [Define if uses qrtr]) + AC_CHECK_HEADERS([linux/qrtr.h linux/rmnet_data.h]) +fi + +AM_CONDITIONAL(USE_QRTR, test "x${with_qrtr}" = "xyes") + +AC_ARG_WITH([msm-ipc], + AC_HELP_STRING([--with-msm-ipc], + [enable msm-ipc, building which use qrtr])) + +if (test "x${with_msm_ipc}" = "xyes"); then + #AC_DEFINE(ENABLE_USEQTRT, 1, [Define if uses qrtr]) + AC_CHECK_HEADERS([linux/msm_ipc.h linux/rmnet_data.h]) +fi + +AM_CONDITIONAL(USE_MSM_IPC, test "x${with_msm_ipc}" = "xyes") + +# Checks for library functions. + +# Does not strictly follow GNU Coding standards +AM_INIT_AUTOMAKE([foreign subdir-objects]) +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/application/quectel_CM_5G/src/default.script b/application/quectel_CM_5G/src/default.script new file mode 100644 index 0000000..26b95c1 --- /dev/null +++ b/application/quectel_CM_5G/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/application/quectel_CM_5G/src/default.script_ip b/application/quectel_CM_5G/src/default.script_ip new file mode 100644 index 0000000..24f8e59 --- /dev/null +++ b/application/quectel_CM_5G/src/default.script_ip @@ -0,0 +1,61 @@ +#!/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" +IPCMD=`which ip` + +case $1 in + bound|renew) + $IPCMD address add broadcast $broadcast $ip/$subnet dev $interface + + if [ -n "$router" ]; then + echo "$0: Resetting default routes" + while $IPCMD route del default dev $interface; do :; done + + metric=0 + for i in $router; do + $IPCMD route add default dev $interface via $router 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 + $IPCMD address flush dev $interface + ;; + + 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/application/quectel_CM_5G/src/device.c b/application/quectel_CM_5G/src/device.c new file mode 100644 index 0000000..58bf298 --- /dev/null +++ b/application/quectel_CM_5G/src/device.c @@ -0,0 +1,746 @@ +/****************************************************************************** + @file device.c + @brief QMI device dirver. + + DESCRIPTION + Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "QMIThread.h" +#include "ethtool-copy.h" + +#define USB_CLASS_VENDOR_SPEC 0xff +#define USB_CLASS_COMM 2 +#define USB_CDC_SUBCLASS_ACM 0x02 +#define USB_CDC_SUBCLASS_ETHERNET 0x06 +#define USB_CDC_SUBCLASS_NCM 0x0d +#define USB_CDC_SUBCLASS_MBIM 0x0e +#define USB_CLASS_WIRELESS_CONTROLLER 0xe0 + +#define CM_MAX_PATHLEN 256 + +#define CM_INVALID_VAL (~((int)0)) +/* get first line from file 'fname' + * And convert the content into a hex number, then return this number */ +static int file_get_value(const char *fname, int base) +{ + FILE *fp = NULL; + long num; + char buff[32 + 1] = {'\0'}; + char *endptr = NULL; + + fp = fopen(fname, "r"); + if (!fp) goto error; + if (fgets(buff, sizeof(buff), fp) == NULL) + goto error; + fclose(fp); + + num = (int)strtol(buff, &endptr, base); + if (errno == ERANGE && (num == LONG_MAX || num == LONG_MIN)) + goto error; + /* if there is no digit in buff */ + if (endptr == buff) + goto error; + + if (debug_qmi) + dbg_time("(%s) = %lx", fname, num); + return (int)num; + +error: + if (fp) fclose(fp); + return CM_INVALID_VAL; +} + +/* + * This function will search the directory 'dirname' and return the first child. + * '.' and '..' is ignored by default + */ +static int dir_get_child(const char *dirname, char *buff, unsigned bufsize, const char *prefix) +{ + struct dirent *entptr = NULL; + DIR *dirptr; + + buff[0] = 0; + + dirptr = opendir(dirname); + if (!dirptr) + return -1; + + while ((entptr = readdir(dirptr))) { + if (entptr->d_name[0] == '.') + continue; + if (prefix && strlen(prefix) && strncmp(entptr->d_name, prefix, strlen(prefix))) + continue; + snprintf(buff, bufsize, "%.31s", entptr->d_name); + break; + } + closedir(dirptr); + + return 0; +} + +static int conf_get_val(const char *fname, const char *key) +{ + char buff[128] = {'\0'}; + FILE *fp = fopen(fname, "r"); + if (!fp) + return CM_INVALID_VAL; + + while (fgets(buff, sizeof(buff)-1, fp)) { + char prefix[128] = {'\0'}; + char tail[128] = {'\0'}; + /* To eliminate cppcheck warnning: Assume string length is no more than 15 */ + sscanf(buff, "%15[^=]=%15s", prefix, tail); + if (!strncasecmp(prefix, key, strlen(key))) { + fclose(fp); + return atoi(tail); + } + } + + fclose(fp); + return CM_INVALID_VAL; +} + +static void query_usb_device_info(char *path, struct usb_device_info *p) { + size_t offset = strlen(path); + + memset(p, 0, sizeof(*p)); + + path[offset] = '\0'; + strcat(path, "/idVendor"); + p->idVendor = file_get_value(path, 16); + + if (p->idVendor == CM_INVALID_VAL) + return; + + path[offset] = '\0'; + strcat(path, "/idProduct"); + p->idProduct = file_get_value(path, 16); + + path[offset] = '\0'; + strcat(path, "/busnum"); + p->busnum = file_get_value(path, 10); + + path[offset] = '\0'; + strcat(path, "/devnum"); + p->devnum = file_get_value(path, 10); + + path[offset] = '\0'; + strcat(path, "/bNumInterfaces"); + p->bNumInterfaces = file_get_value(path, 10); + + path[offset] = '\0'; +} + +static void query_usb_interface_info(char *path, struct usb_interface_info *p) { + char driver[128]; + size_t offset = strlen(path); + int n; + + memset(p, 0, sizeof(*p)); + + path[offset] = '\0'; + strcat(path, "/bNumEndpoints"); + p->bInterfaceClass = file_get_value(path, 16); + + path[offset] = '\0'; + strcat(path, "/bInterfaceClass"); + p->bInterfaceClass = file_get_value(path, 16); + + path[offset] = '\0'; + strcat(path, "/bInterfaceSubClass"); + p->bInterfaceSubClass = file_get_value(path, 16); + + path[offset] = '\0'; + strcat(path, "/bInterfaceProtocol"); + p->bInterfaceProtocol = file_get_value(path, 16); + + path[offset] = '\0'; + strcat(path, "/driver"); + n = readlink(path, driver, sizeof(driver)); + if (n > 0) { + driver[n] = 0; + if (debug_qmi) dbg_time("driver -> %s", driver); + n = strlen(driver); + while (n > 0) { + if (driver[n] == '/') + break; + n--; + } + strncpy(p->driver, &driver[n+1], sizeof(p->driver) - 1); + } + + path[offset] = '\0'; +} + +static int detect_path_cdc_wdm_or_qcqmi(char *path, char *devname, size_t bufsize) +{ + size_t offset = strlen(path); + char tmp[32]; + + devname[0] = 0; + + if (access(path, R_OK)) + return -1; + + path[offset] = '\0'; + strcat(path, "/GobiQMI"); + if (!access(path, R_OK)) + goto step_1; + + path[offset] = '\0'; + strcat(path, "/usbmisc"); + if (!access(path, R_OK)) + goto step_1; + + path[offset] = '\0'; + strcat(path, "/usb"); + if (!access(path, R_OK)) + goto step_1; + + return -1; + +step_1: + /* get device(qcqmiX|cdc-wdmX) */ + if (debug_qmi) dbg_time("%s", path); + dir_get_child(path, tmp, sizeof(tmp), NULL); + if (tmp[0] == '\0') + return -1; + + /* There is a chance that, no device(qcqmiX|cdc-wdmX) is generated. We should warn user about that! */ + snprintf(devname, bufsize, "/dev/%s", tmp); + if (access(devname, R_OK | F_OK) && errno == ENOENT) + { + int major, minor; + + dbg_time("access %s failed, errno: %d (%s)", devname, errno, strerror(errno)); + strcat(path, "/"); + strcat(path, tmp); + strcat(path, "/uevent"); + major = conf_get_val(path, "MAJOR"); + minor = conf_get_val(path, "MINOR"); + + if(major == CM_INVALID_VAL || minor == CM_INVALID_VAL) + dbg_time("get major and minor failed"); + else if (mknod(devname, S_IFCHR|0666, (((major & 0xfff) << 8) | (minor & 0xff) | ((minor & 0xfff00) << 12)))) + dbg_time("please mknod %s c %d %d", devname, major, minor); + } + + return 0; +} + +/* To detect the device info of the modem. + * return: + * FALSE -> fail + * TRUE -> ok + */ +BOOL qmidevice_detect(char *qmichannel, char *usbnet_adapter, unsigned bufsize, PROFILE_T *profile) { + struct dirent* ent = NULL; + DIR *pDir; + const char *rootdir = "/sys/bus/usb/devices"; + struct { + char path[255*2]; + } *pl; + pl = (typeof(pl)) malloc(sizeof(*pl)); + memset(pl, 0x00, sizeof(*pl)); + + pDir = opendir(rootdir); + if (!pDir) { + dbg_time("opendir %s failed: %s", rootdir, strerror(errno)); + goto error; + } + + while ((ent = readdir(pDir)) != NULL) { + char netcard[32+1] = {'\0'}; + char devname[32+5] = {'\0'}; //+strlen("/dev/") + int netIntf; + int driver_type; + + if (ent->d_name[0] == 'u') + continue; + + snprintf(pl->path, sizeof(pl->path), "%s/%s", rootdir, ent->d_name); + query_usb_device_info(pl->path, &profile->usb_dev); + if (profile->usb_dev.idVendor == CM_INVALID_VAL) + continue; + + if (profile->usb_dev.idVendor == 0x2c7c || profile->usb_dev.idVendor == 0x05c6) { + dbg_time("Find %s/%s idVendor=0x%x idProduct=0x%x, bus=0x%03x, dev=0x%03x", + rootdir, ent->d_name, profile->usb_dev.idVendor, profile->usb_dev.idProduct, + profile->usb_dev.busnum, profile->usb_dev.devnum); + } + + /* get network interface */ + /* NOTICE: there is a case that, bNumberInterface=6, but the net interface is 8 */ + /* toolchain-mips_24kc_gcc-5.4.0_musl donot support GLOB_BRACE */ + /* RG500U's MBIM is at inteface 0 */ + for (netIntf = 0; netIntf < (profile->usb_dev.bNumInterfaces + 8); netIntf++) { + snprintf(pl->path, sizeof(pl->path), "%s/%s:1.%d/net", rootdir, ent->d_name, netIntf); + dir_get_child(pl->path, netcard, sizeof(netcard), NULL); + if (netcard[0]) + break; + } + + if (netcard[0] == '\0') { //for centos 2.6.x + const char *n= "usb0"; + const char *c = "qcqmi0"; + + snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4/net:%s", rootdir, ent->d_name, n); + if (!access(pl->path, F_OK)) { + snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4/GobiQMI:%s", rootdir, ent->d_name, c); + if (!access(pl->path, F_OK)) { + snprintf(qmichannel, bufsize, "/dev/%s", c); + snprintf(usbnet_adapter, bufsize, "%s", n); + snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4", rootdir, ent->d_name); + query_usb_interface_info(pl->path, &profile->usb_intf); + break; + } + } + } + + if (netcard[0] == '\0') + continue; + + /* not '-i iface' */ + if (usbnet_adapter[0] && strcmp(usbnet_adapter, netcard)) + continue; + + snprintf(pl->path, sizeof(pl->path), "%s/%s:1.%d", rootdir, ent->d_name, netIntf); + query_usb_interface_info(pl->path, &profile->usb_intf); + driver_type = get_driver_type(profile); + + if (driver_type == SOFTWARE_QMI || driver_type == SOFTWARE_MBIM) { + detect_path_cdc_wdm_or_qcqmi(pl->path, devname, sizeof(devname)); + } + else if (driver_type == SOFTWARE_ECM_RNDIS_NCM) + { + int atIntf = -1; + + if (profile->usb_dev.idVendor == 0x2c7c) { //Quectel + switch (profile->usb_dev.idProduct) { //EC200U + case 0x0901: //EC200U + case 0x8101: //RG801H + atIntf = 2; + break; + case 0x0900: //RG500U + atIntf = 4; + break; + case 0x6026: //EC200T + case 0x6005: //EC200A + case 0x6002: //EC200S + case 0x6001: //EC100Y + atIntf = 3; + break; + default: + dbg_time("unknow at interface for USB idProduct:%04x\n", profile->usb_dev.idProduct); + break; + } + } + + if (atIntf != -1) { + snprintf(pl->path, sizeof(pl->path), "%s/%s:1.%d", rootdir, ent->d_name, atIntf); + dir_get_child(pl->path, devname, sizeof(devname), "tty"); + if (devname[0] && !strcmp(devname, "tty")) { + snprintf(pl->path, sizeof(pl->path), "%s/%s:1.%d/tty", rootdir, ent->d_name, atIntf); + dir_get_child(pl->path, devname, sizeof(devname), "tty"); + } + } + } + + if (netcard[0] && devname[0]) { + if (devname[0] == '/') + snprintf(qmichannel, bufsize, "%s", devname); + else + snprintf(qmichannel, bufsize, "/dev/%s", devname); + snprintf(usbnet_adapter, bufsize, "%s", netcard); + dbg_time("Auto find qmichannel = %s", qmichannel); + dbg_time("Auto find usbnet_adapter = %s", usbnet_adapter); + break; + } + } + closedir(pDir); + + if (qmichannel[0] == '\0' || usbnet_adapter[0] == '\0') { + dbg_time("network interface '%s' or qmidev '%s' is not exist", usbnet_adapter, qmichannel); + goto error; + } + free(pl); + return TRUE; +error: + free(pl); + return FALSE; +} + +int mhidevice_detect(char *qmichannel, char *usbnet_adapter, PROFILE_T *profile) { + struct dirent* ent = NULL; + DIR *pDir; + const char *rootdir_mhi[] = {"/sys/bus/mhi_q/devices", "/sys/bus/mhi/devices", NULL}; + int i = 0; + char path[256]; + int find = 0; + + while (rootdir_mhi[i]) { + const char *rootdir = rootdir_mhi[i++]; + + pDir = opendir(rootdir); + if (!pDir) { + if (errno != ENOENT) + dbg_time("opendir %s failed: %s", rootdir, strerror(errno)); + continue; + } + + while ((ent = readdir(pDir)) != NULL) { + char netcard[32] = {'\0'}; + char devname[32] = {'\0'}; + int software_interface = SOFTWARE_QMI; + char *pNode = NULL; + + pNode = strstr(ent->d_name, "_IP_HW0"); //0306_00.01.00_IP_HW0 + if (!pNode) + continue; + + snprintf(path, sizeof(path), "%s/%.32s/net", rootdir, ent->d_name); + dir_get_child(path, netcard, sizeof(netcard), NULL); + if (!netcard[0]) + continue; + + if (usbnet_adapter[0] && strcmp(netcard, usbnet_adapter)) //not '-i x' + continue; + + if (!strcmp(rootdir, "/sys/bus/mhi/devices")) { + snprintf(path, sizeof(path), "%s/%.13s_IPCR", rootdir, ent->d_name); // 13 is sizeof(0306_00.01.00) + if (!access(path, F_OK)) { + /* we also need 'cat /dev/mhi_0306_00.01.00_pipe_14' to enable rmnet as like USB's DTR + or will get error 'requestSetEthMode QMUXResult = 0x1, QMUXError = 0x46' */ + sprintf(usbnet_adapter, "%s", netcard); + sprintf(qmichannel, "qrtr-%d", 3); // 3 is sdx modem's node id + profile->software_interface = SOFTWARE_QRTR; + find = 1; + break; + } + continue; + } + + snprintf(path, sizeof(path), "%s/%.13s_IPCR", rootdir, ent->d_name); + if (access(path, F_OK)) { + snprintf(path, sizeof(path), "%s/%.13s_QMI0", rootdir, ent->d_name); + if (access(path, F_OK)) { + snprintf(path, sizeof(path), "%s/%.13s_MBIM", rootdir, ent->d_name); + if (!access(path, F_OK)) + software_interface = SOFTWARE_MBIM; + } + } + if (access(path, F_OK)) + continue; + + strncat(path, "/mhi_uci_q", sizeof(path)-1); + dir_get_child(path, devname, sizeof(devname), NULL); + if (!devname[0]) + continue; + + sprintf(usbnet_adapter, "%s", netcard); + sprintf(qmichannel, "/dev/%s", devname); + profile->software_interface = software_interface; + find = 1; + break; + } + + closedir(pDir); + } + + return find; +} + +int atdevice_detect(char *atchannel, char *usbnet_adapter, PROFILE_T *profile) { + if (!access("/sys/class/net/sipa_dummy0", F_OK)) { + strcpy(usbnet_adapter, "sipa_dummy0"); + snprintf(profile->qmapnet_adapter, sizeof(profile->qmapnet_adapter), "%s%d", "pcie", profile->pdp - 1); + } + else { + dbg_time("atdevice_detect failed"); + goto error; + } + + if (!access("/dev/stty_nr31", F_OK)) { + strcpy(atchannel, "/dev/stty_nr31"); + profile->software_interface = SOFTWARE_ECM_RNDIS_NCM; + } + else { + goto error; + } + + return 1; +error: + return 0; +} + + +int get_driver_type(PROFILE_T *profile) +{ + /* QMI_WWAN */ + if (profile->usb_intf.bInterfaceClass == USB_CLASS_VENDOR_SPEC) { + return SOFTWARE_QMI; + } + else if (profile->usb_intf.bInterfaceClass == USB_CLASS_COMM) { + switch (profile->usb_intf.bInterfaceSubClass) { + case USB_CDC_SUBCLASS_MBIM: + return SOFTWARE_MBIM; + break; + case USB_CDC_SUBCLASS_ETHERNET: + case USB_CDC_SUBCLASS_NCM: + return SOFTWARE_ECM_RNDIS_NCM; + break; + default: + break; + } + } + else if (profile->usb_intf.bInterfaceClass == USB_CLASS_WIRELESS_CONTROLLER) { + if (profile->usb_intf.bInterfaceSubClass == 1 && profile->usb_intf.bInterfaceProtocol == 3) + return SOFTWARE_ECM_RNDIS_NCM; + } + + dbg_time("%s unknow bInterfaceClass=%d, bInterfaceSubClass=%d", __func__, + profile->usb_intf.bInterfaceClass, profile->usb_intf.bInterfaceSubClass); + return DRV_INVALID; +} + +struct usbfs_getdriver +{ + unsigned int interface; + char driver[255 + 1]; +}; + +struct usbfs_ioctl +{ + int ifno; /* interface 0..N ; negative numbers reserved */ + int ioctl_code; /* MUST encode size + direction of data so the + * macros in give correct values */ + void *data; /* param buffer (in, or out) */ +}; + +#define IOCTL_USBFS_DISCONNECT _IO('U', 22) +#define IOCTL_USBFS_CONNECT _IO('U', 23) + +int usbfs_is_kernel_driver_alive(int fd, int ifnum) +{ + struct usbfs_getdriver getdrv; + getdrv.interface = ifnum; + if (ioctl(fd, USBDEVFS_GETDRIVER, &getdrv) < 0) { + dbg_time("%s ioctl USBDEVFS_GETDRIVER failed, kernel driver may be inactive", __func__); + return 0; + } + dbg_time("%s find interface %d has match the driver %s", __func__, ifnum, getdrv.driver); + return 1; +} + +void usbfs_detach_kernel_driver(int fd, int ifnum) +{ + struct usbfs_ioctl operate; + operate.data = NULL; + operate.ifno = ifnum; + operate.ioctl_code = IOCTL_USBFS_DISCONNECT; + if (ioctl(fd, USBDEVFS_IOCTL, &operate) < 0) { + dbg_time("%s detach kernel driver failed", __func__); + } else { + dbg_time("%s detach kernel driver success", __func__); + } +} + +void usbfs_attach_kernel_driver(int fd, int ifnum) +{ + struct usbfs_ioctl operate; + operate.data = NULL; + operate.ifno = ifnum; + operate.ioctl_code = IOCTL_USBFS_CONNECT; + if (ioctl(fd, USBDEVFS_IOCTL, &operate) < 0) { + dbg_time("%s detach kernel driver failed", __func__); + } else { + dbg_time("%s detach kernel driver success", __func__); + } +} + +int reattach_driver(PROFILE_T *profile) +{ + int ifnum = 4; + int fd; + char devpath[128] = {'\0'}; + snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", profile->usb_dev.busnum, profile->usb_dev.devnum); + fd = open(devpath, O_RDWR | O_NOCTTY); + if (fd < 0) + { + dbg_time("%s fail to open %s", __func__, devpath); + return -1; + } + usbfs_detach_kernel_driver(fd, ifnum); + usbfs_attach_kernel_driver(fd, ifnum); + close(fd); + return 0; +} + +#define SIOCETHTOOL 0x8946 +int ql_get_netcard_driver_info(const char *devname) +{ + int fd = -1; + struct ethtool_drvinfo drvinfo; + struct ifreq ifr; /* ifreq suitable for ethtool ioctl */ + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, devname); + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + dbg_time("Cannot get control socket: errno(%d)(%s)", errno, strerror(errno)); + return -1; + } + + drvinfo.cmd = ETHTOOL_GDRVINFO; + ifr.ifr_data = (void *)&drvinfo; + + if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) { + dbg_time("ioctl() error: errno(%d)(%s)", errno, strerror(errno)); + close(fd); + return -1; + } + + dbg_time("netcard driver = %s, driver version = %s", drvinfo.driver, drvinfo.version); + + close(fd); + + return 0; +} + +int ql_get_netcard_carrier_state(const char *devname) +{ + int fd = -1; + struct ethtool_value edata; + struct ifreq ifr; /* ifreq suitable for ethtool ioctl */ + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, devname); + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + dbg_time("Cannot get control socket: errno(%d)(%s)", errno, strerror(errno)); + return -1; + } + + edata.cmd = ETHTOOL_GLINK; + edata.data = 0; + ifr.ifr_data = (void *)&edata; + + if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) { + dbg_time("ioctl('%s') error: errno(%d)(%s)", devname, errno, strerror(errno)); + return -1; + } + + if (!edata.data) + dbg_time("netcard carrier = %d", edata.data); + + close(fd); + + return edata.data; +} + +static void *catch_log(void *arg) +{ + PROFILE_T *profile = (PROFILE_T *)arg; + int nreads = 0; + char tbuff[256+32]; + char filter[32]; + size_t tsize = strlen(get_time()) + 1; + + snprintf(filter, sizeof(filter), ":%d:%03d:", profile->usb_dev.busnum, profile->usb_dev.devnum); + + while(1) { + nreads = read(profile->usbmon_fd, tbuff + tsize, sizeof(tbuff) - tsize - 1); + if (nreads <= 0) { + if (nreads == -1 && errno == EINTR) + continue; + break; + } + + tbuff[tsize+nreads] = '\0'; // printf("%s", buff); + + if (!strstr(tbuff+tsize, filter)) + continue; + + snprintf(tbuff, sizeof(tbuff), "%s", get_time()); + tbuff[tsize-1] = ' '; + + fwrite(tbuff, strlen(tbuff), 1, profile->usbmon_logfile_fp); + } + + return NULL; +} + +int ql_capture_usbmon_log(PROFILE_T *profile, const char *log_path) +{ + char usbmon_path[256]; + pthread_t pt; + pthread_attr_t attr; + + if (access("/sys/module/usbmon", F_OK)) { + dbg_time("usbmon is not load, please execute \"modprobe usbmon\" or \"insmod usbmon.ko\""); + return -1; + } + + if (access("/sys/kernel/debug/usb", F_OK)) { + dbg_time("debugfs is not mount, please execute \"mount -t debugfs none_debugs /sys/kernel/debug\""); + return -1; + } + + snprintf(usbmon_path, sizeof(usbmon_path), "/sys/kernel/debug/usb/usbmon/%du", profile->usb_dev.busnum); + profile->usbmon_fd = open(usbmon_path, O_RDONLY); + if (profile->usbmon_fd < 0) { + dbg_time("open %s error(%d) (%s)", usbmon_path, errno, strerror(errno)); + return -1; + } + + snprintf(usbmon_path, sizeof(usbmon_path), "cat /sys/kernel/debug/usb/devices >> %s", log_path); + if (system(usbmon_path) == -1) {}; + + profile->usbmon_logfile_fp = fopen(log_path, "wb"); + if (!profile->usbmon_logfile_fp) { + dbg_time("open %s error(%d) (%s)", log_path, errno, strerror(errno)); + close(profile->usbmon_fd); + profile->usbmon_fd = -1; + return -1; + } + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + pthread_create(&pt, &attr, catch_log, (void *)profile); + + return 0; +} + +void ql_stop_usbmon_log(PROFILE_T *profile) { + if (profile->usbmon_fd > 0) + close(profile->usbmon_fd); + if (profile->usbmon_logfile_fp) + fclose(profile->usbmon_logfile_fp); +} diff --git a/application/quectel_CM_5G/src/ethtool-copy.h b/application/quectel_CM_5G/src/ethtool-copy.h new file mode 100644 index 0000000..b5515c2 --- /dev/null +++ b/application/quectel_CM_5G/src/ethtool-copy.h @@ -0,0 +1,1100 @@ +/* + * ethtool.h: Defines for Linux ethtool. + * + * Copyright (C) 1998 David S. Miller (davem@redhat.com) + * Copyright 2001 Jeff Garzik + * Portions Copyright 2001 Sun Microsystems (thockin@sun.com) + * Portions Copyright 2002 Intel (eli.kupermann@intel.com, + * christopher.leech@intel.com, + * scott.feldman@intel.com) + * Portions Copyright (C) Sun Microsystems 2008 + */ + +#ifndef _LINUX_ETHTOOL_H +#define _LINUX_ETHTOOL_H + +#include +#include + +/* This should work for both 32 and 64 bit userland. */ +struct ethtool_cmd { + __u32 cmd; + __u32 supported; /* Features this interface supports */ + __u32 advertising; /* Features this interface advertises */ + __u16 speed; /* The forced speed (lower bits) in + * Mbps. Please use + * ethtool_cmd_speed()/_set() to + * access it */ + __u8 duplex; /* Duplex, half or full */ + __u8 port; /* Which connector port */ + __u8 phy_address; /* MDIO PHY address (PRTAD for clause 45). + * May be read-only or read-write + * depending on the driver. + */ + __u8 transceiver; /* Which transceiver to use */ + __u8 autoneg; /* Enable or disable autonegotiation */ + __u8 mdio_support; /* MDIO protocols supported. Read-only. + * Not set by all drivers. + */ + __u32 maxtxpkt; /* Tx pkts before generating tx int */ + __u32 maxrxpkt; /* Rx pkts before generating rx int */ + __u16 speed_hi; /* The forced speed (upper + * bits) in Mbps. Please use + * ethtool_cmd_speed()/_set() to + * access it */ + __u8 eth_tp_mdix; /* twisted pair MDI-X status */ + __u8 eth_tp_mdix_ctrl; /* twisted pair MDI-X control, when set, + * link should be renegotiated if necessary + */ + __u32 lp_advertising; /* Features the link partner advertises */ + __u32 reserved[2]; +}; + +static __inline__ void ethtool_cmd_speed_set(struct ethtool_cmd *ep, + __u32 speed) +{ + + ep->speed = (__u16)speed; + ep->speed_hi = (__u16)(speed >> 16); +} + +static __inline__ __u32 ethtool_cmd_speed(const struct ethtool_cmd *ep) +{ + return (ep->speed_hi << 16) | ep->speed; +} + +/* Device supports clause 22 register access to PHY or peripherals + * using the interface defined in . This should not be + * set if there are known to be no such peripherals present or if + * the driver only emulates clause 22 registers for compatibility. + */ +#define ETH_MDIO_SUPPORTS_C22 1 + +/* Device supports clause 45 register access to PHY or peripherals + * using the interface defined in and . + * This should not be set if there are known to be no such peripherals + * present. + */ +#define ETH_MDIO_SUPPORTS_C45 2 + +#define ETHTOOL_FWVERS_LEN 32 +#define ETHTOOL_BUSINFO_LEN 32 +/* these strings are set to whatever the driver author decides... */ +struct ethtool_drvinfo { + __u32 cmd; + char driver[32]; /* driver short name, "tulip", "eepro100" */ + char version[32]; /* driver version string */ + char fw_version[ETHTOOL_FWVERS_LEN]; /* firmware version string */ + char bus_info[ETHTOOL_BUSINFO_LEN]; /* Bus info for this IF. */ + /* For PCI devices, use pci_name(pci_dev). */ + char reserved1[32]; + char reserved2[12]; + /* + * Some struct members below are filled in + * using ops->get_sset_count(). Obtaining + * this info from ethtool_drvinfo is now + * deprecated; Use ETHTOOL_GSSET_INFO + * instead. + */ + __u32 n_priv_flags; /* number of flags valid in ETHTOOL_GPFLAGS */ + __u32 n_stats; /* number of u64's from ETHTOOL_GSTATS */ + __u32 testinfo_len; + __u32 eedump_len; /* Size of data from ETHTOOL_GEEPROM (bytes) */ + __u32 regdump_len; /* Size of data from ETHTOOL_GREGS (bytes) */ +}; + +#define SOPASS_MAX 6 +/* wake-on-lan settings */ +struct ethtool_wolinfo { + __u32 cmd; + __u32 supported; + __u32 wolopts; + __u8 sopass[SOPASS_MAX]; /* SecureOn(tm) password */ +}; + +/* for passing single values */ +struct ethtool_value { + __u32 cmd; + __u32 data; +}; + +/* for passing big chunks of data */ +struct ethtool_regs { + __u32 cmd; + __u32 version; /* driver-specific, indicates different chips/revs */ + __u32 len; /* bytes */ + __u8 data[0]; +}; + +/* for passing EEPROM chunks */ +struct ethtool_eeprom { + __u32 cmd; + __u32 magic; + __u32 offset; /* in bytes */ + __u32 len; /* in bytes */ + __u8 data[0]; +}; + +/** + * struct ethtool_eee - Energy Efficient Ethernet information + * @cmd: ETHTOOL_{G,S}EEE + * @supported: Mask of %SUPPORTED_* flags for the speed/duplex combinations + * for which there is EEE support. + * @advertised: Mask of %ADVERTISED_* flags for the speed/duplex combinations + * advertised as eee capable. + * @lp_advertised: Mask of %ADVERTISED_* flags for the speed/duplex + * combinations advertised by the link partner as eee capable. + * @eee_active: Result of the eee auto negotiation. + * @eee_enabled: EEE configured mode (enabled/disabled). + * @tx_lpi_enabled: Whether the interface should assert its tx lpi, given + * that eee was negotiated. + * @tx_lpi_timer: Time in microseconds the interface delays prior to asserting + * its tx lpi (after reaching 'idle' state). Effective only when eee + * was negotiated and tx_lpi_enabled was set. + */ +struct ethtool_eee { + __u32 cmd; + __u32 supported; + __u32 advertised; + __u32 lp_advertised; + __u32 eee_active; + __u32 eee_enabled; + __u32 tx_lpi_enabled; + __u32 tx_lpi_timer; + __u32 reserved[2]; +}; + +/** + * struct ethtool_modinfo - plugin module eeprom information + * @cmd: %ETHTOOL_GMODULEINFO + * @type: Standard the module information conforms to %ETH_MODULE_SFF_xxxx + * @eeprom_len: Length of the eeprom + * + * This structure is used to return the information to + * properly size memory for a subsequent call to %ETHTOOL_GMODULEEEPROM. + * The type code indicates the eeprom data format + */ +struct ethtool_modinfo { + __u32 cmd; + __u32 type; + __u32 eeprom_len; + __u32 reserved[8]; +}; + +/** + * struct ethtool_coalesce - coalescing parameters for IRQs and stats updates + * @cmd: ETHTOOL_{G,S}COALESCE + * @rx_coalesce_usecs: How many usecs to delay an RX interrupt after + * a packet arrives. + * @rx_max_coalesced_frames: Maximum number of packets to receive + * before an RX interrupt. + * @rx_coalesce_usecs_irq: Same as @rx_coalesce_usecs, except that + * this value applies while an IRQ is being serviced by the host. + * @rx_max_coalesced_frames_irq: Same as @rx_max_coalesced_frames, + * except that this value applies while an IRQ is being serviced + * by the host. + * @tx_coalesce_usecs: How many usecs to delay a TX interrupt after + * a packet is sent. + * @tx_max_coalesced_frames: Maximum number of packets to be sent + * before a TX interrupt. + * @tx_coalesce_usecs_irq: Same as @tx_coalesce_usecs, except that + * this value applies while an IRQ is being serviced by the host. + * @tx_max_coalesced_frames_irq: Same as @tx_max_coalesced_frames, + * except that this value applies while an IRQ is being serviced + * by the host. + * @stats_block_coalesce_usecs: How many usecs to delay in-memory + * statistics block updates. Some drivers do not have an + * in-memory statistic block, and in such cases this value is + * ignored. This value must not be zero. + * @use_adaptive_rx_coalesce: Enable adaptive RX coalescing. + * @use_adaptive_tx_coalesce: Enable adaptive TX coalescing. + * @pkt_rate_low: Threshold for low packet rate (packets per second). + * @rx_coalesce_usecs_low: How many usecs to delay an RX interrupt after + * a packet arrives, when the packet rate is below @pkt_rate_low. + * @rx_max_coalesced_frames_low: Maximum number of packets to be received + * before an RX interrupt, when the packet rate is below @pkt_rate_low. + * @tx_coalesce_usecs_low: How many usecs to delay a TX interrupt after + * a packet is sent, when the packet rate is below @pkt_rate_low. + * @tx_max_coalesced_frames_low: Maximum nuumber of packets to be sent before + * a TX interrupt, when the packet rate is below @pkt_rate_low. + * @pkt_rate_high: Threshold for high packet rate (packets per second). + * @rx_coalesce_usecs_high: How many usecs to delay an RX interrupt after + * a packet arrives, when the packet rate is above @pkt_rate_high. + * @rx_max_coalesced_frames_high: Maximum number of packets to be received + * before an RX interrupt, when the packet rate is above @pkt_rate_high. + * @tx_coalesce_usecs_high: How many usecs to delay a TX interrupt after + * a packet is sent, when the packet rate is above @pkt_rate_high. + * @tx_max_coalesced_frames_high: Maximum number of packets to be sent before + * a TX interrupt, when the packet rate is above @pkt_rate_high. + * @rate_sample_interval: How often to do adaptive coalescing packet rate + * sampling, measured in seconds. Must not be zero. + * + * Each pair of (usecs, max_frames) fields specifies this exit + * condition for interrupt coalescing: + * (usecs > 0 && time_since_first_completion >= usecs) || + * (max_frames > 0 && completed_frames >= max_frames) + * It is illegal to set both usecs and max_frames to zero as this + * would cause interrupts to never be generated. To disable + * coalescing, set usecs = 0 and max_frames = 1. + * + * Some implementations ignore the value of max_frames and use the + * condition: + * time_since_first_completion >= usecs + * This is deprecated. Drivers for hardware that does not support + * counting completions should validate that max_frames == !rx_usecs. + * + * Adaptive RX/TX coalescing is an algorithm implemented by some + * drivers to improve latency under low packet rates and improve + * throughput under high packet rates. Some drivers only implement + * one of RX or TX adaptive coalescing. Anything not implemented by + * the driver causes these values to be silently ignored. + * + * When the packet rate is below @pkt_rate_high but above + * @pkt_rate_low (both measured in packets per second) the + * normal {rx,tx}_* coalescing parameters are used. + */ +struct ethtool_coalesce { + __u32 cmd; + __u32 rx_coalesce_usecs; + __u32 rx_max_coalesced_frames; + __u32 rx_coalesce_usecs_irq; + __u32 rx_max_coalesced_frames_irq; + __u32 tx_coalesce_usecs; + __u32 tx_max_coalesced_frames; + __u32 tx_coalesce_usecs_irq; + __u32 tx_max_coalesced_frames_irq; + __u32 stats_block_coalesce_usecs; + __u32 use_adaptive_rx_coalesce; + __u32 use_adaptive_tx_coalesce; + __u32 pkt_rate_low; + __u32 rx_coalesce_usecs_low; + __u32 rx_max_coalesced_frames_low; + __u32 tx_coalesce_usecs_low; + __u32 tx_max_coalesced_frames_low; + __u32 pkt_rate_high; + __u32 rx_coalesce_usecs_high; + __u32 rx_max_coalesced_frames_high; + __u32 tx_coalesce_usecs_high; + __u32 tx_max_coalesced_frames_high; + __u32 rate_sample_interval; +}; + +/* for configuring RX/TX ring parameters */ +struct ethtool_ringparam { + __u32 cmd; /* ETHTOOL_{G,S}RINGPARAM */ + + /* Read only attributes. These indicate the maximum number + * of pending RX/TX ring entries the driver will allow the + * user to set. + */ + __u32 rx_max_pending; + __u32 rx_mini_max_pending; + __u32 rx_jumbo_max_pending; + __u32 tx_max_pending; + + /* Values changeable by the user. The valid values are + * in the range 1 to the "*_max_pending" counterpart above. + */ + __u32 rx_pending; + __u32 rx_mini_pending; + __u32 rx_jumbo_pending; + __u32 tx_pending; +}; + +/** + * struct ethtool_channels - configuring number of network channel + * @cmd: ETHTOOL_{G,S}CHANNELS + * @max_rx: Read only. Maximum number of receive channel the driver support. + * @max_tx: Read only. Maximum number of transmit channel the driver support. + * @max_other: Read only. Maximum number of other channel the driver support. + * @max_combined: Read only. Maximum number of combined channel the driver + * support. Set of queues RX, TX or other. + * @rx_count: Valid values are in the range 1 to the max_rx. + * @tx_count: Valid values are in the range 1 to the max_tx. + * @other_count: Valid values are in the range 1 to the max_other. + * @combined_count: Valid values are in the range 1 to the max_combined. + * + * This can be used to configure RX, TX and other channels. + */ + +struct ethtool_channels { + __u32 cmd; + __u32 max_rx; + __u32 max_tx; + __u32 max_other; + __u32 max_combined; + __u32 rx_count; + __u32 tx_count; + __u32 other_count; + __u32 combined_count; +}; + +/* for configuring link flow control parameters */ +struct ethtool_pauseparam { + __u32 cmd; /* ETHTOOL_{G,S}PAUSEPARAM */ + + /* If the link is being auto-negotiated (via ethtool_cmd.autoneg + * being true) the user may set 'autoneg' here non-zero to have the + * pause parameters be auto-negotiated too. In such a case, the + * {rx,tx}_pause values below determine what capabilities are + * advertised. + * + * If 'autoneg' is zero or the link is not being auto-negotiated, + * then {rx,tx}_pause force the driver to use/not-use pause + * flow control. + */ + __u32 autoneg; + __u32 rx_pause; + __u32 tx_pause; +}; + +#define ETH_GSTRING_LEN 32 +enum ethtool_stringset { + ETH_SS_TEST = 0, + ETH_SS_STATS, + ETH_SS_PRIV_FLAGS, + ETH_SS_NTUPLE_FILTERS, /* Do not use, GRXNTUPLE is now deprecated */ + ETH_SS_FEATURES, +}; + +/* for passing string sets for data tagging */ +struct ethtool_gstrings { + __u32 cmd; /* ETHTOOL_GSTRINGS */ + __u32 string_set; /* string set id e.c. ETH_SS_TEST, etc*/ + __u32 len; /* number of strings in the string set */ + __u8 data[0]; +}; + +struct ethtool_sset_info { + __u32 cmd; /* ETHTOOL_GSSET_INFO */ + __u32 reserved; + __u64 sset_mask; /* input: each bit selects an sset to query */ + /* output: each bit a returned sset */ + __u32 data[0]; /* ETH_SS_xxx count, in order, based on bits + in sset_mask. One bit implies one + __u32, two bits implies two + __u32's, etc. */ +}; + +/** + * enum ethtool_test_flags - flags definition of ethtool_test + * @ETH_TEST_FL_OFFLINE: if set perform online and offline tests, otherwise + * only online tests. + * @ETH_TEST_FL_FAILED: Driver set this flag if test fails. + * @ETH_TEST_FL_EXTERNAL_LB: Application request to perform external loopback + * test. + * @ETH_TEST_FL_EXTERNAL_LB_DONE: Driver performed the external loopback test + */ + +enum ethtool_test_flags { + ETH_TEST_FL_OFFLINE = (1 << 0), + ETH_TEST_FL_FAILED = (1 << 1), + ETH_TEST_FL_EXTERNAL_LB = (1 << 2), + ETH_TEST_FL_EXTERNAL_LB_DONE = (1 << 3), +}; + +/* for requesting NIC test and getting results*/ +struct ethtool_test { + __u32 cmd; /* ETHTOOL_TEST */ + __u32 flags; /* ETH_TEST_FL_xxx */ + __u32 reserved; + __u32 len; /* result length, in number of u64 elements */ + __u64 data[0]; +}; + +/* for dumping NIC-specific statistics */ +struct ethtool_stats { + __u32 cmd; /* ETHTOOL_GSTATS */ + __u32 n_stats; /* number of u64's being returned */ + __u64 data[0]; +}; + +struct ethtool_perm_addr { + __u32 cmd; /* ETHTOOL_GPERMADDR */ + __u32 size; + __u8 data[0]; +}; + +/* boolean flags controlling per-interface behavior characteristics. + * When reading, the flag indicates whether or not a certain behavior + * is enabled/present. When writing, the flag indicates whether + * or not the driver should turn on (set) or off (clear) a behavior. + * + * Some behaviors may read-only (unconditionally absent or present). + * If such is the case, return EINVAL in the set-flags operation if the + * flag differs from the read-only value. + */ +enum ethtool_flags { + ETH_FLAG_TXVLAN = (1 << 7), /* TX VLAN offload enabled */ + ETH_FLAG_RXVLAN = (1 << 8), /* RX VLAN offload enabled */ + ETH_FLAG_LRO = (1 << 15), /* LRO is enabled */ + ETH_FLAG_NTUPLE = (1 << 27), /* N-tuple filters enabled */ + ETH_FLAG_RXHASH = (1 << 28), +}; + +/* The following structures are for supporting RX network flow + * classification and RX n-tuple configuration. Note, all multibyte + * fields, e.g., ip4src, ip4dst, psrc, pdst, spi, etc. are expected to + * be in network byte order. + */ + +/** + * struct ethtool_tcpip4_spec - flow specification for TCP/IPv4 etc. + * @ip4src: Source host + * @ip4dst: Destination host + * @psrc: Source port + * @pdst: Destination port + * @tos: Type-of-service + * + * This can be used to specify a TCP/IPv4, UDP/IPv4 or SCTP/IPv4 flow. + */ +struct ethtool_tcpip4_spec { + __be32 ip4src; + __be32 ip4dst; + __be16 psrc; + __be16 pdst; + __u8 tos; +}; + +/** + * struct ethtool_ah_espip4_spec - flow specification for IPsec/IPv4 + * @ip4src: Source host + * @ip4dst: Destination host + * @spi: Security parameters index + * @tos: Type-of-service + * + * This can be used to specify an IPsec transport or tunnel over IPv4. + */ +struct ethtool_ah_espip4_spec { + __be32 ip4src; + __be32 ip4dst; + __be32 spi; + __u8 tos; +}; + +#define ETH_RX_NFC_IP4 1 + +/** + * struct ethtool_usrip4_spec - general flow specification for IPv4 + * @ip4src: Source host + * @ip4dst: Destination host + * @l4_4_bytes: First 4 bytes of transport (layer 4) header + * @tos: Type-of-service + * @ip_ver: Value must be %ETH_RX_NFC_IP4; mask must be 0 + * @proto: Transport protocol number; mask must be 0 + */ +struct ethtool_usrip4_spec { + __be32 ip4src; + __be32 ip4dst; + __be32 l4_4_bytes; + __u8 tos; + __u8 ip_ver; + __u8 proto; +}; + +union ethtool_flow_union { + struct ethtool_tcpip4_spec tcp_ip4_spec; + struct ethtool_tcpip4_spec udp_ip4_spec; + struct ethtool_tcpip4_spec sctp_ip4_spec; + struct ethtool_ah_espip4_spec ah_ip4_spec; + struct ethtool_ah_espip4_spec esp_ip4_spec; + struct ethtool_usrip4_spec usr_ip4_spec; + struct ethhdr ether_spec; + __u8 hdata[52]; +}; + +/** + * struct ethtool_flow_ext - additional RX flow fields + * @h_dest: destination MAC address + * @vlan_etype: VLAN EtherType + * @vlan_tci: VLAN tag control information + * @data: user defined data + * + * Note, @vlan_etype, @vlan_tci, and @data are only valid if %FLOW_EXT + * is set in &struct ethtool_rx_flow_spec @flow_type. + * @h_dest is valid if %FLOW_MAC_EXT is set. + */ +struct ethtool_flow_ext { + __u8 padding[2]; + unsigned char h_dest[ETH_ALEN]; + __be16 vlan_etype; + __be16 vlan_tci; + __be32 data[2]; +}; + +/** + * struct ethtool_rx_flow_spec - classification rule for RX flows + * @flow_type: Type of match to perform, e.g. %TCP_V4_FLOW + * @h_u: Flow fields to match (dependent on @flow_type) + * @h_ext: Additional fields to match + * @m_u: Masks for flow field bits to be matched + * @m_ext: Masks for additional field bits to be matched + * Note, all additional fields must be ignored unless @flow_type + * includes the %FLOW_EXT or %FLOW_MAC_EXT flag + * (see &struct ethtool_flow_ext description). + * @ring_cookie: RX ring/queue index to deliver to, or %RX_CLS_FLOW_DISC + * if packets should be discarded + * @location: Location of rule in the table. Locations must be + * numbered such that a flow matching multiple rules will be + * classified according to the first (lowest numbered) rule. + */ +struct ethtool_rx_flow_spec { + __u32 flow_type; + union ethtool_flow_union h_u; + struct ethtool_flow_ext h_ext; + union ethtool_flow_union m_u; + struct ethtool_flow_ext m_ext; + __u64 ring_cookie; + __u32 location; +}; + +/** + * struct ethtool_rxnfc - command to get or set RX flow classification rules + * @cmd: Specific command number - %ETHTOOL_GRXFH, %ETHTOOL_SRXFH, + * %ETHTOOL_GRXRINGS, %ETHTOOL_GRXCLSRLCNT, %ETHTOOL_GRXCLSRULE, + * %ETHTOOL_GRXCLSRLALL, %ETHTOOL_SRXCLSRLDEL or %ETHTOOL_SRXCLSRLINS + * @flow_type: Type of flow to be affected, e.g. %TCP_V4_FLOW + * @data: Command-dependent value + * @fs: Flow classification rule + * @rule_cnt: Number of rules to be affected + * @rule_locs: Array of used rule locations + * + * For %ETHTOOL_GRXFH and %ETHTOOL_SRXFH, @data is a bitmask indicating + * the fields included in the flow hash, e.g. %RXH_IP_SRC. The following + * structure fields must not be used. + * + * For %ETHTOOL_GRXRINGS, @data is set to the number of RX rings/queues + * on return. + * + * For %ETHTOOL_GRXCLSRLCNT, @rule_cnt is set to the number of defined + * rules on return. If @data is non-zero on return then it is the + * size of the rule table, plus the flag %RX_CLS_LOC_SPECIAL if the + * driver supports any special location values. If that flag is not + * set in @data then special location values should not be used. + * + * For %ETHTOOL_GRXCLSRULE, @fs.@location specifies the location of an + * existing rule on entry and @fs contains the rule on return. + * + * For %ETHTOOL_GRXCLSRLALL, @rule_cnt specifies the array size of the + * user buffer for @rule_locs on entry. On return, @data is the size + * of the rule table, @rule_cnt is the number of defined rules, and + * @rule_locs contains the locations of the defined rules. Drivers + * must use the second parameter to get_rxnfc() instead of @rule_locs. + * + * For %ETHTOOL_SRXCLSRLINS, @fs specifies the rule to add or update. + * @fs.@location either specifies the location to use or is a special + * location value with %RX_CLS_LOC_SPECIAL flag set. On return, + * @fs.@location is the actual rule location. + * + * For %ETHTOOL_SRXCLSRLDEL, @fs.@location specifies the location of an + * existing rule on entry. + * + * A driver supporting the special location values for + * %ETHTOOL_SRXCLSRLINS may add the rule at any suitable unused + * location, and may remove a rule at a later location (lower + * priority) that matches exactly the same set of flows. The special + * values are: %RX_CLS_LOC_ANY, selecting any location; + * %RX_CLS_LOC_FIRST, selecting the first suitable location (maximum + * priority); and %RX_CLS_LOC_LAST, selecting the last suitable + * location (minimum priority). Additional special values may be + * defined in future and drivers must return -%EINVAL for any + * unrecognised value. + */ +struct ethtool_rxnfc { + __u32 cmd; + __u32 flow_type; + __u64 data; + struct ethtool_rx_flow_spec fs; + __u32 rule_cnt; + __u32 rule_locs[0]; +}; + + +/** + * struct ethtool_rxfh_indir - command to get or set RX flow hash indirection + * @cmd: Specific command number - %ETHTOOL_GRXFHINDIR or %ETHTOOL_SRXFHINDIR + * @size: On entry, the array size of the user buffer, which may be zero. + * On return from %ETHTOOL_GRXFHINDIR, the array size of the hardware + * indirection table. + * @ring_index: RX ring/queue index for each hash value + * + * For %ETHTOOL_GRXFHINDIR, a @size of zero means that only the size + * should be returned. For %ETHTOOL_SRXFHINDIR, a @size of zero means + * the table should be reset to default values. This last feature + * is not supported by the original implementations. + */ +struct ethtool_rxfh_indir { + __u32 cmd; + __u32 size; + __u32 ring_index[0]; +}; + +/** + * struct ethtool_rx_ntuple_flow_spec - specification for RX flow filter + * @flow_type: Type of match to perform, e.g. %TCP_V4_FLOW + * @h_u: Flow field values to match (dependent on @flow_type) + * @m_u: Masks for flow field value bits to be ignored + * @vlan_tag: VLAN tag to match + * @vlan_tag_mask: Mask for VLAN tag bits to be ignored + * @data: Driver-dependent data to match + * @data_mask: Mask for driver-dependent data bits to be ignored + * @action: RX ring/queue index to deliver to (non-negative) or other action + * (negative, e.g. %ETHTOOL_RXNTUPLE_ACTION_DROP) + * + * For flow types %TCP_V4_FLOW, %UDP_V4_FLOW and %SCTP_V4_FLOW, where + * a field value and mask are both zero this is treated as if all mask + * bits are set i.e. the field is ignored. + */ +struct ethtool_rx_ntuple_flow_spec { + __u32 flow_type; + union { + struct ethtool_tcpip4_spec tcp_ip4_spec; + struct ethtool_tcpip4_spec udp_ip4_spec; + struct ethtool_tcpip4_spec sctp_ip4_spec; + struct ethtool_ah_espip4_spec ah_ip4_spec; + struct ethtool_ah_espip4_spec esp_ip4_spec; + struct ethtool_usrip4_spec usr_ip4_spec; + struct ethhdr ether_spec; + __u8 hdata[72]; + } h_u, m_u; + + __u16 vlan_tag; + __u16 vlan_tag_mask; + __u64 data; + __u64 data_mask; + + __s32 action; +#define ETHTOOL_RXNTUPLE_ACTION_DROP (-1) /* drop packet */ +#define ETHTOOL_RXNTUPLE_ACTION_CLEAR (-2) /* clear filter */ +}; + +/** + * struct ethtool_rx_ntuple - command to set or clear RX flow filter + * @cmd: Command number - %ETHTOOL_SRXNTUPLE + * @fs: Flow filter specification + */ +struct ethtool_rx_ntuple { + __u32 cmd; + struct ethtool_rx_ntuple_flow_spec fs; +}; + +#define ETHTOOL_FLASH_MAX_FILENAME 128 +enum ethtool_flash_op_type { + ETHTOOL_FLASH_ALL_REGIONS = 0, +}; + +/* for passing firmware flashing related parameters */ +struct ethtool_flash { + __u32 cmd; + __u32 region; + char data[ETHTOOL_FLASH_MAX_FILENAME]; +}; + +/** + * struct ethtool_dump - used for retrieving, setting device dump + * @cmd: Command number - %ETHTOOL_GET_DUMP_FLAG, %ETHTOOL_GET_DUMP_DATA, or + * %ETHTOOL_SET_DUMP + * @version: FW version of the dump, filled in by driver + * @flag: driver dependent flag for dump setting, filled in by driver during + * get and filled in by ethtool for set operation. + * flag must be initialized by macro ETH_FW_DUMP_DISABLE value when + * firmware dump is disabled. + * @len: length of dump data, used as the length of the user buffer on entry to + * %ETHTOOL_GET_DUMP_DATA and this is returned as dump length by driver + * for %ETHTOOL_GET_DUMP_FLAG command + * @data: data collected for get dump data operation + */ + +#define ETH_FW_DUMP_DISABLE 0 + +struct ethtool_dump { + __u32 cmd; + __u32 version; + __u32 flag; + __u32 len; + __u8 data[0]; +}; + +/* for returning and changing feature sets */ + +/** + * struct ethtool_get_features_block - block with state of 32 features + * @available: mask of changeable features + * @requested: mask of features requested to be enabled if possible + * @active: mask of currently enabled features + * @never_changed: mask of features not changeable for any device + */ +struct ethtool_get_features_block { + __u32 available; + __u32 requested; + __u32 active; + __u32 never_changed; +}; + +/** + * struct ethtool_gfeatures - command to get state of device's features + * @cmd: command number = %ETHTOOL_GFEATURES + * @size: in: number of elements in the features[] array; + * out: number of elements in features[] needed to hold all features + * @features: state of features + */ +struct ethtool_gfeatures { + __u32 cmd; + __u32 size; + struct ethtool_get_features_block features[0]; +}; + +/** + * struct ethtool_set_features_block - block with request for 32 features + * @valid: mask of features to be changed + * @requested: values of features to be changed + */ +struct ethtool_set_features_block { + __u32 valid; + __u32 requested; +}; + +/** + * struct ethtool_sfeatures - command to request change in device's features + * @cmd: command number = %ETHTOOL_SFEATURES + * @size: array size of the features[] array + * @features: feature change masks + */ +struct ethtool_sfeatures { + __u32 cmd; + __u32 size; + struct ethtool_set_features_block features[0]; +}; + +/** + * struct ethtool_ts_info - holds a device's timestamping and PHC association + * @cmd: command number = %ETHTOOL_GET_TS_INFO + * @so_timestamping: bit mask of the sum of the supported SO_TIMESTAMPING flags + * @phc_index: device index of the associated PHC, or -1 if there is none + * @tx_types: bit mask of the supported hwtstamp_tx_types enumeration values + * @rx_filters: bit mask of the supported hwtstamp_rx_filters enumeration values + * + * The bits in the 'tx_types' and 'rx_filters' fields correspond to + * the 'hwtstamp_tx_types' and 'hwtstamp_rx_filters' enumeration values, + * respectively. For example, if the device supports HWTSTAMP_TX_ON, + * then (1 << HWTSTAMP_TX_ON) in 'tx_types' will be set. + */ +struct ethtool_ts_info { + __u32 cmd; + __u32 so_timestamping; + __s32 phc_index; + __u32 tx_types; + __u32 tx_reserved[3]; + __u32 rx_filters; + __u32 rx_reserved[3]; +}; + +/* + * %ETHTOOL_SFEATURES changes features present in features[].valid to the + * values of corresponding bits in features[].requested. Bits in .requested + * not set in .valid or not changeable are ignored. + * + * Returns %EINVAL when .valid contains undefined or never-changeable bits + * or size is not equal to required number of features words (32-bit blocks). + * Returns >= 0 if request was completed; bits set in the value mean: + * %ETHTOOL_F_UNSUPPORTED - there were bits set in .valid that are not + * changeable (not present in %ETHTOOL_GFEATURES' features[].available) + * those bits were ignored. + * %ETHTOOL_F_WISH - some or all changes requested were recorded but the + * resulting state of bits masked by .valid is not equal to .requested. + * Probably there are other device-specific constraints on some features + * in the set. When %ETHTOOL_F_UNSUPPORTED is set, .valid is considered + * here as though ignored bits were cleared. + * %ETHTOOL_F_COMPAT - some or all changes requested were made by calling + * compatibility functions. Requested offload state cannot be properly + * managed by kernel. + * + * Meaning of bits in the masks are obtained by %ETHTOOL_GSSET_INFO (number of + * bits in the arrays - always multiple of 32) and %ETHTOOL_GSTRINGS commands + * for ETH_SS_FEATURES string set. First entry in the table corresponds to least + * significant bit in features[0] fields. Empty strings mark undefined features. + */ +enum ethtool_sfeatures_retval_bits { + ETHTOOL_F_UNSUPPORTED__BIT, + ETHTOOL_F_WISH__BIT, + ETHTOOL_F_COMPAT__BIT, +}; + +#define ETHTOOL_F_UNSUPPORTED (1 << ETHTOOL_F_UNSUPPORTED__BIT) +#define ETHTOOL_F_WISH (1 << ETHTOOL_F_WISH__BIT) +#define ETHTOOL_F_COMPAT (1 << ETHTOOL_F_COMPAT__BIT) + + +/* CMDs currently supported */ +#define ETHTOOL_GSET 0x00000001 /* Get settings. */ +#define ETHTOOL_SSET 0x00000002 /* Set settings. */ +#define ETHTOOL_GDRVINFO 0x00000003 /* Get driver info. */ +#define ETHTOOL_GREGS 0x00000004 /* Get NIC registers. */ +#define ETHTOOL_GWOL 0x00000005 /* Get wake-on-lan options. */ +#define ETHTOOL_SWOL 0x00000006 /* Set wake-on-lan options. */ +#define ETHTOOL_GMSGLVL 0x00000007 /* Get driver message level */ +#define ETHTOOL_SMSGLVL 0x00000008 /* Set driver msg level. */ +#define ETHTOOL_NWAY_RST 0x00000009 /* Restart autonegotiation. */ +/* Get link status for host, i.e. whether the interface *and* the + * physical port (if there is one) are up (ethtool_value). */ +#define ETHTOOL_GLINK 0x0000000a +#define ETHTOOL_GEEPROM 0x0000000b /* Get EEPROM data */ +#define ETHTOOL_SEEPROM 0x0000000c /* Set EEPROM data. */ +#define ETHTOOL_GCOALESCE 0x0000000e /* Get coalesce config */ +#define ETHTOOL_SCOALESCE 0x0000000f /* Set coalesce config. */ +#define ETHTOOL_GRINGPARAM 0x00000010 /* Get ring parameters */ +#define ETHTOOL_SRINGPARAM 0x00000011 /* Set ring parameters. */ +#define ETHTOOL_GPAUSEPARAM 0x00000012 /* Get pause parameters */ +#define ETHTOOL_SPAUSEPARAM 0x00000013 /* Set pause parameters. */ +#define ETHTOOL_GRXCSUM 0x00000014 /* Get RX hw csum enable (ethtool_value) */ +#define ETHTOOL_SRXCSUM 0x00000015 /* Set RX hw csum enable (ethtool_value) */ +#define ETHTOOL_GTXCSUM 0x00000016 /* Get TX hw csum enable (ethtool_value) */ +#define ETHTOOL_STXCSUM 0x00000017 /* Set TX hw csum enable (ethtool_value) */ +#define ETHTOOL_GSG 0x00000018 /* Get scatter-gather enable + * (ethtool_value) */ +#define ETHTOOL_SSG 0x00000019 /* Set scatter-gather enable + * (ethtool_value). */ +#define ETHTOOL_TEST 0x0000001a /* execute NIC self-test. */ +#define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */ +#define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */ +#define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */ +#define ETHTOOL_GTSO 0x0000001e /* Get TSO enable (ethtool_value) */ +#define ETHTOOL_STSO 0x0000001f /* Set TSO enable (ethtool_value) */ +#define ETHTOOL_GPERMADDR 0x00000020 /* Get permanent hardware address */ +#define ETHTOOL_GUFO 0x00000021 /* Get UFO enable (ethtool_value) */ +#define ETHTOOL_SUFO 0x00000022 /* Set UFO enable (ethtool_value) */ +#define ETHTOOL_GGSO 0x00000023 /* Get GSO enable (ethtool_value) */ +#define ETHTOOL_SGSO 0x00000024 /* Set GSO enable (ethtool_value) */ +#define ETHTOOL_GFLAGS 0x00000025 /* Get flags bitmap(ethtool_value) */ +#define ETHTOOL_SFLAGS 0x00000026 /* Set flags bitmap(ethtool_value) */ +#define ETHTOOL_GPFLAGS 0x00000027 /* Get driver-private flags bitmap */ +#define ETHTOOL_SPFLAGS 0x00000028 /* Set driver-private flags bitmap */ + +#define ETHTOOL_GRXFH 0x00000029 /* Get RX flow hash configuration */ +#define ETHTOOL_SRXFH 0x0000002a /* Set RX flow hash configuration */ +#define ETHTOOL_GGRO 0x0000002b /* Get GRO enable (ethtool_value) */ +#define ETHTOOL_SGRO 0x0000002c /* Set GRO enable (ethtool_value) */ +#define ETHTOOL_GRXRINGS 0x0000002d /* Get RX rings available for LB */ +#define ETHTOOL_GRXCLSRLCNT 0x0000002e /* Get RX class rule count */ +#define ETHTOOL_GRXCLSRULE 0x0000002f /* Get RX classification rule */ +#define ETHTOOL_GRXCLSRLALL 0x00000030 /* Get all RX classification rule */ +#define ETHTOOL_SRXCLSRLDEL 0x00000031 /* Delete RX classification rule */ +#define ETHTOOL_SRXCLSRLINS 0x00000032 /* Insert RX classification rule */ +#define ETHTOOL_FLASHDEV 0x00000033 /* Flash firmware to device */ +#define ETHTOOL_RESET 0x00000034 /* Reset hardware */ +#define ETHTOOL_SRXNTUPLE 0x00000035 /* Add an n-tuple filter to device */ +#define ETHTOOL_GRXNTUPLE 0x00000036 /* deprecated */ +#define ETHTOOL_GSSET_INFO 0x00000037 /* Get string set info */ +#define ETHTOOL_GRXFHINDIR 0x00000038 /* Get RX flow hash indir'n table */ +#define ETHTOOL_SRXFHINDIR 0x00000039 /* Set RX flow hash indir'n table */ + +#define ETHTOOL_GFEATURES 0x0000003a /* Get device offload settings */ +#define ETHTOOL_SFEATURES 0x0000003b /* Change device offload settings */ +#define ETHTOOL_GCHANNELS 0x0000003c /* Get no of channels */ +#define ETHTOOL_SCHANNELS 0x0000003d /* Set no of channels */ +#define ETHTOOL_SET_DUMP 0x0000003e /* Set dump settings */ +#define ETHTOOL_GET_DUMP_FLAG 0x0000003f /* Get dump settings */ +#define ETHTOOL_GET_DUMP_DATA 0x00000040 /* Get dump data */ +#define ETHTOOL_GET_TS_INFO 0x00000041 /* Get time stamping and PHC info */ +#define ETHTOOL_GMODULEINFO 0x00000042 /* Get plug-in module information */ +#define ETHTOOL_GMODULEEEPROM 0x00000043 /* Get plug-in module eeprom */ +#define ETHTOOL_GEEE 0x00000044 /* Get EEE settings */ +#define ETHTOOL_SEEE 0x00000045 /* Set EEE settings */ + +/* compatibility with older code */ +#define SPARC_ETH_GSET ETHTOOL_GSET +#define SPARC_ETH_SSET ETHTOOL_SSET + +/* Indicates what features are supported by the interface. */ +#define SUPPORTED_10baseT_Half (1 << 0) +#define SUPPORTED_10baseT_Full (1 << 1) +#define SUPPORTED_100baseT_Half (1 << 2) +#define SUPPORTED_100baseT_Full (1 << 3) +#define SUPPORTED_1000baseT_Half (1 << 4) +#define SUPPORTED_1000baseT_Full (1 << 5) +#define SUPPORTED_Autoneg (1 << 6) +#define SUPPORTED_TP (1 << 7) +#define SUPPORTED_AUI (1 << 8) +#define SUPPORTED_MII (1 << 9) +#define SUPPORTED_FIBRE (1 << 10) +#define SUPPORTED_BNC (1 << 11) +#define SUPPORTED_10000baseT_Full (1 << 12) +#define SUPPORTED_Pause (1 << 13) +#define SUPPORTED_Asym_Pause (1 << 14) +#define SUPPORTED_2500baseX_Full (1 << 15) +#define SUPPORTED_Backplane (1 << 16) +#define SUPPORTED_1000baseKX_Full (1 << 17) +#define SUPPORTED_10000baseKX4_Full (1 << 18) +#define SUPPORTED_10000baseKR_Full (1 << 19) +#define SUPPORTED_10000baseR_FEC (1 << 20) +#define SUPPORTED_20000baseMLD2_Full (1 << 21) +#define SUPPORTED_20000baseKR2_Full (1 << 22) +#define SUPPORTED_40000baseKR4_Full (1 << 23) +#define SUPPORTED_40000baseCR4_Full (1 << 24) +#define SUPPORTED_40000baseSR4_Full (1 << 25) +#define SUPPORTED_40000baseLR4_Full (1 << 26) + +/* Indicates what features are advertised by the interface. */ +#define ADVERTISED_10baseT_Half (1 << 0) +#define ADVERTISED_10baseT_Full (1 << 1) +#define ADVERTISED_100baseT_Half (1 << 2) +#define ADVERTISED_100baseT_Full (1 << 3) +#define ADVERTISED_1000baseT_Half (1 << 4) +#define ADVERTISED_1000baseT_Full (1 << 5) +#define ADVERTISED_Autoneg (1 << 6) +#define ADVERTISED_TP (1 << 7) +#define ADVERTISED_AUI (1 << 8) +#define ADVERTISED_MII (1 << 9) +#define ADVERTISED_FIBRE (1 << 10) +#define ADVERTISED_BNC (1 << 11) +#define ADVERTISED_10000baseT_Full (1 << 12) +#define ADVERTISED_Pause (1 << 13) +#define ADVERTISED_Asym_Pause (1 << 14) +#define ADVERTISED_2500baseX_Full (1 << 15) +#define ADVERTISED_Backplane (1 << 16) +#define ADVERTISED_1000baseKX_Full (1 << 17) +#define ADVERTISED_10000baseKX4_Full (1 << 18) +#define ADVERTISED_10000baseKR_Full (1 << 19) +#define ADVERTISED_10000baseR_FEC (1 << 20) +#define ADVERTISED_20000baseMLD2_Full (1 << 21) +#define ADVERTISED_20000baseKR2_Full (1 << 22) +#define ADVERTISED_40000baseKR4_Full (1 << 23) +#define ADVERTISED_40000baseCR4_Full (1 << 24) +#define ADVERTISED_40000baseSR4_Full (1 << 25) +#define ADVERTISED_40000baseLR4_Full (1 << 26) + +/* The following are all involved in forcing a particular link + * mode for the device for setting things. When getting the + * devices settings, these indicate the current mode and whether + * it was forced up into this mode or autonegotiated. + */ + +/* The forced speed, 10Mb, 100Mb, gigabit, 2.5Gb, 10GbE. */ +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define SPEED_2500 2500 +#define SPEED_10000 10000 +#define SPEED_UNKNOWN -1 + +/* Duplex, half or full. */ +#define DUPLEX_HALF 0x00 +#define DUPLEX_FULL 0x01 +#define DUPLEX_UNKNOWN 0xff + +/* Which connector port. */ +#define PORT_TP 0x00 +#define PORT_AUI 0x01 +#define PORT_MII 0x02 +#define PORT_FIBRE 0x03 +#define PORT_BNC 0x04 +#define PORT_DA 0x05 +#define PORT_NONE 0xef +#define PORT_OTHER 0xff + +/* Which transceiver to use. */ +#define XCVR_INTERNAL 0x00 +#define XCVR_EXTERNAL 0x01 +#define XCVR_DUMMY1 0x02 +#define XCVR_DUMMY2 0x03 +#define XCVR_DUMMY3 0x04 + +/* Enable or disable autonegotiation. If this is set to enable, + * the forced link modes above are completely ignored. + */ +#define AUTONEG_DISABLE 0x00 +#define AUTONEG_ENABLE 0x01 + +/* MDI or MDI-X status/control - if MDI/MDI_X/AUTO is set then + * the driver is required to renegotiate link + */ +#define ETH_TP_MDI_INVALID 0x00 /* status: unknown; control: unsupported */ +#define ETH_TP_MDI 0x01 /* status: MDI; control: force MDI */ +#define ETH_TP_MDI_X 0x02 /* status: MDI-X; control: force MDI-X */ +#define ETH_TP_MDI_AUTO 0x03 /* control: auto-select */ + +/* Wake-On-Lan options. */ +#define WAKE_PHY (1 << 0) +#define WAKE_UCAST (1 << 1) +#define WAKE_MCAST (1 << 2) +#define WAKE_BCAST (1 << 3) +#define WAKE_ARP (1 << 4) +#define WAKE_MAGIC (1 << 5) +#define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ + +/* L2-L4 network traffic flow types */ +#define TCP_V4_FLOW 0x01 /* hash or spec (tcp_ip4_spec) */ +#define UDP_V4_FLOW 0x02 /* hash or spec (udp_ip4_spec) */ +#define SCTP_V4_FLOW 0x03 /* hash or spec (sctp_ip4_spec) */ +#define AH_ESP_V4_FLOW 0x04 /* hash only */ +#define TCP_V6_FLOW 0x05 /* hash only */ +#define UDP_V6_FLOW 0x06 /* hash only */ +#define SCTP_V6_FLOW 0x07 /* hash only */ +#define AH_ESP_V6_FLOW 0x08 /* hash only */ +#define AH_V4_FLOW 0x09 /* hash or spec (ah_ip4_spec) */ +#define ESP_V4_FLOW 0x0a /* hash or spec (esp_ip4_spec) */ +#define AH_V6_FLOW 0x0b /* hash only */ +#define ESP_V6_FLOW 0x0c /* hash only */ +#define IP_USER_FLOW 0x0d /* spec only (usr_ip4_spec) */ +#define IPV4_FLOW 0x10 /* hash only */ +#define IPV6_FLOW 0x11 /* hash only */ +#define ETHER_FLOW 0x12 /* spec only (ether_spec) */ +/* Flag to enable additional fields in struct ethtool_rx_flow_spec */ +#define FLOW_EXT 0x80000000 +#define FLOW_MAC_EXT 0x40000000 + +/* L3-L4 network traffic flow hash options */ +#define RXH_L2DA (1 << 1) +#define RXH_VLAN (1 << 2) +#define RXH_L3_PROTO (1 << 3) +#define RXH_IP_SRC (1 << 4) +#define RXH_IP_DST (1 << 5) +#define RXH_L4_B_0_1 (1 << 6) /* src port in case of TCP/UDP/SCTP */ +#define RXH_L4_B_2_3 (1 << 7) /* dst port in case of TCP/UDP/SCTP */ +#define RXH_DISCARD (1 << 31) + +#define RX_CLS_FLOW_DISC 0xffffffffffffffffULL + +/* Special RX classification rule insert location values */ +#define RX_CLS_LOC_SPECIAL 0x80000000 /* flag */ +#define RX_CLS_LOC_ANY 0xffffffff +#define RX_CLS_LOC_FIRST 0xfffffffe +#define RX_CLS_LOC_LAST 0xfffffffd + +/* EEPROM Standards for plug in modules */ +#define ETH_MODULE_SFF_8079 0x1 +#define ETH_MODULE_SFF_8079_LEN 256 +#define ETH_MODULE_SFF_8472 0x2 +#define ETH_MODULE_SFF_8472_LEN 512 + +/* Reset flags */ +/* The reset() operation must clear the flags for the components which + * were actually reset. On successful return, the flags indicate the + * components which were not reset, either because they do not exist + * in the hardware or because they cannot be reset independently. The + * driver must never reset any components that were not requested. + */ +enum ethtool_reset_flags { + /* These flags represent components dedicated to the interface + * the command is addressed to. Shift any flag left by + * ETH_RESET_SHARED_SHIFT to reset a shared component of the + * same type. + */ + ETH_RESET_MGMT = 1 << 0, /* Management processor */ + ETH_RESET_IRQ = 1 << 1, /* Interrupt requester */ + ETH_RESET_DMA = 1 << 2, /* DMA engine */ + ETH_RESET_FILTER = 1 << 3, /* Filtering/flow direction */ + ETH_RESET_OFFLOAD = 1 << 4, /* Protocol offload */ + ETH_RESET_MAC = 1 << 5, /* Media access controller */ + ETH_RESET_PHY = 1 << 6, /* Transceiver/PHY */ + ETH_RESET_RAM = 1 << 7, /* RAM shared between + * multiple components */ + + ETH_RESET_DEDICATED = 0x0000ffff, /* All components dedicated to + * this interface */ + ETH_RESET_ALL = 0xffffffff, /* All components used by this + * interface, even if shared */ +}; +#define ETH_RESET_SHARED_SHIFT 16 + +#endif /* _LINUX_ETHTOOL_H */ diff --git a/application/quectel_CM_5G/src/log/cdc_mbim.txt b/application/quectel_CM_5G/src/log/cdc_mbim.txt new file mode 100644 index 0000000..8acdfde --- /dev/null +++ b/application/quectel_CM_5G/src/log/cdc_mbim.txt @@ -0,0 +1,71 @@ +root@ZhuoTK:/# dmesg +[ 788.920000] usb 1-1.3: new high-speed USB device number 4 using ehci-platform +[ 789.160000] cdc_mbim 1-1.3:1.4: cdc-wdm0: USB WDM device +[ 789.170000] cdc_mbim 1-1.3:1.4 wwan0: register 'cdc_mbim' at usb-101c0000.ehci-1.3, CDC MBIM, a2:58:dc:4d:dd:ca + +root@ZhuoTK:/# quectel-CM -s cmnet & +[04-13_05:24:38:767] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_05:24:38:769] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x004 +[04-13_05:24:38:771] Auto find qmichannel = /dev/cdc-wdm0 +[04-13_05:24:38:771] Auto find usbnet_adapter = wwan0 +[04-13_05:24:38:771] netcard driver = cdc_mbim, driver version = 22-Aug-2005 +[04-13_05:24:38:771] Modem works in MBIM mode +[04-13_05:24:38:779] cdc_wdm_fd = 7 +[04-13_05:24:38:779] mbim_open_device() +[04-13_05:24:39:624] mbim_device_caps_query() +[04-13_05:24:39:656] DeviceId: 866758045439136 +[04-13_05:24:39:656] FirmwareInfo: EC25EFAR06A11M4G +[04-13_05:24:39:656] HardwareInfo: QUECTEL Mobile Broadband Modul +[04-13_05:24:39:657] mbim_device_services_query() +[04-13_05:24:39:688] mbim_set_radio_state( 1 ) +[04-13_05:24:39:721] HwRadioState: 1, SwRadioState: 1 +[04-13_05:24:39:721] mbim_subscriber_status_query() +[04-13_05:24:39:784] SubscriberId: 460028563800461 +[04-13_05:24:39:784] SimIccId: 89860015120716380461 +[04-13_05:24:39:785] SubscriberReadyState NotInitialized -> Initialized +[04-13_05:24:39:785] mbim_register_state_query() +[04-13_05:24:39:816] RegisterState Unknown -> Home +[04-13_05:24:39:816] mbim_packet_service_query() +[04-13_05:24:39:848] PacketServiceState Unknown -> Attached +[04-13_05:24:39:848] mbim_query_connect(sessionID=0) +[04-13_05:24:39:880] ActivationState Unknown -> Deactivated +[04-13_05:24:39:881] ifconfig wwan0 0.0.0.0 +[04-13_05:24:39:899] ifconfig wwan0 down +[04-13_05:24:39:913] mbim_set_connect(onoff=1, sessionID=0) +[04-13_05:24:39:976] ActivationState Deactivated -> Activated +[04-13_05:24:39:977] mbim_ip_config(sessionID=0) +[04-13_05:24:40:008] < SessionId = 0 +[04-13_05:24:40:008] < IPv4ConfigurationAvailable = 0xf +[04-13_05:24:40:008] < IPv6ConfigurationAvailable = 0x0 +[04-13_05:24:40:008] < IPv4AddressCount = 0x1 +[04-13_05:24:40:008] < IPv4AddressOffset = 0x3c +[04-13_05:24:40:009] < IPv6AddressCount = 0x0 +[04-13_05:24:40:009] < IPv6AddressOffset = 0x0 +[04-13_05:24:40:009] < IPv4 = 10.129.90.29/30 +[04-13_05:24:40:009] < gw = 10.129.90.30 +[04-13_05:24:40:009] < dns1 = 211.138.180.2 +[04-13_05:24:40:009 < dns2 = 211.138.180.3 +[04-13_05:24:40:009] < ipv4 mtu = 1500 +[04-13_05:24:40:041] ifconfig wwan0 up +[04-13_05:24:40:063] ip -4 address flush dev wwan0 +[04-13_05:24:40:073] ip -4 address add 10.129.90.29/30 dev wwan0 +[04-13_05:24:40:084] ip -4 route add default via 10.129.90.30 dev wwan0 + +root@ZhuoTK:/# ifconfig wwan0 +wwan0 Link encap:Ethernet HWaddr A2:58:DC:4D:DD:CA + inet addr:10.129.90.29 Bcast:0.0.0.0 Mask:255.255.255.252 + inet6 addr: fe80::a058:dcff:fe4d:ddca/64 Scope:Link + UP BROADCAST RUNNING NOARP MULTICAST MTU:1500 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:5 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:0 (0.0 B) TX bytes:380 (380.0 B) + +root@ZhuoTK:/# ip ro show +default via 10.129.90.30 dev wwan0 +10.129.90.28/30 dev wwan0 proto kernel scope link src 10.129.90.29 +192.168.1.0/24 dev br-lan proto kernel scope link src 192.168.1.251 + +root@ZhuoTK:/# ping www.qq.com +PING www.qq.com (183.194.238.117): 56 data bytes +64 bytes from 183.194.238.117: seq=0 ttl=53 time=58.674 ms \ No newline at end of file diff --git a/application/quectel_CM_5G/src/log/cdc_mbim_vlan.txt b/application/quectel_CM_5G/src/log/cdc_mbim_vlan.txt new file mode 100644 index 0000000..79e15a4 --- /dev/null +++ b/application/quectel_CM_5G/src/log/cdc_mbim_vlan.txt @@ -0,0 +1,168 @@ +root@ZhuoTK:/# dmesg +[ 788.920000] usb 1-1.3: new high-speed USB device number 4 using ehci-platform +[ 789.160000] cdc_mbim 1-1.3:1.4: cdc-wdm0: USB WDM device +[ 789.170000] cdc_mbim 1-1.3:1.4 wwan0: register 'cdc_mbim' at usb-101c0000.ehci-1.3, CDC MBIM, a2:58:dc:4d:dd:ca + +root@ZhuoTK:/# ip link add link wwan0 name wwan0.1 type vlan id 1 +root@ZhuoTK:/# ip link add link wwan0 name wwan0.2 type vlan id 2 +root@ZhuoTK:/# ifconfig wwan0.1 +wwan0.1 Link encap:Ethernet HWaddr A2:58:DC:4D:DD:CA + BROADCAST NOARP MULTICAST MTU:1500 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) + +root@ZhuoTK:/# ifconfig wwan0.2 +wwan0.2 Link encap:Ethernet HWaddr A2:58:DC:4D:DD:CA + BROADCAST NOARP MULTICAST MTU:1500 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) + +root@ZhuoTK:/# quectel-mbim-proxy & +root@ZhuoTK:/# [04-13_07:04:27:543] mbim_dev_fd=3 +[04-13_07:04:27:543] mbim_send_open_msg() +[04-13_07:04:28:321] receive MBIM_OPEN_DONE, status=0 +[04-13_07:04:28:321] mbim_server_fd=4 + +root@ZhuoTK:/# quectel-CM -n 1 -s cmnet & +[04-13_07:04:34:256] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_07:04:34:259] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x004 +[04-13_07:04:34:260] Auto find qmichannel = /dev/cdc-wdm0 +[04-13_07:04:34:260] Auto find usbnet_adapter = wwan0 +[04-13_07:04:34:260] netcard driver = cdc_mbim, driver version = 22-Aug-2005 +[04-13_07:04:34:261] mbim_qmap_mode = 4, vlan_id = 0x01, qmap_netcard = wwan0.1 +[04-13_07:04:34:261] Modem works in MBIM mode +[04-13_07:04:34:261] handle_client_connect client_fd=5, client_idx=1 +[04-13_07:04:34:262] connect to quectel-mbim-proxy sockfd = 7 +[04-13_07:04:34:262] cdc_wdm_fd = 7 +[04-13_07:04:34:262] mbim_open_device() +[04-13_07:04:35:106] mbim_device_caps_query() +[04-13_07:04:35:139] DeviceId: 866758045439136 +[04-13_07:04:35:139] FirmwareInfo: EC25EFAR06A11M4G +[04-13_07:04:35:139] HardwareInfo: QUECTEL Mobile Broadband Modul +[04-13_07:04:35:139] mbim_device_services_query() +[04-13_07:04:35:170] mbim_set_radio_state( 1 ) +[04-13_07:04:35:202] HwRadioState: 1, SwRadioState: 1 +[04-13_07:04:35:202] mbim_subscriber_status_query() +[04-13_07:04:35:267] SubscriberId: 460028563800461 +[04-13_07:04:35:267] SimIccId: 89860015120716380461 +[04-13_07:04:35:267] SubscriberReadyState NotInitialized -> Initialized +[04-13_07:04:35:267] mbim_register_state_query() +[04-13_07:04:35:297] RegisterState Unknown -> Home +[04-13_07:04:35:298] mbim_packet_service_query() +[04-13_07:04:35:329] PacketServiceState Unknown -> Attached +[04-13_07:04:35:330] mbim_query_connect(sessionID=1) +[04-13_07:04:35:361] ActivationState Unknown -> Deactivated +[04-13_07:04:35:362] ifconfig wwan0.1 0.0.0.0 +[04-13_07:04:35:373] ifconfig wwan0.1 down +[04-13_07:04:35:383] mbim_set_connect(onoff=1, sessionID=1) +[04-13_07:04:35:426] ActivationState Deactivated -> Activated +[04-13_07:04:35:426] mbim_ip_config(sessionID=1) +[04-13_07:04:35:457] < SessionId = 1 +[04-13_07:04:35:457] < IPv4ConfigurationAvailable = 0xf +[04-13_07:04:35:457] < IPv6ConfigurationAvailable = 0x0 +[04-13_07:04:35:457] < IPv4AddressCount = 0x1 +[04-13_07:04:35:458] < IPv4AddressOffset = 0x3c +[04-13_07:04:35:458] < IPv6AddressCount = 0x0 +[04-13_07:04:35:458] < IPv6AddressOffset = 0x0 +[04-13_07:04:35:458] < IPv4 = 10.129.90.29/30 +[04-13_07:04:35:458] < gw = 10.129.90.30 +[04-13_07:04:35:458] < dns1 = 211.138.180.2 +[04-13_07:04:35:458] < dns2 = 211.138.180.3 +[04-13_07:04:35:458] < ipv4 mtu = 1500 +[04-13_07:04:35:489] ifconfig wwan0 up +[04-13_07:04:35:509] ifconfig wwan0.1 down +[04-13_07:04:35:522] ifconfig wwan0.1 up +[04-13_07:04:35:535] ip -4 address flush dev wwan0.1 +[04-13_07:04:35:545] ip -4 address add 10.129.90.29/30 dev wwan0.1 +[04-13_07:04:35:556] ip -4 route add default via 10.129.90.30 dev wwan0.1 + +root@ZhuoTK:/# quectel-CM -n 2 -s 4gnet & +[04-13_07:04:45:150] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_07:04:45:152] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x004 +[04-13_07:04:45:154] Auto find qmichannel = /dev/cdc-wdm0 +[04-13_07:04:45:154] Auto find usbnet_adapter = wwan0 +[04-13_07:04:45:154] netcard driver = cdc_mbim, driver version = 22-Aug-2005 +[04-13_07:04:45:155] mbim_qmap_mode = 4, vlan_id = 0x02, qmap_netcard = wwan0.2 +[04-13_07:04:45:155] Modem works in MBIM mode +[04-13_07:04:45:155] handle_client_connect client_fd=6, client_idx=2 +[04-13_07:04:45:156] connect to quectel-mbim-proxy sockfd = 7 +[04-13_07:04:45:156] cdc_wdm_fd = 7 +[04-13_07:04:45:156] mbim_open_device() +[04-13_07:04:46:025] mbim_device_caps_query() +[04-13_07:04:46:056] DeviceId: 866758045439136 +[04-13_07:04:46:056] FirmwareInfo: EC25EFAR06A11M4G +[04-13_07:04:46:056] HardwareInfo: QUECTEL Mobile Broadband Modul +[04-13_07:04:46:056] mbim_device_services_query() +[04-13_07:04:46:088] mbim_set_radio_state( 1 ) +[04-13_07:04:46:119] HwRadioState: 1, SwRadioState: 1 +[04-13_07:04:46:119] mbim_subscriber_status_query() +[04-13_07:04:46:183] SubscriberId: 460028563800461 +[04-13_07:04:46:184] SimIccId: 89860015120716380461 +[04-13_07:04:46:184] SubscriberReadyState NotInitialized -> Initialized +[04-13_07:04:46:184] mbim_register_state_query() +[04-13_07:04:46:216] RegisterState Unknown -> Home +[04-13_07:04:46:216] mbim_packet_service_query() +[04-13_07:04:46:248] PacketServiceState Unknown -> Attached +[04-13_07:04:46:248] mbim_query_connect(sessionID=2) +[04-13_07:04:46:280] ActivationState Unknown -> Deactivated +[04-13_07:04:46:280] ifconfig wwan0.2 0.0.0.0 +[04-13_07:04:46:291] ifconfig wwan0.2 down +[04-13_07:04:46:304] mbim_set_connect(onoff=1, sessionID=2) +[04-13_07:04:46:504] ActivationState Deactivated -> Activated +[04-13_07:04:46:505] mbim_ip_config(sessionID=2) +[04-13_07:04:46:537] < SessionId = 2 +[04-13_07:04:46:537] < IPv4ConfigurationAvailable = 0xf +[04-13_07:04:46:537] < IPv6ConfigurationAvailable = 0x0 +[04-13_07:04:46:538] < IPv4AddressCount = 0x1 +[04-13_07:04:46:538] < IPv4AddressOffset = 0x3c +[04-13_07:04:46:538] < IPv6AddressCount = 0x0 +[04-13_07:04:46:538] < IPv6AddressOffset = 0x0 +[04-13_07:04:46:538] < IPv4 = 10.129.37.205/30 +[04-13_07:04:46:538] < gw = 10.129.37.206 +[04-13_07:04:46:538] < dns1 = 211.138.180.2 +[04-13_07:04:46:538] < dns2 = 211.138.180.3 +[04-13_07:04:46:538] < ipv4 mtu = 1500 +[04-13_07:04:46:569] ifconfig wwan0 up +[04-13_07:04:46:579] ifconfig wwan0.2 up +[04-13_07:04:46:592] ip -4 address flush dev wwan0.2 +[04-13_07:04:46:602] ip -4 address add 10.129.37.205/30 dev wwan0.2 +[04-13_07:04:46:613] ip -4 route add default via 10.129.37.206 dev wwan0.2 + +root@ZhuoTK:/# ifconfig wwan0.1 +wwan0.1 Link encap:Ethernet HWaddr A2:58:DC:4D:DD:CA + inet addr:10.129.90.29 Bcast:0.0.0.0 Mask:255.255.255.252 + inet6 addr: fe80::a058:dcff:fe4d:ddca/64 Scope:Link + UP BROADCAST RUNNING NOARP MULTICAST MTU:1500 Metric:1 + RX packets:4 errors:0 dropped:0 overruns:0 frame:0 + TX packets:13 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:304 (304.0 B) TX bytes:1170 (1.1 KiB) + +root@ZhuoTK:/# ifconfig wwan0.2 +wwan0.2 Link encap:Ethernet HWaddr A2:58:DC:4D:DD:CA + inet addr:10.129.37.205 Bcast:0.0.0.0 Mask:255.255.255.252 + inet6 addr: fe80::a058:dcff:fe4d:ddca/64 Scope:Link + UP BROADCAST RUNNING NOARP MULTICAST MTU:1500 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:8 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:0 + RX bytes:0 (0.0 B) TX bytes:720 (720.0 B) + +root@ZhuoTK:/# ip ro del 8.8.8.8/32 +RTNETLINK answers: No such process +root@ZhuoTK:/# ip ro add 8.8.8.8/32 dev wwan0.1 +root@ZhuoTK:/# ping 8.8.8.8 +PING 8.8.8.8 (8.8.8.8): 56 data bytes +64 bytes from 8.8.8.8: seq=0 ttl=52 time=98.584 ms + +root@ZhuoTK:/# ip ro del 8.8.8.8/32 +root@ZhuoTK:/# ip ro del 8.8.8.8/32 +RTNETLINK answers: No such process +root@ZhuoTK:/# ip ro add 8.8.8.8/32 dev wwan0.2 +root@ZhuoTK:/# ping 8.8.8.8 +PING 8.8.8.8 (8.8.8.8): 56 data bytes +64 bytes from 8.8.8.8: seq=0 ttl=111 time=101.770 ms \ No newline at end of file diff --git a/application/quectel_CM_5G/src/log/ecm_ncm_rndis.txt b/application/quectel_CM_5G/src/log/ecm_ncm_rndis.txt new file mode 100644 index 0000000..2ae7e6b --- /dev/null +++ b/application/quectel_CM_5G/src/log/ecm_ncm_rndis.txt @@ -0,0 +1,129 @@ +# ./quectel-CM -s cmnet & +[04-21_17:35:14:362] Quectel_QConnectManager_Linux_V1.6.0.26 +[04-21_17:35:14:363] Find /sys/bus/usb/devices/2-4 idVendor=0x2c7c idProduct=0x8101, bus=0x002, dev=0x016 +[04-21_17:35:14:363] Auto find qmichannel = /dev/ttyUSB0 +[04-21_17:35:14:363] Auto find usbnet_adapter = usb0 +[04-21_17:35:14:363] netcard driver = cdc_ncm, driver version = 22-Aug-2005 +[04-21_17:35:14:363] Modem works in ECM_RNDIS_NCM mode +[04-21_17:35:14:371] atc_fd = 7 +[04-21_17:35:14:371] AT> ATE0Q0V1 +[04-21_17:35:14:372] AT< RDATE0Q0V1 +[04-21_17:35:14:372] AT< COMMAND NOT SUPPORT +[04-21_17:35:15:373] AT> AT+QCFG="usbnet" +[04-21_17:35:15:373] AT< +QCFG: "usbnet",5 +[04-21_17:35:15:373] AT< OK +[04-21_17:35:15:373] AT> AT+QNETDEVCTL=? +[04-21_17:35:15:374] AT< +QNETDEVCTL: (1-11),(0,1),(0,1) +[04-21_17:35:15:374] AT< OK +[04-21_17:35:15:374] AT> AT+CGREG=2 +[04-21_17:35:15:376] AT< OK +[04-21_17:35:15:376] AT> AT+CEREG=2 +[04-21_17:35:15:381] AT< OK +[04-21_17:35:15:381] AT> AT+C5GREG=2 +[04-21_17:35:15:384] AT< OK +[04-21_17:35:15:384] AT> AT+QNETDEVSTATUS=? +[04-21_17:35:15:385] AT< +QNETDEVSTATUS: (1-11) +[04-21_17:35:15:385] AT< OK +[04-21_17:35:15:385] AT> AT+QCFG="NAT" +[04-21_17:35:15:385] AT< +QCFG: "nat",0 +[04-21_17:35:15:385] AT< OK +[04-21_17:35:15:385] AT> AT+CGMR +[04-21_17:35:15:386] AT< RG801HEAAAR03A01M8G +[04-21_17:35:15:386] AT< OK +[04-21_17:35:15:386] AT> AT+CPIN? +[04-21_17:35:15:388] AT< +CPIN: READY +[04-21_17:35:15:388] AT< OK +[04-21_17:35:15:389] AT> AT+QCCID +[04-21_17:35:15:393] AT< +QCCID: 89860015120716380461 +[04-21_17:35:15:393] AT< OK +[04-21_17:35:15:393] requestGetICCID 89860015120716380461 +[04-21_17:35:15:393] AT> AT+CIMI +[04-21_17:35:15:409] AT< 460028563800461 +[04-21_17:35:15:409] AT< OK +[04-21_17:35:15:409] requestGetIMSI 460028563800461 +[04-21_17:35:15:409] AT> AT+QICSGP=1 +[04-21_17:35:15:411] AT< +QICSGP: 1,1,"cment","","",0,,0, +[04-21_17:35:15:411] AT< OK +[04-21_17:35:15:411] AT> AT+QICSGP=1 +[04-21_17:35:15:415] AT< +QICSGP: 1,1,"cment","","",0,,0, +[04-21_17:35:15:415] AT< OK +[04-21_17:35:15:415] AT> AT+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS? +[04-21_17:35:15:421] AT< +COPS: 0,0,"CHINA MOBILE",12 +[04-21_17:35:15:421] AT< +COPS: 0,1,"CMCC",12 +[04-21_17:35:15:421] AT< +COPS: 0,2,"46000",12 +[04-21_17:35:15:421] AT< OK +[04-21_17:35:15:421] AT> AT+C5GREG? +[04-21_17:35:15:424] AT< +C5GREG: 2,1,"46550B","0000000170C23000",11,1,"01" +[04-21_17:35:15:424] AT< OK +[04-21_17:35:15:424] AT> at+cops? +[04-21_17:35:15:427] AT< +COPS: 0,2,"46000",12 +[04-21_17:35:15:427] AT< OK +[04-21_17:35:15:427] AT> at+qeng="servingcell" +[04-21_17:35:15:441] AT< +QENG: "servingcell","NOCONN","NR5G-SA","TDD",460,00,170C23000,901,46550B,504990,41,-,-54,-10,16,-,- +[04-21_17:35:15:441] AT< OK +[04-21_17:35:15:441] AT> AT+QNETDEVSTATUS=1 +[04-21_17:35:15:445] AT< ERROR +[04-21_17:35:15:445] ifconfig usb0 0.0.0.0 +[04-21_17:35:15:446] ifconfig usb0 down +[04-21_17:35:15:448] AT> AT+QNETDEVCTL=1,1,0 +[04-21_17:35:15:454] AT< OK +[04-21_17:35:15:456] AT> AT+QNETDEVSTATUS=1 +[04-21_17:35:15:458] AT< ERROR +[04-21_17:35:15:989] AT< +QNETDEVSTATUS:1,1,"IPV4" +[04-21_17:35:16:459] AT> AT+QNETDEVSTATUS=1 +[04-21_17:35:16:461] AT< +QNETDEVSTATUS: 4F10190A,E0FFFFFF,4110190A,4110190A,02B48AD3,03B48AD3, 85600, 85600 +[04-21_17:35:16:461] AT< OK +[04-21_17:35:16:461] AT> AT+QNETDEVSTATUS=1 +[04-21_17:35:16:462] AT< +QNETDEVSTATUS: 4F10190A,E0FFFFFF,4110190A,4110190A,02B48AD3,03B48AD3, 85600, 85600 +[04-21_17:35:16:462] AT< OK +[04-21_17:35:16:462] requestGetIPAddress 10.25.16.79 +[04-21_17:35:16:462] AT> at+cops? +[04-21_17:35:16:463] AT< +COPS: 0,2,"46000",12 +[04-21_17:35:16:463] AT< OK +[04-21_17:35:16:463] AT> at+qeng="servingcell" +[04-21_17:35:16:465] AT< +QENG: "servingcell","CONNECT","NR5G-SA","TDD",460,00,170C23000,901,46550B,504990,41,-,-52,-11,15,-,- +[04-21_17:35:16:465] AT< OK +[04-21_17:35:16:465] AT> AT+QNETDEVSTATUS=1 +[04-21_17:35:16:466] AT< +QNETDEVSTATUS: 4F10190A,E0FFFFFF,4110190A,4110190A,02B48AD3,03B48AD3, 85600, 85600 +[04-21_17:35:16:466] AT< OK +[04-21_17:35:16:466] ifconfig usb0 up +[04-21_17:35:16:470] busybox udhcpc -f -n -q -t 5 -i usb0 +udhcpc: started, v1.30.1 +udhcpc: sending discover +udhcpc: sending select for 10.25.16.79 +udhcpc: lease of 10.25.16.79 obtained, lease time 518400 +[04-21_17:35:16:602] /etc/udhcpc/default.script: Resetting default routes +SIOCDELRT: No such process +SIOCADDRT: Network is unreachable +[04-21_17:35:16:606] /etc/udhcpc/default.script: Adding DNS 211.138.180.2 +[04-21_17:35:16:606] /etc/udhcpc/default.script: Adding DNS 211.138.180.3 +[04-21_17:35:16:655] AT> at+cops? +[04-21_17:35:16:656] AT< +COPS: 0,2,"46000",12 +[04-21_17:35:16:656] AT< OK +[04-21_17:35:16:656] AT> at+qeng="servingcell" +[04-21_17:35:16:657] AT< +QENG: "servingcell","CONNECT","NR5G-SA","TDD",460,00,170C23000,901,46550B,504990,41,-,-50,-11,17,-,- +[04-21_17:35:16:658] AT< OK +err = 16 +[04-21_17:35:16:658] AT> AT+QNETDEVSTATUS=1 +[04-21_17:35:16:659] AT< +QNETDEVSTATUS: 4F10190A,E0FFFFFF,4110190A,4110190A,02B48AD3,03B48AD3, 85600, 85600 +[04-21_17:35:16:659] AT< OK + +root@carl-ThinkPad-X1-Carbon-7th:/home/carl/q/quectel-CM# ifconfig usb0 +usb0: flags=4163 mtu 1500 + inet 10.25.16.79 netmask 255.255.255.224 broadcast 10.25.16.95 + inet6 fe80::5c98:e9d4:c82d:5f prefixlen 64 scopeid 0x20 + ether 0c:5b:8f:27:9a:64 txqueuelen 1000 (Ethernet) + RX packets 7 bytes 1656 (1.6 KB) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 32 bytes 5112 (5.1 KB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +root@carl-ThinkPad-X1-Carbon-7th:/home/carl/q/quectel-CM# [04-21_17:35:31:670] AT> at+cops? +[04-21_17:35:31:671] AT< +COPS: 0,2,"46000",12 +[04-21_17:35:31:671] AT< OK +[04-21_17:35:31:671] AT> at+qeng="servingcell" +[04-21_17:35:31:673] AT< +QENG: "servingcell","CONNECT","NR5G-SA","TDD",460,00,170C23000,901,46550B,504990,41,-,-48,-10,17,-,- +[04-21_17:35:31:673] AT< OK +[04-21_17:35:31:673] AT> AT+QNETDEVSTATUS=1 +[04-21_17:35:31:674] AT< +QNETDEVSTATUS: 4F10190A,E0FFFFFF,4110190A,4110190A,02B48AD3,03B48AD3, 85600, 85600 +[04-21_17:35:31:674] AT< OK diff --git a/application/quectel_CM_5G/src/log/gobinet.txt b/application/quectel_CM_5G/src/log/gobinet.txt new file mode 100644 index 0000000..2b286de --- /dev/null +++ b/application/quectel_CM_5G/src/log/gobinet.txt @@ -0,0 +1,62 @@ +root@ZhuoTK:/# dmesg +[ 230.590000] GobiNet 1-1.3:1.4 usb0: register 'GobiNet' at usb-101c0000.ehci-1.3, GobiNet Ethernet Device, 02:50:f4:00:00:00 +[ 230.600000] creating qcqmi0 + +root@ZhuoTK:/# quectel-CM -s cmnet & +[04-13_03:24:58:213] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_03:24:58:216] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x004 +[04-13_03:24:58:218] Auto find qmichannel = /dev/qcqmi0 +[04-13_03:24:58:218] Auto find usbnet_adapter = usb0 +[04-13_03:24:58:218] netcard driver = GobiNet, driver version = V1.6.2.13 +[04-13_03:24:58:219] Modem works in QMI mode +[04-13_03:24:58:260] Get clientWDS = 7 +[04-13_03:24:58:292] Get clientDMS = 8 +[04-13_03:24:58:324] Get clientNAS = 9 +[04-13_03:24:58:355] Get clientUIM = 10 +[04-13_03:24:58:388] Get clientWDA = 11 +[04-13_03:24:58:420] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_03:24:58:548] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_03:24:58:549] requestSetProfile[1] cmnet///0 +[04-13_03:24:58:613] requestGetProfile[1] cmnet///0 +[04-13_03:24:58:645] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_03:24:58:677] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-13_03:24:58:677] ifconfig usb0 0.0.0.0 +[04-13_03:24:58:696] ifconfig usb0 down +[04-13_03:24:59:028] requestSetupDataCall WdsConnectionIPv4Handle: 0x87245bd0 +[04-13_03:24:59:189] ifconfig usb0 up +[04-13_03:24:59:214] you are use OpenWrt? +[04-13_03:24:59:215] should not calling udhcpc manually? +[04-13_03:24:59:215] should modify /etc/config/network as below? +[04-13_03:24:59:215] config interface wan +[04-13_03:24:59:215] option ifname usb0 +[04-13_03:24:59:215] option proto dhcp +[04-13_03:24:59:215] should use "/sbin/ifstaus wan" to check usb0 's status? +[04-13_03:24:59:216] busybox udhcpc -f -n -q -t 5 -i usb0 +[04-13_03:24:59:226] udhcpc (v1.23.2) started +[04-13_03:24:59:238] Sending discover... +[04-13_03:24:59:248] Sending select for 10.198.78.154... +[04-13_03:24:59:251] Lease of 10.198.78.154 obtained, lease time 7200 +[04-13_03:24:59:257] udhcpc: ifconfig usb0 10.198.78.154 netmask 255.255.255.252 broadcast + +[04-13_03:24:59:266] udhcpc: setting default routers: 10.198.78.153 + +root@ZhuoTK:/# ifconfig usb0 +usb0 Link encap:Ethernet HWaddr 02:50:F4:00:00:00 + inet addr:10.198.78.154 Mask:255.255.255.252 + inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link + UP RUNNING NOARP MTU:1500 Metric:1 + RX packets:6 errors:0 dropped:0 overruns:0 frame:0 + TX packets:6 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:916 (916.0 B) TX bytes:960 (960.0 B) + +root@ZhuoTK:/# ip ro show +default via 10.198.78.153 dev usb0 +10.198.78.152/30 dev usb0 proto kernel scope link src 10.198.78.154 +192.168.1.0/24 dev br-lan proto kernel scope link src 192.168.1.251 + +root@ZhuoTK:/# killall quectel-CM +[04-13_03:25:38:779] requestDeactivateDefaultPDP WdsConnectionIPv4Handle +[04-13_03:25:39:061] ifconfig usb0 0.0.0.0 +[04-13_03:25:39:072] ifconfig usb0 down +[04-13_03:25:39:284] GobiNetThread exit +[04-13_03:25:39:285] qmi_main exit diff --git a/application/quectel_CM_5G/src/log/gobinet_bridge.txt b/application/quectel_CM_5G/src/log/gobinet_bridge.txt new file mode 100644 index 0000000..7ed229e --- /dev/null +++ b/application/quectel_CM_5G/src/log/gobinet_bridge.txt @@ -0,0 +1,60 @@ +root@ZhuoTK:/# insmod GobiNet.ko +[ 80.460000] GobiNet: Quectel_Linux&Android_GobiNet_Driver_V1.6.2.13 +[ 80.460000] usbcore: registered new interface driver GobiNet +[ 97.710000] usb 1-1.3: new high-speed USB device number 3 using ehci-platform +[ 97.930000] usb 1-1.3: GSM modem (1-port) converter now attached to ttyUSB103 +[ 97.950000] GobiNet 1-1.3:1.4 usb0: register 'GobiNet' at usb-101c0000.ehci-1.3, GobiNet Ethernet Device, 02:50:f4:00:00:00 +[ 97.960000] creating qcqmi0 + +root@ZhuoTK:/# brctl addbr br0 +root@ZhuoTK:/# brctl addif br0 eth0.1 +root@ZhuoTK:/# brctl addif br0 usb0 +root@ZhuoTK:/# brctl show +bridge name bridge id STP enabled interfaces +br0 8000.00ca019197b9 no eth0.1 + usb0 + +root@ZhuoTK:/# quectel-CM -s cment -b & +[04-13_05:13:18:213] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_05:13:18:216] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x003 +[ 122.270000] net usb0: bridge_mode change to 0x1 +[04-13_05:13:18:218] Auto find qmichannel = /dev/qcqmi0 +[04-13_05:13:18:218] Auto find usbnet_adapter = usb0 +[04-13_05:13:18:218] netcard driver = GobiNet, driver version = V1.6.2.13 +[04-13_05:13:18:224] Modem works in QMI mode +[04-13_05:13:18:251] Get clientWDS = 7 +[04-13_05:13:18:282] Get clientDMS = 8 +[04-13_05:13:18:316] Get clientNAS = 9 +[04-13_05:13:18:347] Get clientUIM = 10 +[04-13_05:13:18:379] Get clientWDA = 11 +[04-13_05:13:18:411] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_05:13:18:539] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_05:13:18:540] requestSetProfile[1] cment///0 +[04-13_05:13:18:603] requestGetProfile[1] cment///0 +[04-13_05:13:18:637] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_05:13:18:666] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-13_05:13:18:667] ifconfig usb0 0.0.0.0 +[04-13_05:13:18:687] ifconfig usb0 down +[04-13_05:13:19:083] requestSetupDataCall WdsConnectionIPv4Handle: 0x8724d220 +[04-13_05:13:19:243] ifconfig usb0 up +[04-13_05:13:19:270] echo '0xa218480' > /sys/class/net/usb0/bridge_ipv4 + +root@ZhuoTK:/# ifconfig br0 up +[ 135.530000] usb0 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 135.570000] usb0 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 135.580000] usb0 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 135.610000] usb0 sip = 10.33.132.128, tip=10.33.132.129, ipv4=10.33.132.128 +[ 135.620000] usb0 sip = 10.33.132.128, tip=10.33.132.129, ipv4=10.33.132.128 +[ 135.910000] usb0 sip = 0.0.0.0, tip=10.33.132.128, ipv4=10.33.132.128 +[ 136.000000] usb0 sip = 10.33.132.128, tip=10.33.132.129, ipv4=10.33.132.128 +[ 136.910000] usb0 sip = 0.0.0.0, tip=10.33.132.128, ipv4=10.33.132.128 +[ 137.910000] usb0 sip = 0.0.0.0, tip=10.33.132.128, ipv4=10.33.132.128 +[ 138.740000] usb0 sip = 10.33.132.128, tip=10.33.132.129, ipv4=10.33.132.128 +[ 138.910000] usb0 sip = 10.33.132.128, tip=10.33.132.128, ipv4=10.33.132.128 +[ 139.000000] usb0 sip = 10.33.132.128, tip=10.33.132.129, ipv4=10.33.132.128 +[ 140.860000] usb0 sip = 10.33.132.128, tip=10.33.132.129, ipv4=10.33.132.128 +[ 143.160000] br0: port 2(usb0) entered forwarding state +[ 143.160000] br0: port 1(eth0.1) entered forwarding state +[ 148.870000] usb0 sip = 10.33.132.128, tip=10.33.132.129, ipv4=10.33.132.128 +[ 149.010000] usb0 sip = 10.33.132.128, tip=10.33.132.129, ipv4=10.33.132.128 +[ 165.630000] usb0 sip = 10.33.132.128, tip=10.33.132.129, ipv4=10.33.132.128 diff --git a/application/quectel_CM_5G/src/log/gobinet_qmap=1.txt b/application/quectel_CM_5G/src/log/gobinet_qmap=1.txt new file mode 100644 index 0000000..83cedd9 --- /dev/null +++ b/application/quectel_CM_5G/src/log/gobinet_qmap=1.txt @@ -0,0 +1,45 @@ +root@ZhuoTK:/# insmod GobiNet.ko qmap_mode=1 +[ 798.480000] GobiNet: Quectel_Linux&Android_GobiNet_Driver_V1.6.2.13 +[ 798.490000] GobiNet 1-1.3:1.4 usb0: register 'GobiNet' at usb-101c0000.ehci-1.3, GobiNet Ethernet Device, 02:50:f4:00:00:00 +[ 798.510000] creating qcqmi0 +[ 798.510000] usbcore: registered new interface driver GobiNet +[ 799.620000] GobiNet::QMIWDASetDataFormat qmap settings qmap_version=5, rx_size=4096, tx_size=4096 +[ 799.630000] GobiNet::QMIWDASetDataFormat qmap settings ul_data_aggregation_max_size=4096, ul_data_aggregation_max_datagrams=16 + +root@ZhuoTK:/# quectel-CM -s cmnet & +[04-13_03:32:31:248] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_03:32:31:251] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x005 +[04-13_03:32:31:253] Auto find qmichannel = /dev/qcqmi0 +[04-13_03:32:31:253] Auto find usbnet_adapter = usb0 +[04-13_03:32:31:253] netcard driver = GobiNet, driver version = V1.6.2.13 +[04-13_03:32:31:253] qmap_mode = 1, qmap_version = 5, qmap_size = 4096, muxid = 0x81, qmap_netcard = usb0 +[04-13_03:32:31:254] Modem works in QMI mode +[04-13_03:32:31:289] Get clientWDS = 7 +[04-13_03:32:31:320] Get clientDMS = 8 +[04-13_03:32:31:353] Get clientNAS = 9 +[04-13_03:32:31:385] Get clientUIM = 10 +[04-13_03:32:31:417] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_03:32:31:545] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_03:32:31:545] requestSetProfile[1] cmnet///0 +[04-13_03:32:31:609] requestGetProfile[1] cmnet///0 +[04-13_03:32:31:641] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_03:32:31:673] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-13_03:32:31:674] ifconfig usb0 0.0.0.0 +[04-13_03:32:31:698] ifconfig usb0 down +[04-13_03:32:31:770] requestSetupDataCall WdsConnectionIPv4Handle: 0x872481a0 +[ 857.000000] net usb0: link_state 0x0 -> 0x1 +[04-13_03:32:31:902] ifconfig usb0 up +[04-13_03:32:31:928] you are use OpenWrt? +[04-13_03:32:31:928] should not calling udhcpc manually? +[04-13_03:32:31:928] should modify /etc/config/network as below? +[04-13_03:32:31:928] config interface wan +[04-13_03:32:31:928] option ifname usb0 +[04-13_03:32:31:929] option proto dhcp +[04-13_03:32:31:929] should use "/sbin/ifstaus wan" to check usb0 's status? +[04-13_03:32:31:929] busybox udhcpc -f -n -q -t 5 -i usb0 +[04-13_03:32:31:939] udhcpc (v1.23.2) started +[04-13_03:32:31:951] Sending discover... +[04-13_03:32:31:956] Sending select for 10.199.102.71... +[04-13_03:32:31:959] Lease of 10.199.102.71 obtained, lease time 7200 +[04-13_03:32:31:964] udhcpc: ifconfig usb0 10.199.102.71 netmask 255.255.255.240 broadcast + +[04-13_03:32:31:974] udhcpc: setting default routers: 10.199.102.72 diff --git a/application/quectel_CM_5G/src/log/gobinet_qmap=1_bridge.txt b/application/quectel_CM_5G/src/log/gobinet_qmap=1_bridge.txt new file mode 100644 index 0000000..64a8374 --- /dev/null +++ b/application/quectel_CM_5G/src/log/gobinet_qmap=1_bridge.txt @@ -0,0 +1,62 @@ +root@ZhuoTK:/# insmod GobiNet.ko qmap_mode=1 +[ 41.540000] GobiNet: Quectel_Linux&Android_GobiNet_Driver_V1.6.2.13 +[ 41.550000] GobiNet 1-1.3:1.4 usb0: register 'GobiNet' at usb-101c0000.ehci-1.3, GobiNet Ethernet Device, 02:50:f4:00:00:00 +[ 41.570000] creating qcqmi0 +[ 41.570000] usbcore: registered new interface driver GobiNet +[ 42.700000] GobiNet::QMIWDASetDataFormat qmap settings qmap_version=5, rx_size=4096, tx_size=4096 +[ 42.710000] GobiNet::QMIWDASetDataFormat qmap settings ul_data_aggregation_max_size=4096, ul_data_aggregation_max_datagrams=16 + +root@ZhuoTK:/# brctl addbr br0 +root@ZhuoTK:/# brctl addif br0 eth0.1 +root@ZhuoTK:/# brctl addif br0 usb0 +root@ZhuoTK:/# brctl show +bridge name bridge id STP enabled interfaces +br0 8000.00ca019197b9 no eth0.1 + usb0 + +root@ZhuoTK:/# quectel-CM -s cmnet -b & +# [04-13_05:12:29:338] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_05:12:29:340] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x003 +[ 73.380000] net usb0: bridge_mode change to 0x1 +[04-13_05:12:29:342] Auto find qmichannel = /dev/qcqmi0 +[04-13_05:12:29:342] Auto find usbnet_adapter = usb0 +[04-13_05:12:29:342] netcard driver = GobiNet, driver version = V1.6.2.13 +[04-13_05:12:29:343] qmap_mode = 1, qmap_version = 5, qmap_size = 4096, muxid = 0x81, qmap_netcard = usb0 +[04-13_05:12:29:348] Modem works in QMI mode +[04-13_05:12:29:382] Get clientWDS = 7 +[04-13_05:12:29:414] Get clientDMS = 8 +[04-13_05:12:29:447] Get clientNAS = 9 +[04-13_05:12:29:479] Get clientUIM = 10 +[04-13_05:12:29:512] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_05:12:29:640] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_05:12:29:640] requestSetProfile[1] cmnet///0 +[04-13_05:12:29:704] requestGetProfile[1] cmnet///0 +[04-13_05:12:29:735] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_05:12:29:767] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-13_05:12:29:768] ifconfig usb0 0.0.0.0 +[04-13_05:12:29:792] ifconfig usb0 down +[04-13_05:12:29:863] requestSetupDataCall WdsConnectionIPv4Handle: 0x8724d820 +[ 74.030000] net usb0: link_state 0x0 -> 0x1 +[04-13_05:12:29:996] ifconfig usb0 up +[04-13_05:12:30:022] echo '0xa16b769' > /sys/class/net/usb0/bridge_ipv4 + +root@ZhuoTK:/# ifconfig br0 up +[ 82.210000] br0: port 2(usb0) entered forwarding state +[ 82.210000] br0: port 2(usb0) entered forwarding state +[ 82.220000] br0: port 1(eth0.1) entered forwarding state +[ 82.220000] br0: port 1(eth0.1) entered forwarding state +[ 88.830000] rt305x-esw 10110000.esw: link changed 0x01 +[ 89.010000] usb0 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 89.040000] usb0 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 89.050000] usb0 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 89.120000] usb0 sip = 10.22.183.105, tip=10.22.183.106, ipv4=10.22.183.105 +[ 89.350000] usb0 sip = 0.0.0.0, tip=10.22.183.105, ipv4=10.22.183.105 +[ 89.400000] usb0 sip = 10.22.183.105, tip=10.22.183.106, ipv4=10.22.183.105 +[ 89.520000] usb0 sip = 10.22.183.105, tip=10.22.183.106, ipv4=10.22.183.105 +[ 90.350000] usb0 sip = 0.0.0.0, tip=10.22.183.105, ipv4=10.22.183.105 +[ 91.350000] usb0 sip = 0.0.0.0, tip=10.22.183.105, ipv4=10.22.183.105 +[ 92.350000] usb0 sip = 10.22.183.105, tip=10.22.183.105, ipv4=10.22.183.105 +[ 92.430000] usb0 sip = 10.22.183.105, tip=10.22.183.106, ipv4=10.22.183.105 +[ 92.660000] usb0 sip = 10.22.183.105, tip=10.22.183.106, ipv4=10.22.183.105 +[ 97.240000] br0: port 2(usb0) entered forwarding state +[ 97.240000] br0: port 1(eth0.1) entered forwarding state diff --git a/application/quectel_CM_5G/src/log/gobinet_qmap=4.txt b/application/quectel_CM_5G/src/log/gobinet_qmap=4.txt new file mode 100644 index 0000000..5ec02bf --- /dev/null +++ b/application/quectel_CM_5G/src/log/gobinet_qmap=4.txt @@ -0,0 +1,146 @@ +root@ZhuoTK:/# insmod GobiNet.ko qmap_mode=4 +[ 970.380000] GobiNet: Quectel_Linux&Android_GobiNet_Driver_V1.6.2.13 +[ 970.380000] usbcore: registered new interface driver GobiNet +[ 989.620000] usb 1-1.3: new high-speed USB device number 6 using ehci-platform +[ 989.860000] GobiNet 1-1.3:1.4 usb0: register 'GobiNet' at usb-101c0000.ehci-1.3, GobiNet Ethernet Device, 02:50:f4:00:00:00 +[ 989.870000] creating qcqmi0 +[ 989.880000] GobiNet::qmap_register_device usb0.1 +[ 989.880000] GobiNet::qmap_register_device usb0.2 +[ 989.890000] GobiNet::qmap_register_device usb0.3 +[ 989.890000] GobiNet::qmap_register_device usb0.4 +[ 994.820000] GobiNet::QMIWDASetDataFormat qmap settings qmap_version=5, rx_size=4096, tx_size=4096 +[ 994.830000] GobiNet::QMIWDASetDataFormat qmap settings ul_data_aggregation_max_size=4096, ul_data_aggregation_max_datagrams=16 + +root@ZhuoTK:/# quectel-CM -n 1 -s cmnet & +[04-13_03:35:31:878] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_03:35:31:881] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x006 +[04-13_03:35:31:882] Auto find qmichannel = /dev/qcqmi0 +[04-13_03:35:31:882] Auto find usbnet_adapter = usb0 +[04-13_03:35:31:883] netcard driver = GobiNet, driver version = V1.6.2.13 +[04-13_03:35:31:883] qmap_mode = 4, qmap_version = 5, qmap_size = 4096, muxid = 0x81, qmap_netcard = usb0.1 +[04-13_03:35:31:883] Modem works in QMI mode +[04-13_03:35:31:896] Get clientWDS = 7 +[04-13_03:35:31:927] Get clientDMS = 8 +[04-13_03:35:31:959] Get clientNAS = 9 +[04-13_03:35:31:992] Get clientUIM = 10 +[04-13_03:35:32:024] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_03:35:32:152] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_03:35:32:152] requestSetProfile[1] cmnet///0 +[04-13_03:35:32:216] requestGetProfile[1] cmnet///0 +[04-13_03:35:32:248] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_03:35:32:279] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-13_03:35:32:280] ifconfig usb0 down +[04-13_03:35:32:290] ifconfig usb0.1 0.0.0.0 +[04-13_03:35:32:301] ifconfig usb0.1 down +[04-13_03:35:32:344] requestSetupDataCall WdsConnectionIPv4Handle: 0x8723eef0 +[ 1037.570000] net usb0: link_state 0x0 -> 0x1 +[04-13_03:35:32:477] ifconfig usb0 up +[04-13_03:35:32:496] ifconfig usb0.1 up +[04-13_03:35:32:508] you are use OpenWrt? +[04-13_03:35:32:509] should not calling udhcpc manually? +[04-13_03:35:32:509] should modify /etc/config/network as below? +[04-13_03:35:32:509] config interface wan +[04-13_03:35:32:509] option ifname usb0.1 +[04-13_03:35:32:509] option proto dhcp +[04-13_03:35:32:509] should use "/sbin/ifstaus wan" to check usb0.1 's status? +[04-13_03:35:32:510] busybox udhcpc -f -n -q -t 5 -i usb0.1 +[04-13_03:35:32:520] udhcpc (v1.23.2) started +[04-13_03:35:32:532] Sending discover... +[04-13_03:35:32:540] Sending select for 10.187.142.20... +[04-13_03:35:32:545] Lease of 10.187.142.20 obtained, lease time 7200 +[04-13_03:35:32:550] udhcpc: ifconfig usb0.1 10.187.142.20 netmask 255.255.255.248 broadcast + +[04-13_03:35:32:560] udhcpc: setting default routers: 10.187.142.21 + +root@ZhuoTK:/# quectel-CM -n 2 -s 4gnet & +[04-13_03:35:38:766] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_03:35:38:769] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x006 +[04-13_03:35:38:770] Auto find qmichannel = /dev/qcqmi0 +[04-13_03:35:38:770] Auto find usbnet_adapter = usb0 +[04-13_03:35:38:771] netcard driver = GobiNet, driver version = V1.6.2.13 +[04-13_03:35:38:771] qmap_mode = 4, qmap_version = 5, qmap_size = 4096, muxid = 0x82, qmap_netcard = usb0.2 +[04-13_03:35:38:771] Modem works in QMI mode +[04-13_03:35:38:809] Get clientWDS = 7 +[04-13_03:35:38:841] Get clientDMS = 8 +[04-13_03:35:38:873] Get clientNAS = 9 +[04-13_03:35:38:905] Get clientUIM = 10 +[04-13_03:35:38:937] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_03:35:39:065] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_03:35:39:065] requestSetProfile[2] 4gnet///0 +[04-13_03:35:39:129] requestGetProfile[2] 4gnet///0 +[04-13_03:35:39:161] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_03:35:39:193] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-13_03:35:39:193] ifconfig usb0.2 0.0.0.0 +[04-13_03:35:39:206] ifconfig usb0.2 down +[04-13_03:35:39:417] requestSetupDataCall WdsConnectionIPv4Handle: 0x87252eb0 +[ 1044.650000] net usb0: link_state 0x1 -> 0x3 +[04-13_03:35:39:550] ifconfig usb0 up +[04-13_03:35:39:560] ifconfig usb0.2 up +[04-13_03:35:39:573] you are use OpenWrt? +[04-13_03:35:39:573] should not calling udhcpc manually? +[04-13_03:35:39:573] should modify /etc/config/network as below? +[04-13_03:35:39:573] config interface wan +[04-13_03:35:39:573] option ifname usb0.2 +[04-13_03:35:39:573] option proto dhcp +[04-13_03:35:39:573] should use "/sbin/ifstaus wan" to check usb0.2 's status? +[04-13_03:35:39:574] busybox udhcpc -f -n -q -t 5 -i usb0.2 +[04-13_03:35:39:585] udhcpc (v1.23.2) started +[04-13_03:35:39:597] Sending discover... +[04-13_03:35:39:601] Sending select for 10.197.125.183... +[04-13_03:35:39:606] Lease of 10.197.125.183 obtained, lease time 7200 +[04-13_03:35:39:611] udhcpc: ifconfig usb0.2 10.197.125.183 netmask 255.255.255.240 broadcast + +[04-13_03:35:39:621] udhcpc: setting default routers: 10.197.125.184 + +root@ZhuoTK:/# ifconfig usb0.1 +usb0.1 Link encap:Ethernet HWaddr 02:50:F4:00:00:00 + inet addr:10.187.142.20 Mask:255.255.255.248 + inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link + UP RUNNING NOARP MTU:1500 Metric:1 + RX packets:4 errors:0 dropped:0 overruns:0 frame:0 + TX packets:10 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:764 (764.0 B) TX bytes:1824 (1.7 KiB) + +root@ZhuoTK:/# ifconfig usb0.2 +usb0.2 Link encap:Ethernet HWaddr 02:50:F4:00:00:00 + inet addr:10.197.125.183 Mask:255.255.255.240 + inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link + UP RUNNING NOARP MTU:1500 Metric:1 + RX packets:3 errors:0 dropped:0 overruns:0 frame:0 + TX packets:9 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:688 (688.0 B) TX bytes:1224 (1.1 KiB) + +root@ZhuoTK:/# ip ro add 8.8.8.8/32 dev usb0.1 +root@ZhuoTK:/# ping 8.8.8.8 +PING 8.8.8.8 (8.8.8.8): 56 data bytes +64 bytes from 8.8.8.8: seq=0 ttl=52 time=74.450 ms + +root@ZhuoTK:/# ip ro del 8.8.8.8/32 +root@ZhuoTK:/# ip ro del 8.8.8.8/32 +RTNETLINK answers: No such process + +root@ZhuoTK:/# ip ro add 8.8.8.8/32 dev usb0.2 +root@ZhuoTK:/# ping 8.8.8.8 +PING 8.8.8.8 (8.8.8.8): 56 data bytes +64 bytes from 8.8.8.8: seq=0 ttl=52 time=257.851 ms + +root@ZhuoTK:/# quectel-CM -k 2 +[04-13_03:39:16:986] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_03:39:16:988] /proc/2294/cmdline: quectel-CM -n 2 -s 4gnet +[04-13_03:39:16:988] send SIGINT to process 2294 +[04-13_03:39:16:989] requestDeactivateDefaultPDP WdsConnectionIPv4Handle +[ 1262.310000] net usb0: link_state 0x3 -> 0x1 +[04-13_03:39:17:216] ifconfig usb0.2 0.0.0.0 +[04-13_03:39:17:228] ifconfig usb0.2 down +[04-13_03:39:17:370] GobiNetThread exit +[04-13_03:39:17:371] qmi_main exit + +[2]+ Done quectel-CM -n 2 -s 4gnet + +root@ZhuoTK:/# ifconfig usb0.2 +usb0.2 Link encap:Ethernet HWaddr 02:50:F4:00:00:00 + NOARP MTU:1500 Metric:1 + RX packets:30 errors:0 dropped:0 overruns:0 frame:0 + TX packets:35 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:2816 (2.7 KiB) TX bytes:3408 (3.3 KiB) diff --git a/application/quectel_CM_5G/src/log/gobinet_qmap=4_bridge.txt b/application/quectel_CM_5G/src/log/gobinet_qmap=4_bridge.txt new file mode 100644 index 0000000..ebfd4fb --- /dev/null +++ b/application/quectel_CM_5G/src/log/gobinet_qmap=4_bridge.txt @@ -0,0 +1,114 @@ +root@ZhuoTK:/# insmod GobiNet.ko qmap_mode=4 +[ 42.120000] GobiNet: Quectel_Linux&Android_GobiNet_Driver_V1.6.2.13 +[ 42.130000] GobiNet 1-1.3:1.4 usb0: register 'GobiNet' at usb-101c0000.ehci-1.3, GobiNet Ethernet Device, 02:50:f4:00:00:00 +[ 42.140000] creating qcqmi0 +[ 42.150000] GobiNet::qmap_register_device usb0.1 +[ 42.150000] GobiNet::qmap_register_device usb0.2 +[ 42.160000] GobiNet::qmap_register_device usb0.3 +[ 42.160000] GobiNet::qmap_register_device usb0.4 +[ 42.170000] usbcore: registered new interface driver GobiNet +[ 43.270000] GobiNet::QMIWDASetDataFormat qmap settings qmap_version=5, rx_size=4096, tx_size=4096 +[ 43.280000] GobiNet::QMIWDASetDataFormat qmap settings ul_data_aggregation_max_size=4096, ul_data_aggregation_max_datagrams=16 + +root@ZhuoTK:/# brctl addbr br0 +root@ZhuoTK:/# brctl addif br0 eth0.1 +root@ZhuoTK:/# brctl addif br0 usb0.1 +root@ZhuoTK:/# brctl show +bridge name bridge id STP enabled interfaces +br0 8000.00ca019197b9 no eth0.1 + usb0.1 + +root@ZhuoTK:/# quectel-CM -n 1 -s cmnet -b & +[04-13_05:12:42:155] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_05:12:42:158] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x003 +[ 86.130000] net usb0.1: bridge_mode change to 0x1 +[04-13_05:12:42:159] Auto find qmichannel = /dev/qcqmi0 +[04-13_05:12:42:160] Auto find usbnet_adapter = usb0 +[04-13_05:12:42:160] netcard driver = GobiNet, driver version = V1.6.2.13 +[04-13_05:12:42:160] qmap_mode = 4, qmap_version = 5, qmap_size = 4096, muxid = 0x81, qmap_netcard = usb0.1 +[04-13_05:12:42:166] Modem works in QMI mode +[04-13_05:12:42:181] Get clientWDS = 7 +[04-13_05:12:42:213] Get clientDMS = 8 +[04-13_05:12:42:246] Get clientNAS = 9 +[04-13_05:12:42:278] Get clientUIM = 10 +[04-13_05:12:42:310] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_05:12:42:438] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_05:12:42:439] requestSetProfile[1] cmnet///0 +[04-13_05:12:42:502] requestGetProfile[1] cmnet///0 +[04-13_05:12:42:534] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_05:12:42:565] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-13_05:12:42:566] ifconfig usb0 down +[04-13_05:12:42:576] ifconfig usb0.1 0.0.0.0 +[04-13_05:12:42:587] ifconfig usb0.1 down +[04-13_05:12:42:629] requestSetupDataCall WdsConnectionIPv4Handle: 0x8724d740 +[ 86.730000] net usb0: link_state 0x0 -> 0x1 +[04-13_05:12:42:762] ifconfig usb0 up +[04-13_05:12:42:782] ifconfig usb0.1 up +[04-13_05:12:42:794] echo '0xa16b769' > /sys/class/net/usb0.1/bridge_ipv4 + +root@ZhuoTK:/# ifconfig br0 up +[ 98.270000] usb0.1 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 98.360000] usb0.1 sip = 0.0.0.0, tip=10.22.183.105, ipv4=10.22.183.105 +[ 98.370000] usb0.1 sip = 10.22.183.105, tip=10.22.183.106, ipv4=10.22.183.105 +[ 99.360000] usb0.1 sip = 0.0.0.0, tip=10.22.183.105, ipv4=10.22.183.105 +[ 100.360000] usb0.1 sip = 0.0.0.0, tip=10.22.183.105, ipv4=10.22.183.105 +[ 100.500000] usb0.1 sip = 10.22.183.105, tip=10.22.183.106, ipv4=10.22.183.105 + +root@ZhuoTK:/# quectel-CM -n 2 -s 4gnet & +[04-13_05:13:05:715] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_05:13:05:717] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x003 +[04-13_05:13:05:719] Auto find qmichannel = /dev/qcqmi0 +[04-13_05:13:05:719] Auto find usbnet_adapter = usb0 +[04-13_05:13:05:719] netcard driver = GobiNet, driver version = V1.6.2.13 +[04-13_05:13:05:719] qmap_mode = 4, qmap_version = 5, qmap_size = 4096, muxid = 0x82, qmap_netcard = usb0.2 +[04-13_05:13:05:720] Modem works in QMI mode +[04-13_05:13:05:734] Get clientWDS = 7 +[04-13_05:13:05:766] Get clientDMS = 8 +[04-13_05:13:05:798] Get clientNAS = 9 +[04-13_05:13:05:830] Get clientUIM = 10 +[04-13_05:13:05:861] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_05:13:05:990] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_05:13:05:991] requestSetProfile[2] 4gnet///0 +[04-13_05:13:06:054] requestGetProfile[2] 4gnet///0 +[04-13_05:13:06:086] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_05:13:06:118] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-13_05:13:06:119] ifconfig usb0.2 0.0.0.0 +[04-13_05:13:06:131] ifconfig usb0.2 down +[04-13_05:13:06:375] requestSetupDataCall WdsConnectionIPv4Handle: 0x872b8c50 +[ 110.470000] net usb0: link_state 0x1 -> 0x3 +[04-13_05:13:06:507] ifconfig usb0 up +[04-13_05:13:06:518] ifconfig usb0.2 up +[04-13_05:13:06:539] you are use OpenWrt? +[04-13_05:13:06:540] should not calling udhcpc manually? +[04-13_05:13:06:540] should modify /etc/config/network as below? +[04-13_05:13:06:540] config interface wan +[04-13_05:13:06:540] option ifname usb0.2 +[04-13_05:13:06:540] option proto dhcp +[04-13_05:13:06:540] should use "/sbin/ifstaus wan" to check usb0.2 's status? +[04-13_05:13:06:540] busybox udhcpc -f -n -q -t 5 -i usb0.2 +[04-13_05:13:06:554] udhcpc (v1.23.2) started +[04-13_05:13:06:614] Sending discover... +[04-13_05:13:06:619] Sending select for 10.22.58.141... +[04-13_05:13:06:623] Lease of 10.22.58.141 obtained, lease time 7200 +[04-13_05:13:06:629] udhcpc: ifconfig usb0.2 10.22.58.141 netmask 255.255.255.252 broadcast + +[04-13_05:13:06:638] udhcpc: setting default routers: 10.22.58.142 + +root@ZhuoTK:/# ifconfig usb0.2 +usb0.2 Link encap:Ethernet HWaddr 02:50:F4:00:00:00 + inet addr:10.22.58.141 Mask:255.255.255.252 + 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:7 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:612 (612.0 B) TX bytes:1064 (1.0 KiB) + +root@ZhuoTK:/# ip ro show +default via 10.22.58.142 dev usb0.2 +10.22.58.140/30 dev usb0.2 proto kernel scope link src 10.22.58.141 +192.168.1.0/24 dev br-lan proto kernel scope link src 192.168.1.251 + +root@ZhuoTK:/# ping 8.8.8.8 +PING 8.8.8.8 (8.8.8.8): 56 data bytes +64 bytes from 8.8.8.8: seq=0 ttl=52 time=69.822 ms + diff --git a/application/quectel_CM_5G/src/log/pcie_mhi_mbim.txt b/application/quectel_CM_5G/src/log/pcie_mhi_mbim.txt new file mode 100644 index 0000000..7a988a9 --- /dev/null +++ b/application/quectel_CM_5G/src/log/pcie_mhi_mbim.txt @@ -0,0 +1,80 @@ +root@OpenWrt:/# lspci +00:00.0 Class 0604: 17cb:1001 +01:00.0 Class ff00: 17cb:0306 + +root@OpenWrt:/# insmod pcie_mhi.ko mhi_mbim_enabled=1 +[ 63.094154] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.17 +[ 63.094739] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306 +[ 63.099373] mhi_q 0000:01:00.0: BAR 0: assigned [mem 0x48000000-0x48000fff 64bit] +[ 63.108476] mhi_q 0000:01:00.0: enabling device (0140 -> 0142) +[ 63.293451] [I][mhi_netdev_enable_iface] Prepare the channels for transfer +[ 63.324757] [I][mhi_netdev_enable_iface] Exited. +[ 63.326265] rmnet_vnd_register_device(rmnet_mhi0.1)=0 + +root@OpenWrt:/# quectel-CM -s cment & +[04-13_09:25:23:910] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_09:25:23:912] network interface '' or qmidev '' is not exist +[04-13_09:25:23:912] netcard driver = pcie_mhi, driver version = V1.3.0.17 +[04-13_09:25:23:913] mbim_qmap_mode = 1, vlan_id = 0x00, qmap_netcard = rmnet_mhi0.1 +[04-13_09:25:23:913] Modem works in MBIM mode +[04-13_09:25:23:965] cdc_wdm_fd = 7 +[04-13_09:25:23:965] mbim_open_device() +[04-13_09:25:24:549] mbim_device_caps_query() +[04-13_09:25:24:575] DeviceId: 869710030002905 +[04-13_09:25:24:575] FirmwareInfo: RM500QGLABR10A03M4G_01.001.03 +[04-13_09:25:24:575] HardwareInfo: RM500QGL_VH +[04-13_09:25:24:576] mbim_device_services_query() +[04-13_09:25:24:585] mbim_set_radio_state( 1 ) +[04-13_09:25:24:588] HwRadioState: 1, SwRadioState: 1 +[04-13_09:25:24:588] mbim_subscriber_status_query() +[04-13_09:25:24:612] SubscriberId: 460028563800461 +[04-13_09:25:24:612] SimIccId: 89860015120716380461 +[04-13_09:25:24:613] SubscriberReadyState NotInitialized -> Initialized +[04-13_09:25:24:613] mbim_register_state_query() +[04-13_09:25:24:617] RegisterState Unknown -> Home +[04-13_09:25:24:617] mbim_packet_service_query() +[04-13_09:25:24:619] PacketServiceState Unknown -> Attached +[04-13_09:25:24:619] CurrentDataClass = 5G_NSA +[04-13_09:25:24:620] mbim_query_connect(sessionID=0) +[04-13_09:25:24:631] ActivationState Unknown -> Deactivated +[04-13_09:25:24:631] ifconfig rmnet_mhi0 down +[04-13_09:25:24:657] ifconfig rmnet_mhi0.1 0.0.0.0 +ifconfig: SIOCSIFFLAGS: Network is down +[04-13_09:25:24:681] ifconfig rmnet_mhi0.1 down +[04-13_09:25:24:705] mbim_register_state_query() +[04-13_09:25:24:709] mbim_packet_service_query() +[04-13_09:25:24:713] CurrentDataClass = 5G_NSA +[04-13_09:25:24:713] mbim_set_connect(onoff=1, sessionID=0) +[04-13_09:25:25:096] ActivationState Deactivated -> Activated +[04-13_09:25:25:097] mbim_ip_config(sessionID=0) +[04-13_09:25:25:100] < SessionId = 0 +[04-13_09:25:25:100] < IPv4ConfigurationAvailable = 0xf +[04-13_09:25:25:100] < IPv6ConfigurationAvailable = 0x0 +[04-13_09:25:25:101] < IPv4AddressCount = 0x1 +[04-13_09:25:25:101] < IPv4AddressOffset = 0x3c +[04-13_09:25:25:101] < IPv6AddressCount = 0x0 +[04-13_09:25:25:102] < IPv6AddressOffset = 0x0 +[04-13_09:25:25:102] < IPv4 = 10.190.166.229/30 +[04-13_09:25:25:103] < gw = 10.190.166.230 +[04-13_09:25:25:103] < dns1 = 211.138.180.2 +[04-13_09:25:25:103] < dns2 = 211.138.180.3 +[04-13_09:25:25:104] < ipv4 mtu = 1500 +[04-13_09:25:25:112] ifconfig rmnet_mhi0 up +[04-13_09:25:25:141] ifconfig rmnet_mhi0.1 up +[04-13_09:25:25:170] ip -4 address flush dev rmnet_mhi0.1 +[04-13_09:25:25:190] ip -4 address add 10.190.166.229/30 dev rmnet_mhi0.1 +[04-13_09:25:25:213] ip -4 route add default via 10.190.166.230 dev rmnet_mhi0.1 + +root@OpenWrt:/# ifconfig rmnet_mhi0.1 +rmnet_mhi0.1 Link encap:Ethernet HWaddr 02:50:F4:00:00:00 + inet addr:10.190.166.229 Mask:255.255.255.252 + inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link + UP RUNNING NOARP MTU:1500 Metric:1 + RX packets:19 errors:0 dropped:0 overruns:0 frame:0 + TX packets:29 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:2326 (2.2 KiB) TX bytes:2991 (2.9 KiB) + +root@OpenWrt:/# ping 8.8.8.8 +PING 8.8.8.8 (8.8.8.8): 56 data bytes +64 bytes from 8.8.8.8: seq=0 ttl=52 time=278.561 ms diff --git a/application/quectel_CM_5G/src/log/pcie_mhi_mbim_qmap=4.txt b/application/quectel_CM_5G/src/log/pcie_mhi_mbim_qmap=4.txt new file mode 100644 index 0000000..4d6cdf0 --- /dev/null +++ b/application/quectel_CM_5G/src/log/pcie_mhi_mbim_qmap=4.txt @@ -0,0 +1,170 @@ +root@OpenWrt:/# lspci +00:00.0 Class 0604: 17cb:1001 +01:00.0 Class ff00: 17cb:0304 +root@OpenWrt:/# insmod pcie_mhi.ko mhi_mbim_enabled=1 qmap_mode=4 +[ 76.596827] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.17 +[ 76.598596] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0304 +[ 76.602863] mhi_q 0000:01:00.0: BAR 0: assigned [mem 0x48000000-0x48000fff 64bit] +[ 76.611323] mhi_q 0000:01:00.0: enabling device (0140 -> 0142) +[ 76.760239] [I][mhi_netdev_enable_iface] Prepare the channels for transfer +[ 76.828699] [I][mhi_netdev_enable_iface] Exited. +[ 76.832727] rmnet_vnd_register_device(rmnet_mhi0.1)=0 +[ 76.836596] rmnet_vnd_register_device(rmnet_mhi0.2)=0 +[ 76.841170] rmnet_vnd_register_device(rmnet_mhi0.3)=0 +[ 76.846373] rmnet_vnd_register_device(rmnet_mhi0.4)=0 + +root@OpenWrt:~# quectel-mbim-proxy -d /dev/mhi_MBIM & +root@OpenWrt:~# [04-14_03:05:36:296] mbim_dev_fd=3 +[04-14_03:05:36:297] mbim_send_open_msg() +[04-14_03:05:36:669] receive MBIM_OPEN_DONE, status=0 +[04-14_03:05:36:670] mbim_server_fd=4 + +root@OpenWrt:~# quectel-CM -n 1 -s cmnet & +[04-14_03:05:45:955] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-14_03:05:45:956] network interface '' or qmidev '' is not exist +[04-14_03:05:45:957] netcard driver = pcie_mhi, driver version = V1.3.0.17 +[04-14_03:05:45:957] mbim_qmap_mode = 4, vlan_id = 0x01, qmap_netcard = rmnet_mhi0.1 +[04-14_03:05:45:958] Modem works in MBIM mode +[04-14_03:05:45:959] connect to quectel-mbim-proxy sockfd = 7 +[04-14_03:05:45:959] handle_client_connect client_fd=5, client_idx=1 +[04-14_03:05:45:959] cdc_wdm_fd = 7 +[04-14_03:05:45:960] mbim_open_device() +[04-14_03:05:45:961] mbim_device_caps_query() +[04-14_03:05:45:967] DeviceId: 860459050041596 +[04-14_03:05:45:968] FirmwareInfo: EM120RGLAPR02A03M4G_01.001.07 + +[04-14_03:05:45:968] HardwareInfo: EM120R_GL +[04-14_03:05:45:968] mbim_device_services_query() +[04-14_03:05:45:972] mbim_set_radio_state( 1 ) +[04-14_03:05:45:976] HwRadioState: 1, SwRadioState: 1 +[04-14_03:05:45:976] mbim_subscriber_status_query() +[04-14_03:05:45:985] SubscriberId: 460028563800461 +[04-14_03:05:45:985] SimIccId: 89860015120716380461 +[04-14_03:05:45:986] SubscriberReadyState NotInitialized -> Initialized +[04-14_03:05:45:986] mbim_register_state_query() +[04-14_03:05:45:991] RegisterState Unknown -> Home +[04-14_03:05:45:991] mbim_packet_service_query() +[04-14_03:05:45:995] PacketServiceState Unknown -> Attached +[04-14_03:05:45:996] mbim_query_connect(sessionID=1) +[04-14_03:05:46:000] ActivationState Unknown -> Deactivated +[04-14_03:05:46:000] ifconfig rmnet_mhi0 down +[04-14_03:05:46:024] ifconfig rmnet_mhi0.1 0.0.0.0 +ifconfig: SIOCSIFFLAGS: Network is down +[04-14_03:05:46:049] ifconfig rmnet_mhi0.1 down +[04-14_03:05:46:072] mbim_set_connect(onoff=1, sessionID=1) +[04-14_03:05:46:099] ActivationState Deactivated -> Activated +[04-14_03:05:46:099] mbim_ip_config(sessionID=1) +[ 222.484298] net rmnet_mhi0: link_state 0x0 -> 0x1 +[04-14_03:05:46:103] < SessionId = 1 +[04-14_03:05:46:104] < IPv4ConfigurationAvailable = 0xf +[04-14_03:05:46:104] < IPv6ConfigurationAvailable = 0x0 +[04-14_03:05:46:104] < IPv4AddressCount = 0x1 +[04-14_03:05:46:105] < IPv4AddressOffset = 0x3c +[ 222.507775] [I][mhi_netdev_open] Opened net dev interface +[04-14_03:05:46:105] < IPv6AddressCount = 0x0 +[04-14_03:05:46:105] < IPv6AddressOffset = 0x0 +[04-14_03:05:46:106] < IPv4 = 10.38.21.158/30 +[04-14_03:05:46:106] < gw = 10.38.21.157 +[04-14_03:05:46:106] < dns1 = 211.138.180.2 +[04-14_03:05:46:107] < dns2 = 211.138.180.3 +[04-14_03:05:46:107] < ipv4 mtu = 1500 +[04-14_03:05:46:112] ifconfig rmnet_mhi0 up +[04-14_03:05:46:140] ifconfig rmnet_mhi0.1 up +[04-14_03:05:46:168] ip -4 address flush dev rmnet_mhi0.1 +[04-14_03:05:46:190] ip -4 address add 10.38.21.158/30 dev rmnet_mhi0.1 +[04-14_03:05:46:212] ip -4 route add default via 10.38.21.157 dev rmnet_mhi0.1 +[04-14_03:05:50:730] handle_client_connect client_fd=6, client_idx=2 +[ 227.558631] net rmnet_mhi0: link_state 0x1 -> 0x3 + +root@OpenWrt:~# quectel-CM -n 2 -s 4gnet +[04-14_03:05:50:725] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-14_03:05:50:726] network interface '' or qmidev '' is not exist +[04-14_03:05:50:727] netcard driver = pcie_mhi, driver version = V1.3.0.17 +[04-14_03:05:50:728] mbim_qmap_mode = 4, vlan_id = 0x02, qmap_netcard = rmnet_mhi0.2 +[04-14_03:05:50:729] Modem works in MBIM mode +[04-14_03:05:50:730] connect to quectel-mbim-proxy sockfd = 8 +[04-14_03:05:50:730] cdc_wdm_fd = 8 +[04-14_03:05:50:731] mbim_open_device() +[04-14_03:05:50:732] mbim_device_caps_query() +[04-14_03:05:50:738] DeviceId: 860459050041596 +[04-14_03:05:50:739] FirmwareInfo: EM120RGLAPR02A03M4G_01.001.07 + +[04-14_03:05:50:739] HardwareInfo: EM120R_GL +[04-14_03:05:50:740] mbim_device_services_query() +[04-14_03:05:50:744] mbim_set_radio_state( 1 ) +[04-14_03:05:50:747] HwRadioState: 1, SwRadioState: 1 +[04-14_03:05:50:747] mbim_subscriber_status_query() +[04-14_03:05:50:757] SubscriberId: 460028563800461 +[04-14_03:05:50:758] SimIccId: 89860015120716380461 +[04-14_03:05:50:758] SubscriberReadyState NotInitialized -> Initialized +[04-14_03:05:50:759] mbim_register_state_query() +[04-14_03:05:50:763] RegisterState Unknown -> Home +[04-14_03:05:50:764] mbim_packet_service_query() +[04-14_03:05:50:768] PacketServiceState Unknown -> Attached +[04-14_03:05:50:769] mbim_query_connect(sessionID=2) +[04-14_03:05:50:772] ActivationState Unknown -> Deactivated +[04-14_03:05:50:773] ifconfig rmnet_mhi0.2 0.0.0.0 +[04-14_03:05:50:799] ifconfig rmnet_mhi0.2 down +[04-14_03:05:50:834] mbim_set_connect(onoff=1, sessionID=2) +[04-14_03:05:51:170] ActivationState Deactivated -> Activated +[04-14_03:05:51:171] mbim_ip_config(sessionID=2) +[04-14_03:05:51:174] < SessionId = 2 +[04-14_03:05:51:174] < IPv4ConfigurationAvailable = 0xf +[04-14_03:05:51:175] < IPv6ConfigurationAvailable = 0x0 +[04-14_03:05:51:175] < IPv4AddressCount = 0x1 +[04-14_03:05:51:175] < IPv4AddressOffset = 0x3c +[04-14_03:05:51:176] < IPv6AddressCount = 0x0 +[04-14_03:05:51:176] < IPv6AddressOffset = 0x0 +[04-14_03:05:51:176] < IPv4 = 10.36.109.217/30 +[04-14_03:05:51:177] < gw = 10.36.109.218 +[04-14_03:05:51:177] < dns1 = 211.138.180.2 +[04-14_03:05:51:178] < dns2 = 211.138.180.3 +[04-14_03:05:51:178] < ipv4 mtu = 1500 +[04-14_03:05:51:182] ifconfig rmnet_mhi0 up +[04-14_03:05:51:206] ifconfig rmnet_mhi0.2 up +[04-14_03:05:51:233] ip -4 address flush dev rmnet_mhi0.2 +[04-14_03:05:51:254] ip -4 address add 10.36.109.217/30 dev rmnet_mhi0.2 +[04-14_03:05:51:277] ip -4 route add default via 10.36.109.218 dev rmnet_mhi0.2 + +root@OpenWrt:~# ifconfig rmnet_mhi0.1 +rmnet_mhi0.1 Link encap:Ethernet HWaddr 02:50:F4:00:00:00 + inet addr:10.38.21.158 Mask:255.255.255.252 + inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link + UP RUNNING NOARP MTU:1500 Metric:1 + RX packets:37 errors:0 dropped:0 overruns:0 frame:0 + TX packets:29 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:9907 (9.6 KiB) TX bytes:2764 (2.6 KiB) + +root@OpenWrt:~# ifconfig rmnet_mhi0.2 +rmnet_mhi0.2 Link encap:Ethernet HWaddr 02:50:F4:00:00:00 + inet addr:10.36.109.217 Mask:255.255.255.252 + inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link + UP RUNNING NOARP MTU:1500 Metric:1 + RX packets:5 errors:0 dropped:0 overruns:0 frame:0 + TX packets:18 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:344 (344.0 B) TX bytes:1152 (1.1 KiB) + +root@OpenWrt:~# ip ro del 8.8.8.8/32 +RTNETLINK answers: No such process +root@OpenWrt:~# ip ro add 8.8.8.8/32 dev rmnet_mhi0.1 +root@OpenWrt:~# ping 8.8.8.8 +PING 8.8.8.8 (8.8.8.8): 56 data bytes +64 bytes from 8.8.8.8: seq=0 ttl=52 time=73.248 ms + +root@OpenWrt:~# ip ro del 8.8.8.8/32 +root@OpenWrt:~# ip ro del 8.8.8.8/32 +RTNETLINK answers: No such process +root@OpenWrt:~# ip ro add 8.8.8.8/32 dev rmnet_mhi0.2 +root@OpenWrt:~# ping 8.8.8.8 +PING 8.8.8.8 (8.8.8.8): 56 data bytes +64 bytes from 8.8.8.8: seq=0 ttl=52 time=99.637 ms + +root@OpenWrt:~# quectel-CM -k 2 +[04-14_03:06:58:912] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-14_03:06:59:063] /proc/3565/cmdline: quectel-CM -n 2 -s 4gnet +[04-14_03:06:59:064] send SIGINT to process 3565 +[ 295.719442] net rmnet_mhi0: link_state 0x3 -> 0x1 +[04-14_03:06:59:407] proxy_loop poll fd = 6, revents = 0011 +[04-14_03:06:59:408] handle_client_disconnect client_fd=6, client_idx=2 diff --git a/application/quectel_CM_5G/src/log/pcie_mhi_qmap=1.txt b/application/quectel_CM_5G/src/log/pcie_mhi_qmap=1.txt new file mode 100644 index 0000000..c93af7e --- /dev/null +++ b/application/quectel_CM_5G/src/log/pcie_mhi_qmap=1.txt @@ -0,0 +1,127 @@ +root@OpenWrt:/# cat /sys/class/net/rmnet_mhi0/qmap_mode +1 +root@OpenWrt:/# cat /sys/module/pcie_mhi/parameters/mhi_mbim_enabled +0 +root@OpenWrt:/# dmesg | grep mhi +[ 18.442226] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.17 +[ 18.443032] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306 +[ 18.447488] mhi_q 0000:01:00.0: BAR 0: assigned [mem 0x48000000-0x48000fff 64bit] +[ 18.456563] mhi_q 0000:01:00.0: enabling device (0140 -> 0142) +[ 18.464184] [I][mhi_init_pci_dev] msi_required = 5, msi_allocated = 5, msi_irq = 197 +[ 18.464215] [I][mhi_power_up] dev_state:RESET +[ 18.464225] [I][mhi_async_power_up] Requested to power on +[ 18.464432] [I][mhi_alloc_coherent] size = 114688, dma_handle = 8d400000 +[ 18.464445] [I][mhi_init_dev_ctxt] mhi_ctxt->ctrl_seg = d1766000 +[ 18.466003] [I][mhi_async_power_up] dev_state:RESET ee:AMSS +[ 18.466080] [I][mhi_pm_st_worker] Transition to state:READY +[ 18.466109] [I][mhi_pm_st_worker] INVALID_EE -> AMSS +[ 18.466135] [I][mhi_ready_state_transition] Waiting to enter READY state +[ 18.466224] [I][mhi_async_power_up] Power on setup success +[ 18.466265] [I][mhi_pci_probe] Return successful +[ 18.577299] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, pm_state:POR +[ 18.577312] [I][mhi_ready_state_transition] Device in READY State +[ 18.577325] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, INVALID_EE +[ 18.577329] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:POR +[ 18.577337] [I][mhi_init_mmio] Initializing MMIO +[ 18.577344] [I][mhi_init_mmio] CHDBOFF:0x300 +[ 18.577361] [I][mhi_init_mmio] ERDBOFF:0x700 +[ 18.577372] [I][mhi_init_mmio] Programming all MMIO values. +[ 18.690834] [I][mhi_dump_tre] carl_ev evt_state_change mhistate=2 +[ 18.690854] [I][mhi_process_ctrl_ev_ring] MHI state change event to state:M0 +[ 18.690866] [I][mhi_pm_m0_transition] Entered With State:READY PM_STATE:POR +[ 18.690879] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:M0 +[ 18.694229] [I][mhi_dump_tre] carl_ev evt_ee_state execenv=2 +[ 18.694241] [I][mhi_process_ctrl_ev_ring] MHI EE received event:AMSS +[ 18.694293] [I][mhi_pm_st_worker] Transition to state:MISSION MODE +[ 18.694310] [I][mhi_pm_st_worker] INVALID_EE -> AMSS +[ 18.694319] [I][mhi_pm_mission_mode_transition] Processing Mission Mode Transition +[ 18.694341] [I][mhi_init_timesync] No timesync capability found +[ 18.694350] [I][mhi_pm_mission_mode_transition] Adding new devices +[ 18.696365] [I][mhi_dtr_probe] Enter for DTR control channel +[ 18.696383] [I][__mhi_prepare_channel] Entered: preparing channel:18 +[ 18.703113] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1, type=33 +[ 18.703164] [I][__mhi_prepare_channel] Chan:18 successfully moved to start state +[ 18.703174] [I][__mhi_prepare_channel] Entered: preparing channel:19 +[ 18.710681] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1, type=33 +[ 18.710734] [I][__mhi_prepare_channel] Chan:19 successfully moved to start state +[ 18.710804] [I][mhi_dtr_probe] Exit with ret:0 +[ 18.711774] [I][mhi_netdev_enable_iface] Prepare the channels for transfer +[ 18.711811] [I][__mhi_prepare_channel] Entered: preparing channel:100 +[ 18.732097] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1, type=33 +[ 18.732151] [I][__mhi_prepare_channel] Chan:100 successfully moved to start state +[ 18.732162] [I][__mhi_prepare_channel] Entered: preparing channel:101 +[ 18.744170] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1, type=33 +[ 18.744219] [I][__mhi_prepare_channel] Chan:101 successfully moved to start state +[ 18.749132] [I][mhi_netdev_enable_iface] Exited. +[ 18.750306] rmnet_vnd_register_device(rmnet_mhi0.1)=0 +[ 18.752927] [I][mhi_pm_mission_mode_transition] Exit with ret:0 + +root@OpenWrt:/# busybox microcom /dev/mhi_DUN +at+cpin? ++CPIN: READY + +OK +at+cops? ++COPS: 0,0,"CHINA MOBILE",13 + +OK +at+csq ++csq: 23,99 + +OK + +root@OpenWrt:/# quectel-CM -s cmnet & +[04-13_09:26:58:077] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_09:26:58:078] network interface '' or qmidev '' is not exist +[04-13_09:26:58:079] netcard driver = pcie_mhi, driver version = V1.3.0.17 +[04-13_09:26:58:080] qmap_mode = 1, qmap_version = 9, qmap_size = 16384, muxid = 0x81, qmap_netcard = rmnet_mhi0.1 +[04-13_09:26:58:080] Modem works in QMI mode +[04-13_09:26:58:131] cdc_wdm_fd = 7 +[04-13_09:26:59:132] QmiWwanInit message timeout +[04-13_09:27:00:140] Get clientWDS = 15 +[04-13_09:27:00:144] Get clientDMS = 1 +[04-13_09:27:00:147] Get clientNAS = 4 +[04-13_09:27:00:151] Get clientUIM = 1 +[04-13_09:27:00:155] Get clientWDA = 1 +[04-13_09:27:00:158] requestBaseBandVersion RM500QGLABR10A03M4G +[04-13_09:27:00:161] qmap_settings.rx_urb_size = 16384 +[04-13_09:27:00:162] qmap_settings.ul_data_aggregation_max_datagrams = 11 +[04-13_09:27:00:162] qmap_settings.ul_data_aggregation_max_size = 8192 +[04-13_09:27:00:163] qmap_settings.dl_minimum_padding = 0 +[04-13_09:27:00:176] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_09:27:00:177] requestSetProfile[1] cmnet///0 +[04-13_09:27:00:190] requestGetProfile[1] cmnet///0 +[04-13_09:27:00:193] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: 5G_NSA +[04-13_09:27:00:197] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-13_09:27:00:198] ifconfig rmnet_mhi0 down +[04-13_09:27:00:222] ifconfig rmnet_mhi0.1 0.0.0.0 +[04-13_09:27:00:247] ifconfig rmnet_mhi0.1 down +[04-13_09:27:00:281] requestSetupDataCall WdsConnectionIPv4Handle: 0x1228bb20 +[ 245.284909] net rmnet_mhi0: link_state 0x0 -> 0x1 +[04-13_09:27:00:293] ifconfig rmnet_mhi0 up +[ 245.308696] [I][mhi_netdev_open] Opened net dev interface +[04-13_09:27:00:318] ifconfig rmnet_mhi0.1 up +[04-13_09:27:00:353] you are use OpenWrt? +[04-13_09:27:00:354] should not calling udhcpc manually? +[04-13_09:27:00:354] should modify /etc/config/network as below? +[04-13_09:27:00:355] config interface wan +[04-13_09:27:00:355] option ifname rmnet_mhi0.1 +[04-13_09:27:00:355] option proto dhcp +[04-13_09:27:00:356] should use "/sbin/ifstaus wan" to check rmnet_mhi0.1 's status? +[04-13_09:27:00:356] busybox udhcpc -f -n -q -t 5 -i rmnet_mhi0.1 +udhcpc: started, v1.28.3 +udhcpc: sending discover +udhcpc: sending select for 10.128.73.23 +udhcpc: lease of 10.128.73.23 obtained, lease time 7200 +[04-13_09:27:00:710] udhcpc: ifconfig rmnet_mhi0.1 10.128.73.23 netmask 255.255.255.240 broadcast + +[04-13_09:27:00:742] udhcpc: setting default routers: 10.128.73.24 + +root@OpenWrt:/# ifconfig rmnet_mhi0.1 +rmnet_mhi0.1 Link encap:Ethernet HWaddr 02:50:F4:00:00:00 + inet addr:10.128.73.23 Mask:255.255.255.240 + 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:612 (612.0 B) TX bytes:684 (684.0 B) diff --git a/application/quectel_CM_5G/src/log/pcie_mhi_qmap=1_bridge.txt b/application/quectel_CM_5G/src/log/pcie_mhi_qmap=1_bridge.txt new file mode 100644 index 0000000..0ab6de7 --- /dev/null +++ b/application/quectel_CM_5G/src/log/pcie_mhi_qmap=1_bridge.txt @@ -0,0 +1,76 @@ +root@OpenWrt:/# lspci +00:00.0 Class 0604: 17cb:1001 +01:00.0 Class ff00: 17cb:0306 + +root@OpenWrt:~# insmod pcie_mhi.ko +[ 77.949271] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.17 +[ 77.950949] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306 +[ 77.955331] mhi_q 0000:01:00.0: BAR 0: assigned [mem 0x48000000-0x48000fff 64bit] +[ 77.963756] mhi_q 0000:01:00.0: enabling device (0140 -> 0142) +[ 78.048911] [I][mhi_netdev_enable_iface] Prepare the channels for transfer +[ 78.092304] [I][mhi_netdev_enable_iface] Exited. +[ 78.096580] rmnet_vnd_register_device(rmnet_mhi0.1)=0 + +root@OpenWrt:~# brctl addbr br0 +root@OpenWrt:~# brctl addif br0 rmnet_mhi0.1 +root@OpenWrt:~# brctl addif br0 eth1 +[ 250.017213] device eth1 entered promiscuous mode +root@OpenWrt:~# brctl show +bridge name bridge id STP enabled interfaces +br0 8000.00001c353487 no eth1 + rmnet_mhi0.1 + +root@OpenWrt:~# quectel-CM -s cmnet -b & +[04-14_06:43:28:473] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-14_06:43:28:474] network interface '' or qmidev '' is not exist +[04-14_06:43:28:475] netcard driver = pcie_mhi, driver version = V1.3.0.17 +[04-14_06:43:28:476] qmap_mode = 1, qmap_version = 9, qmap_size = 16384, muxid = 0x81, qmap_netcard = rmnet_mhi0.1 +[04-14_06:43:28:477] Modem works in QMI mode +[04-14_06:43:28:531] cdc_wdm_fd = 7 +[04-14_06:43:29:532] QmiWwanInit message timeout +[04-14_06:43:30:540] Get clientWDS = 15 +[04-14_06:43:30:543] Get clientDMS = 1 +[04-14_06:43:30:546] Get clientNAS = 4 +[04-14_06:43:30:550] Get clientUIM = 1 +[04-14_06:43:30:553] Get clientWDA = 1 +[04-14_06:43:30:557] requestBaseBandVersion RM500QGLABR10A03M4G +[04-14_06:43:30:560] qmap_settings.rx_urb_size = 16384 +[04-14_06:43:30:561] qmap_settings.ul_data_aggregation_max_datagrams = 11 +[04-14_06:43:30:561] qmap_settings.ul_data_aggregation_max_size = 8192 +[04-14_06:43:30:561] qmap_settings.dl_minimum_padding = 0 +[04-14_06:43:30:575] requestGetSIMStatus SIMStatus: SIM_READY +[04-14_06:43:30:575] requestSetProfile[1] cmnet///0 +[04-14_06:43:30:588] requestGetProfile[1] cmnet///0 +[04-14_06:43:30:591] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: 5G_NSA +[04-14_06:43:30:595] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-14_06:43:30:595] ifconfig rmnet_mhi0 down +[04-14_06:43:30:620] ifconfig rmnet_mhi0.1 0.0.0.0 +ifconfig: SIOCSIFFLAGS: Network is down +[04-14_06:43:30:644] ifconfig rmnet_mhi0.1 down +[04-14_06:43:30:679] requestSetupDataCall WdsConnectionIPv4Handle: 0xb41f47d0 +[ 263.869899] net rmnet_mhi0: link_state 0x0 -> 0x1 +[04-14_06:43:30:693] ifconfig rmnet_mhi0 up +[ 263.892647] [I][mhi_netdev_open] Opened net dev interface +[04-14_06:43:30:718] ifconfig rmnet_mhi0.1 up +[04-14_06:43:30:746] echo '0xa59316b' > /sys/class/net/rmnet_mhi0.1/bridge_ipv4 + +root@OpenWrt:~# ifconfig br0 up +[ 268.800026] br0: port 2(eth1) entered forwarding state +[ 268.800336] br0: port 2(eth1) entered forwarding state +[ 268.804251] br0: port 1(rmnet_mhi0.1) entered forwarding state +[ 268.809465] br0: port 1(rmnet_mhi0.1) entered forwarding state +[ 283.845790] br0: port 2(eth1) entered forwarding state + +[ 296.512489] rmnet_mhi0.1 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 296.515756] rmnet_mhi0.1 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 296.586584] rmnet_mhi0.1 sip = 10.89.49.107, tip=10.89.49.108, ipv4=10.89.49.107 +[ 296.672356] rmnet_mhi0.1 sip = 10.89.49.107, tip=10.89.49.108, ipv4=10.89.49.107 +[ 296.792061] rmnet_mhi0.1 sip = 10.89.49.107, tip=10.89.49.108, ipv4=10.89.49.107 +[ 296.832822] rmnet_mhi0.1 sip = 10.89.49.107, tip=10.89.49.108, ipv4=10.89.49.107 +[ 296.941073] rmnet_mhi0.1 sip = 0.0.0.0, tip=10.89.49.107, ipv4=10.89.49.107 +[ 297.941310] rmnet_mhi0.1 sip = 0.0.0.0, tip=10.89.49.107, ipv4=10.89.49.107 +[ 298.941528] rmnet_mhi0.1 sip = 0.0.0.0, tip=10.89.49.107, ipv4=10.89.49.107 +[ 299.941704] rmnet_mhi0.1 sip = 10.89.49.107, tip=10.89.49.107, ipv4=10.89.49.107 +[ 300.024484] rmnet_mhi0.1 sip = 10.89.49.107, tip=10.89.49.108, ipv4=10.89.49.107 +[ 300.051995] rmnet_mhi0.1 sip = 10.89.49.107, tip=10.89.49.108, ipv4=10.89.49.107 +[ 303.915933] rmnet_mhi0.1 sip = 10.89.49.107, tip=10.89.49.108, ipv4=10.89.49.107 diff --git a/application/quectel_CM_5G/src/log/pcie_mhi_qmap=4.txt b/application/quectel_CM_5G/src/log/pcie_mhi_qmap=4.txt new file mode 100644 index 0000000..5f43a74 --- /dev/null +++ b/application/quectel_CM_5G/src/log/pcie_mhi_qmap=4.txt @@ -0,0 +1,138 @@ +root@OpenWrt:/# lspci +00:00.0 Class 0604: 17cb:1001 +01:00.0 Class ff00: 17cb:0306 + +root@OpenWrt:/# insmod pcie_mhi.ko qmap_mode=4 +[ 61.988878] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.17 +[ 61.989484] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306 +[ 61.994039] mhi_q 0000:01:00.0: BAR 0: assigned [mem 0x48000000-0x48000fff 64bit] +[ 62.003208] mhi_q 0000:01:00.0: enabling device (0140 -> 0142) +[ 62.191947] [I][mhi_netdev_enable_iface] Prepare the channels for transfer +[ 62.224065] [I][mhi_netdev_enable_iface] Exited. +[ 62.225619] rmnet_vnd_register_device(rmnet_mhi0.1)=0 +[ 62.229289] rmnet_vnd_register_device(rmnet_mhi0.2)=0 +[ 62.234378] rmnet_vnd_register_device(rmnet_mhi0.3)=0 +[ 62.240039] rmnet_vnd_register_device(rmnet_mhi0.4)=0 + +root@OpenWrt:/# quectel-qmi-proxy -d /dev/mhi_QMI0 & +[04-13_09:25:12:278] Will use cdc-wdm='/dev/mhi_QMI0', proxy='quectel-qmi-proxy0' +[04-13_09:25:12:297] qmi_proxy_init enter +[04-13_09:25:12:297] qmi_proxy_loop enter thread_id 0xb6e88d44 +[04-13_09:25:14:298] qmi_proxy_init succful +[04-13_09:25:14:299] local server: quectel-qmi-proxy0 sockfd = 4 +[04-13_09:25:14:299] qmi_proxy_server_fd = 4 + +root@OpenWrt:/# quectel-CM -n 1 -s cmnet & +[04-13_09:25:32:336] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_09:25:32:337] network interface '' or qmidev '' is not exist +[04-13_09:25:32:338] netcard driver = pcie_mhi, driver version = V1.3.0.17 +[04-13_09:25:32:339] qmap_mode = 4, qmap_version = 9, qmap_size = 16384, muxid = 0x81, qmap_netcard = rmnet_mhi0.1 +[04-13_09:25:32:340] Modem works in QMI mode +[04-13_09:25:32:341] connect to quectel-qmi-proxy0 sockfd = 7 +[04-13_09:25:32:342] cdc_wdm_fd = 7 +[04-13_09:25:32:380] requestBaseBandVersion RM500QGLABR10A03M4G +[04-13_09:25:32:382] qmap_settings.rx_urb_size = 16384 +[04-13_09:25:32:383] qmap_settings.ul_data_aggregation_max_datagrams = 11 +[04-13_09:25:32:383] qmap_settings.ul_data_aggregation_max_size = 8192 +[04-13_09:25:32:384] qmap_settings.dl_minimum_padding = 0 +[04-13_09:25:32:394] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_09:25:32:395] requestSetProfile[1] cmnet///0 +[04-13_09:25:32:409] requestGetProfile[1] cmnet///0 +[04-13_09:25:32:414] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: 5G_NSA +[04-13_09:25:32:418] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-13_09:25:32:419] ifconfig rmnet_mhi0 down +[04-13_09:25:32:448] ifconfig rmnet_mhi0.1 0.0.0.0 +[04-13_09:25:32:473] ifconfig rmnet_mhi0.1 down +[04-13_09:25:32:514] requestSetupDataCall WdsConnectionIPv4Handle: 0x2313a2a0 +[ 121.648172] net rmnet_mhi0: link_state 0x0 -> 0x1 +[04-13_09:25:32:525] ifconfig rmnet_mhi0 up +[ 121.671210] [I][mhi_netdev_open] Opened net dev interface +[04-13_09:25:32:551] ifconfig rmnet_mhi0.1 up +[04-13_09:25:32:586] you are use OpenWrt? +[04-13_09:25:32:587] should not calling udhcpc manually? +[04-13_09:25:32:587] should modify /etc/config/network as below? +[04-13_09:25:32:587] config interface wan +[04-13_09:25:32:588] option ifname rmnet_mhi0.1 +[04-13_09:25:32:588] option proto dhcp +[04-13_09:25:32:589] should use "/sbin/ifstaus wan" to check rmnet_mhi0.1 's status? +[04-13_09:25:32:589] busybox udhcpc -f -n -q -t 5 -i rmnet_mhi0.1 +udhcpc: started, v1.28.3 +udhcpc: sending discover +udhcpc: sending select for 10.174.91.70 +udhcpc: lease of 10.174.91.70 obtained, lease time 7200 +[04-13_09:25:32:980] udhcpc: ifconfig rmnet_mhi0.1 10.174.91.70 netmask 255.255.255.252 broadcast + +[04-13_09:25:33:007] udhcpc: setting default routers: 10.174.91.69 + +root@OpenWrt:/# quectel-CM -n 2 -s 4gnet & +[04-13_09:25:42:976] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_09:25:42:977] network interface '' or qmidev '' is not exist +[04-13_09:25:42:978] netcard driver = pcie_mhi, driver version = V1.3.0.17 +[04-13_09:25:42:978] qmap_mode = 4, qmap_version = 9, qmap_size = 16384, muxid = 0x82, qmap_netcard = rmnet_mhi0.2 +[04-13_09:25:42:979] Modem works in QMI mode +[04-13_09:25:42:981] connect to quectel-qmi-proxy0 sockfd = 7 +[04-13_09:25:42:982] cdc_wdm_fd = 7 +[04-13_09:25:43:010] requestBaseBandVersion RM500QGLABR10A03M4G +[04-13_09:25:43:013] qmap_settings.rx_urb_size = 16384 +[04-13_09:25:43:014] qmap_settings.ul_data_aggregation_max_datagrams = 11 +[04-13_09:25:43:014] qmap_settings.ul_data_aggregation_max_size = 8192 +[04-13_09:25:43:015] qmap_settings.dl_minimum_padding = 0 +[04-13_09:25:43:030] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_09:25:43:030] requestSetProfile[2] 4gnet///0 +[04-13_09:25:43:046] requestGetProfile[2] 4gnet///0 +[04-13_09:25:43:050] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: 5G_NSA +[04-13_09:25:43:054] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-13_09:25:43:055] ifconfig rmnet_mhi0.2 0.0.0.0 +[04-13_09:25:43:082] ifconfig rmnet_mhi0.2 down +[04-13_09:25:43:507] requestSetupDataCall WdsConnectionIPv4Handle: 0x2332a780 +[ 132.641313] net rmnet_mhi0: link_state 0x1 -> 0x3 +[04-13_09:25:43:519] ifconfig rmnet_mhi0 up +[04-13_09:25:43:543] ifconfig rmnet_mhi0.2 up +[04-13_09:25:43:570] you are use OpenWrt? +[04-13_09:25:43:570] should not calling udhcpc manually? +[04-13_09:25:43:571] should modify /etc/config/network as below? +[04-13_09:25:43:571] config interface wan +[04-13_09:25:43:571] option ifname rmnet_mhi0.2 +[04-13_09:25:43:572] option proto dhcp +[04-13_09:25:43:572] should use "/sbin/ifstaus wan" to check rmnet_mhi0.2 's status? +[04-13_09:25:43:573] busybox udhcpc -f -n -q -t 5 -i rmnet_mhi0.2 +udhcpc: started, v1.28.3 +udhcpc: sending discover +udhcpc: sending select for 10.163.253.197 +udhcpc: lease of 10.163.253.197 obtained, lease time 7200 +[04-13_09:25:43:810] udhcpc: ifconfig rmnet_mhi0.2 10.163.253.197 netmask 255.255.255.252 broadcast + +[04-13_09:25:43:836] udhcpc: setting default routers: 10.163.253.198 + +root@OpenWrt:/# ifconfig rmnet_mhi0.1 +rmnet_mhi0.1 Link encap:Ethernet HWaddr 02:50:F4:00:00:00 + inet addr:10.174.91.70 Mask:255.255.255.252 + 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:4 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:612 (612.0 B) TX bytes:1380 (1.3 KiB) + +root@OpenWrt:/# ifconfig rmnet_mhi0.2 +rmnet_mhi0.2 Link encap:Ethernet HWaddr 02:50:F4:00:00:00 + inet addr:10.163.253.197 Mask:255.255.255.252 + 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:612 (612.0 B) TX bytes:684 (684.0 B) + +root@OpenWrt:/# ip ro del 8.8.8.8/32 +RTNETLINK answers: No such process +root@OpenWrt:/# ip ro add 8.8.8.8/32 dev rmnet_mhi0.1 +root@OpenWrt:/# ping 8.8.8.8 +PING 8.8.8.8 (8.8.8.8): 56 data bytes +64 bytes from 8.8.8.8: seq=0 ttl=52 time=390.869 ms + +root@OpenWrt:/# ip ro del 8.8.8.8/32 +root@OpenWrt:/# ip ro del 8.8.8.8/32 +RTNETLINK answers: No such process +root@OpenWrt:/# ip ro add 8.8.8.8/32 dev rmnet_mhi0.2 +root@OpenWrt:/# ping 8.8.8.8 +PING 8.8.8.8 (8.8.8.8): 56 data bytes +64 bytes from 8.8.8.8: seq=0 ttl=111 time=314.395 ms \ No newline at end of file diff --git a/application/quectel_CM_5G/src/log/pcie_mhi_qmap=4_bridge.txt b/application/quectel_CM_5G/src/log/pcie_mhi_qmap=4_bridge.txt new file mode 100644 index 0000000..0af5ff0 --- /dev/null +++ b/application/quectel_CM_5G/src/log/pcie_mhi_qmap=4_bridge.txt @@ -0,0 +1,147 @@ +root@OpenWrt:~# lspci +00:00.0 Class 0604: 17cb:1001 +01:00.0 Class ff00: 17cb:0306 +root@OpenWrt:~# + +root@OpenWrt:~# insmod pcie_mhi.ko qmap_mode=4 +[ 200.906104] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.17 +[ 200.907913] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306 +[ 200.912164] mhi_q 0000:01:00.0: BAR 0: assigned [mem 0x48000000-0x48000fff 64bit] +[ 200.920593] mhi_q 0000:01:00.0: enabling device (0140 -> 0142) +root@OpenWrt:~# [ 201.112214] [I][mhi_netdev_enable_iface] Prepare the channels for transfer +[ 201.154640] [I][mhi_netdev_enable_iface] Exited. +[ 201.159271] rmnet_vnd_register_device(rmnet_mhi0.1)=0 +[ 201.162953] rmnet_vnd_register_device(rmnet_mhi0.2)=0 +[ 201.167698] rmnet_vnd_register_device(rmnet_mhi0.3)=0 +[ 201.172178] rmnet_vnd_register_device(rmnet_mhi0.4)=0 + +root@OpenWrt:~# brctl addbr br0 +root@OpenWrt:~# brctl addif br0 eth1 +root@OpenWrt:~# brctl addif br0 rmnet_mhi0.2 +root@OpenWrt:~# brctl show +bridge name bridge id STP enabled interfaces +br0 8000.00001c353487 no eth1 + rmnet_mhi0.2 + +root@OpenWrt:~# quectel-qmi-proxy -d /dev/mhi_QMI0 & +[04-14_06:44:01:556] Will use cdc-wdm='/dev/mhi_QMI0', proxy='quectel-qmi-proxy0' +[04-14_06:44:01:573] qmi_proxy_init enter +[04-14_06:44:01:573] qmi_proxy_loop enter thread_id 0xb6f20d44 +[04-14_06:44:03:574] qmi_proxy_init succful +[04-14_06:44:03:574] local server: quectel-qmi-proxy0 sockfd = 4 +[04-14_06:44:03:575] qmi_proxy_server_fd = 4 + + +root@OpenWrt:~# quectel-CM -n 1 -s cmnet & +[04-14_06:47:53:303] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-14_06:47:53:314] network interface '' or qmidev '' is not exist +[04-14_06:47:53:315] netcard driver = pcie_mhi, driver version = V1.3.0.17 +[04-14_06:47:53:316] qmap_mode = 4, qmap_version = 9, qmap_size = 16384, muxid = 0x81, qmap_netcard = rmnet_mhi0.1 +[04-14_06:47:53:316] Modem works in QMI mode +[04-14_06:47:53:318] connect to quectel-qmi-proxy0 sockfd = 7 +[04-14_06:47:53:318] cdc_wdm_fd = 7 +[04-14_06:47:53:326] Get clientWDS = 15 +[04-14_06:47:53:329] Get clientDMS = 2 +[04-14_06:47:53:334] Get clientNAS = 4 +[04-14_06:47:53:338] Get clientUIM = 1 +[04-14_06:47:53:343] Get clientWDA = 1 +[04-14_06:47:53:347] requestBaseBandVersion RM500QGLABR10A03M4G +[04-14_06:47:53:351] qmap_settings.rx_urb_size = 16384 +[04-14_06:47:53:352] qmap_settings.ul_data_aggregation_max_datagrams = 11 +[04-14_06:47:53:352] qmap_settings.ul_data_aggregation_max_size = 8192 +[04-14_06:47:53:352] qmap_settings.dl_minimum_padding = 0 +[04-14_06:47:53:369] requestGetSIMStatus SIMStatus: SIM_READY +[04-14_06:47:53:370] requestSetProfile[1] cmnet///0 +[04-14_06:47:53:402] requestGetProfile[1] cmnet///0 +[04-14_06:47:53:407] requestRegistrationState2 MCC: 0, MNC: 0, PS: Detached, DataCap: UNKNOW +[04-14_06:47:53:411] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-14_06:47:53:412] ifconfig rmnet_mhi0 down +[04-14_06:47:53:436] ifconfig rmnet_mhi0.1 0.0.0.0 +[04-14_06:47:53:460] ifconfig rmnet_mhi0.1 down +[04-14_06:48:26:399] requestRegistrationState2 MCC: 460, MNC: 0, PS: Detached, DataCap: UNKNOW +[04-14_06:48:26:405] requestRegistrationState2 MCC: 460, MNC: 0, PS: Detached, DataCap: UNKNOW +[04-14_06:48:26:411] requestRegistrationState2 MCC: 460, MNC: 0, PS: Detached, DataCap: UNKNOW +[04-14_06:48:26:970] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: 5G_NSA +[04-14_06:48:26:992] requestSetupDataCall WdsConnectionIPv4Handle: 0x34176710 +[04-14_06:48:27:005] ifconfig rmnet_mhi0 up +[04-14_06:48:27:031] ifconfig rmnet_mhi0.1 up +[04-14_06:48:27:057] you are use OpenWrt? +[04-14_06:48:27:057] should not calling udhcpc manually? +[04-14_06:48:27:080] should use "/sbin/ifstaus wan" to check rmnet_mhi0.1 's status? +[04-14_06:48:27:081] busybox udhcpc -f -n -q -t 5 -i rmnet_mhi0.1 +[04-14_06:48:27:363] udhcpc: ifconfig rmnet_mhi0.1 10.245.22.3 netmask 255.255.255.248 broadcast + +[04-14_06:48:27:398] udhcpc: setting default routers: 10.245.22.4 +[04-14_06:48:27:491] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: 5G_NSA + +root@OpenWrt:~# quectel-CM -n 2 -s 4gnet -b & +[04-14_06:48:06:842] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-14_06:48:06:853] network interface '' or qmidev '' is not exist +[04-14_06:48:06:854] netcard driver = pcie_mhi, driver version = V1.3.0.17 +[04-14_06:48:06:855] qmap_mode = 4, qmap_version = 9, qmap_size = 16384, muxid = 0x82, qmap_netcard = rmnet_mhi0.2 +[04-14_06:48:06:855] Modem works in QMI mode +[04-14_06:48:06:857] connect to quectel-qmi-proxy0 sockfd = 7 +[04-14_06:48:06:858] cdc_wdm_fd = 7 +[04-14_06:48:06:864] Get clientWDS = 16 +[04-14_06:48:06:867] Get clientDMS = 3 +[04-14_06:48:06:871] Get clientNAS = 5 +[04-14_06:48:06:874] Get clientUIM = 2 +[04-14_06:48:06:879] Get clientWDA = 2 +[04-14_06:48:06:886] requestBaseBandVersion RM500QGLABR10A03M4G +[04-14_06:48:06:891] qmap_settings.rx_urb_size = 16384 +[04-14_06:48:06:891] qmap_settings.ul_data_aggregation_max_datagrams = 11 +[04-14_06:48:06:892] qmap_settings.ul_data_aggregation_max_size = 8192 +[04-14_06:48:06:892] qmap_settings.dl_minimum_padding = 0 +[04-14_06:48:06:909] requestGetSIMStatus SIMStatus: SIM_READY +[04-14_06:48:06:909] requestSetProfile[2] 4gnet///0 +[04-14_06:48:06:940] requestGetProfile[2] 4gnet///0 +[04-14_06:48:06:944] requestRegistrationState2 MCC: 0, MNC: 0, PS: Detached, DataCap: UNKNOW +[04-14_06:48:06:949] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-14_06:48:06:949] ifconfig rmnet_mhi0 down +[04-14_06:48:06:973] ifconfig rmnet_mhi0.2 0.0.0.0 +[04-14_06:48:06:998] ifconfig rmnet_mhi0.2 down +[04-14_06:48:26:400] requestRegistrationState2 MCC: 460, MNC: 0, PS: Detached, DataCap: UNKNOW +[04-14_06:48:26:405] requestRegistrationState2 MCC: 460, MNC: 0, PS: Detached, DataCap: UNKNOW +[04-14_06:48:26:411] requestRegistrationState2 MCC: 460, MNC: 0, PS: Detached, DataCap: UNKNOW +[04-14_06:48:26:970] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: 5G_NSA +[04-14_06:48:27:220] requestSetupDataCall WdsConnectionIPv4Handle: 0x341450a0 +[04-14_06:48:27:228] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: 5G_NSA +[04-14_06:48:27:238] ifconfig rmnet_mhi0 up +[04-14_06:48:27:263] ifconfig rmnet_mhi0.2 up +[04-14_06:48:27:313] echo '0xaf51be9' > /sys/class/net/rmnet_mhi0.2/bridge_ipv4 + +root@OpenWrt:~# ifconfig rmnet_mhi0.1 +rmnet_mhi0.1 Link encap:Ethernet HWaddr 02:50:F4:00:00:00 + inet addr:10.245.22.3 Mask:255.255.255.248 + inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link + UP RUNNING NOARP MTU:1500 Metric:1 + RX packets:6 errors:0 dropped:0 overruns:0 frame:0 + TX packets:6 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:1836 (1.7 KiB) TX bytes:2052 (2.0 KiB) + +root@OpenWrt:~# ifconfig rmnet_mhi0.2 +rmnet_mhi0.2 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:347 errors:0 dropped:0 overruns:0 frame:0 + TX packets:795 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:119871 (117.0 KiB) TX bytes:121254 (118.4 KiB) + +root@OpenWrt:~# ifconfig br0 up +[ 520.005476] rmnet_mhi0.2 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 520.025896] rmnet_mhi0.2 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 520.028002] rmnet_mhi0.2 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 520.144371] rmnet_mhi0.2 sip = 10.245.27.233, tip=10.245.27.234, ipv4=10.245.27.233 +[ 520.410052] rmnet_mhi0.2 sip = 0.0.0.0, tip=10.245.27.233, ipv4=10.245.27.233 +[ 520.414504] rmnet_mhi0.2 sip = 10.245.27.233, tip=10.245.27.234, ipv4=10.245.27.233 +[ 520.847074] rmnet_mhi0.2 sip = 10.245.27.233, tip=10.245.27.234, ipv4=10.245.27.233 +[ 521.410241] rmnet_mhi0.2 sip = 0.0.0.0, tip=10.245.27.233, ipv4=10.245.27.233 +[ 522.410455] rmnet_mhi0.2 sip = 0.0.0.0, tip=10.245.27.233, ipv4=10.245.27.233 +[ 522.822594] rmnet_mhi0.2 sip = 10.245.27.233, tip=10.245.27.234, ipv4=10.245.27.233 +[ 523.410638] rmnet_mhi0.2 sip = 10.245.27.233, tip=10.245.27.233, ipv4=10.245.27.233 +[ 523.510028] rmnet_mhi0.2 sip = 10.245.27.233, tip=10.245.27.234, ipv4=10.245.27.233 +[ 523.997961] rmnet_mhi0.2 sip = 10.245.27.233, tip=10.245.27.234, ipv4=10.245.27.233 +[ 543.799483] rmnet_mhi0.2 sip = 10.245.27.233, tip=10.245.27.234, ipv4=10.245.27.233 +[ 543.929301] rmnet_mhi0.2 sip = 10.245.27.233, tip=10.245.27.234, ipv4=10.245.27.233 + diff --git a/application/quectel_CM_5G/src/log/qmi_wwan_q.txt b/application/quectel_CM_5G/src/log/qmi_wwan_q.txt new file mode 100644 index 0000000..161dbbd --- /dev/null +++ b/application/quectel_CM_5G/src/log/qmi_wwan_q.txt @@ -0,0 +1,65 @@ +root@ZhuoTK:/# dmesg +[ 15.840000] qmi_wwan_q 1-1.3:1.4: cdc-wdm0: USB WDM device +[ 15.860000] qmi_wwan_q 1-1.3:1.4: Quectel Android work on RawIP mode +[ 15.860000] qmi_wwan_q 1-1.3:1.4: rx_urb_size = 1520 +[ 15.870000] qmi_wwan_q 1-1.3:1.4 wwan0: register 'qmi_wwan_q' at usb-101c0000.ehci-1.3, WWAN/QMI device, da:0b:ce:b2:db:21 + +root@ZhuoTK:/# quectel-CM -s cment & +[04-13_03:20:20:456] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_03:20:20:459] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x003 +[04-13_03:20:20:460] Auto find qmichannel = /dev/cdc-wdm0 +[04-13_03:20:20:460] Auto find usbnet_adapter = wwan0 +[04-13_03:20:20:461] netcard driver = qmi_wwan_q, driver version = V1.2.0.23 +[04-13_03:20:20:461] Modem works in QMI mode +[04-13_03:20:20:469] cdc_wdm_fd = 7 +[04-13_03:20:20:547] Get clientWDS = 4 +[04-13_03:20:20:579] Get clientDMS = 1 +[04-13_03:20:20:611] Get clientNAS = 4 +[04-13_03:20:20:643] Get clientUIM = 1 +[04-13_03:20:20:675] Get clientWDA = 1 +[04-13_03:20:20:707] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_03:20:20:836] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_03:20:20:836] requestSetProfile[1] cment///0 +[04-13_03:20:20:899] requestGetProfile[1] cment///0 +[04-13_03:20:20:931] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_03:20:20:963] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-13_03:20:20:963] ifconfig wwan0 0.0.0.0 +[04-13_03:20:20:976] ifconfig wwan0 down +[04-13_03:20:21:186] requestSetupDataCall WdsConnectionIPv4Handle: 0x8723e780 +[04-13_03:20:21:316] ifconfig wwan0 up +[04-13_03:20:21:329] you are use OpenWrt? +[04-13_03:20:21:330] should not calling udhcpc manually? +[04-13_03:20:21:330] should modify /etc/config/network as below? +[04-13_03:20:21:330] config interface wan +[04-13_03:20:21:330] option ifname wwan0 +[04-13_03:20:21:330] option proto dhcp +[04-13_03:20:21:330] should use "/sbin/ifstaus wan" to check wwan0 's status? +[04-13_03:20:21:331] busybox udhcpc -f -n -q -t 5 -i wwan0 +[04-13_03:20:21:341] udhcpc (v1.23.2) started +[04-13_03:20:21:353] Sending discover... +[04-13_03:20:21:362] Sending select for 10.90.1.113... +[04-13_03:20:21:365] Lease of 10.90.1.113 obtained, lease time 7200 +[04-13_03:20:21:370] udhcpc: ifconfig wwan0 10.90.1.113 netmask 255.255.255.252 broadcast + +[04-13_03:20:21:380] udhcpc: setting default routers: 10.90.1.114 + +root@ZhuoTK:/# ifconfig wwan0 +wwan0 Link encap:Ethernet HWaddr 00:CA:01:91:97:BA + inet addr:10.90.1.113 Mask:255.255.255.252 + inet6 addr: fe80::2ca:1ff:fe91:97ba/64 Scope:Link + UP RUNNING NOARP MTU:1500 Metric:1 + RX packets:38 errors:0 dropped:0 overruns:0 frame:0 + TX packets:46 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:5244 (5.1 KiB) TX bytes:6964 (6.8 KiB) + +root@ZhuoTK:/# ip ro show +default via 10.90.1.114 dev wwan0 +10.90.1.112/30 dev wwan0 proto kernel scope link src 10.90.1.113 +192.168.1.0/24 dev br-lan proto kernel scope link src 192.168.1.251 + +root@ZhuoTK:/# killall quectel-CM +[04-13_03:20:46:130] requestDeactivateDefaultPDP WdsConnectionIPv4Handle +[04-13_03:20:46:406] ifconfig wwan0 0.0.0.0 +[04-13_03:20:46:418] ifconfig wwan0 down +[04-13_03:20:46:600] QmiWwanThread exit +[04-13_03:20:46:600] qmi_main exit diff --git a/application/quectel_CM_5G/src/log/qmi_wwan_q_bridge.txt b/application/quectel_CM_5G/src/log/qmi_wwan_q_bridge.txt new file mode 100644 index 0000000..b979af9 --- /dev/null +++ b/application/quectel_CM_5G/src/log/qmi_wwan_q_bridge.txt @@ -0,0 +1,57 @@ +root@ZhuoTK:/# insmod qmi_wwan_q.ko +[ 116.910000] qmi_wwan_q 1-1.3:1.4: cdc-wdm0: USB WDM device +[ 116.930000] qmi_wwan_q 1-1.3:1.4: Quectel Android work on RawIP mode +[ 116.930000] qmi_wwan_q 1-1.3:1.4: rx_urb_size = 1520 +[ 116.940000] qmi_wwan_q 1-1.3:1.4 wwan0: register 'qmi_wwan_q' at usb-101c0000.ehci-1.3, WWAN/QMI device, 06:fb:51:a3:d6:c5 +[ 116.950000] usbcore: registered new interface driver qmi_wwan_q + +root@ZhuoTK:/# brctl addbr br0 +root@ZhuoTK:/# brctl addif br0 eth0.1 +root@ZhuoTK:/# brctl addif br0 wwan0 +root@ZhuoTK:/# brctl show +bridge name bridge id STP enabled interfaces +br0 8000.00ca019197b9 no eth0.1 + wwan0 + +root@ZhuoTK:/# quectel-CM -s cmnet -b & +root@ZhuoTK:/# [04-13_05:13:39:369] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_05:13:39:372] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x003 +[ 143.340000] net wwan0: bridge_mode change to 0x1 +[04-13_05:13:39:373] Auto find qmichannel = /dev/cdc-wdm0 +[04-13_05:13:39:374] Auto find usbnet_adapter = wwan0 +[04-13_05:13:39:374] netcard driver = qmi_wwan_q, driver version = V1.2.0.23 +[04-13_05:13:39:380] Modem works in QMI mode +[04-13_05:13:39:388] cdc_wdm_fd = 7 +[04-13_05:13:39:466] Get clientWDS = 5 +[04-13_05:13:39:496] Get clientDMS = 2 +[04-13_05:13:39:527] Get clientNAS = 4 +[04-13_05:13:39:559] Get clientUIM = 1 +[04-13_05:13:39:592] Get clientWDA = 1 +[04-13_05:13:39:626] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_05:13:39:752] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_05:13:39:752] requestSetProfile[1] cmnet///0 +[04-13_05:13:39:816] requestGetProfile[1] cmnet///0 +[04-13_05:13:39:848] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_05:13:39:879] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-13_05:13:39:880] ifconfig wwan0 0.0.0.0 +[04-13_05:13:39:893] ifconfig wwan0 down +[04-13_05:13:39:943] requestSetupDataCall WdsConnectionIPv4Handle: 0x872627c0 +[04-13_05:13:40:073] ifconfig wwan0 up +[04-13_05:13:40:085] echo '0xa8d9237' > /sys/class/net/wwan0/bridge_ipv4 + +root@ZhuoTK:/# ifconfig br0 up +[ 165.730000] wwan0 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 165.750000] wwan0 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 165.860000] wwan0 sip = 10.141.146.55, tip=10.141.146.56, ipv4=10.141.146.55 +[ 165.870000] wwan0 sip = 10.141.146.55, tip=10.141.146.56, ipv4=10.141.146.55 +[ 165.990000] wwan0 sip = 10.141.146.55, tip=10.141.146.56, ipv4=10.141.146.55 +[ 166.010000] wwan0 sip = 0.0.0.0, tip=10.141.146.55, ipv4=10.141.146.55 +[ 166.070000] wwan0 sip = 10.141.146.55, tip=10.141.146.56, ipv4=10.141.146.55 +[ 167.010000] wwan0 sip = 0.0.0.0, tip=10.141.146.55, ipv4=10.141.146.55 +[ 167.480000] br0: port 2(wwan0) entered forwarding state +[ 167.520000] br0: port 1(eth0.1) entered forwarding state +[ 168.020000] wwan0 sip = 0.0.0.0, tip=10.141.146.55, ipv4=10.141.146.55 +[ 169.010000] wwan0 sip = 10.141.146.55, tip=10.141.146.55, ipv4=10.141.146.55 +[ 169.120000] wwan0 sip = 10.141.146.55, tip=10.141.146.56, ipv4=10.141.146.55 +[ 169.130000] wwan0 sip = 10.141.146.55, tip=10.141.146.56, ipv4=10.141.146.55 +[ 176.620000] wwan0 sip = 10.141.146.55, tip=10.141.146.56, ipv4=10.141.146.55 diff --git a/application/quectel_CM_5G/src/log/qmi_wwan_q_qmap=1.txt b/application/quectel_CM_5G/src/log/qmi_wwan_q_qmap=1.txt new file mode 100644 index 0000000..9f088e5 --- /dev/null +++ b/application/quectel_CM_5G/src/log/qmi_wwan_q_qmap=1.txt @@ -0,0 +1,54 @@ +root@ZhuoTK:/# insmod qmi_wwan_q.ko qmap_mode=1 +[ 1367.200000] usbcore: registered new interface driver qmi_wwan_q +[ 1383.840000] usb 1-1.3: new high-speed USB device number 7 using ehci-platform +[ 1384.080000] qmi_wwan_q 1-1.3:1.4: cdc-wdm0: USB WDM device +[ 1384.080000] qmi_wwan_q 1-1.3:1.4: Quectel Android work on RawIP mode +[ 1384.100000] qmi_wwan_q 1-1.3:1.4: rx_urb_size = 4096 +[ 1384.100000] qmi_wwan_q 1-1.3:1.4 wwan0: register 'qmi_wwan_q' at usb-101c0000.ehci-1.3, WWAN/QMI device, da:0b:ce:b2:db:21 + +root@ZhuoTK:/# quectel-CM -s cmnet & +[04-13_03:41:28:144] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_03:41:28:146] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x007 +[04-13_03:41:28:148] Auto find qmichannel = /dev/cdc-wdm0 +[04-13_03:41:28:148] Auto find usbnet_adapter = wwan0 +[04-13_03:41:28:148] netcard driver = qmi_wwan_q, driver version = V1.2.0.23 +[04-13_03:41:28:149] qmap_mode = 1, qmap_version = 5, qmap_size = 4096, muxid = 0x81, qmap_netcard = wwan0 +[04-13_03:41:28:150] Modem works in QMI mode +[04-13_03:41:28:158] cdc_wdm_fd = 7 +[04-13_03:41:28:238] Get clientWDS = 4 +[04-13_03:41:28:271] Get clientDMS = 1 +[04-13_03:41:28:302] Get clientNAS = 4 +[04-13_03:41:28:334] Get clientUIM = 1 +[04-13_03:41:28:365] Get clientWDA = 1 +[04-13_03:41:28:397] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_03:41:28:430] qmap_settings.rx_urb_size = 4096 +[ 1393.530000] net wwan0: ul_data_aggregation_max_datagrams=11, ul_data_aggregation_max_size=4096, dl_minimum_padding=0 +[04-13_03:41:28:431] qmap_settings.ul_data_aggregation_max_datagrams = 11 +[04-13_03:41:28:431] qmap_settings.ul_data_aggregation_max_size = 4096 +[04-13_03:41:28:431] qmap_settings.dl_minimum_padding = 0 +[04-13_03:41:28:557] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_03:41:28:558] requestSetProfile[1] cmnet///0 +[04-13_03:41:28:622] requestGetProfile[1] cmnet///0 +[04-13_03:41:28:654] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_03:41:28:685] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[ 1393.790000] net wwan0: link_state 0x1 -> 0x0 +[04-13_03:41:28:692] ifconfig wwan0 0.0.0.0 +[04-13_03:41:28:703] ifconfig wwan0 down +[04-13_03:41:28:751] requestSetupDataCall WdsConnectionIPv4Handle: 0x8729a6b0 +[ 1393.980000] net wwan0: link_state 0x0 -> 0x1 +[04-13_03:41:28:882] ifconfig wwan0 up +[04-13_03:41:28:895] you are use OpenWrt? +[04-13_03:41:28:895] should not calling udhcpc manually? +[04-13_03:41:28:895] should modify /etc/config/network as below? +[04-13_03:41:28:896] config interface wan +[04-13_03:41:28:896] option ifname wwan0 +[04-13_03:41:28:896] option proto dhcp +[04-13_03:41:28:896] should use "/sbin/ifstaus wan" to check wwan0 's status? +[04-13_03:41:28:896] busybox udhcpc -f -n -q -t 5 -i wwan0 +[04-13_03:41:28:907] udhcpc (v1.23.2) started +[04-13_03:41:28:919] Sending discover... +[04-13_03:41:28:925] Sending select for 10.129.198.20... +[04-13_03:41:28:929] Lease of 10.129.198.20 obtained, lease time 7200 +[04-13_03:41:28:934] udhcpc: ifconfig wwan0 10.129.198.20 netmask 255.255.255.248 broadcast + +[04-13_03:41:28:949] udhcpc: setting default routers: 10.129.198.21 + diff --git a/application/quectel_CM_5G/src/log/qmi_wwan_q_qmap=1_bridge.txt b/application/quectel_CM_5G/src/log/qmi_wwan_q_qmap=1_bridge.txt new file mode 100644 index 0000000..05919d6 --- /dev/null +++ b/application/quectel_CM_5G/src/log/qmi_wwan_q_qmap=1_bridge.txt @@ -0,0 +1,86 @@ +root@ZhuoTK:/# insmod qmi_wwan_q.ko qmap_mode=1 +[ 49.000000] qmi_wwan_q 1-1.3:1.4: cdc-wdm0: USB WDM device +[ 49.000000] qmi_wwan_q 1-1.3:1.4: Quectel Android work on RawIP mode +[ 49.020000] qmi_wwan_q 1-1.3:1.4: rx_urb_size = 4096 +[ 49.020000] qmi_wwan_q 1-1.3:1.4 wwan0: register 'qmi_wwan_q' at usb-101c0000.ehci-1.3, WWAN/QMI device, de:ae:5c:82:b5:b2 +[ 49.030000] usbcore: registered new interface driver qmi_wwan_q + +root@ZhuoTK:/# brctl addbr br0 +root@ZhuoTK:/# brctl addif br0 eth0.1 +root@ZhuoTK:/# brctl addif br0 wwan0 +root@ZhuoTK:/# brctl show +bridge name bridge id STP enabled interfaces +br0 8000.00ca019197b9 no eth0.1 + wwan0 + +root@ZhuoTK:/# quectel-CM -s cmnet -b & +[04-13_05:11:46:442] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_05:11:46:444] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x003 +[ 84.340000] net wwan0: bridge_mode change to 0x1 +[04-13_05:11:46:446] Auto find qmichannel = /dev/cdc-wdm0 +[04-13_05:11:46:446] Auto find usbnet_adapter = wwan0 +[04-13_05:11:46:446] netcard driver = qmi_wwan_q, driver version = V1.2.0.23 +[04-13_05:11:46:447] qmap_mode = 1, qmap_version = 5, qmap_size = 4096, muxid = 0x81, qmap_netcard = wwan0 +[04-13_05:11:46:454] Modem works in QMI mode +[04-13_05:11:46:462] cdc_wdm_fd = 7 +[04-13_05:11:46:537] Get clientWDS = 5 +[04-13_05:11:46:569] Get clientDMS = 1 +[04-13_05:11:46:601] Get clientNAS = 4 +[04-13_05:11:46:633] Get clientUIM = 1 +[04-13_05:11:46:666] Get clientWDA = 1 +[04-13_05:11:46:697] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_05:11:46:730] qmap_settings.rx_urb_size = 4096 +[ 84.620000] net wwan0: ul_data_aggregation_max_datagrams=11, ul_data_aggregation_max_size=4096, dl_minimum_padding=0 +[04-13_05:11:46:730] qmap_settings.ul_data_aggregation_max_datagrams = 11 +[04-13_05:11:46:730] qmap_settings.ul_data_aggregation_max_size = 4096 +[04-13_05:11:46:730] qmap_settings.dl_minimum_padding = 0 +[04-13_05:11:46:859] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_05:11:46:859] requestSetProfile[1] cmnet///0 +[04-13_05:11:46:922] requestGetProfile[1] cmnet///0 +[04-13_05:11:46:954] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_05:11:46:986] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[ 84.880000] net wwan0: link_state 0x1 -> 0x0 +[04-13_05:11:46:992] ifconfig wwan0 0.0.0.0 +[04-13_05:11:47:005] ifconfig wwan0 down +[04-13_05:11:47:050] requestSetupDataCall WdsConnectionIPv4Handle: 0x872a5830 +[ 85.070000] net wwan0: link_state 0x0 -> 0x1 +[04-13_05:11:47:183] ifconfig wwan0 up +[04-13_05:11:47:195] echo '0xa54a78b' > /sys/class/net/wwan0/bridge_ipv4 + +root@ZhuoTK:/# ifconfig wwan0 +wwan0 Link encap:Ethernet HWaddr DE:AE:5C:82:B5:B2 + inet6 addr: fe80::dcae:5cff:fe82:b5b2/64 Scope:Link + UP RUNNING NOARP MTU:1500 Metric:1 + RX packets:3792 errors:0 dropped:0 overruns:0 frame:0 + TX packets:3271 errors:0 dropped:36 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:2271762 (2.1 MiB) TX bytes:565184 (551.9 KiB) + +root@ZhuoTK:/# ifconfig br0 up +[ 89.530000] br0: port 2(wwan0) entered forwarding state +[ 89.530000] br0: port 2(wwan0) entered forwarding state +[ 89.540000] br0: port 1(eth0.1) entered forwarding state +[ 89.540000] br0: port 1(eth0.1) entered forwarding state + +root@ZhuoTK:/# +[ 93.720000] wwan0 sip = 192.168.1.153, tip=10.84.167.140, ipv4=10.84.167.139 +[ 104.560000] br0: port 2(wwan0) entered forwarding state +[ 104.560000] br0: port 1(eth0.1) entered forwarding state +[ 111.750000] rt305x-esw 10110000.esw: link changed 0x00 +[ 116.440000] rt305x-esw 10110000.esw: link changed 0x01 +[ 116.620000] wwan0 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 116.680000] wwan0 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 116.690000] wwan0 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 116.760000] wwan0 sip = 10.84.167.139, tip=10.84.167.140, ipv4=10.84.167.139 +[ 117.050000] wwan0 sip = 10.84.167.139, tip=10.84.167.140, ipv4=10.84.167.139 +[ 117.220000] wwan0 sip = 0.0.0.0, tip=10.84.167.139, ipv4=10.84.167.139 +[ 117.820000] wwan0 sip = 10.84.167.139, tip=10.84.167.140, ipv4=10.84.167.139 +[ 118.220000] wwan0 sip = 0.0.0.0, tip=10.84.167.139, ipv4=10.84.167.139 +[ 118.300000] wwan0 sip = 10.84.167.139, tip=10.84.167.140, ipv4=10.84.167.139 +[ 119.220000] wwan0 sip = 0.0.0.0, tip=10.84.167.139, ipv4=10.84.167.139 +[ 120.220000] wwan0 sip = 10.84.167.139, tip=10.84.167.139, ipv4=10.84.167.139 +[ 120.300000] wwan0 sip = 10.84.167.139, tip=10.84.167.140, ipv4=10.84.167.139 +[ 121.430000] wwan0 sip = 10.84.167.139, tip=10.84.167.140, ipv4=10.84.167.139 +[ 141.730000] wwan0 sip = 10.84.167.139, tip=10.84.167.140, ipv4=10.84.167.139 +[ 144.390000] wwan0 sip = 10.84.167.139, tip=10.84.167.140, ipv4=10.84.167.139 +[ 144.510000] wwan0 sip = 10.84.167.139, tip=10.84.167.140, ipv4=10.84.167.139 diff --git a/application/quectel_CM_5G/src/log/qmi_wwan_q_qmap=4.txt b/application/quectel_CM_5G/src/log/qmi_wwan_q_qmap=4.txt new file mode 100644 index 0000000..6464f27 --- /dev/null +++ b/application/quectel_CM_5G/src/log/qmi_wwan_q_qmap=4.txt @@ -0,0 +1,185 @@ +root@ZhuoTK:/# insmod qmi_wwan_q.ko qmap_mode=4 +[ 1515.180000] usbcore: registered new interface driver qmi_wwan_q +[ 1530.260000] usb 1-1.3: new high-speed USB device number 8 using ehci-platform +[ 1530.500000] qmi_wwan_q 1-1.3:1.4: cdc-wdm0: USB WDM device +[ 1530.500000] qmi_wwan_q 1-1.3:1.4: Quectel Android work on RawIP mode +[ 1530.520000] qmi_wwan_q 1-1.3:1.4: rx_urb_size = 4096 +[ 1530.520000] qmi_wwan_q 1-1.3:1.4 wwan0: register 'qmi_wwan_q' at usb-101c0000.ehci-1.3, RMNET/USB device, da:0b:ce:b2:db:21 +[ 1530.530000] net wwan0: qmap_register_device wwan0_1 +[ 1530.540000] net wwan0: qmap_register_device wwan0_2 +[ 1530.550000] net wwan0: qmap_register_device wwan0_3 +[ 1530.550000] net wwan0: qmap_register_device wwan0_4 + +root@ZhuoTK:~# quectel-qmi-proxy & +[04-13_03:44:53:958] Will use cdc-wdm='/dev/cdc-wdm0', proxy='quectel-qmi-proxy0' +[04-13_03:44:53:959] qmi_proxy_init enter +[04-13_03:44:53:960] qmi_proxy_loop enter thread_id 0x77c07530 +[04-13_03:44:54:960] qmi_proxy_init succful +[04-13_03:44:54:960] local server: quectel-qmi-proxy0 sockfd = 4 +[04-13_03:44:54:960] qmi_proxy_server_fd = 4 +[04-13_03:45:04:346] +++ ClientFd=5 +[04-13_03:45:04:410] +++ ClientFd=5 QMIType=1 ClientId=4 +[04-13_03:45:04:442] +++ ClientFd=5 QMIType=2 ClientId=1 +[04-13_03:45:04:474] +++ ClientFd=5 QMIType=3 ClientId=4 +[04-13_03:45:04:506] +++ ClientFd=5 QMIType=11 ClientId=1 +[04-13_03:45:04:539] +++ ClientFd=5 QMIType=26 ClientId=1 +[04-13_03:45:10:770] +++ ClientFd=6 +[04-13_03:45:10:811] +++ ClientFd=6 QMIType=1 ClientId=21 +[04-13_03:45:10:843] +++ ClientFd=6 QMIType=2 ClientId=2 +[04-13_03:45:10:875] +++ ClientFd=6 QMIType=3 ClientId=5 +[04-13_03:45:10:907] +++ ClientFd=6 QMIType=11 ClientId=2 +[04-13_03:46:31:419] --- ClientFd=6 QMIType=1 ClientId=21 +[04-13_03:46:31:451] --- ClientFd=6 QMIType=2 ClientId=2 +[04-13_03:46:31:484] --- ClientFd=6 QMIType=3 ClientId=5 +[04-13_03:46:31:517] --- ClientFd=6 QMIType=11 ClientId=2 +[04-13_03:46:31:518] qmi_proxy_loop poll fd = 6, revents = 0011 +[04-13_03:46:31:519] --- ClientFd=6 + +root@ZhuoTK:/# quectel-CM -n 1 -s cmnet & +root@ZhuoTK:/# [04-13_03:45:04:340] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_03:45:04:343] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x008 +[04-13_03:45:04:344] Auto find qmichannel = /dev/cdc-wdm0 +[04-13_03:45:04:344] Auto find usbnet_adapter = wwan0 +[04-13_03:45:04:345] netcard driver = qmi_wwan_q, driver version = V1.2.0.23 +[04-13_03:45:04:345] qmap_mode = 4, qmap_version = 5, qmap_size = 4096, muxid = 0x81, qmap_netcard = wwan0_1 +[04-13_03:45:04:345] Modem works in QMI mode +[04-13_03:45:04:347] connect to quectel-qmi-proxy0 sockfd = 7 +[04-13_03:45:04:347] cdc_wdm_fd = 7 +[04-13_03:45:04:411] Get clientWDS = 4 +[04-13_03:45:04:443] Get clientDMS = 1 +[04-13_03:45:04:475] Get clientNAS = 4 +[04-13_03:45:04:507] Get clientUIM = 1 +[04-13_03:45:04:540] Get clientWDA = 1 +[04-13_03:45:04:571] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_03:45:04:602] qmap_settings.rx_urb_size = 4096 +[ 1609.700000] net wwan0: ul_data_aggregation_max_datagrams=11, ul_data_aggregation_max_size=4096, dl_minimum_padding=0 +[04-13_03:45:04:603] qmap_settings.ul_data_aggregation_max_datagrams = 11 +[04-13_03:45:04:603] qmap_settings.ul_data_aggregation_max_size = 4096 +[04-13_03:45:04:603] qmap_settings.dl_minimum_padding = 0 +[04-13_03:45:04:731] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_03:45:04:731] requestSetProfile[1] cmnet///0 +[04-13_03:45:04:795] requestGetProfile[1] cmnet///0 +[04-13_03:45:04:827] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_03:45:04:858] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[ 1609.960000] net wwan0: link_state 0x1 -> 0x0 +[04-13_03:45:04:865] ifconfig wwan0 down +[04-13_03:45:04:879] ifconfig wwan0_1 0.0.0.0 +[04-13_03:45:04:889] ifconfig wwan0_1 down +[04-13_03:45:04:955] requestSetupDataCall WdsConnectionIPv4Handle: 0x87253410 +[ 1610.180000] net wwan0: link_state 0x0 -> 0x1 +[04-13_03:45:05:087] ifconfig wwan0 up +[ 1610.200000] IPv6: ADDRCONF(NETDEV_UP): wwan0: link is not ready +[04-13_03:45:05:105] ifconfig wwan0_1 up +[ 1610.220000] IPv6: ADDRCONF(NETDEV_CHANGE): wwan0: link becomes ready +[04-13_03:45:05:125] you are use OpenWrt? +[04-13_03:45:05:125] should not calling udhcpc manually? +[04-13_03:45:05:125] should modify /etc/config/network as below? +[04-13_03:45:05:125] config interface wan +[04-13_03:45:05:125] option ifname wwan0_1 +[04-13_03:45:05:125] option proto dhcp +[04-13_03:45:05:126] should use "/sbin/ifstaus wan" to check wwan0_1 's status? +[04-13_03:45:05:126] busybox udhcpc -f -n -q -t 5 -i wwan0_1 +[04-13_03:45:05:136] udhcpc (v1.23.2) started +[04-13_03:45:05:148] Sending discover... +[04-13_03:45:05:155] Sending select for 10.244.10.206... +[04-13_03:45:05:160] Lease of 10.244.10.206 obtained, lease time 7200 +[04-13_03:45:05:165] udhcpc: ifconfig wwan0_1 10.244.10.206 netmask 255.255.255.252 broadcast + +[04-13_03:45:05:174] udhcpc: setting default routers: 10.244.10.205 + +root@ZhuoTK:/# quectel-CM -n 2 -s 4gnet & +[04-13_03:45:10:764] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_03:45:10:767] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x008 +[04-13_03:45:10:768] Auto find qmichannel = /dev/cdc-wdm0 +[04-13_03:45:10:768] Auto find usbnet_adapter = wwan0 +[04-13_03:45:10:768] netcard driver = qmi_wwan_q, driver version = V1.2.0.23 +[04-13_03:45:10:769] qmap_mode = 4, qmap_version = 5, qmap_size = 4096, muxid = 0x82, qmap_netcard = wwan0_2 +[04-13_03:45:10:769] Modem works in QMI mode +[04-13_03:45:10:771] connect to quectel-qmi-proxy0 sockfd = 7 +[04-13_03:45:10:771] cdc_wdm_fd = 7 +[04-13_03:45:10:812] Get clientWDS = 21 +[04-13_03:45:10:844] Get clientDMS = 2 +[04-13_03:45:10:876] Get clientNAS = 5 +[04-13_03:45:10:908] Get clientUIM = 2 +[04-13_03:45:10:971] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_03:45:11:099] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_03:45:11:099] requestSetProfile[2] 4gnet///0 +[04-13_03:45:11:163] requestGetProfile[2] 4gnet///0 +[04-13_03:45:11:195] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_03:45:11:227] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-13_03:45:11:227] ifconfig wwan0_2 0.0.0.0 +[ 1616.340000] IPv6: ADDRCONF(NETDEV_UP): wwan0_2: link is not ready +[04-13_03:45:11:246] ifconfig wwan0_2 down +[04-13_03:45:11:642] requestSetupDataCall WdsConnectionIPv4Handle: 0x87254580 +[ 1616.870000] net wwan0: link_state 0x1 -> 0x3 +[04-13_03:45:11:775] ifconfig wwan0 up +[04-13_03:45:11:785] ifconfig wwan0_2 up +[04-13_03:45:11:798] you are use OpenWrt? +[04-13_03:45:11:798] should not calling udhcpc manually? +[04-13_03:45:11:798] should modify /etc/config/network as below? +[04-13_03:45:11:798] config interface wan +[04-13_03:45:11:798] option ifname wwan0_2 +[04-13_03:45:11:798] option proto dhcp +[04-13_03:45:11:798] should use "/sbin/ifstaus wan" to check wwan0_2 's status? +[04-13_03:45:11:799] busybox udhcpc -f -n -q -t 5 -i wwan0_2 +[04-13_03:45:11:809] udhcpc (v1.23.2) started +[04-13_03:45:11:821] Sending discover... +[04-13_03:45:11:830] Sending select for 10.245.78.212... +[04-13_03:45:11:836] Lease of 10.245.78.212 obtained, lease time 7200 +[04-13_03:45:11:842] udhcpc: ifconfig wwan0_2 10.245.78.212 netmask 255.255.255.248 broadcast + +[04-13_03:45:11:852] udhcpc: setting default routers: 10.245.78.213 + +root@ZhuoTK:/# ifconfig wwan0_1 +wwan0_1 Link encap:Ethernet HWaddr DA:0B:CE:B2:DB:21 + inet addr:10.244.10.206 Mask:255.255.255.252 + inet6 addr: fe80::d80b:ceff:feb2:db21/64 Scope:Link + UP RUNNING NOARP MTU:1500 Metric:1 + RX packets:2 errors:0 dropped:0 overruns:0 frame:0 + TX packets:4 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:640 (640.0 B) TX bytes:1344 (1.3 KiB) + +root@ZhuoTK:/# ifconfig wwan0_2 +wwan0_2 Link encap:Ethernet HWaddr DA:0B:CE:B2:DB:21 + inet addr:10.245.78.212 Mask:255.255.255.248 + inet6 addr: fe80::d80b:ceff:feb2:db21/64 Scope:Link + UP RUNNING NOARP MTU:1500 Metric:1 + RX packets:8 errors:0 dropped:0 overruns:0 frame:0 + TX packets:7 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:1193 (1.1 KiB) TX bytes:1028 (1.0 KiB) + +root@ZhuoTK:/# ip ro del 8.8.8.8/32 +RTNETLINK answers: No such process + +root@ZhuoTK:/# ip ro add 8.8.8.8/32 dev wwan0_1 +root@ZhuoTK:/# ping 8.8.8.8 +PING 8.8.8.8 (8.8.8.8): 56 data bytes +64 bytes from 8.8.8.8: seq=0 ttl=52 time=113.508 ms + +root@ZhuoTK:/# ip ro del 8.8.8.8/32 +root@ZhuoTK:/# ip ro del 8.8.8.8/32 +RTNETLINK answers: No such process + +root@ZhuoTK:/# ip ro add 8.8.8.8/32 dev wwan0_2 +root@ZhuoTK:/# ping 8.8.8.8 +PING 8.8.8.8 (8.8.8.8): 56 data bytes +64 bytes from 8.8.8.8: seq=0 ttl=111 time=123.651 ms + +root@ZhuoTK:/# quectel-CM -k 2 +[04-13_03:46:30:808] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_03:46:30:811] /proc/2834/cmdline: quectel-CM -n 2 -s 4gnet +[04-13_03:46:30:811] send SIGINT to process 2834 +[04-13_03:46:30:811] requestDeactivateDefaultPDP WdsConnectionIPv4Handle +[ 1696.460000] net wwan0: link_state 0x3 -> 0x1 +[04-13_03:46:31:361] ifconfig wwan0_2 0.0.0.0 +[04-13_03:46:31:373] ifconfig wwan0_2 down +[04-13_03:46:31:516] QmiWwanThread exit +[04-13_03:46:31:516] qmi_main exit + +root@ZhuoTK:/# ifconfig wwan0_2 +wwan0_2 Link encap:Ethernet HWaddr DA:0B:CE:B2:DB:21 + NOARP MTU:1500 Metric:1 + RX packets:16 errors:0 dropped:0 overruns:0 frame:0 + TX packets:15 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:1865 (1.8 KiB) TX bytes:1620 (1.5 KiB) diff --git a/application/quectel_CM_5G/src/log/qmi_wwan_q_qmap=4_bridge.txt b/application/quectel_CM_5G/src/log/qmi_wwan_q_qmap=4_bridge.txt new file mode 100644 index 0000000..504d11a --- /dev/null +++ b/application/quectel_CM_5G/src/log/qmi_wwan_q_qmap=4_bridge.txt @@ -0,0 +1,132 @@ +root@ZhuoTK:/# brctl addbr br0 +brctl: bridge br0: File exists +root@ZhuoTK:/# brctl delbr br0 +root@ZhuoTK:/# brctl addbr br0 +root@ZhuoTK:/# brctl addif br0 eth0.1 + +root@ZhuoTK:/# insmod qmi_wwan_q.ko qmap_mode=4 +[ 365.340000] usbcore: registered new interface driver qmi_wwan_q +[ 380.860000] usb 1-1.3: new high-speed USB device number 4 using ehci-platform +[ 381.100000] qmi_wwan_q 1-1.3:1.4: cdc-wdm0: USB WDM device +[ 381.100000] qmi_wwan_q 1-1.3:1.4: Quectel Android work on RawIP mode +[ 381.120000] qmi_wwan_q 1-1.3:1.4: rx_urb_size = 4096 +[ 381.120000] qmi_wwan_q 1-1.3:1.4 wwan0: register 'qmi_wwan_q' at usb-101c0000.ehci-1.3, RMNET/USB device, fa:24:73:b5:39:a8 +[ 381.130000] net wwan0: qmap_register_device wwan0_1 +[ 381.140000] net wwan0: qmap_register_device wwan0_2 +[ 381.150000] net wwan0: qmap_register_device wwan0_3 +[ 381.150000] net wwan0: qmap_register_device wwan0_4 + +root@ZhuoTK:/# brctl addbr br0 +root@ZhuoTK:/# brctl addif br0 eth0.1 +root@ZhuoTK:/# brctl addif br0 wwan0_2 +root@ZhuoTK:/# brctl show +bridge name bridge id STP enabled interfaces +br0 8000.00ca019197b9 no eth0.1 + wwan0_2 + +root@ZhuoTK:/# quectel-qmi-proxy & +[04-13_05:18:10:832] Will use cdc-wdm='/dev/cdc-wdm0', proxy='quectel-qmi-proxy0' +[04-13_05:18:10:833] qmi_proxy_init enter +[04-13_05:18:10:833] qmi_proxy_loop enter thread_id 0x77995530 +[04-13_05:18:11:833] qmi_proxy_init succful +[04-13_05:18:11:833] local server: quectel-qmi-proxy0 sockfd = 4 +[04-13_05:18:11:833] qmi_proxy_server_fd = 4 + +root@ZhuoTK:/# quectel-CM -n 2 -s 4gnet -b & +[04-13_05:18:20:144] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_05:18:20:146] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x004 +[04-13_05:18:20:147] Auto find qmichannel = /dev/cdc-wdm0 +[04-13_05:18:20:148] Auto find usbnet_adapter = wwan0 +[04-13_05:18:20:148] netcard driver = qmi_wwan_q, driver version = V1.2.0.23 +[04-13_05:18:20:148] qmap_mode = 4, qmap_version = 5, qmap_size = 4096, muxid = 0x82, qmap_netcard = wwan0_2 +[04-13_05:18:20:149] Modem works in QMI mode +[04-13_05:18:20:150] connect to quectel-qmi-proxy0 sockfd = 7 +[04-13_05:18:20:150] cdc_wdm_fd = 7 +[04-13_05:18:20:370] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_05:18:20:403] qmap_settings.rx_urb_size = 4096 +[04-13_05:18:20:404] qmap_settings.ul_data_aggregation_max_datagrams = 11 +[04-13_05:18:20:404] qmap_settings.ul_data_aggregation_max_size = 4096 +[04-13_05:18:20:404] qmap_settings.dl_minimum_padding = 0 +[04-13_05:18:20:530] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_05:18:20:530] requestSetProfile[2] 4gnet///0 +[04-13_05:18:20:594] requestGetProfile[2] 4gnet///0 +[04-13_05:18:20:626] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_05:18:20:657] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-13_05:18:20:658] ifconfig wwan0_2 0.0.0.0 +[04-13_05:18:20:669] ifconfig wwan0_2 down +[04-13_05:18:21:010] requestSetupDataCall WdsConnectionIPv4Handle: 0x87249650 +[ 425.100000] net wwan0: link_state 0x1 -> 0x3 +[04-13_05:18:21:143] ifconfig wwan0 up +[04-13_05:18:21:156] ifconfig wwan0_2 up +[04-13_05:18:21:168] echo '0xa8ceec7' > /sys/class/net/wwan0_2/bridge_ipv4 + +root@ZhuoTK:/# ifconfig br0 up + +[ 450.520000] br0: port 2(wwan0_2) entered forwarding state +[ 450.520000] br0: port 1(eth0.1) entered forwarding state +[ 450.770000] wwan0_2 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 450.790000] wwan0_2 PC Mac Address: 00:0e:c6:a6:6c:f1 +[ 450.840000] wwan0_2 sip = 10.140.238.199, tip=10.140.238.200, ipv4=10.140.238.199 +[ 450.950000] wwan0_2 sip = 10.140.238.199, tip=10.140.238.200, ipv4=10.140.238.199 +[ 450.950000] wwan0_2 sip = 10.140.238.199, tip=10.140.238.200, ipv4=10.140.238.199 +[ 451.120000] wwan0_2 sip = 0.0.0.0, tip=10.140.238.199, ipv4=10.140.238.199 +[ 451.180000] wwan0_2 sip = 10.140.238.199, tip=10.140.238.200, ipv4=10.140.238.199 +[ 452.120000] wwan0_2 sip = 0.0.0.0, tip=10.140.238.199, ipv4=10.140.238.199 +[ 453.080000] wwan0_2 sip = 10.140.238.199, tip=10.140.238.200, ipv4=10.140.238.199 +[ 453.120000] wwan0_2 sip = 0.0.0.0, tip=10.140.238.199, ipv4=10.140.238.199 +[ 454.120000] wwan0_2 sip = 10.140.238.199, tip=10.140.238.199, ipv4=10.140.238.199 +[ 454.220000] wwan0_2 sip = 10.140.238.199, tip=10.140.238.200, ipv4=10.140.238.199 +[ 456.200000] wwan0_2 sip = 10.140.238.199, tip=10.140.238.200, ipv4=10.140.238.199 +[ 458.120000] wwan0_2 sip = 10.140.238.199, tip=10.140.238.200, ipv4=10.140.238.199 +[ 459.240000] wwan0_2 sip = 10.140.238.199, tip=10.140.238.200, ipv4=10.140.238.199 + +root@ZhuoTK:/# quectel-CM -n 1 -s cmnet & +[04-13_05:19:21:122] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_05:19:21:125] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x004 +[04-13_05:19:21:126] Auto find qmichannel = /dev/cdc-wdm0 +[04-13_05:19:21:126] Auto find usbnet_adapter = wwan0 +[04-13_05:19:21:127] netcard driver = qmi_wwan_q, driver version = V1.2.0.23 +[04-13_05:19:21:127] qmap_mode = 4, qmap_version = 5, qmap_size = 4096, muxid = 0x81, qmap_netcard = wwan0_1 +[04-13_05:19:21:127] Modem works in QMI mode +[04-13_05:19:21:128] connect to quectel-qmi-proxy0 sockfd = 7 +[04-13_05:19:21:129] cdc_wdm_fd = 7 +[04-13_05:19:21:331] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_05:19:21:459] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_05:19:21:459] requestSetProfile[1] cmnet///0 +[04-13_05:19:21:522] requestGetProfile[1] cmnet///0 +[04-13_05:19:21:554] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_05:19:21:585] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[ 485.550000] net wwan0: link_state 0x3 -> 0x2 +[04-13_05:19:21:592] ifconfig wwan0_1 0.0.0.0 +[ 485.570000] IPv6: ADDRCONF(NETDEV_UP): wwan0_1: link is not ready +[04-13_05:19:21:610] ifconfig wwan0_1 down +[04-13_05:19:21:682] requestSetupDataCall WdsConnectionIPv4Handle: 0x8725ed70 +[ 485.780000] net wwan0: link_state 0x2 -> 0x3 +[04-13_05:19:21:815] ifconfig wwan0 up +[04-13_05:19:21:826] ifconfig wwan0_1 up +[04-13_05:19:21:845] you are use OpenWrt? +[04-13_05:19:21:845] should not calling udhcpc manually? +[04-13_05:19:21:845] should modify /etc/config/network as below? +[04-13_05:19:21:845] config interface wan +[04-13_05:19:21:845] option ifname wwan0_1 +[04-13_05:19:21:845] option proto dhcp +[04-13_05:19:21:845] should use "/sbin/ifstaus wan" to check wwan0_1 's status? +[04-13_05:19:21:846] busybox udhcpc -f -n -q -t 5 -i wwan0_1 +[04-13_05:19:21:863] udhcpc (v1.23.2) started +[04-13_05:19:21:923] Sending discover... +[04-13_05:19:21:927] Sending select for 10.141.146.55... +[04-13_05:19:21:932] Lease of 10.141.146.55 obtained, lease time 7200 +[04-13_05:19:21:938] udhcpc: ifconfig wwan0_1 10.141.146.55 netmask 255.255.255.240 broadcast + +[04-13_05:19:21:949] udhcpc: setting default routers: 10.141.146.56 + +root@ZhuoTK:/# ip ro show +default via 10.141.146.56 dev wwan0_1 +10.141.146.48/28 dev wwan0_1 proto kernel scope link src 10.141.146.55 +192.168.1.0/24 dev br-lan proto kernel scope link src 192.168.1.251 + +root@ZhuoTK:/# ping 8.8.8.8 +PING 8.8.8.8 (8.8.8.8): 56 data bytes +64 bytes from 8.8.8.8: seq=0 ttl=111 time=86.006 ms +64 bytes from 8.8.8.8: seq=1 ttl=111 time=74.763 ms +64 bytes from 8.8.8.8: seq=2 ttl=111 time=85.501 ms +64 bytes from 8.8.8.8: seq=3 ttl=111 time=74.231 ms diff --git a/application/quectel_CM_5G/src/log/qmi_wwan_qmap=4.txt b/application/quectel_CM_5G/src/log/qmi_wwan_qmap=4.txt new file mode 100644 index 0000000..c0067bf --- /dev/null +++ b/application/quectel_CM_5G/src/log/qmi_wwan_qmap=4.txt @@ -0,0 +1,55 @@ +# dmesg +[ 1737.738025] usb 1-1.2: new high-speed USB device number 5 using xhci-hcd +[ 1737.838917] usb 1-1.2: New USB device found, idVendor=2c7c, idProduct=0512, bcdDevice= 3.18 +[ 1737.838948] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3 +[ 1737.838963] usb 1-1.2: Product: EG12-EA +[ 1737.838975] usb 1-1.2: Manufacturer: Quectel +[ 1737.838986] usb 1-1.2: SerialNumber: 0123456789ABCDE +[ 1737.994955] option 1-1.2:1.0: GSM modem (1-port) converter detected +[ 1737.995430] usb 1-1.2: GSM modem (1-port) converter now attached to ttyUSB0 +[ 1737.995978] option 1-1.2:1.1: GSM modem (1-port) converter detected +[ 1737.996409] usb 1-1.2: GSM modem (1-port) converter now attached to ttyUSB1 +[ 1737.996963] option 1-1.2:1.2: GSM modem (1-port) converter detected +[ 1737.997351] usb 1-1.2: GSM modem (1-port) converter now attached to ttyUSB2 +[ 1737.997909] option 1-1.2:1.3: GSM modem (1-port) converter detected +[ 1737.998976] usb 1-1.2: GSM modem (1-port) converter now attached to ttyUSB3 +[ 1825.835796] qmi_wwan 1-1.2:1.4: cdc-wdm0: USB WDM device +[ 1825.839685] qmi_wwan 1-1.2:1.4 wwan0: register 'qmi_wwan' at usb-fe9c0000.xhci-1.2, WWAN/QMI device, 0e:80:14:b1:f6:b9 +[ 1825.840062] usbcore: registered new interface driver qmi_wwan + +# ifconfig wwan0 down +# echo Y > /sys/class/net/wwan0/qmi/raw_ip + +# echo 1 > /sys/class/net/wwan0/qmi/add_mux +# ifconfig qmimux0 +qmimux0: flags=4240 mtu 1500 + unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 1000 (UNSPEC) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 0 bytes 0 (0.0 B) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +# echo 2 > /sys/class/net/wwan0/qmi/add_mux +# echo 3 > /sys/class/net/wwan0/qmi/add_mux +# echo 4 > /sys/class/net/wwan0/qmi/add_mux +# ifconfig qmimux3 +qmimux3: flags=4240 mtu 1500 + unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 1000 (UNSPEC) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 0 bytes 0 (0.0 B) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +set wwan0's mtu to max qmap packet size, for usbnet.c:usbnet_change_mtu() do not accept +# ifconfig wwan0 mtu 16385 + +run qmi proxy programm, +# quectel-CM/quectel-qmi-proxy -d /dev/cdc-wdm0 +or libqmi's qmi-proxy, if use libqmi's qmi-proxy, you can use qmicli to setup data call. +# /usr/libexec/qmi-proxy --verbose --no-exit + +use quectel-CM to setup data call, if use libqmi's qmi-proxy, use '-p qmi-proxy' instead of '-p quectel-qmi-proxy' +# quectel-CM -p quectel-qmi-proxy -n 4 -s cmnet4 +# quectel-CM -p quectel-qmi-proxy -n 1 -s cmnet + +for how to use libqmi to setup data call, please refer to https://knowledge.quectel.com/display/SWSYSTLinuxAndroid/libqmi_How+to+using+QMAP+multiplexing \ No newline at end of file diff --git a/application/quectel_CM_5G/src/log/usage_of_argument/6.txt b/application/quectel_CM_5G/src/log/usage_of_argument/6.txt new file mode 100644 index 0000000..5ab9533 --- /dev/null +++ b/application/quectel_CM_5G/src/log/usage_of_argument/6.txt @@ -0,0 +1,68 @@ +root@OpenWrt:~# quectel-CM -s cmnet -4 -6 +[04-14_06:56:51:778] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-14_06:56:51:779] network interface '' or qmidev '' is not exist +[04-14_06:56:51:780] netcard driver = pcie_mhi, driver version = V1.3.0.17 +[04-14_06:56:51:781] qmap_mode = 4, qmap_version = 9, qmap_size = 16384, muxid = 0x81, qmap_netcard = rmnet_mhi0.1 +[04-14_06:56:51:782] Modem works in QMI mode +[04-14_06:56:51:783] connect to quectel-qmi-proxy0 sockfd = 7 +[04-14_06:56:51:783] cdc_wdm_fd = 7 +[04-14_06:56:51:789] Get clientWDS = 15 +[04-14_06:56:51:792] Get clientWDS = 16 +[04-14_06:56:51:794] Get clientDMS = 3 +[04-14_06:56:51:798] Get clientNAS = 4 +[04-14_06:56:51:801] Get clientUIM = 1 +[04-14_06:56:51:805] Get clientWDA = 1 +[04-14_06:56:51:809] requestBaseBandVersion RM500QGLABR10A03M4G +[04-14_06:56:51:813] qmap_settings.rx_urb_size = 16384 +[04-14_06:56:51:813] qmap_settings.ul_data_aggregation_max_datagrams = 11 +[04-14_06:56:51:814] qmap_settings.ul_data_aggregation_max_size = 8192 +[04-14_06:56:51:814] qmap_settings.dl_minimum_padding = 0 +[04-14_06:56:51:835] requestGetSIMStatus SIMStatus: SIM_READY +[04-14_06:56:51:836] requestSetProfile[1] cmnet///0 +[04-14_06:56:51:848] requestGetProfile[1] cmnet///0 +[04-14_06:56:51:852] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: 5G_NSA +[04-14_06:56:51:857] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-14_06:56:51:860] requestQueryDataCall IPv6ConnectionStatus: DISCONNECTED +[04-14_06:56:51:861] ifconfig rmnet_mhi0 down +[04-14_06:56:51:885] ifconfig rmnet_mhi0.1 0.0.0.0 +ifconfig: SIOCSIFFLAGS: Network is down +[04-14_06:56:51:909] ifconfig rmnet_mhi0.1 down +[04-14_06:56:51:943] requestSetupDataCall WdsConnectionIPv4Handle: 0x341450a0 +[04-14_06:56:52:423] requestSetupDataCall WdsConnectionIPv6Handle: 0x341439f0 +[ 1001.561353] net rmnet_mhi0: link_state 0x0 -> 0x1 +[04-14_06:56:52:441] ifconfig rmnet_mhi0 up +[ 1001.584623] [I][mhi_netdev_open] Opened net dev interface +[04-14_06:56:52:467] ifconfig rmnet_mhi0.1 up +[04-14_06:56:52:495] you are use OpenWrt? +[04-14_06:56:52:496] should not calling udhcpc manually? +[04-14_06:56:52:496] should modify /etc/config/network as below? +[04-14_06:56:52:497] config interface wan +[04-14_06:56:52:497] option ifname rmnet_mhi0.1 +[04-14_06:56:52:497] option proto dhcp +[04-14_06:56:52:498] should use "/sbin/ifstaus wan" to check rmnet_mhi0.1 's status? +[04-14_06:56:52:498] busybox udhcpc -f -n -q -t 5 -i rmnet_mhi0.1 +udhcpc: started, v1.28.3 +udhcpc: sending discover +udhcpc: sending select for 10.245.22.3 +udhcpc: lease of 10.245.22.3 obtained, lease time 7200 +[04-14_06:56:52:713] udhcpc: ifconfig rmnet_mhi0.1 10.245.22.3 netmask 255.255.255.248 broadcast + +[04-14_06:56:52:754] udhcpc: setting default routers: 10.245.22.4 +[04-14_06:56:52:838] ip -6 address flush dev rmnet_mhi0.1 +[04-14_06:56:52:861] ip -6 address add 2409:8930:463:3daa:7c8e:429a:c902:c6cc/64 dev rmnet_mhi0.1 +[04-14_06:56:52:884] ip -6 route add default dev rmnet_mhi0.1 + +root@OpenWrt:~# cat /etc/resolv.conf +nameserver 2409:8030:2000:0:0:0:0:1 # IPV6 rmnet_mhi0.1 +nameserver 2409:8030:2000:0:0:0:0:2 # IPV6 rmnet_mhi0.1 +search lan +nameserver 127.0.0.1 + +root@OpenWrt:~# ip -6 ro show +2409:8930:463:96df::/64 dev rmnet_mhi0.1 proto kernel metric 256 +fe80::/64 dev br-lan proto kernel metric 256 +fe80::/64 dev br0 proto kernel metric 256 +default dev rmnet_mhi0.1 metric 1024 + +root@OpenWrt:~# ping6 www.qq.com +PING www.qq.com (2402:4e00:1430:1301::9227:79cc:76f2): 56 data bytes +64 bytes from 2402:4e00:1430:1301::9227:79cc:76f2: seq=0 ttl=51 time=97.230 ms diff --git a/application/quectel_CM_5G/src/log/usage_of_argument/m.txt b/application/quectel_CM_5G/src/log/usage_of_argument/m.txt new file mode 100644 index 0000000..c4c46b8 --- /dev/null +++ b/application/quectel_CM_5G/src/log/usage_of_argument/m.txt @@ -0,0 +1,58 @@ +root@ZhuoTK:/# quectel-CM -n 1 -m 4 -s cmnet & +[04-13_05:12:07:455] Quectel_QConnectManager_Linux_V1.6.0.25 +[04-13_05:12:07:458] Find /sys/bus/usb/devices/1-1.3 idVendor=0x2c7c idProduct=0x125, bus=0x001, dev=0x003 +[04-13_05:12:07:459] Auto find qmichannel = /dev/qcqmi0 +[04-13_05:12:07:459] Auto find usbnet_adapter = usb0 +[04-13_05:12:07:467] netcard driver = GobiNet, driver version = V1.6.2.13 +[04-13_05:12:07:467] qmap_mode = 4, qmap_version = 5, qmap_size = 4096, muxid = 0x84, qmap_netcard = usb0.4 +[04-13_05:12:07:467] Modem works in QMI mode +[04-13_05:12:07:495] Get clientWDS = 7 +[04-13_05:12:07:529] Get clientDMS = 8 +[04-13_05:12:07:561] Get clientNAS = 9 +[04-13_05:12:07:591] Get clientUIM = 10 +[04-13_05:12:07:623] requestBaseBandVersion EC25EFAR06A11M4G +[04-13_05:12:07:752] requestGetSIMStatus SIMStatus: SIM_READY +[04-13_05:12:07:752] requestSetProfile[1] cmnet///0 +[04-13_05:12:07:817] requestGetProfile[1] cmnet///0 +[04-13_05:12:07:849] requestRegistrationState2 MCC: 460, MNC: 0, PS: Attached, DataCap: LTE +[04-13_05:12:07:881] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED +[04-13_05:12:07:881] ifconfig usb0 down +[04-13_05:12:07:892] ifconfig usb0.4 0.0.0.0 +[04-13_05:12:07:903] ifconfig usb0.4 down +[04-13_05:12:07:944] requestSetupDataCall WdsConnectionIPv4Handle: 0x87265c40 +[ 52.020000] net usb0: link_state 0x0 -> 0x8 +[04-13_05:12:08:077] ifconfig usb0 up +[04-13_05:12:08:096] ifconfig usb0.4 up +[04-13_05:12:08:116] you are use OpenWrt? +[04-13_05:12:08:116] should not calling udhcpc manually? +[04-13_05:12:08:116] should modify /etc/config/network as below? +[04-13_05:12:08:116] config interface wan +[04-13_05:12:08:116] option ifname usb0.4 +[04-13_05:12:08:116] option proto dhcp +[04-13_05:12:08:116] should use "/sbin/ifstaus wan" to check usb0.4 's status? +[04-13_05:12:08:117] busybox udhcpc -f -n -q -t 5 -i usb0.4 +[04-13_05:12:08:134] udhcpc (v1.23.2) started +[04-13_05:12:08:193] Sending discover... +[04-13_05:12:08:197] Sending select for 10.84.241.180... +[04-13_05:12:08:203] Lease of 10.84.241.180 obtained, lease time 7200 +[04-13_05:12:08:208] udhcpc: ifconfig usb0.4 10.84.241.180 netmask 255.255.255.248 broadcast + +[04-13_05:12:08:221] udhcpc: setting default routers: 10.84.241.181 + +root@ZhuoTK:/# ifconfig usb0.4 +usb0.4 Link encap:Ethernet HWaddr 02:50:F4:00:00:00 + inet addr:10.84.241.180 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:6 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:612 (612.0 B) TX bytes:984 (984.0 B) + +root@ZhuoTK:/# ip ro show +default via 10.84.241.181 dev usb0.4 +10.84.241.176/29 dev usb0.4 proto kernel scope link src 10.84.241.180 +192.168.1.0/24 dev br-lan proto kernel scope link src 192.168.1.251 + +root@ZhuoTK:/# ping 8.8.8.8 +PING 8.8.8.8 (8.8.8.8): 56 data bytes +64 bytes from 8.8.8.8: seq=0 ttl=52 time=99.431 ms diff --git a/application/quectel_CM_5G/src/main.c b/application/quectel_CM_5G/src/main.c new file mode 100644 index 0000000..158920a --- /dev/null +++ b/application/quectel_CM_5G/src/main.c @@ -0,0 +1,961 @@ +/****************************************************************************** + @file main.c + @brief The entry program. + + DESCRIPTION + Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 -2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ + +#include "QMIThread.h" +#include +#include +#include +#include + +#include "util.h" +//#define CONFIG_PID_FILE_FORMAT "/var/run/quectel-CM-%s.pid" //for example /var/run/quectel-CM-wwan0.pid + +static PROFILE_T s_profile; +int debug_qmi = 0; +int qmidevice_control_fd[2]; +static int signal_control_fd[2]; +int g_donot_exit_when_modem_hangup = 0; +extern int ql_ifconfig(int argc, char *argv[]); +extern int ql_get_netcard_driver_info(const char*); +extern int ql_capture_usbmon_log(PROFILE_T *profile, const char *log_path); +extern void ql_stop_usbmon_log(PROFILE_T *profile); +//UINT ifc_get_addr(const char *ifname); +static int s_link = -1; +static void usbnet_link_change(int link, PROFILE_T *profile) { + if (s_link == link) + return; + + s_link = link; + + if (!(link & (1<ipv4, 0, sizeof(IPV4_T)); + + if (!(link & (1<ipv6, 0, sizeof(IPV6_T)); + + if (link) { + udhcpc_start(profile); + } else { + udhcpc_stop(profile); + } +} + +static int check_ipv4_address(PROFILE_T *profile) { + uint32_t oldAddress = profile->ipv4.Address; + + if (profile->request_ops == &mbim_request_ops) + return 1; //we will get a new ipv6 address per requestGetIPAddress() + if (profile->request_ops == &atc_request_ops) { + if (!profile->udhcpc_ip) return 1; + oldAddress = profile->udhcpc_ip; + } + + if (profile->request_ops->requestGetIPAddress(profile, IpFamilyV4) == 0) { + if (profile->ipv4.Address != oldAddress || debug_qmi) { + unsigned char *l = (unsigned char *)&oldAddress; + unsigned char *r = (unsigned char *)&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 (profile->ipv4.Address == oldAddress); + } + + return 0; +} + +static void main_send_event_to_qmidevice(int triger_event) { + if (write(qmidevice_control_fd[0], &triger_event, sizeof(triger_event)) == -1) {}; +} + +static void send_signo_to_main(int signo) { + if (write(signal_control_fd[0], &signo, sizeof(signo)) == -1) {}; +} + +void qmidevice_send_event_to_main(int triger_event) { + if (write(qmidevice_control_fd[1], &triger_event, sizeof(triger_event)) == -1) {}; +} + +void qmidevice_send_event_to_main_ext(int triger_event, void *data, unsigned len) { + if (write(qmidevice_control_fd[1], &triger_event, sizeof(triger_event)) == -1) {}; + if (write(qmidevice_control_fd[1], data, len) == -1) {}; +} + +#define MAX_PATH 256 + +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*2+6]; + char filename[MAX_PATH]; + int linksize; + + snprintf(linkname, sizeof(linkname), "%.256s/%s", dir, file); + linksize = readlink(linkname, filename, sizeof(filename)); + 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*2+6]; + 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, sizeof(linkname), "%s/%s/exe", dir, file); + linksize = readlink(linkname, filename, sizeof(filename)); + if (linksize <= 0) + return 0; + + filename[linksize] = 0; + + pid = atoi(file); + if (pid >= getpid()) + return 0; + + snprintf(linkname, sizeof(linkname), "%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, sizeof(myself)); + if (filenamesize <= 0) + return 0; + myself[filenamesize] = 0; + + if (ls_dir("/proc", is_brother_process, argv)) + sleep(1); + + return 0; +} + +static int kill_data_call_pdp(int pdp, char *self) { + int pid; + char *p = NULL; + + p = self; + while (*self) { + if (*self == '/') + p = self+1; + self++; + } + + pid = getpid_by_pdp(pdp, p); + if (pid > 0) { + dbg_time("send SIGINT to process %d", pid); + return kill(pid, SIGINT); + } + + return -1; +} + +static void ql_sigaction(int signo) { + if (SIGALRM == signo) + send_signo_to_main(SIG_EVENT_START); + else + { + g_donot_exit_when_modem_hangup = 0; + send_signo_to_main(SIG_EVENT_STOP); + main_send_event_to_qmidevice(SIG_EVENT_STOP); //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. auth: 1~pap, 2~chap, 3~MsChapV2"); + dbg_time("-p pincode Verify sim card pin if sim card is locked"); + dbg_time("-p [quectel-][qmi|mbim]-proxy Request to use proxy"); + dbg_time("-f logfilename Save log message of this program to file"); + dbg_time("-u usbmonlog filename Save usbmon log to file"); + dbg_time("-i interface Specify which network interface to setup data call when multi-modems exits"); + dbg_time("-4 Setup IPv4 data call (default)"); + dbg_time("-6 Setup IPv6 data call"); + dbg_time("-n pdn Specify which pdn to setup data call (default 1 for QMI, 0 for MBIM)"); + dbg_time("-k pdn Specify which pdn to hangup data call (by send SIGINT to 'quectel-CM -n pdn')"); + dbg_time("-m iface-idx Bind QMI data call to wwan0_ when QMAP used. E.g '-n 7 -m 1' bind pdn-7 data call to wwan0_1"); + dbg_time("-b Enable network interface bridge function (default 0)"); + dbg_time("-v Verbose log mode, for debug purpose."); + dbg_time("-d Obtain the IP address and dns through qmi"); + 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 1 -p 1234 -f gobinet_log.txt", progname); + return 0; +} + +static int qmi_main(PROFILE_T *profile) +{ + int triger_event = 0; + int signo; +#ifdef CONFIG_SIM + SIM_Status SIMStatus = SIM_ABSENT; +#endif + UCHAR PSAttachedState = 0; + UCHAR IPv4ConnectionStatus = QWDS_PKT_DATA_UNKNOW; + UCHAR IPv6ConnectionStatus = QWDS_PKT_DATA_UNKNOW; + unsigned SetupCallFail = 0; + unsigned long SetupCallAllowTime = clock_msec(); +#ifdef REBOOT_SIM_CARD_WHEN_LONG_TIME_NO_PS + unsigned PsAttachFail = 0; + unsigned long PsAttachTime = clock_msec(); +#endif + int qmierr = 0; + const struct request_ops *request_ops = profile ->request_ops; + pthread_t gQmiThreadID = 0; + +//sudo apt-get install udhcpc +//sudo apt-get remove ModemManager + if (profile->reattach_flag) { + if (!reattach_driver(profile)) + sleep(2); + } + + /* try to recreate FDs*/ + 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; + } + + if ((profile->qmap_mode == 0 || profile->qmap_mode == 1) + && (!profile->proxy[0] || strstr(profile->qmichannel, "_IPCR"))) { + 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; + } + + if (request_ops->requestBaseBandVersion) + request_ops->requestBaseBandVersion(profile); + + if (request_ops->requestSetEthMode) + request_ops->requestSetEthMode(profile); + + if (request_ops->requestSetLoopBackState && profile->loopback_state) { + qmierr = request_ops->requestSetLoopBackState(profile->loopback_state, profile->replication_factor); + if (qmierr != QMI_ERR_INVALID_QMI_CMD) //X20 return this error + profile->loopback_state = 0; //wait for RIL_UNSOL_LOOPBACK_CONFIG_IND + } + + if (request_ops->requestGetSIMStatus) { + qmierr = request_ops->requestGetSIMStatus(&SIMStatus); + + while (qmierr == QMI_ERR_OP_DEVICE_UNSUPPORTED) { + sleep(1); + qmierr = request_ops->requestGetSIMStatus(&SIMStatus); + } + + if ((SIMStatus == SIM_PIN) && profile->pincode && request_ops->requestEnterSimPin) { + request_ops->requestEnterSimPin(profile->pincode); + } + } + + if (SIMStatus == SIM_READY) { + if (request_ops->requestGetICCID) + request_ops->requestGetICCID(); + + if (request_ops->requestGetIMSI) + request_ops->requestGetIMSI(); + } + + if (request_ops->requestGetProfile) + request_ops->requestGetProfile(profile); + + if (request_ops->requestSetProfile && (profile->apn || profile->user || profile->password)) { + if (request_ops->requestSetProfile(profile) == 1) { +#ifdef REBOOT_SIM_CARD_WHEN_APN_CHANGE //enable at only when customer asked + if (request_ops->requestRadioPower) { + request_ops->requestRadioPower(0); + request_ops->requestRadioPower(1); + } +#endif + } + } + + request_ops->requestRegistrationState(&PSAttachedState); + +#ifdef CONFIG_ENABLE_QOS + request_ops->requestRegisterQos(profile); +#endif + +#if 1 //USB disconnnect and re-connect, but not reboot modem, will get this bug + if (profile->enable_ipv4 + && profile->request_ops == &atc_request_ops + && !request_ops->requestQueryDataCall(&IPv4ConnectionStatus, IpFamilyV4) + && IPv4ConnectionStatus == QWDS_PKT_DATA_CONNECTED) { + request_ops->requestDeactivateDefaultPDP(profile, IpFamilyV4); + } +#endif + + send_signo_to_main(SIG_EVENT_CHECK); + + 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(SIG_EVENT_CHECK); + 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; + + 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); + switch (signo) + { + case SIG_EVENT_START: + if (PSAttachedState != 1 && profile->loopback_state == 0) + break; + + if (SetupCallAllowTime > clock_msec()) { + alarm((SetupCallAllowTime - clock_msec()+999)/1000); + break; + } + + if (profile->enable_ipv4 && IPv4ConnectionStatus != QWDS_PKT_DATA_CONNECTED) { + qmierr = request_ops->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 = request_ops->requestSetupDataCall(profile, IpFamilyV4); + + if (qmierr) + profile->auth = old_auto; //still fail, restore old auth moe + } + + if (!qmierr) { + qmierr = request_ops->requestGetIPAddress(profile, IpFamilyV4); + if (!qmierr) + IPv4ConnectionStatus = QWDS_PKT_DATA_CONNECTED; + } + + } + + if (profile->enable_ipv6 && IPv6ConnectionStatus != QWDS_PKT_DATA_CONNECTED) { + if (profile->enable_ipv4 && profile->request_ops != &qmi_request_ops) { + IPv6ConnectionStatus = IPv4ConnectionStatus; + } + else { + qmierr = request_ops->requestSetupDataCall(profile, IpFamilyV6); + + if (!qmierr) { + qmierr = request_ops->requestGetIPAddress(profile, IpFamilyV6); + if (!qmierr) + IPv6ConnectionStatus = QWDS_PKT_DATA_CONNECTED; + } + } + } + + if ((profile->enable_ipv4 && IPv4ConnectionStatus == QWDS_PKT_DATA_DISCONNECTED) + || (profile->enable_ipv6 && IPv6ConnectionStatus == QWDS_PKT_DATA_DISCONNECTED)) { + const unsigned allow_time[] = {5, 10, 20, 40, 60}; + + if (SetupCallFail < (sizeof(allow_time)/sizeof(unsigned))) + SetupCallAllowTime = allow_time[SetupCallFail]; + else + SetupCallAllowTime = 60; + SetupCallFail++; + dbg_time("try to requestSetupDataCall %ld second later", SetupCallAllowTime); + alarm(SetupCallAllowTime); + SetupCallAllowTime = SetupCallAllowTime*1000 + clock_msec(); + } + else if (IPv4ConnectionStatus == QWDS_PKT_DATA_CONNECTED || IPv6ConnectionStatus == QWDS_PKT_DATA_CONNECTED) { + SetupCallFail = 0; + SetupCallAllowTime = clock_msec(); + } + break; + + case SIG_EVENT_CHECK: + if (request_ops->requestGetSignalInfo) + request_ops->requestGetSignalInfo(); + + if (request_ops->requestGetCellInfoList) + request_ops->requestGetCellInfoList(); + + if (request_ops->requestGetCoexWWANState) + request_ops->requestGetCoexWWANState(); + + if (PSAttachedState != 1) + request_ops->requestRegistrationState(&PSAttachedState); + +#ifdef REBOOT_SIM_CARD_WHEN_LONG_TIME_NO_PS + if (PSAttachedState) { + PsAttachTime = clock_msec(); + PsAttachFail = 0; + } + else { + unsigned long diff = (clock_msec() - PsAttachTime) / 1000; + unsigned long threshold = REBOOT_SIM_CARD_WHEN_LONG_TIME_NO_PS << PsAttachFail; + + if (diff > threshold || diff > 960) { + //interval time is 60 -> 120 - > 240 - > 480 -> 960 + PsAttachTime = clock_msec(); + PsAttachFail++; + + if (request_ops->requestRadioPower) { + request_ops->requestRadioPower(0); + request_ops->requestRadioPower(1); + } + } + } +#endif + + if (profile->enable_ipv4 && IPv4ConnectionStatus != QWDS_PKT_DATA_DISCONNECTED + && !request_ops->requestQueryDataCall(&IPv4ConnectionStatus, IpFamilyV4)) + { + if (QWDS_PKT_DATA_CONNECTED == IPv4ConnectionStatus && profile->ipv4.Address == 0) { + //killall -9 quectel-CM for MBIM and ATC call + qmierr = request_ops->requestGetIPAddress(profile, IpFamilyV4); + if (qmierr) + IPv4ConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; + } + + //local ip is different with remote ip + if (QWDS_PKT_DATA_CONNECTED == IPv4ConnectionStatus && check_ipv4_address(profile) == 0) { + request_ops->requestDeactivateDefaultPDP(profile, IpFamilyV4); + IPv4ConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; + } + } + else { + IPv4ConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; + } + + if (profile->enable_ipv6 && IPv6ConnectionStatus != QWDS_PKT_DATA_DISCONNECTED) { + if (profile->enable_ipv4 && profile->request_ops != &qmi_request_ops) { + IPv6ConnectionStatus = IPv4ConnectionStatus; + } + else { + request_ops->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); + } + else if (IPv4ConnectionStatus == QWDS_PKT_DATA_CONNECTED || IPv6ConnectionStatus == QWDS_PKT_DATA_CONNECTED) { + int link = 0; + if (IPv4ConnectionStatus == QWDS_PKT_DATA_CONNECTED) + link |= (1<enable_ipv4 && IPv4ConnectionStatus == QWDS_PKT_DATA_DISCONNECTED) + || (profile->enable_ipv6 && IPv6ConnectionStatus == QWDS_PKT_DATA_DISCONNECTED)) { + send_signo_to_main(SIG_EVENT_START); + } + break; + + case SIG_EVENT_STOP: + if (profile->enable_ipv4 && IPv4ConnectionStatus == QWDS_PKT_DATA_CONNECTED) { + request_ops->requestDeactivateDefaultPDP(profile, IpFamilyV4); + } + if (profile->enable_ipv6 && IPv6ConnectionStatus == QWDS_PKT_DATA_CONNECTED) { + if (profile->enable_ipv4 && profile->request_ops != &qmi_request_ops) { + + } + else { + request_ops->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); + goto __main_quit; + break; + + case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED: + request_ops->requestRegistrationState(&PSAttachedState); + if (PSAttachedState == 1) { + if ((profile->enable_ipv4 && IPv4ConnectionStatus == QWDS_PKT_DATA_DISCONNECTED) + || (profile->enable_ipv6 && IPv6ConnectionStatus == QWDS_PKT_DATA_DISCONNECTED)) { + send_signo_to_main(SIG_EVENT_START); + } + } else { + SetupCallAllowTime = clock_msec(); + } + break; + + case RIL_UNSOL_DATA_CALL_LIST_CHANGED: + if (IPv4ConnectionStatus == QWDS_PKT_DATA_CONNECTED || IPv6ConnectionStatus == QWDS_PKT_DATA_CONNECTED) { + SetupCallAllowTime = clock_msec() + 1000; //from connect -> disconnect, do not re-dail immediately, wait network stable + } + send_signo_to_main(SIG_EVENT_CHECK); + break; + + case MODEM_REPORT_RESET_EVENT: + { + dbg_time("main recv MODEM RESET SIGNAL"); + main_send_event_to_qmidevice(RIL_REQUEST_QUIT); + g_donot_exit_when_modem_hangup = 1; + goto __main_quit; + } + break; + + case RIL_UNSOL_LOOPBACK_CONFIG_IND: + { + QMI_WDA_SET_LOOPBACK_CONFIG_IND_MSG SetLoopBackInd; + if (read(fd, &SetLoopBackInd, sizeof(SetLoopBackInd)) == sizeof(SetLoopBackInd)) { + profile->loopback_state = SetLoopBackInd.loopback_state.TLVVaule; + profile->replication_factor = le32_to_cpu(SetLoopBackInd.replication_factor.TLVVaule); + dbg_time("SetLoopBackInd: loopback_state=%d, replication_factor=%u", + profile->loopback_state, profile->replication_factor); + if (profile->loopback_state) + send_signo_to_main(SIG_EVENT_START); + } + } + break; +#ifdef CONFIG_REG_QOS_IND + case RIL_UNSOL_GLOBAL_QOS_FLOW_IND_QOS_ID: + { + UINT qos_id = 0; + if (read(fd, &qos_id, sizeof(qos_id)) == sizeof(qos_id)) { + profile->qos_id = qos_id; + } + } + break; +#endif + default: + break; + } + } + } + } + } + +__main_quit: + usbnet_link_change(0, profile); + if (gQmiThreadID && 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__); + + return 0; +} + +static int quectel_CM(PROFILE_T *profile) +{ + int ret = 0; + char qmichannel[32] = {'\0'}; + char usbnet_adapter[32] = {'\0'}; + + if (profile->expect_adapter[0]) + strncpy(usbnet_adapter, profile->expect_adapter, sizeof(usbnet_adapter)); + + if (qmidevice_detect(qmichannel, usbnet_adapter, sizeof(qmichannel), profile)) { + profile->hardware_interface = HARDWARE_USB; + } + else if (mhidevice_detect(qmichannel, usbnet_adapter, profile)) { + profile->hardware_interface = HARDWARE_PCIE; + } + else if (atdevice_detect(qmichannel, usbnet_adapter, profile)) { + profile->hardware_interface = HARDWARE_PCIE; + } +#ifdef CONFIG_QRTR + else if (1) { + strcpy(qmichannel, "qrtr"); + strcpy(usbnet_adapter, "rmnet_mhi0"); + profile->hardware_interface = HARDWARE_PCIE; + profile->software_interface = SOFTWARE_QRTR; + } +#endif + else { + dbg_time("qmidevice_detect failed"); + goto error; + } + + strncpy(profile->qmichannel, qmichannel, sizeof(profile->qmichannel)); + strncpy(profile->usbnet_adapter, usbnet_adapter, sizeof(profile->usbnet_adapter)); + ql_get_netcard_driver_info(profile->usbnet_adapter); + + if ((profile->hardware_interface == HARDWARE_USB) && profile->usblogfile) + ql_capture_usbmon_log(profile, profile->usblogfile); + + if (profile->hardware_interface == HARDWARE_USB) { + profile->software_interface = get_driver_type(profile); + } + + ql_qmap_mode_detect(profile); + + if (profile->software_interface == SOFTWARE_MBIM) { + dbg_time("Modem works in MBIM mode"); + profile->request_ops = &mbim_request_ops; + profile->qmi_ops = &mbim_dev_ops; + if (!profile->apn || !profile->apn[0]) { + //see FAE-51804 FAE-59811 + dbg_time("When MBIM mode, must specify APN with '-s', or setup data call may fail!"); + exit(-404); //if no such issue on your side, please comment this + } + ret = qmi_main(profile); + } + else if (profile->software_interface == SOFTWARE_QMI) { + dbg_time("Modem works in QMI mode"); + profile->request_ops = &qmi_request_ops; + if (qmidev_is_gobinet(profile->qmichannel)) + profile->qmi_ops = &gobi_qmidev_ops; + else + profile->qmi_ops = &qmiwwan_qmidev_ops; + qmidev_send = profile->qmi_ops->send; + ret = qmi_main(profile); + } + else if (profile->software_interface == SOFTWARE_ECM_RNDIS_NCM) { + dbg_time("Modem works in ECM_RNDIS_NCM mode"); + profile->request_ops = &atc_request_ops; + profile->qmi_ops = &atc_dev_ops; + ret = qmi_main(profile); + } +#ifdef CONFIG_QRTR + else if (profile->software_interface == SOFTWARE_QRTR) { + dbg_time("Modem works in QRTR mode"); + profile->request_ops = &qmi_request_ops; + profile->qmi_ops = &qrtr_qmidev_ops; + qmidev_send = profile->qmi_ops->send; + ret = qmi_main(profile); + } +#endif + else { + dbg_time("unsupport software_interface %d", profile->software_interface); + } + + ql_stop_usbmon_log(profile); + +error: + return ret; +} + +static int parse_user_input(int argc, char **argv, PROFILE_T *profile) { + int opt = 1; + + profile->pdp = CONFIG_DEFAULT_PDP; + profile->profile_index = CONFIG_DEFAULT_PDP; + + if (!strcmp(argv[argc-1], "&")) + argc--; + +#define has_more_argv() ((opt < argc) && (argv[opt][0] != '-')) + while (opt < argc) { + if (argv[opt][0] != '-') { + return usage(argv[0]); + } + + switch (argv[opt++][1]) + { + 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()) { + const char *auth = argv[opt++]; + + if (!strcmp(auth, "0") || !strcasecmp(auth, "none")) { + profile->auth = 0; + } + else if (!strcmp(auth, "1") || !strcasecmp(auth, "pap")) { + profile->auth = 1; + } + else if (!strcmp(auth, "2") || !strcasecmp(auth, "chap")) { + profile->auth = 2; + } + else if (!strcmp(auth, "3") || !strcasecmp(auth, "MsChapV2")) { + profile->auth = 3; + } + else { + dbg_time("unknow auth '%s'", auth); + return usage(argv[0]); + } + } + break; + + case 'p': + if (has_more_argv()) { + const char *arg = argv[opt++]; + + if (!strcmp(arg, QUECTEL_QMI_PROXY) || !strcmp(arg, QUECTEL_MBIM_PROXY) + || !strcmp(arg, LIBQMI_PROXY) || !strcmp(arg, LIBMBIM_PROXY) || !strcmp(arg, QUECTEL_ATC_PROXY)) { + strncpy(profile->proxy, arg, sizeof(profile->proxy) - 1); + } + else if ((999 < atoi(arg)) && (atoi(arg) < 10000)) { + profile->pincode = arg; + } + else { + dbg_time("unknow -p '%s'", arg); + return usage(argv[0]); + } + } + break; + + case 'm': + if (has_more_argv()) + profile->muxid = argv[opt++][0] - '0' + 0x80; + break; + + case 'n': + if (has_more_argv()) + profile->pdp = argv[opt++][0] - '0'; + break; + + case 'f': + if (has_more_argv()) { + profile->logfile = argv[opt++]; + } + break; + + case 'i': + if (has_more_argv()) { + strncpy(profile->expect_adapter, argv[opt++], sizeof(profile->expect_adapter) - 1); + } + 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; + } + } + break; + + case '4': + profile->enable_ipv4 = 1; + break; + + case '6': + profile->enable_ipv6 = 1; + break; + + case 'd': + profile->no_dhcp = 1; + break; + + case 'u': + if (has_more_argv()) { + profile->usblogfile = argv[opt++]; + } + break; + + case 'b': + profile->enable_bridge = 1; + break; + + case 'k': + if (has_more_argv()) { + profile->kill_pdp = argv[opt++][0] - '0'; + } + break; + case 't': + if (has_more_argv()) { + profile->metric = atoi(argv[opt++]); + } + break; + + default: + return usage(argv[0]); + break; + } + } + + if (profile->enable_ipv4 != 1 && profile->enable_ipv6 != 1) { // default enable IPv4 + profile->enable_ipv4 = 1; + } + + return 1; +} + +int main(int argc, char *argv[]) +{ + int ret; + PROFILE_T *ctx = &s_profile; + + dbg_time("QConnectManager_Linux_V1.6.5"); + + ret = parse_user_input(argc, argv, ctx); + if (!ret) + return ret; + + if (ctx->kill_pdp) { + return kill_data_call_pdp(ctx->kill_pdp, argv[0]); + } + + if (ctx->logfile) { + logfilefp = fopen(ctx->logfile, "a+"); + if (!logfilefp) { + dbg_time("Fail to open %s, errno: %d(%s)", ctx->logfile, errno, strerror(errno)); + } + } + + signal(SIGINT, ql_sigaction); + signal(SIGTERM, ql_sigaction); + signal(SIGALRM, ql_sigaction); + + do { + ret = quectel_CM(ctx); + if (g_donot_exit_when_modem_hangup > 0) + sleep(3); + } while (g_donot_exit_when_modem_hangup > 0); + + if (logfilefp) { + fclose(logfilefp); + } + + return ret; +} diff --git a/application/quectel_CM_5G/src/mbim-cm.c b/application/quectel_CM_5G/src/mbim-cm.c new file mode 100644 index 0000000..0d10f1f --- /dev/null +++ b/application/quectel_CM_5G/src/mbim-cm.c @@ -0,0 +1,2426 @@ +/****************************************************************************** + @file mbim-cm.c + @brief MIBIM drivers. + + DESCRIPTION + Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "QMIThread.h" + +#define mbim_debug dbg_time + +#define UUID_BASIC_CONNECT "a289cc33-bcbb-8b4f-b6b0-133ec2aae6df" +//https://docs.microsoft.com/en-us/windows-hardware/drivers/network/mb-5g-data-class-support +#define UUID_BASIC_CONNECT_EXT "3d01dcc5-fef5-4d05-0d3a-bef7058e9aaf" +#define UUID_SMS "533fbeeb-14fe-4467-9f90-33a223e56c3f" +#define UUID_USSD "e550a0c8-5e82-479e-82f7-10abf4c3351f" +#define UUID_PHONEBOOK "4bf38476-1e6a-41db-b1d8-bed289c25bdb" +#define UUID_STK "d8f20131-fcb5-4e17-8602-d6ed3816164c" +#define UUID_AUTH "1d2b5ff7-0aa1-48b2-aa52-50f15767174e" +#define UUID_DSS "c08a26dd-7718-4382-8482-6e0d583c4d0e" +#define uuid_ext_qmux "d1a30bc2-f97a-6e43-bf65-c7e24fb0f0d3" +#define uuid_mshsd "883b7c26-985f-43fa-9804-27d7fb80959c" +#define uuid_qmbe "2d0c12c9-0e6a-495a-915c-8d174fe5d63c" +#define UUID_MSFWID "e9f7dea2-feaf-4009-93ce-90a3694103b6" +#define uuid_atds "5967bdcc-7fd2-49a2-9f5c-b2e70e527db3" +#define uuid_qdu "6427015f-579d-48f5-8c54-f43ed1e76f83" +#define UUID_MS_UICC_LOW_LEVEL "c2f6588e-f037-4bc9-8665-f4d44bd09367" +#define UUID_MS_SARControl "68223D04-9F6C-4E0F-822D-28441FB72340" +#define UUID_VOICEEXTENSIONS "8d8b9eba-37be-449b-8f1e-61cb034a702e" +#define UUID_LIBMBIM_PROXY "838cf7fb-8d0d-4d7f-871e-d71dbefbb39b" + +#define UUID_MBIMContextTypeInternet "7E5E2A7E-4E6F-7272-736B-656E7E5E2A7E" + +typedef unsigned char UINT8; +typedef unsigned short UINT16; +typedef unsigned int UINT32; +typedef unsigned long long UINT64; + +#pragma pack(4) +typedef enum { + MBIM_CID_CMD_TYPE_QUERY = 0, + MBIM_CID_CMD_TYPE_SET = 1, +} MBIM_CID_CMD_TYPE_E; + +typedef enum { + MBIM_CID_DEVICE_CAPS = 1, + MBIM_CID_SUBSCRIBER_READY_STATUS = 2, + MBIM_CID_RADIO_STATE = 3, MBIM_CID_PIN = 4, + MBIM_CID_PIN_LIS = 5, + MBIM_CID_HOME_PROVIDER = 6, + MBIM_CID_PREFERRED_PROVIDERS = 7, + MBIM_CID_VISIBLE_PROVIDERS = 8, + MBIM_CID_REGISTER_STATE = 9, + MBIM_CID_PACKET_SERVICE = 10, + MBIM_CID_SIGNAL_STATE = 11, + MBIM_CID_CONNECT = 12, + MBIM_CID_PROVISIONED_CONTEXTS = 13, + MBIM_CID_SERVICE_ACTIVATION = 14, + MBIM_CID_IP_CONFIGURATION = 15, + MBIM_CID_DEVICE_SERVICES = 16, + MBIM_CID_DEVICE_SERVICE_SUBSCRIBE_LIST = 19, + MBIM_CID_PACKET_STATISTICS = 20, + MBIM_CID_NETWORK_IDLE_HINT = 21, + MBIM_CID_EMERGENCY_MODE = 22, + MBIM_CID_IP_PACKET_FILTERS = 23, + MBIM_CID_MULTICARRIER_PROVIDERS = 24, +} UUID_BASIC_CONNECT_CID_E; + +typedef enum{ + MBIM_CID_MS_PROVISIONED_CONTEXT_V2 = 1, + MBIM_CID_MS_NETWORK_BLACKLIST = 2, + MBIM_CID_MS_LTE_ATTACH_CONFIG = 3, + MBIM_CID_MS_LTE_ATTACH_STATUS = 4, + MBIM_CID_MS_SYS_CAPS = 5, + MBIM_CID_MS_DEVICE_CAPS_V2 = 6, + MBIM_CID_MS_DEVICE_SLOT_MAPPING = 7, + MBIM_CID_MS_SLOT_INFO_STATUS = 8, + MBIM_CID_MS_PCO = 9, + MBIM_CID_MS_DEVICE_RESET = 10, + MBIM_CID_MS_BASE_STATIONS_INFO = 11, + MBIM_CID_MS_LOCATION_INFO_STATUS = 12, + MBIM_CID_NOT_DEFINED = 13, + MBIM_CID_MS_PIN_EX = 14, + MBIM_CID_MS_VERSION = 15, +} UUID_BASIC_CONNECT_EXT_CID_E; + +typedef enum { + MBIM_CID_SMS_CONFIGURATION = 1, // Y Y Y + MBIM_CID_SMS_READ = 2, // N Y Y + MBIM_CID_SMS_SEND = 3, // Y N N + MBIM_CID_SMS_DELETE = 4, // Y N N + MBIM_CID_SMS_MESSAGE_STORE_STATUS = 5, // N Y Y +} UUID_SMS_CID_E; + +typedef enum { + MBIM_CID_DSS_CONNECT = 1, // Y N N +} UUID_DSS_CID_E; + +typedef enum{ + MBIM_OPEN_MSG = 1, + MBIM_CLOSE_MSG = 2, + MBIM_COMMAND_MSG = 3, + MBIM_HOST_ERROR_MSG = 4, + MBIM_OPEN_DONE = 0x80000001, + MBIM_CLOSE_DONE = 0x80000002, + MBIM_COMMAND_DONE = 0x80000003, + MBIM_FUNCTION_ERROR_MSG = 0x80000004, + MBIM_INDICATE_STATUS_MSG = 0x80000007, +} MBIM_MSG_Type_E; + +typedef enum { /*< since=1.10 >*/ + MBIM_CID_PROXY_CONTROL_UNKNOWN = 0, + MBIM_CID_PROXY_CONTROL_CONFIGURATION = 1 +} UUID_LIBMBIM_PROXY_CID_E; + +typedef enum { + MBIM_CID_MS_UICC_ATR = 1, + MBIM_CID_MS_UICC_OPEN_CHANNEL = 2, + MBIM_CID_MS_UICC_CLOSE_CHANNEL = 3, + MBIM_CID_MS_UICC_APDU = 4, + MBIM_CID_MS_UICC_TERMINAL_CAPABILITY = 5, + MBIM_CID_MS_UICC_RESET = 6, + MBIM_CID_MS_APP_LIST = 7, +} UUID_MS_UICC_CID_E; + +typedef enum { + MBIM_ERROR_TIMEOUT_FRAGMENT = 1, + MBIM_ERROR_FRAGMENT_OUT_OF_SEQUENCE = 2, + MBIM_ERROR_LENGTH_MISMATCH = 3, + MBIM_ERROR_DUPLICATED_TID = 4, + MBIM_ERROR_NOT_OPENED = 5, + MBIM_ERROR_UNKNOWN = 6, + MBIM_ERROR_CANCEL = 7, + MBIM_ERROR_MAX_TRANSFER = 8, +} MBIM_ERROR_E; + +typedef enum { + MBIM_STATUS_SUCCESS = 0, + MBIM_STATUS_BUSY = 1, + MBIM_STATUS_FAILURE = 2, + MBIM_STATUS_SIM_NOT_INSERTED = 3, + MBIM_STATUS_BAD_SIM = 4, + MBIM_STATUS_PIN_REQUIRED = 5, + MBIM_STATUS_PIN_DISABLED = 6, + MBIM_STATUS_NOT_REGISTERED = 7, + MBIM_STATUS_PROVIDERS_NOT_FOUND = 8, + MBIM_STATUS_NO_DEVICE_SUPPORT = 9, + MBIM_STATUS_PROVIDER_NOT_VISIBLE = 10, + MBIM_STATUS_DATA_CLASS_NOT_AVAILABL = 11, + MBIM_STATUS_PACKET_SERVICE_DETACHED = 12, +} MBIM_STATUS_CODES_E; + +typedef enum { + MBIMPacketServiceActionAttach = 0, + MBIMPacketServiceActionDetach = 1, +} MBIM_PACKET_SERVICE_ACTION_E; + +typedef enum { + MBIMPacketServiceStateUnknown = 0, + MBIMPacketServiceStateAttaching = 1, + MBIMPacketServiceStateAttached = 2, + MBIMPacketServiceStateDetaching = 3, + MBIMPacketServiceStateDetached = 4, +} MBIM_PACKET_SERVICE_STATE_E; + +static const char *MBIMPacketServiceStateStr(int _val) { + struct { int val;char *name;} _enumstr[] = { + {MBIMPacketServiceStateUnknown, "Unknown"}, + {MBIMPacketServiceStateAttaching, "Attaching"}, + {MBIMPacketServiceStateAttached, "Attached"}, + {MBIMPacketServiceStateDetaching, "Detaching"}, + {MBIMPacketServiceStateDetached, "Detached"}, + }; + int idx; + + for (idx = 0; idx < (int)(sizeof(_enumstr)/sizeof(_enumstr[0])); idx++) { + if (_val == _enumstr[idx].val) + return _enumstr[idx].name; + } + + return "Undefined"; +}; + +typedef enum { + MBIMDataClassNone = 0x0, + MBIMDataClassGPRS = 0x1, + MBIMDataClassEDGE = 0x2, + MBIMDataClassUMTS = 0x4, + MBIMDataClassHSDPA = 0x8, + MBIMDataClassHSUPA = 0x10, + MBIMDataClassLTE = 0x20, + MBIMDataClass5G_NSA = 0x40, + MBIMDataClass5G_SA = 0x80, + MBIMDataClass1XRTT = 0x10000, + MBIMDataClass1XEVDO = 0x20000, + MBIMDataClass1XEVDORevA = 0x40000, + MBIMDataClass1XEVDV = 0x80000, + MBIMDataClass3XRTT = 0x100000, + MBIMDataClass1XEVDORevB = 0x200000, + MBIMDataClassUMB = 0x400000, + MBIMDataClassCustom = 0x80000000, +} MBIM_DATA_CLASS_E; + +static const char *MBIMDataClassStr(int _val) { + struct { int val;char *name;} _enumstr[] = { + {MBIMDataClassNone, "None"}, + {MBIMDataClassGPRS, "GPRS"}, + {MBIMDataClassEDGE, "EDGE"}, + {MBIMDataClassUMTS, "UMTS"}, + {MBIMDataClassHSDPA, "HSDPA"}, + {MBIMDataClassHSUPA, "HSUPA"}, + {MBIMDataClassLTE, "LTE"}, + {MBIMDataClass5G_NSA, "5G_NSA"}, + {MBIMDataClass5G_SA, "5G_SA"}, + {MBIMDataClass1XRTT, "1XRTT"}, + {MBIMDataClass1XEVDO, "1XEVDO"}, + {MBIMDataClass1XEVDORevA, "1XEVDORevA"}, + {MBIMDataClass1XEVDV, "1XEVDV"}, + {MBIMDataClass3XRTT, "3XRTT"}, + {MBIMDataClass1XEVDORevB, "1XEVDORevB"}, + {MBIMDataClassUMB, "UMB"}, + {MBIMDataClassCustom, "Custom"}, + }; + int idx; + + for (idx = 0; idx < (int)(sizeof(_enumstr)/sizeof(_enumstr[0])); idx++) { + if (_val == _enumstr[idx].val) + return _enumstr[idx].name; + } + + return "Unknow"; +}; + +typedef struct { + UINT32 NwError; + UINT32 PacketServiceState; //MBIM_PACKET_SERVICE_STATE_E + UINT32 HighestAvailableDataClass; //MBIM_DATA_CLASS_E + UINT64 UplinkSpeed; + UINT64 DownlinkSpeed; +} MBIM_PACKET_SERVICE_INFO_T; + +typedef struct { + UINT32 NwError; + UINT32 PacketServiceState; //MBIM_PACKET_SERVICE_STATE_E + UINT32 CurrentDataClass; //MBIM_DATA_CLASS_E + UINT64 UplinkSpeed; + UINT64 DownlinkSpeed; + UINT32 FrequencyRange; +} MBIM_PACKET_SERVICE_INFO_V2_T; + +typedef enum { + MBIMSubscriberReadyStateNotInitialized = 0, + MBIMSubscriberReadyStateInitialized = 1, + MBIMSubscriberReadyStateSimNotInserted = 2, + MBIMSubscriberReadyStateBadSim = 3, + MBIMSubscriberReadyStateFailure = 4, + MBIMSubscriberReadyStateNotActivated = 5, + MBIMSubscriberReadyStateDeviceLocked = 6, +}MBIM_SUBSCRIBER_READY_STATE_E; + +static const char *MBIMSubscriberReadyStateStr(int _val) { + struct { int val;char *name;} _enumstr[] = { + {MBIMSubscriberReadyStateNotInitialized, "NotInitialized"}, + {MBIMSubscriberReadyStateInitialized, "Initialized"}, + {MBIMSubscriberReadyStateSimNotInserted, "NotInserted"}, + {MBIMSubscriberReadyStateBadSim, "BadSim"}, + {MBIMSubscriberReadyStateFailure, "Failure"}, + {MBIMSubscriberReadyStateNotActivated, "NotActivated"}, + {MBIMSubscriberReadyStateDeviceLocked, "DeviceLocked"}, + }; + int idx; + + for (idx = 0; idx < (int)(sizeof(_enumstr)/sizeof(_enumstr[0])); idx++) { + if (_val == _enumstr[idx].val) + return _enumstr[idx].name; + } + + return "Undefined"; +}; + +typedef struct { + UINT32 DeviceType; //MBIM_DEVICE_TYPE + UINT32 CellularClass; //MBIM_CELLULAR_CLASS + UINT32 VoiceClass; //MBIM_VOICE_CLASS + UINT32 SimClass; //MBIM_SIM_CLASS + UINT32 DataClass; //MBIM_DATA_CLASS + UINT32 SmsCaps; //MBIM_SMS_CAPS + UINT32 ControlCaps; //MBIM_CTRL_CAPS + UINT32 MaxSessions; + UINT32 CustomDataClassOffset; + UINT32 CustomDataClassSize; + UINT32 DeviceIdOffset; + UINT32 DeviceIdSize; + UINT32 FirmwareInfoOffset; + UINT32 FirmwareInfoSize; + UINT32 HardwareInfoOffset; + UINT32 HardwareInfoSize; + UINT8 DataBuffer[0]; //DeviceId FirmwareInfo HardwareInfo +} MBIM_DEVICE_CAPS_INFO_T; + +typedef enum { + MBIMRadioOff = 0, + MBIMRadioOn = 1, +} MBIM_RADIO_SWITCH_STATE_E; + +typedef struct { + MBIM_RADIO_SWITCH_STATE_E RadioState; +} MBIM_SET_RADIO_STATE_T; + +typedef struct { + MBIM_RADIO_SWITCH_STATE_E HwRadioState; + MBIM_RADIO_SWITCH_STATE_E SwRadioState; +} MBIM_RADIO_STATE_INFO_T; + +typedef enum { + MBIMReadyInfoFlagsNone, + MBIMReadyInfoFlagsProtectUniqueID, +}MBIM_UNIQUE_ID_FLAGS; + +typedef struct { + UINT32 ReadyState; + UINT32 SubscriberIdOffset; + UINT32 SubscriberIdSize; + UINT32 SimIccIdOffset; + UINT32 SimIccIdSize; + UINT32 ReadyInfo; + UINT32 ElementCount; + UINT8 *TelephoneNumbersRefList; + UINT8 *DataBuffer; +} MBIM_SUBSCRIBER_READY_STATUS_T; + +typedef enum { + MBIMRegisterActionAutomatic, + MBIMRegisterActionManual, +}MBIM_REGISTER_ACTION_E; + +typedef enum { + MBIMRegisterStateUnknown = 0, + MBIMRegisterStateDeregistered = 1, + MBIMRegisterStateSearching = 2, + MBIMRegisterStateHome = 3, + MBIMRegisterStateRoaming = 4, + MBIMRegisterStatePartner = 5, + MBIMRegisterStateDenied = 6, +}MBIM_REGISTER_STATE_E; + +typedef enum { + MBIMRegisterModeUnknown = 0, + MBIMRegisterModeAutomatic = 1, + MBIMRegisterModeManual = 2, +}MBIM_REGISTER_MODE_E; + +static const char *MBIMRegisterStateStr(int _val) { + struct { int val;char *name;} _enumstr[] ={ + {MBIMRegisterStateUnknown, "Unknown"}, + {MBIMRegisterStateDeregistered, "Deregistered"}, + {MBIMRegisterStateSearching, "Searching"}, + {MBIMRegisterStateHome, "Home"}, + {MBIMRegisterStateRoaming, "Roaming"}, + {MBIMRegisterStatePartner, "Partner"}, + {MBIMRegisterStateDenied, "Denied"}, + }; + int idx; + + for (idx = 0; idx < (int)(sizeof(_enumstr)/sizeof(_enumstr[0])); idx++) { + if (_val == _enumstr[idx].val) + return _enumstr[idx].name; + } + + return "Undefined"; +}; + +static const char *MBIMRegisterModeStr(int _val) { + struct { int val;char *name;} _enumstr[] = { + {MBIMRegisterModeUnknown, "Unknown"}, + {MBIMRegisterModeAutomatic, "Automatic"}, + {MBIMRegisterModeManual, "Manual"}, + }; + int idx; + + for (idx = 0; idx < (int)(sizeof(_enumstr)/sizeof(_enumstr[0])); idx++) { + if (_val == _enumstr[idx].val) + return _enumstr[idx].name; + } + + return "Undefined"; +}; + +typedef enum { + MBIM_REGISTRATION_NONE, + MBIM_REGISTRATION_MANUAL_SELECTION_NOT_AVAILABLE, + MBIM_REGISTRATION_PACKET_SERVICE_AUTOMATIC_ATTACH, +}MBIM_REGISTRATION_FLAGS_E; + +typedef struct { + UINT32 NwError; + UINT32 RegisterState; //MBIM_REGISTER_STATE_E + UINT32 RegisterMode; + UINT32 AvailableDataClasses; + UINT32 CurrentCellularClass; + UINT32 ProviderIdOffset; + UINT32 ProviderIdSize; + UINT32 ProviderNameOffset; + UINT32 ProviderNameSize; + UINT32 RoamingTextOffset; + UINT32 RoamingTextSize; + UINT32 RegistrationFlag; + UINT8 *DataBuffer; +} MBIM_REGISTRATION_STATE_INFO_T; + +typedef struct { + UINT32 NwError; + UINT32 RegisterState; //MBIM_REGISTER_STATE_E + UINT32 RegisterMode; + UINT32 AvailableDataClasses; + UINT32 CurrentCellularClass; + UINT32 ProviderIdOffset; + UINT32 ProviderIdSize; + UINT32 ProviderNameOffset; + UINT32 ProviderNameSize; + UINT32 RoamingTextOffset; + UINT32 RoamingTextSize; + UINT32 RegistrationFlag; + UINT32 PreferredDataClass; + UINT8 *DataBuffer; +} MBIM_REGISTRATION_STATE_INFO_V2_T; + +typedef struct { + UINT32 MessageType; //Specifies the MBIM message type. + UINT32 MessageLength; //Specifies the total length of this MBIM message in bytes. + /* Specifies the MBIM message id value. This value is used to match host sent messages with function responses. + This value must be unique among all outstanding transactions. + For notifications, the TransactionId must be set to 0 by the function */ + UINT32 TransactionId; +} MBIM_MESSAGE_HEADER; + +typedef struct { + UINT32 TotalFragments; //this field indicates how many fragments there are intotal. + UINT32 CurrentFragment; //This field indicates which fragment this message is. Values are 0 to TotalFragments?\1 +} MBIM_FRAGMENT_HEADER; + +typedef struct { + MBIM_MESSAGE_HEADER MessageHeader; + UINT32 MaxControlTransfer; +} MBIM_OPEN_MSG_T; + +typedef struct { + MBIM_MESSAGE_HEADER MessageHeader; + UINT32 Status; //MBIM_STATUS_CODES_E +} MBIM_OPEN_DONE_T; + +typedef struct { + MBIM_MESSAGE_HEADER MessageHeader; +} MBIM_CLOSE_MSG_T; + +typedef struct { + MBIM_MESSAGE_HEADER MessageHeader; + UINT32 Status; +} MBIM_CLOSE_DONE_T; + +typedef struct { + UINT8 uuid[16]; +} UUID_T; + +typedef struct { + MBIM_MESSAGE_HEADER MessageHeader; + MBIM_FRAGMENT_HEADER FragmentHeader; + UUID_T DeviceServiceId; //A 16 byte UUID that identifies the device service the following CID value applies. + UINT32 CID; //Specifies the CID that identifies the parameter being queried for + UINT32 CommandType; //0 for a query operation, 1 for a Set operation + UINT32 InformationBufferLength; //Size of the Total InformationBuffer, may be larger than current message if fragmented. + UINT8 InformationBuffer[0]; //Data supplied to device specific to the CID +} MBIM_COMMAND_MSG_T; + +typedef struct { + MBIM_MESSAGE_HEADER MessageHeader; + MBIM_FRAGMENT_HEADER FragmentHeader; + UUID_T DeviceServiceId; //A 16 byte UUID that identifies the device service the following CID value applies. + UINT32 CID; //Specifies the CID that identifies the parameter being queried for + UINT32 Status; + UINT32 InformationBufferLength; //Size of the Total InformationBuffer, may be larger than current message if fragmented. + UINT8 InformationBuffer[0]; //Data supplied to device specific to the CID +} MBIM_COMMAND_DONE_T; + +typedef struct { + MBIM_MESSAGE_HEADER MessageHeader; + UINT32 ErrorStatusCode; +} MBIM_HOST_ERROR_MSG_T; + +typedef struct { + MBIM_MESSAGE_HEADER MessageHeader; + UINT32 ErrorStatusCode; +} MBIM_FUNCTION_ERROR_MSG_T; + +typedef struct { + MBIM_MESSAGE_HEADER MessageHeader; + MBIM_FRAGMENT_HEADER FragmentHeader; + UUID_T DeviceServiceId; //A 16 byte UUID that identifies the device service the following CID value applies. + UINT32 CID; //Specifies the CID that identifies the parameter being queried for + UINT32 InformationBufferLength; //Size of the Total InformationBuffer, may be larger than current message if fragmented. + UINT8 InformationBuffer[0]; //Data supplied to device specific to the CID +} MBIM_INDICATE_STATUS_MSG_T; + +typedef struct { + UINT32 offset; + UINT32 size; +} OL_PAIR_LIST; + +typedef struct { + UUID_T DeviceServiceId; + UINT32 DssPayload; + UINT32 MaxDssInstances; + UINT32 CidCount; + UINT32 CidList[]; +} MBIM_DEVICE_SERVICE_ELEMENT_T; + +typedef struct { + UINT32 DeviceServicesCount; + UINT32 MaxDssSessions; + OL_PAIR_LIST DeviceServicesRefList[]; +} MBIM_DEVICE_SERVICES_INFO_T; + +typedef enum { + MBIMActivationCommandDeactivate = 0, + MBIMActivationCommandActivate = 1, +} MBIM_ACTIVATION_COMMAND_E; + +typedef enum { + MBIMCompressionNone = 0, + MBIMCompressionEnable = 1, +} MBIM_COMPRESSION_E; + +typedef enum { + MBIMAuthProtocolNone = 0, + MBIMAuthProtocolPap = 1, + MBIMAuthProtocolChap = 2, + MBIMAuthProtocolMsChapV2 = 3, +} MBIM_AUTH_PROTOCOL_E; + +typedef enum { + MBIMContextIPTypeDefault = 0, + MBIMContextIPTypeIPv4 = 1, + MBIMContextIPTypeIPv6 = 2, + MBIMContextIPTypeIPv4v6 = 3, + MBIMContextIPTypeIPv4AndIPv6 = 4, +} MBIM_CONTEXT_IP_TYPE_E; + +typedef enum { + MBIMActivationStateUnknown = 0, + MBIMActivationStateActivated = 1, + MBIMActivationStateActivating = 2, + MBIMActivationStateDeactivated = 3, + MBIMActivationStateDeactivating = 4, +} MBIM_ACTIVATION_STATE_E; + +typedef enum { + MBIMVoiceCallStateNone = 0, + MBIMVoiceCallStateInProgress = 1, + MBIMVoiceCallStateHangUp = 2, +} MBIM_VOICECALL_STATE_E; + +static const char *MBIMMSGTypeStr(int _val) { + struct { int val;char *name;} _enumstr[] = { + {MBIM_OPEN_MSG, "MBIM_OPEN_MSG"}, + {MBIM_CLOSE_MSG, "MBIM_CLOSE_MSG"}, + {MBIM_COMMAND_MSG, "MBIM_COMMAND_MSG"}, + {MBIM_HOST_ERROR_MSG, "MBIM_HOST_ERROR_MSG"}, + {MBIM_OPEN_DONE, "MBIM_OPEN_DONE"}, + {MBIM_CLOSE_DONE, "MBIM_CLOSE_DONE"}, + {MBIM_COMMAND_DONE, "MBIM_COMMAND_DONE"}, + {MBIM_FUNCTION_ERROR_MSG, "MBIM_FUNCTION_ERROR_MSG"}, + {MBIM_INDICATE_STATUS_MSG, "MBIM_INDICATE_STATUS_MSG"}, + }; + int idx; + + for (idx = 0; idx < (int)(sizeof(_enumstr)/sizeof(_enumstr[0])); idx++) { + if (_val == _enumstr[idx].val) + return _enumstr[idx].name; + } + + return "MBIMMSGTypeUnknow"; +}; + +static const char *MBIMContextIPTypeStr(int _val) { + struct { int val;char *name;} _enumstr[] = { + {MBIMContextIPTypeDefault, "MBIMContextIPTypeDefault"}, + {MBIMContextIPTypeIPv4, "MBIMContextIPTypeIPv4"}, + {MBIMContextIPTypeIPv6, "MBIMContextIPTypeIPv6"}, + {MBIMContextIPTypeIPv4v6, "MBIMContextIPTypeIPv4v6"}, + {MBIMContextIPTypeIPv4AndIPv6, "MBIMContextIPTypeIPv4AndIPv6"}, + }; + int idx; + + for (idx = 0; idx < (int)(sizeof(_enumstr)/sizeof(_enumstr[0])); idx++) { + if (_val == _enumstr[idx].val) + return _enumstr[idx].name; + } + + return "MBIMContextIPTypeUnknow"; +} + +static const char *MBIMActivationStateStr(int _val) { + struct { int val;char *name;} _enumstr[] = { + {MBIMActivationStateUnknown, "Unknown"}, + {MBIMActivationStateActivated, "Activated"}, + {MBIMActivationStateActivating, "Activating"}, + {MBIMActivationStateDeactivated, "Deactivated"}, + {MBIMActivationStateDeactivating, "Deactivating"}, + }; + int idx; + + for (idx = 0; idx < (int)(sizeof(_enumstr)/sizeof(_enumstr[0])); idx++) { + if (_val == _enumstr[idx].val) + return _enumstr[idx].name; + } + + return "Undefined"; +}; + +static const char *MBIMVoiceCallStateStr(int _val) { + struct { int val;char *name;} _enumstr[] = { + {MBIMVoiceCallStateNone, "None"}, + {MBIMVoiceCallStateInProgress, "InProgress"}, + {MBIMVoiceCallStateHangUp, "HangUp"}, + }; + int idx; + + for (idx = 0; idx < (int)(sizeof(_enumstr)/sizeof(_enumstr[0])); idx++) { + if (_val == _enumstr[idx].val) + return _enumstr[idx].name; + } + + return "Undefined"; +}; + +typedef struct { + const char *uuid; + UINT32 cid; + const char *name; +} UUID_CID_STR; + +static const UUID_CID_STR uuid_cid_string[] = { + {UUID_BASIC_CONNECT, MBIM_CID_DEVICE_CAPS, "MBIM_CID_DEVICE_CAPS"}, + {UUID_BASIC_CONNECT, MBIM_CID_SUBSCRIBER_READY_STATUS, "MBIM_CID_SUBSCRIBER_READY_STATUS"}, + {UUID_BASIC_CONNECT, MBIM_CID_RADIO_STATE, "MBIM_CID_RADIO_STATE"}, + {UUID_BASIC_CONNECT, MBIM_CID_PIN, "MBIM_CID_PIN"}, + {UUID_BASIC_CONNECT, MBIM_CID_PIN_LIS, "MBIM_CID_PIN_LIS"}, + {UUID_BASIC_CONNECT, MBIM_CID_HOME_PROVIDER, "MBIM_CID_HOME_PROVIDER"}, + {UUID_BASIC_CONNECT, MBIM_CID_PREFERRED_PROVIDERS, "MBIM_CID_PREFERRED_PROVIDERS"}, + {UUID_BASIC_CONNECT, MBIM_CID_VISIBLE_PROVIDERS, "MBIM_CID_VISIBLE_PROVIDERS"}, + {UUID_BASIC_CONNECT, MBIM_CID_REGISTER_STATE, "MBIM_CID_REGISTER_STATE"}, + {UUID_BASIC_CONNECT, MBIM_CID_PACKET_SERVICE, "MBIM_CID_PACKET_SERVICE"}, + {UUID_BASIC_CONNECT, MBIM_CID_SIGNAL_STATE, "MBIM_CID_SIGNAL_STATE"}, + {UUID_BASIC_CONNECT, MBIM_CID_CONNECT, "MBIM_CID_CONNECT"}, + {UUID_BASIC_CONNECT, MBIM_CID_PROVISIONED_CONTEXTS, "MBIM_CID_PROVISIONED_CONTEXTS"}, + {UUID_BASIC_CONNECT, MBIM_CID_SERVICE_ACTIVATION, "MBIM_CID_SERVICE_ACTIVATION"}, + {UUID_BASIC_CONNECT, MBIM_CID_IP_CONFIGURATION, "MBIM_CID_IP_CONFIGURATION"}, + {UUID_BASIC_CONNECT, MBIM_CID_DEVICE_SERVICES, "MBIM_CID_DEVICE_SERVICES"}, + {UUID_BASIC_CONNECT, MBIM_CID_DEVICE_SERVICE_SUBSCRIBE_LIST, "MBIM_CID_DEVICE_SERVICE_SUBSCRIBE_LIST"}, + {UUID_BASIC_CONNECT, MBIM_CID_PACKET_STATISTICS, "MBIM_CID_PACKET_STATISTICS"}, + {UUID_BASIC_CONNECT, MBIM_CID_NETWORK_IDLE_HINT, "MBIM_CID_NETWORK_IDLE_HINT"}, + {UUID_BASIC_CONNECT, MBIM_CID_EMERGENCY_MODE, "MBIM_CID_EMERGENCY_MODE"}, + {UUID_BASIC_CONNECT, MBIM_CID_IP_PACKET_FILTERS, "MBIM_CID_IP_PACKET_FILTERS"}, + {UUID_BASIC_CONNECT, MBIM_CID_MULTICARRIER_PROVIDERS, "MBIM_CID_MULTICARRIER_PROVIDERS"}, + + {UUID_BASIC_CONNECT_EXT, MBIM_CID_MS_PROVISIONED_CONTEXT_V2, "MBIM_CID_MS_PROVISIONED_CONTEXT_V2"}, + {UUID_BASIC_CONNECT_EXT, MBIM_CID_MS_NETWORK_BLACKLIST, "MBIM_CID_MS_NETWORK_BLACKLIST"}, + {UUID_BASIC_CONNECT_EXT, MBIM_CID_MS_LTE_ATTACH_CONFIG, "MBIM_CID_MS_LTE_ATTACH_CONFIG"}, + {UUID_BASIC_CONNECT_EXT, MBIM_CID_MS_LTE_ATTACH_STATUS, "MBIM_CID_MS_LTE_ATTACH_STATUS"}, + {UUID_BASIC_CONNECT_EXT, MBIM_CID_MS_SYS_CAPS, "MBIM_CID_MS_SYS_CAPS"}, + {UUID_BASIC_CONNECT_EXT, MBIM_CID_MS_DEVICE_CAPS_V2, "MBIM_CID_MS_DEVICE_CAPS_V2"}, + {UUID_BASIC_CONNECT_EXT, MBIM_CID_MS_DEVICE_SLOT_MAPPING, "MBIM_CID_MS_DEVICE_SLOT_MAPPING"}, + {UUID_BASIC_CONNECT_EXT, MBIM_CID_MS_SLOT_INFO_STATUS, "MBIM_CID_MS_SLOT_INFO_STATUS"}, + {UUID_BASIC_CONNECT_EXT, MBIM_CID_MS_PCO, "MBIM_CID_MS_PCO"}, + {UUID_BASIC_CONNECT_EXT, MBIM_CID_MS_DEVICE_RESET, "MBIM_CID_MS_DEVICE_RESET"}, + {UUID_BASIC_CONNECT_EXT, MBIM_CID_MS_BASE_STATIONS_INFO, "MBIM_CID_MS_BASE_STATIONS_INFO"}, + {UUID_BASIC_CONNECT_EXT, MBIM_CID_MS_LOCATION_INFO_STATUS, "MBIM_CID_MS_LOCATION_INFO_STATUS"}, + {UUID_BASIC_CONNECT_EXT, MBIM_CID_NOT_DEFINED, "MBIM_CID_NOT_DEFINED"}, + {UUID_BASIC_CONNECT_EXT, MBIM_CID_MS_PIN_EX, "MBIM_CID_MS_PIN_EX"}, + {UUID_BASIC_CONNECT_EXT, MBIM_CID_MS_VERSION, "MBIM_CID_MS_VERSION"}, + + {UUID_MS_UICC_LOW_LEVEL, MBIM_CID_MS_UICC_ATR, "MBIM_CID_MS_UICC_ATR"}, + {UUID_MS_UICC_LOW_LEVEL, MBIM_CID_MS_UICC_OPEN_CHANNEL, "MBIM_CID_MS_UICC_OPEN_CHANNEL"}, + {UUID_MS_UICC_LOW_LEVEL, MBIM_CID_MS_UICC_CLOSE_CHANNEL, "MBIM_CID_MS_UICC_CLOSE_CHANNEL"}, + {UUID_MS_UICC_LOW_LEVEL, MBIM_CID_MS_UICC_APDU, "MBIM_CID_MS_UICC_APDU"}, + {UUID_MS_UICC_LOW_LEVEL, MBIM_CID_MS_UICC_TERMINAL_CAPABILITY, "MBIM_CID_MS_UICC_TERMINAL_CAPABILITY"}, + {UUID_MS_UICC_LOW_LEVEL, MBIM_CID_MS_UICC_RESET, "MBIM_CID_MS_UICC_RESET"}, + {UUID_MS_UICC_LOW_LEVEL, MBIM_CID_MS_APP_LIST, "MBIM_CID_MS_APP_LIST"}, +}; + +typedef struct { + UINT32 SessionId; + UINT32 ActivationCommand; //MBIM_ACTIVATION_COMMAND_E + UINT32 AccessStringOffset; + UINT32 AccessStringSize; + UINT32 UserNameOffset; + UINT32 UserNameSize; + UINT32 PasswordOffset; + UINT32 PasswordSize; + UINT32 Compression; //MBIM_COMPRESSION_E + UINT32 AuthProtocol; //MBIM_AUTH_PROTOCOL_E + UINT32 IPType; //MBIM_CONTEXT_IP_TYPE_E + UUID_T ContextType; + UINT8 DataBuffer[0]; /* apn, username, password */ +} MBIM_SET_CONNECT_T; + +typedef struct { + UINT32 SessionId; + UINT32 ActivationState; //MBIM_ACTIVATION_STATE_E + UINT32 VoiceCallState; + UINT32 IPType; //MBIM_CONTEXT_IP_TYPE_E + UUID_T ContextType; + UINT32 NwError; +} MBIM_CONNECT_T; + +typedef struct { + UINT32 OnLinkPrefixLength; + UINT8 IPv4Address[4]; +} MBIM_IPV4_ELEMENT_T; + +typedef struct { + UINT32 OnLinkPrefixLength; + UINT8 IPv6Address[16]; +} MBIM_IPV6_ELEMENT_T; + +typedef struct { + UINT32 SessionId; + UINT32 IPv4ConfigurationAvailable; //bit0~Address, bit1~gateway, bit2~DNS, bit3~MTU + UINT32 IPv6ConfigurationAvailable; //bit0~Address, bit1~gateway, bit2~DNS, bit3~MTU + UINT32 IPv4AddressCount; + UINT32 IPv4AddressOffset; + UINT32 IPv6AddressCount; + UINT32 IPv6AddressOffset; + UINT32 IPv4GatewayOffset; + UINT32 IPv6GatewayOffset; + UINT32 IPv4DnsServerCount; + UINT32 IPv4DnsServerOffset; + UINT32 IPv6DnsServerCount; + UINT32 IPv6DnsServerOffset; + UINT32 IPv4Mtu; + UINT32 IPv6Mtu; + UINT8 DataBuffer[]; +} MBIM_IP_CONFIGURATION_INFO_T; + +typedef struct { + UINT32 RSRP; + UINT32 SNR; + UINT32 RSRPThreshold; + UINT32 SNRThreshold; + UINT32 SystemType; +} MBIM_RSRP_SNR_INFO_T; + +typedef struct { + UINT32 Elementcount; + MBIM_RSRP_SNR_INFO_T RsrpSnr[0]; +} MBIM_RSRP_SNR_T; + +typedef struct { + UINT32 Rssi; + UINT32 ErrorRate; + UINT32 SignalStrengthInterval; + UINT32 RssiThreshold; + UINT32 ErrorRateThreshold; +} MBIM_SIGNAL_STATE_INFO_T; + +typedef struct { + UINT32 Rssi; + UINT32 ErrorRate; + UINT32 SignalStrengthInterval; + UINT32 RssiThreshold; + UINT32 ErrorRateThreshold; + UINT32 RsrpSnrOffset; + UINT32 RsrpSnrSize; + UINT8 DataBuffer[]; +} MBIM_SIGNAL_STATE_INFO_V2_T; + +typedef struct { + UINT32 SignalStrengthInterval; + UINT32 RssiThreshold; + UINT32 ErrorRateThreshold; +} MBIM_SET_SIGNAL_STATE_T; + +typedef struct { + UINT32 DevicePathOffset; + UINT32 DevicePathSize; + UINT32 Timeout; + UINT8 DataBuffer[]; +} MBIM_LIBQMI_PROXY_CONFIG_T; + +typedef struct { + UINT32 AtrSize; + UINT32 AtrOffset; + UINT8 DataBuffer[]; +} MBIM_MS_ATR_INFO_T; + +#pragma pack() + +static pthread_t s_tid_reader = 0; +static int mbim_verbose = 0; +static UINT32 TransactionId = 1; +static unsigned mbim_default_timeout = 30000; +static const char *mbim_apn = NULL; +static const char *mbim_user = NULL; +static const char *mbim_passwd = NULL; +static int mbim_iptype = MBIMContextIPTypeDefault; +static int mbim_auth = MBIMAuthProtocolNone; +static int mbim_sessionID = 0; +static int mbim_fd = -1; +static MBIM_MESSAGE_HEADER *mbim_pRequest; +static MBIM_MESSAGE_HEADER *mbim_pResponse; + +static unsigned int qmi_over_mbim_support = 0; +static int qmi_over_mbim_sk[2] = {-1, -1}; +static pthread_mutex_t mbim_command_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t mbim_command_cond = PTHREAD_COND_INITIALIZER; +static int mbim_ms_version = 1; +static uint8_t qmi_over_mbim_nas = 0; +int qmi_over_mbim_qmidev_send(PQCQMIMSG pQMI); + +static const UUID_T * str2uuid(const char *str) { + static UUID_T uuid; + UINT32 d[16]; + char tmp[16*2+4+1]; + unsigned i = 0; + + while (str[i]) { + tmp[i] = tolower(str[i]); + i++; + } + tmp[i] = '\0'; + + sscanf(tmp, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + &d[0], &d[1], &d[2], &d[3], &d[4], &d[5], &d[6], &d[7], + &d[8], &d[9], &d[10], &d[11], &d[12], &d[13], &d[14], &d[15]); + + for (i = 0; i < 16; i++) { + uuid.uuid[i] = d[i]&0xFF; + } + + return &uuid; +} + +static void wchar2char(const char *src, size_t src_size, char *dst, size_t dst_len) { + size_t i; + + for (i = 0; i < (dst_len-1) && i < (src_size/2); i++) + dst[i] = src[i*2]; + dst[i] = 0; +} + +static size_t char2wchar(const char *src, size_t src_len, uint8_t *dst, size_t dst_len) { + size_t i; + + if (src_len > (dst_len/2)) + src_len = (dst_len/2); + + for (i = 0; i < src_len; i++) { + *dst++ = *src++; + *dst++ = 0; + } + + return i*2; +} + +#define mbim_alloc( _size) malloc(_size) +#define mbim_free(_mem) do { if (_mem) { free(_mem); _mem = NULL;}} while(0) + +static int mbim_open_state = 0; +static MBIM_SUBSCRIBER_READY_STATE_E ReadyState = MBIMSubscriberReadyStateNotInitialized; +static MBIM_REGISTER_STATE_E RegisterState = MBIMRegisterStateUnknown; +static MBIM_PACKET_SERVICE_STATE_E PacketServiceState = MBIMPacketServiceStateUnknown; +static MBIM_ACTIVATION_STATE_E ActivationState = MBIMActivationStateUnknown; +static MBIM_SUBSCRIBER_READY_STATE_E oldReadyState = MBIMSubscriberReadyStateNotInitialized; +static MBIM_REGISTER_STATE_E oldRegisterState = MBIMRegisterStateUnknown; +static MBIM_PACKET_SERVICE_STATE_E oldPacketServiceState = MBIMPacketServiceStateUnknown; +static MBIM_ACTIVATION_STATE_E oldActivationState = MBIMActivationStateUnknown; +static int mbim_update_state(void); + +static __inline uint32_t mbim2qmi_ipv4addr(uint32_t addr) { + return (addr>>24) | (addr>>8&0xff00) | (addr<<8&0xff0000) | (addr<<24); +} + +static __inline void mbim2qmi_ipv6addr(const unsigned char *src, unsigned char *dst) { + int i; + + for (i = 0; i < 16 ; i++) { + dst[i] = src[i]; + } +} + +static MBIM_MESSAGE_HEADER *compose_open_command(UINT32 MaxControlTransfer) +{ + MBIM_OPEN_MSG_T *pRequest = (MBIM_OPEN_MSG_T *)mbim_alloc(sizeof(MBIM_OPEN_MSG_T)); + + if(!pRequest) + return NULL; + + pRequest->MessageHeader.MessageType = htole32(MBIM_OPEN_MSG); + pRequest->MessageHeader.MessageLength = htole32(sizeof(MBIM_OPEN_MSG_T)); + pRequest->MessageHeader.TransactionId = htole32(TransactionId++); + pRequest->MaxControlTransfer = htole32(MaxControlTransfer); + + return &pRequest->MessageHeader; +} + +static MBIM_MESSAGE_HEADER *compose_close_command(void) +{ + MBIM_CLOSE_MSG_T *pRequest = (MBIM_CLOSE_MSG_T *)mbim_alloc(sizeof(MBIM_CLOSE_MSG_T)); + + if(!pRequest) + return NULL; + + pRequest->MessageHeader.MessageType = htole32(MBIM_CLOSE_MSG); + pRequest->MessageHeader.MessageLength = htole32(sizeof(MBIM_CLOSE_MSG_T)); + pRequest->MessageHeader.TransactionId = htole32(TransactionId++); + + return &pRequest->MessageHeader; +} + +static MBIM_MESSAGE_HEADER *compose_basic_connect_command(UINT32 CID, UINT32 CommandType, void *pInformationBuffer, UINT32 InformationBufferLength) +{ + MBIM_COMMAND_MSG_T *pRequest = (MBIM_COMMAND_MSG_T *)mbim_alloc(sizeof(MBIM_COMMAND_MSG_T) + InformationBufferLength); + + if (!pRequest) + return NULL; + + pRequest->MessageHeader.MessageType = htole32(MBIM_COMMAND_MSG); + pRequest->MessageHeader.MessageLength = htole32((sizeof(MBIM_COMMAND_MSG_T) + InformationBufferLength)); + pRequest->MessageHeader.TransactionId = htole32(TransactionId++); + + pRequest->FragmentHeader.TotalFragments = htole32(1); + pRequest->FragmentHeader.CurrentFragment= htole32(0); + + memcpy(pRequest->DeviceServiceId.uuid, str2uuid(UUID_BASIC_CONNECT), 16); + + pRequest->CID = htole32(CID); + pRequest->CommandType = htole32(CommandType); + if (InformationBufferLength && pInformationBuffer) { + pRequest->InformationBufferLength = htole32(InformationBufferLength); + memcpy(pRequest->InformationBuffer, pInformationBuffer, InformationBufferLength); + } else { + pRequest->InformationBufferLength = htole32(0); + } + + return &pRequest->MessageHeader; +} + +static MBIM_MESSAGE_HEADER *compose_basic_connect_ext_command(UINT32 CID, UINT32 CommandType, void *pInformationBuffer, UINT32 InformationBufferLength) +{ + MBIM_COMMAND_MSG_T *pRequest = (MBIM_COMMAND_MSG_T *)compose_basic_connect_command(CID, CommandType, pInformationBuffer, InformationBufferLength); + + if (!pRequest) + return NULL; + + memcpy(pRequest->DeviceServiceId.uuid, str2uuid(UUID_BASIC_CONNECT_EXT), 16); + + return &pRequest->MessageHeader; +} + +static MBIM_MESSAGE_HEADER *compose_qmi_over_mbim_command(UINT32 CID, UINT32 CommandType, void *pInformationBuffer, UINT32 InformationBufferLength) +{ + MBIM_COMMAND_MSG_T *pRequest = (MBIM_COMMAND_MSG_T *)compose_basic_connect_command(CID, CommandType, pInformationBuffer, InformationBufferLength); + + if (!pRequest) + return NULL; + + memcpy(pRequest->DeviceServiceId.uuid, str2uuid(uuid_ext_qmux), 16); + + return &pRequest->MessageHeader; +} + +static const char * uuid2str(const UUID_T *pUUID) { + static char str[16*2+4+1]; + const UINT8 *d = pUUID->uuid; + + snprintf(str, sizeof(str), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], + d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); + + return str; +} + +static const char *DeviceServiceId2str(const UUID_T *pUUID) { + const char *str = uuid2str(pUUID); + + struct { char *val;char *name;} _enumstr[] = { + {UUID_BASIC_CONNECT, "UUID_BASIC_CONNECT"}, + {UUID_BASIC_CONNECT_EXT, "UUID_BASIC_CONNECT_EXT"}, + {UUID_SMS, "UUID_SMS"}, + {UUID_USSD, "UUID_USSD"}, + {UUID_PHONEBOOK, "UUID_PHONEBOOK"}, + {UUID_STK, "UUID_STK"}, + {UUID_AUTH, "UUID_AUTH"}, + {UUID_DSS, "UUID_DSS"}, + {uuid_ext_qmux, "uuid_ext_qmux"}, + {uuid_mshsd, "uuid_mshsd"}, + {uuid_qmbe, "uuid_qmbe"}, + {UUID_MSFWID, "UUID_MSFWID"}, + {uuid_atds, "uuid_atds"}, + {uuid_qdu, "uuid_qdu"}, + {UUID_MS_UICC_LOW_LEVEL, "UUID_MS_UICC_LOW_LEVEL"}, + {UUID_MS_SARControl, "UUID_MS_SARControl"}, + {UUID_VOICEEXTENSIONS, "UUID_VOICEEXTENSIONS"}, + {UUID_LIBMBIM_PROXY, "UUID_LIBMBIM_PROXY"}, + }; + int idx; + + for (idx = 0; idx < (int)(sizeof(_enumstr)/sizeof(_enumstr[0])); idx++) { + if (!strcasecmp(str, _enumstr[idx].val)) + return _enumstr[idx].name; + } + + return str; +} + +static const char *mbim_get_segment(void *_pMsg, UINT32 offset, UINT32 len) +{ + int idx; + static char buff[256] = {'\0'}; + UINT8 *pMsg = (UINT8*)_pMsg; + + for (idx = 0; idx < (int)(len/2); idx++) + buff[idx] = pMsg[offset+idx*2]; + buff[idx] = '\0'; + return buff; +} + +static void mbim_dump_header(MBIM_MESSAGE_HEADER *pMsg, const char *direction) { + mbim_debug("%s Header:", direction); + mbim_debug("%s MessageLength = %u", direction, le32toh(pMsg->MessageLength)); + mbim_debug("%s MessageType = %s (0x%08x)", direction, MBIMMSGTypeStr(le32toh(pMsg->MessageType)), le32toh(pMsg->MessageType)); + mbim_debug("%s TransactionId = %u", direction, le32toh(pMsg->TransactionId)); + mbim_debug("%s Contents:", direction); +} + +static void mbim_dump_uuid_cid(const UUID_T *pUUID, UINT32 CID, const char *direction) { + size_t idx; + const char *uuidStr = uuid2str(pUUID); + const char *cidStr = "unknow"; + + for (idx = 0; idx < (sizeof(uuid_cid_string)/sizeof(uuid_cid_string[0])); idx++) { + if (!strcmp(uuidStr, uuid_cid_string[idx].uuid) && uuid_cid_string[idx].cid == CID) { + cidStr = uuid_cid_string[idx].name; + } + } + + mbim_debug("%s DeviceServiceId = %s (%s)", direction, DeviceServiceId2str(pUUID), uuidStr); + mbim_debug("%s CID = %s (%u)", direction, cidStr, le32toh(CID)); +} + + +static void mbim_dump_command_msg(MBIM_COMMAND_MSG_T *pCmdMsg, const char *direction) { + mbim_dump_uuid_cid(&pCmdMsg->DeviceServiceId, le32toh(pCmdMsg->CID), direction); + mbim_debug("%s CommandType = %s (%u)", direction, le32toh(pCmdMsg->CommandType) ? "set" : "query", le32toh(pCmdMsg->CommandType)); + mbim_debug("%s InformationBufferLength = %u", direction, le32toh(pCmdMsg->InformationBufferLength)); +} + +static void mbim_dump_command_done(MBIM_COMMAND_DONE_T *pCmdDone, const char *direction) { + mbim_dump_uuid_cid(&pCmdDone->DeviceServiceId, le32toh(pCmdDone->CID), direction); + mbim_debug("%s Status = %u", direction, le32toh(pCmdDone->Status)); + mbim_debug("%s InformationBufferLength = %u", direction, le32toh(pCmdDone->InformationBufferLength)); +} + +static void mbim_dump_indicate_msg(MBIM_INDICATE_STATUS_MSG_T *pIndMsg, const char *direction) { + mbim_dump_uuid_cid(&pIndMsg->DeviceServiceId, le32toh(pIndMsg->CID), direction); + mbim_debug("%s InformationBufferLength = %u", direction, le32toh(pIndMsg->InformationBufferLength)); +} + +static void mbim_dump_connect(MBIM_CONNECT_T *pInfo, const char *direction) { + mbim_debug("%s SessionId = %u", direction, le32toh(pInfo->SessionId)); + mbim_debug("%s ActivationState = %s (%u)", direction, MBIMActivationStateStr(le32toh(pInfo->ActivationState)), le32toh(pInfo->ActivationState)); + mbim_debug("%s IPType = %s", direction, MBIMContextIPTypeStr(le32toh(pInfo->IPType))); + mbim_debug("%s VoiceCallState = %s", direction, MBIMVoiceCallStateStr(le32toh(pInfo->VoiceCallState))); + mbim_debug("%s ContextType = %s", direction, uuid2str(&pInfo->ContextType)); + mbim_debug("%s NwError = %u", direction, le32toh(pInfo->NwError)); +} + +static void mbim_dump_signal_state(MBIM_SIGNAL_STATE_INFO_T *pInfo, const char *direction) +{ + mbim_debug("%s Rssi = %u", direction, le32toh(pInfo->Rssi)); + mbim_debug("%s ErrorRate = %u", direction, le32toh(pInfo->ErrorRate)); + mbim_debug("%s SignalStrengthInterval = %u", direction, le32toh(pInfo->SignalStrengthInterval)); + mbim_debug("%s RssiThreshold = %u", direction, le32toh(pInfo->RssiThreshold)); + mbim_debug("%s ErrorRateThreshold = %u", direction, le32toh(pInfo->ErrorRateThreshold)); +} + +static void mbim_dump_packet_service(MBIM_PACKET_SERVICE_INFO_T *pInfo, const char *direction) +{ + mbim_debug("%s NwError = %u", direction, le32toh(pInfo->NwError)); + mbim_debug("%s PacketServiceState = %s", direction, MBIMPacketServiceStateStr(le32toh(pInfo->PacketServiceState))); + mbim_debug("%s HighestAvailableDataClass = %s", direction, MBIMDataClassStr(le32toh(pInfo->HighestAvailableDataClass))); + mbim_debug("%s UplinkSpeed = %ld", direction, (long)le64toh(pInfo->UplinkSpeed)); + mbim_debug("%s DownlinkSpeed = %ld", direction, (long)le64toh(pInfo->DownlinkSpeed)); +} + +static void mbim_dump_subscriber_status(MBIM_SUBSCRIBER_READY_STATUS_T *pInfo, const char *direction) +{ + mbim_debug("%s ReadyState = %s", direction, MBIMSubscriberReadyStateStr(le32toh(pInfo->ReadyState))); + mbim_debug("%s SIMICCID = %s", direction, mbim_get_segment(pInfo, le32toh(pInfo->SimIccIdOffset), le32toh(pInfo->SimIccIdSize))); + mbim_debug("%s SubscriberID = %s", direction, mbim_get_segment(pInfo, le32toh(pInfo->SubscriberIdOffset), le32toh(pInfo->SubscriberIdSize))); + /* maybe more than one number */ + uint32_t idx; + for (idx = 0; idx < le32toh(pInfo->ElementCount); idx++) { + UINT32 offset = ((UINT32*)((UINT8*)pInfo+offsetof(MBIM_SUBSCRIBER_READY_STATUS_T, TelephoneNumbersRefList)))[0]; + UINT32 length = ((UINT32*)((UINT8*)pInfo+offsetof(MBIM_SUBSCRIBER_READY_STATUS_T, TelephoneNumbersRefList)))[1]; + mbim_debug("%s Number = %s", direction, mbim_get_segment(pInfo, le32toh(offset), le32toh(length))); + } +} + +static void mbim_dump_regiester_status(MBIM_REGISTRATION_STATE_INFO_T *pInfo, const char *direction) +{ + mbim_debug("%s NwError = %u", direction, le32toh(pInfo->NwError)); + mbim_debug("%s RegisterState = %s", direction, MBIMRegisterStateStr(le32toh(pInfo->RegisterState))); + mbim_debug("%s RegisterMode = %s", direction, MBIMRegisterModeStr(le32toh(pInfo->RegisterMode))); +} + +static void mbim_dump_ipconfig(MBIM_IP_CONFIGURATION_INFO_T *pInfo, const char *direction) +{ + UINT8 prefix = 0, *ipv4=NULL, *ipv6=NULL, *gw=NULL, *dns1=NULL, *dns2=NULL; + + mbim_debug("%s SessionId = %u", direction, le32toh(pInfo->SessionId)); + mbim_debug("%s IPv4ConfigurationAvailable = 0x%x", direction, le32toh(pInfo->IPv4ConfigurationAvailable)); + mbim_debug("%s IPv6ConfigurationAvailable = 0x%x", direction, le32toh(pInfo->IPv6ConfigurationAvailable)); + mbim_debug("%s IPv4AddressCount = 0x%x", direction, le32toh(pInfo->IPv4AddressCount)); + mbim_debug("%s IPv4AddressOffset = 0x%x", direction, le32toh(pInfo->IPv4AddressOffset)); + mbim_debug("%s IPv6AddressCount = 0x%x", direction, le32toh(pInfo->IPv6AddressCount)); + mbim_debug("%s IPv6AddressOffset = 0x%x", direction, le32toh(pInfo->IPv6AddressOffset)); + + /* IPv4 */ + if (le32toh(pInfo->IPv4ConfigurationAvailable)&0x1) { + MBIM_IPV4_ELEMENT_T *pAddress = (MBIM_IPV4_ELEMENT_T *)(&pInfo->DataBuffer[le32toh(pInfo->IPv4AddressOffset)-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + prefix = le32toh(pAddress->OnLinkPrefixLength); + ipv4 = pAddress->IPv4Address; + mbim_debug("%s IPv4 = %u.%u.%u.%u/%u", direction, ipv4[0], ipv4[1], ipv4[2], ipv4[3], prefix); + } + if (le32toh(pInfo->IPv4ConfigurationAvailable)&0x2) { + gw = (UINT8 *)(&pInfo->DataBuffer[le32toh(pInfo->IPv4GatewayOffset)-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + mbim_debug("%s gw = %u.%u.%u.%u", direction, gw[0], gw[1], gw[2], gw[3]); + } + if (le32toh(pInfo->IPv4ConfigurationAvailable)&0x3) { + dns1 = (UINT8 *)(&pInfo->DataBuffer[le32toh(pInfo->IPv4DnsServerOffset) -sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + mbim_debug("%s dns1 = %u.%u.%u.%u", direction, dns1[0], dns1[1], dns1[2], dns1[3]); + if (le32toh(pInfo->IPv4DnsServerCount) == 2) { + dns2 = dns1 + 4; + mbim_debug("%s dns2 = %u.%u.%u.%u", direction, dns2[0], dns2[1], dns2[2], dns2[3]); + } + } + if (le32toh(pInfo->IPv4Mtu)) mbim_debug("%s ipv4 mtu = %u", direction, le32toh(pInfo->IPv4Mtu)); + + /* IPv6 */ + if (le32toh(pInfo->IPv6ConfigurationAvailable)&0x1) { + MBIM_IPV6_ELEMENT_T *pAddress = (MBIM_IPV6_ELEMENT_T *)(&pInfo->DataBuffer[le32toh(pInfo->IPv6AddressOffset)-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + prefix = le32toh(pAddress->OnLinkPrefixLength); + ipv6 = pAddress->IPv6Address; + mbim_debug("%s IPv6 = %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x/%d", \ + direction, ipv6[0], ipv6[1], ipv6[2], ipv6[3], ipv6[4], ipv6[5], ipv6[6], ipv6[7], \ + ipv6[8], ipv6[9], ipv6[10], ipv6[11], ipv6[12], ipv6[13], ipv6[14], ipv6[15], prefix); + } + if (le32toh(pInfo->IPv6ConfigurationAvailable)&0x2) { + gw = (UINT8 *)(&pInfo->DataBuffer[le32toh(pInfo->IPv6GatewayOffset)-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + mbim_debug("%s gw = %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", \ + direction, gw[0], gw[1], gw[2], gw[3], gw[4], gw[5], gw[6], gw[7], \ + gw[8], gw[9], gw[10], gw[11], gw[12], gw[13], gw[14], gw[15]); + } + if (le32toh(pInfo->IPv6ConfigurationAvailable)&0x3) { + dns1 = (UINT8 *)(&pInfo->DataBuffer[le32toh(pInfo->IPv6DnsServerOffset)-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + mbim_debug("%s dns1 = %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", \ + direction, dns1[0], dns1[1], dns1[2], dns1[3], dns1[4], dns1[5], dns1[6], dns1[7], \ + dns1[8], dns1[9], dns1[10], dns1[11], dns1[12], dns1[13], dns1[14], dns1[15]); + if (le32toh(pInfo->IPv6DnsServerCount) == 2) { + dns2 = dns1 + 16; + mbim_debug("%s dns2 = %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", \ + direction, dns2[0], dns2[1], dns2[2], dns2[3], dns1[4], dns1[5], dns1[6], dns1[7], + dns2[8], dns2[9], dns2[10], dns2[11], dns2[12], dns2[13], dns2[14], dns2[15]); + } + } + if (le32toh(pInfo->IPv6Mtu)) mbim_debug("%s ipv6 mtu = %u", direction, le32toh(pInfo->IPv6Mtu)); +} + +static void mbim_dump(MBIM_MESSAGE_HEADER *pMsg, int mbim_verbose) { + unsigned char *data = (unsigned char *)pMsg; + const char *direction = (le32toh(pMsg->MessageType) & 0x80000000) ? "<" : ">"; + + if (!mbim_verbose) + return; + + if (mbim_verbose) { + unsigned i; + static char *_tmp = NULL; + + if (!_tmp) + _tmp = (char *)malloc(4096); + + if (_tmp) { + _tmp[0] = (le32toh(pMsg->MessageType) & 0x80000000) ? '<' : '>'; + _tmp[1] = '\0'; + for (i = 0; i < le32toh(pMsg->MessageLength) && i < 4096; i++) + snprintf(_tmp + strlen(_tmp), 4096 - strlen(_tmp), "%02X:", data[i]); + mbim_debug("%s", _tmp); + } + } + + mbim_dump_header(pMsg, direction); + + switch (le32toh(pMsg->MessageType)) { + case MBIM_OPEN_MSG: { + MBIM_OPEN_MSG_T *pOpenMsg = (MBIM_OPEN_MSG_T *)pMsg; + mbim_debug("%s MaxControlTransfer = %u", direction, le32toh(pOpenMsg->MaxControlTransfer)); + } + break; + case MBIM_OPEN_DONE: { + MBIM_OPEN_DONE_T *pOpenDone = (MBIM_OPEN_DONE_T *)pMsg; + mbim_debug("%s Status = %u", direction, le32toh(pOpenDone->Status)); + } + break; + case MBIM_CLOSE_MSG: { + + } + break; + case MBIM_CLOSE_DONE: { + MBIM_CLOSE_DONE_T *pCloseDone = (MBIM_CLOSE_DONE_T *)pMsg; + mbim_debug("%s Status = %u", direction, le32toh(pCloseDone->Status)); + } + break; + case MBIM_COMMAND_MSG: { + MBIM_COMMAND_MSG_T *pCmdMsg = (MBIM_COMMAND_MSG_T *)pMsg; + + mbim_dump_command_msg(pCmdMsg, direction); + if (!memcmp(pCmdMsg->DeviceServiceId.uuid, str2uuid(UUID_BASIC_CONNECT), 16)) { + switch (le32toh(pCmdMsg->CID)) { + case MBIM_CID_CONNECT: { + MBIM_SET_CONNECT_T *pInfo = (MBIM_SET_CONNECT_T *)pCmdMsg->InformationBuffer; + mbim_debug("%s SessionId = %u", direction, le32toh(pInfo->SessionId)); + } + break; + case MBIM_CID_IP_CONFIGURATION: { + MBIM_IP_CONFIGURATION_INFO_T *pInfo = (MBIM_IP_CONFIGURATION_INFO_T *)pCmdMsg->InformationBuffer; + mbim_debug("%s SessionId = %u", direction, le32toh(pInfo->SessionId)); + } + break; + default: + break; + } + } + } + break; + case MBIM_COMMAND_DONE: { + MBIM_COMMAND_DONE_T *pCmdDone = (MBIM_COMMAND_DONE_T *)pMsg; + + mbim_dump_command_done(pCmdDone, direction); + if (le32toh(pCmdDone->InformationBufferLength) == 0) + return; + + if (!memcmp(pCmdDone->DeviceServiceId.uuid, str2uuid(UUID_BASIC_CONNECT), 16)) { + switch (le32toh(pCmdDone->CID)) { + case MBIM_CID_CONNECT: { + MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pCmdDone->InformationBuffer; + mbim_dump_connect(pInfo, direction); + } + break; + case MBIM_CID_IP_CONFIGURATION: { + //MBIM_IP_CONFIGURATION_INFO_T *pInfo = (MBIM_IP_CONFIGURATION_INFO_T *)pCmdDone->InformationBuffer; + //mbim_dump_ipconfig(pInfo, direction); + } + break; + case MBIM_CID_PACKET_SERVICE: { + MBIM_PACKET_SERVICE_INFO_T *pInfo = (MBIM_PACKET_SERVICE_INFO_T *)pCmdDone->InformationBuffer; + mbim_dump_packet_service(pInfo, direction); + } + break; + case MBIM_CID_SUBSCRIBER_READY_STATUS: { + MBIM_SUBSCRIBER_READY_STATUS_T *pInfo = (MBIM_SUBSCRIBER_READY_STATUS_T *)pCmdDone->InformationBuffer; + mbim_dump_subscriber_status(pInfo, direction); + } + break; + case MBIM_CID_REGISTER_STATE: { + MBIM_REGISTRATION_STATE_INFO_T *pInfo = (MBIM_REGISTRATION_STATE_INFO_T *)pCmdDone->InformationBuffer; + mbim_dump_regiester_status(pInfo, direction); + } + break; + default: + break; + } + } + } + break; + case MBIM_INDICATE_STATUS_MSG: { + MBIM_INDICATE_STATUS_MSG_T *pIndMsg = (MBIM_INDICATE_STATUS_MSG_T *)pMsg; + + mbim_dump_indicate_msg(pIndMsg, direction); + if (le32toh(pIndMsg->InformationBufferLength) == 0) + return; + + if (!memcmp(pIndMsg->DeviceServiceId.uuid, str2uuid(UUID_BASIC_CONNECT), 16)) { + switch (le32toh(pIndMsg->CID)) { + case MBIM_CID_CONNECT: { + MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pIndMsg->InformationBuffer; + mbim_dump_connect(pInfo, direction); + } + break; + case MBIM_CID_SIGNAL_STATE: { + MBIM_SIGNAL_STATE_INFO_T *pInfo = (MBIM_SIGNAL_STATE_INFO_T *)pIndMsg->InformationBuffer; + mbim_dump_signal_state(pInfo, direction); + } + break; + case MBIM_CID_SUBSCRIBER_READY_STATUS: { + MBIM_SUBSCRIBER_READY_STATUS_T *pInfo = (MBIM_SUBSCRIBER_READY_STATUS_T *)pIndMsg->InformationBuffer; + mbim_dump_subscriber_status(pInfo, direction); + } + break; + case MBIM_CID_REGISTER_STATE: { + MBIM_REGISTRATION_STATE_INFO_T *pInfo = (MBIM_REGISTRATION_STATE_INFO_T *)pIndMsg->InformationBuffer; + mbim_dump_regiester_status(pInfo, direction); + } + break; + case MBIM_CID_PACKET_SERVICE: { + MBIM_PACKET_SERVICE_INFO_T *pInfo = (MBIM_PACKET_SERVICE_INFO_T *)pIndMsg->InformationBuffer; + mbim_dump_packet_service(pInfo, direction); + } + break; + default: + break; + } + } + else if (!memcmp(pIndMsg->DeviceServiceId.uuid, str2uuid(UUID_BASIC_CONNECT_EXT), 16)) { + } + } + break; + case MBIM_FUNCTION_ERROR_MSG: { + MBIM_FUNCTION_ERROR_MSG_T *pErrMsg = (MBIM_FUNCTION_ERROR_MSG_T*)pMsg; + mbim_debug("%s ErrorStatusCode = %u", direction, le32toh(pErrMsg->ErrorStatusCode)); + } + break; + default: + break; + } +} + +static void mbim_recv_command(MBIM_MESSAGE_HEADER *pResponse, unsigned size) +{ + (void)size; + pthread_mutex_lock(&mbim_command_mutex); + + if (pResponse) + mbim_dump(pResponse, mbim_verbose); + + if (pResponse == NULL) { + pthread_cond_signal(&mbim_command_cond); + } + else if (mbim_pRequest && le32toh(mbim_pRequest->TransactionId) == le32toh(pResponse->TransactionId)) { + mbim_pResponse = mbim_alloc(le32toh(pResponse->MessageLength)); + if (mbim_pResponse) + memcpy(mbim_pResponse, pResponse, le32toh(pResponse->MessageLength)); + pthread_cond_signal(&mbim_command_cond); + } + else if (le32toh(pResponse->MessageType) == MBIM_INDICATE_STATUS_MSG) { + MBIM_INDICATE_STATUS_MSG_T *pIndMsg = (MBIM_INDICATE_STATUS_MSG_T *)pResponse; + + if (!memcmp(pIndMsg->DeviceServiceId.uuid, str2uuid(UUID_BASIC_CONNECT), 16)) + { + switch (le32toh(pIndMsg->CID)) { + case MBIM_CID_SUBSCRIBER_READY_STATUS: { + MBIM_SUBSCRIBER_READY_STATUS_T *pInfo = (MBIM_SUBSCRIBER_READY_STATUS_T *)pIndMsg->InformationBuffer; + if (oldReadyState != le32toh(pInfo->ReadyState)) + qmidevice_send_event_to_main(RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED); + } + break; + case MBIM_CID_REGISTER_STATE: { + MBIM_REGISTRATION_STATE_INFO_T *pInfo = (MBIM_REGISTRATION_STATE_INFO_T *)pIndMsg->InformationBuffer; + if (oldRegisterState != le32toh(pInfo->RegisterState)) + qmidevice_send_event_to_main(RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED); + } + break; + case MBIM_CID_PACKET_SERVICE: { + MBIM_PACKET_SERVICE_INFO_T *pInfo = (MBIM_PACKET_SERVICE_INFO_T *)pIndMsg->InformationBuffer; + MBIM_PACKET_SERVICE_STATE_E state = le32toh(pInfo->PacketServiceState); + + if (oldPacketServiceState != state + && (1 || MBIMPacketServiceStateAttached == state || MBIMPacketServiceStateDetached == state)) + qmidevice_send_event_to_main(RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED); + } + break; + case MBIM_CID_CONNECT: { + MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pIndMsg->InformationBuffer; + if (pInfo->SessionId == (uint32_t)mbim_sessionID) { + MBIM_ACTIVATION_STATE_E state = le32toh(pInfo->ActivationState); + + if (oldActivationState != state + && (1 || MBIMActivationStateActivated == state || MBIMActivationStateDeactivated == state)) + qmidevice_send_event_to_main(RIL_UNSOL_DATA_CALL_LIST_CHANGED); + } + } + break; + default: + break; + } + } + } + + pthread_mutex_unlock(&mbim_command_mutex); +} + +static int mbim_send_command(MBIM_MESSAGE_HEADER *pRequest, MBIM_COMMAND_DONE_T **ppCmdDone, unsigned msecs) { + int ret; + + if (ppCmdDone) + *ppCmdDone = NULL; + + if (mbim_fd <= 0) + return -ENODEV; + + if (s_tid_reader == 0) + return -EINVAL; + + if (!pRequest) + return -ENOMEM; + + pthread_mutex_lock(&mbim_command_mutex); + + if (pRequest) { + if (pRequest->TransactionId == (0xFFFFFF + 1)) { //quectel-mbim-proxy need 0xFF000000 to indicat client + TransactionId = 1; + pRequest->TransactionId = htole32(TransactionId++); + } + mbim_dump(pRequest, mbim_verbose); + } + + mbim_pRequest = pRequest; + mbim_pResponse = NULL; + + ret = write(mbim_fd, pRequest, le32toh(pRequest->MessageLength)); + + if (ret > 0 && (uint32_t)ret == le32toh(pRequest->MessageLength)) { + ret = pthread_cond_timeout_np(&mbim_command_cond, &mbim_command_mutex, msecs); + if (!ret) { + if (mbim_pResponse && ppCmdDone) { + *ppCmdDone = (MBIM_COMMAND_DONE_T *)mbim_pResponse; + } + } + } else { + mbim_debug("%s pthread_cond_timeout_np=%d", __func__, ret); + } + + mbim_pRequest = mbim_pResponse = NULL; + + pthread_mutex_unlock(&mbim_command_mutex); + + return ret; +} + +static ssize_t mbim_proxy_read (int fd, MBIM_MESSAGE_HEADER *pResponse, size_t size) { + ssize_t nreads; + + nreads = read(fd, pResponse, sizeof(MBIM_MESSAGE_HEADER)); + if (nreads == sizeof(MBIM_MESSAGE_HEADER) && le32toh(pResponse->MessageLength) <= size) { + nreads += read(fd, pResponse+1, le32toh(pResponse->MessageLength) - sizeof(MBIM_MESSAGE_HEADER)); + } + + return nreads; +} + +static void * mbim_read_thread(void *param) { + PROFILE_T *profile = (PROFILE_T *)param; + const char *cdc_wdm = (const char *)profile->qmichannel; + int wait_for_request_quit = 0; + + mbim_verbose = debug_qmi; + s_tid_reader = pthread_self(); + + if (profile->qmap_mode > 1 && profile->qmapnet_adapter[0]) { + if (!profile->proxy[0]) + sprintf(profile->proxy, "%s", QUECTEL_MBIM_PROXY); + mbim_sessionID = profile->pdp; + } + + if (profile->proxy[0]) { + mbim_fd = cm_open_proxy(profile->proxy); + } + else { + mbim_fd = cm_open_dev(cdc_wdm); + } + + if (mbim_fd <= 0) { + mbim_debug("fail to open (%s), errno: %d (%s)", cdc_wdm, errno, strerror(errno)); + goto __quit; + } + + dbg_time("cdc_wdm_fd = %d", mbim_fd); + + qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED); + + while (mbim_fd > 0) { + struct pollfd pollfds[] = {{mbim_fd, POLLIN, 0}, {qmidevice_control_fd[1], POLLIN, 0}, {qmi_over_mbim_sk[1], POLLIN, 0}}; + int ne, ret, nevents = 2; + + if (pollfds[nevents].fd != -1) + nevents++; + + ret = poll(pollfds, nevents, wait_for_request_quit ? 1000 : -1); + + if (ret == 0 && wait_for_request_quit) { + break; + } + + if (ret < 0) { + mbim_debug("%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)) { + mbim_debug("%s poll err/hup/inval", __func__); + mbim_debug("epoll fd = %d, events = 0x%04x", fd, revents); + if (revents & (POLLERR | POLLHUP | POLLNVAL)) + goto __quit; + } + + if ((revents & POLLIN) == 0) + continue; + + if (mbim_fd == fd) { + ssize_t nreads; + MBIM_MESSAGE_HEADER *pResponse = (MBIM_MESSAGE_HEADER *) cm_recv_buf; + + if (profile->proxy[0]) + nreads = mbim_proxy_read(fd, pResponse, sizeof(cm_recv_buf)); + else + nreads = read(fd, pResponse, sizeof(cm_recv_buf)); + + if (nreads <= 0) { + mbim_debug("%s read=%d errno: %d (%s)", __func__, (int)nreads, errno, strerror(errno)); + break; + } + + mbim_recv_command(pResponse, nreads); + } + else if (fd == qmidevice_control_fd[1]) { + int triger_event; + if (read(fd, &triger_event, sizeof(triger_event)) == sizeof(triger_event)) { + //mbim_debug("triger_event = 0x%x", triger_event); + switch (triger_event) { + case RIL_REQUEST_QUIT: + goto __quit; + break; + case SIG_EVENT_STOP: + wait_for_request_quit = 1; + break; + default: + break; + } + } + } + else if (fd == qmi_over_mbim_sk[1]) { + ssize_t nreads = read(fd, cm_recv_buf, sizeof(cm_recv_buf)); + if (nreads > 0) + QmiThreadRecvQMI((PQCQMIMSG)cm_recv_buf); + } + } + } + +__quit: + if (mbim_fd != -1) { close(mbim_fd); mbim_fd = -1; } + mbim_recv_command(NULL, 0); + qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED); + mbim_debug("%s exit", __func__); + s_tid_reader = 0; + + return NULL; +} + +static int mbim_status_code(MBIM_MESSAGE_HEADER *pMsgHdr) { + int status = 0; + + if (!pMsgHdr) + return 0; + + switch (le32toh(pMsgHdr->MessageType)) { + case MBIM_OPEN_DONE: { + MBIM_OPEN_DONE_T *pOpenDone = (MBIM_OPEN_DONE_T *)pMsgHdr; + status = le32toh(pOpenDone->Status); + } + break; + case MBIM_CLOSE_DONE: { + MBIM_CLOSE_DONE_T *pCloseDone = (MBIM_CLOSE_DONE_T *)pMsgHdr; + status = le32toh(pCloseDone->Status); + } + break; + case MBIM_COMMAND_DONE: { + MBIM_COMMAND_DONE_T *pCmdDone = (MBIM_COMMAND_DONE_T *)pMsgHdr; + status = le32toh(pCmdDone->Status); + } + break; + case MBIM_FUNCTION_ERROR_MSG: { + MBIM_FUNCTION_ERROR_MSG_T *pErrMsg = (MBIM_FUNCTION_ERROR_MSG_T *)pMsgHdr; + status = le32toh(pErrMsg->ErrorStatusCode); + if (status == MBIM_ERROR_NOT_OPENED) + mbim_open_state = 0; //EM06ELAR03A05M4G when suspend/resume, may get this error + } + break; + default: + break; + } + + return status; +} + +#define mbim_check_err(err, pRequest, pCmdDone) do { \ + int _status = mbim_status_code(pCmdDone ? &pCmdDone->MessageHeader : NULL); \ + if (err || _status || !pCmdDone) { \ + if (pCmdDone) { mbim_dump(&pCmdDone->MessageHeader, (mbim_verbose == 0)); } \ + mbim_free(pRequest); mbim_free(pCmdDone); \ + mbim_debug("%s:%d err=%d, Status=%d", __func__, __LINE__, err, _status); \ + if (err) return err; \ + if (_status) return _status; \ + return 8888; \ + } \ +} while(0) + +/* + * MBIM device can be open repeatly without error + * So, we can call the function, no matter it have been opened or not + */ +static int mbim_open_device(uint32_t MaxControlTransfer) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_OPEN_DONE_T *pOpenDone = NULL; + int err = 0; + + mbim_debug("%s()", __func__); + pRequest = compose_open_command(MaxControlTransfer); + err = mbim_send_command(pRequest, (MBIM_COMMAND_DONE_T **)&pOpenDone, 3*1000); //EM06ELAR03A09M4G take about 2.5 seconds + mbim_check_err(err, pRequest, pOpenDone); + + err = le32toh(pOpenDone->Status); + mbim_free(pRequest); mbim_free(pOpenDone); + + return err; +} + +static int mbim_close_device(void) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_CLOSE_DONE_T *pCloseDone = NULL; + int err = 0; + + mbim_debug("%s()", __func__); + pRequest = compose_close_command(); + err = mbim_send_command(pRequest, (MBIM_COMMAND_DONE_T **)&pCloseDone, mbim_default_timeout); + mbim_check_err(err, pRequest, pCloseDone); + + err = le32toh(pCloseDone->Status); + mbim_free(pRequest); mbim_free(pCloseDone); + + return err; +} + +static int mbim_query_connect(int sessionID) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_COMMAND_DONE_T *pCmdDone = NULL; + MBIM_SET_CONNECT_T set_connect; + int err; + + if (ActivationState != MBIMActivationStateActivated || mbim_verbose) + mbim_debug("%s(sessionID=%d)", __func__, sessionID); //avoid two many log + set_connect.SessionId = htole32(sessionID); + pRequest = compose_basic_connect_command(MBIM_CID_CONNECT, MBIM_CID_CMD_TYPE_QUERY, &set_connect, sizeof(set_connect)); + err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout); + mbim_check_err(err, pRequest, pCmdDone); + + if (le32toh(pCmdDone->InformationBufferLength)) + { + MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pCmdDone->InformationBuffer; + ActivationState = le32toh(pInfo->ActivationState); + mbim_update_state(); + } + mbim_free(pRequest); mbim_free(pCmdDone); + return err; +} + +static int mbim_ms_version_query(void) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_COMMAND_DONE_T *pCmdDone = NULL; + int err; + + struct _bc_ext_version { + UINT8 ver_minor; + UINT8 ver_major; + UINT8 ext_ver_minor; + UINT8 ext_ver_major; + } __attribute__ ((packed)) bc_ext_version; + + bc_ext_version.ver_major = 1; + bc_ext_version.ver_minor = 0; + bc_ext_version.ext_ver_major = 2; + bc_ext_version.ext_ver_minor = 0; + + pRequest = compose_basic_connect_ext_command(MBIM_CID_MS_VERSION, MBIM_CID_CMD_TYPE_QUERY, &bc_ext_version, sizeof(bc_ext_version)); + err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout); + mbim_check_err(err, pRequest, pCmdDone); + + if (le32toh(pCmdDone->InformationBufferLength)) { + struct _bc_ext_version *pInfo = (struct _bc_ext_version *)pCmdDone->InformationBuffer; + //mbim_debug("%s ext_rel_ver major=%d, minor=%d", __func__, pInfo->ext_ver_major, pInfo->ext_ver_minor); + mbim_ms_version = pInfo->ext_ver_major; + } + + mbim_free(pRequest); mbim_free(pCmdDone); + return err; +} + +static int mbim_device_services_query(void) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_COMMAND_DONE_T *pCmdDone = NULL; + int err; + int mbim_v2_support = 0; + + mbim_debug("%s()", __func__); + pRequest = compose_basic_connect_command(MBIM_CID_DEVICE_SERVICES, MBIM_CID_CMD_TYPE_QUERY, NULL, 0); + err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout); + mbim_check_err(err, pRequest, pCmdDone); + + if (pCmdDone->InformationBufferLength) { + MBIM_DEVICE_SERVICES_INFO_T *pInfo = (MBIM_DEVICE_SERVICES_INFO_T *)pCmdDone->InformationBuffer; + UINT32 i; + + for (i = 0; i < le32toh(pInfo->DeviceServicesCount) ; i++) { + //UINT32 size = pInfo->DeviceServicesRefList[i].size; + UINT32 offset = le32toh(pInfo->DeviceServicesRefList[i].offset); + MBIM_DEVICE_SERVICE_ELEMENT_T *pSrvEle = (MBIM_DEVICE_SERVICE_ELEMENT_T *)((void *)pInfo + offset); + + //mbim_debug("\t[%2d] %s (%s)", i, DeviceServiceId2str(&pSrvEle->DeviceServiceId), uuid2str(&pSrvEle->DeviceServiceId)); + if (!strcasecmp(UUID_BASIC_CONNECT_EXT, uuid2str(&pSrvEle->DeviceServiceId))) { + UINT32 cid = 0; + + for (cid = 0; cid < le32toh(pSrvEle->CidCount); cid++) { + if (MBIM_CID_MS_VERSION == le32toh(pSrvEle->CidList[cid])) { + mbim_v2_support = 1; + } + } + } + else if (!strcasecmp(uuid_ext_qmux, uuid2str(&pSrvEle->DeviceServiceId))) { + qmi_over_mbim_support = 1; + } + } + } + mbim_free(pRequest); mbim_free(pCmdDone); + + if (mbim_v2_support) { + mbim_ms_version_query(); + } + + return err; +} + +static int mbim_device_caps_query(PROFILE_T *profile) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_COMMAND_DONE_T *pCmdDone = NULL; + int err; + + mbim_debug("%s()", __func__); + pRequest = compose_basic_connect_command(MBIM_CID_DEVICE_CAPS, MBIM_CID_CMD_TYPE_QUERY, NULL, 0); + err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout); + mbim_check_err(err, pRequest, pCmdDone); + + if (le32toh(pCmdDone->InformationBufferLength)) { + MBIM_DEVICE_CAPS_INFO_T *pInfo = (MBIM_DEVICE_CAPS_INFO_T *)pCmdDone->InformationBuffer; + char tmp[32]; + + if (le32toh(pInfo->DeviceIdOffset) && le32toh(pInfo->DeviceIdSize)) { + wchar2char((const char *)pInfo + le32toh(pInfo->DeviceIdOffset), le32toh(pInfo->DeviceIdSize), tmp, sizeof(tmp)); + mbim_debug("DeviceId: %s", tmp); + } + if (le32toh(pInfo->FirmwareInfoOffset) && le32toh(pInfo->FirmwareInfoSize)) { + wchar2char((const char *)pInfo + le32toh(pInfo->FirmwareInfoOffset), le32toh(pInfo->FirmwareInfoSize), tmp, sizeof(tmp)); + strncpy(profile->BaseBandVersion, tmp, sizeof(profile->BaseBandVersion)); + mbim_debug("FirmwareInfo: %s", tmp); + } + if (le32toh(pInfo->HardwareInfoOffset) && le32toh(pInfo->HardwareInfoSize)) { + wchar2char((const char *)pInfo + le32toh(pInfo->HardwareInfoOffset), le32toh(pInfo->HardwareInfoSize), tmp, sizeof(tmp)); + mbim_debug("HardwareInfo: %s", tmp); + } + } + mbim_free(pRequest); mbim_free(pCmdDone); + return err; +} + +#if 0 +static int mbim_radio_state_query(void) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_COMMAND_DONE_T *pCmdDone = NULL; + int err; + + mbim_debug("%s()", __func__); + pRequest = compose_basic_connect_command(MBIM_CID_RADIO_STATE, MBIM_CID_CMD_TYPE_QUERY, NULL, 0); + err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout); + mbim_check_err(err, pRequest, pCmdDone); + + if (pCmdDone->InformationBufferLength) { + MBIM_RADIO_STATE_INFO_T *pInfo = (MBIM_RADIO_STATE_INFO_T *)pCmdDone->InformationBuffer; + mbim_debug("HwRadioState: %d, SwRadioState: %d", pInfo->HwRadioState, pInfo->SwRadioState); + } + mbim_free(pRequest); mbim_free(pCmdDone); + return err; +} +#endif + +static int mbim_set_radio_state(MBIM_RADIO_SWITCH_STATE_E RadioState) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_COMMAND_DONE_T *pCmdDone = NULL; + UINT32 value = htole32(RadioState); + int err; + + mbim_debug("%s( %d )", __func__, RadioState); + pRequest = compose_basic_connect_command(MBIM_CID_RADIO_STATE, MBIM_CID_CMD_TYPE_SET, &value, sizeof(value)); + err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout); + mbim_check_err(err, pRequest, pCmdDone); + + if (le32toh(pCmdDone->InformationBufferLength)) { + MBIM_RADIO_STATE_INFO_T *pInfo = (MBIM_RADIO_STATE_INFO_T *)pCmdDone->InformationBuffer; + mbim_debug("HwRadioState: %d, SwRadioState: %d", le32toh(pInfo->HwRadioState), le32toh(pInfo->SwRadioState)); + } + mbim_free(pRequest); mbim_free(pCmdDone); + return err; +} + +static int mbim_subscriber_status_query(void) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_COMMAND_DONE_T *pCmdDone = NULL; + int err; + + mbim_debug("%s()", __func__); + pRequest = compose_basic_connect_command(MBIM_CID_SUBSCRIBER_READY_STATUS, MBIM_CID_CMD_TYPE_QUERY, NULL, 0); + err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout); + mbim_check_err(err, pRequest, pCmdDone); + + if (le32toh(pCmdDone->InformationBufferLength)) { + MBIM_SUBSCRIBER_READY_STATUS_T *pInfo = (MBIM_SUBSCRIBER_READY_STATUS_T *)pCmdDone->InformationBuffer; + char tmp[32]; + + wchar2char((const char *)pInfo + le32toh(pInfo->SubscriberIdOffset), le32toh(pInfo->SubscriberIdSize), tmp, sizeof(tmp)); + mbim_debug("SubscriberId: %s", tmp); + wchar2char((const char *)pInfo + le32toh(pInfo->SimIccIdOffset), le32toh(pInfo->SimIccIdSize), tmp, sizeof(tmp)); + mbim_debug("SimIccId: %s", tmp); + ReadyState = le32toh(pInfo->ReadyState); + mbim_update_state(); + } + mbim_free(pRequest); mbim_free(pCmdDone); + return err; +} + +static int mbim_register_state_query(void) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_COMMAND_DONE_T *pCmdDone = NULL; + int err; + + mbim_debug("%s()", __func__); + pRequest = compose_basic_connect_command(MBIM_CID_REGISTER_STATE, MBIM_CID_CMD_TYPE_QUERY, NULL, 0); + err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout); + mbim_check_err(err, pRequest, pCmdDone); + + if (le32toh(pCmdDone->InformationBufferLength)) { + MBIM_REGISTRATION_STATE_INFO_T *pInfo = (MBIM_REGISTRATION_STATE_INFO_T *)pCmdDone->InformationBuffer;; + RegisterState = le32toh(pInfo->RegisterState); + mbim_update_state(); + } + mbim_free(pRequest); mbim_free(pCmdDone); + return err; +} + +static int mbim_packet_service_query(void) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_COMMAND_DONE_T *pCmdDone = NULL; + int err; + + mbim_debug("%s()", __func__); + pRequest = compose_basic_connect_command(MBIM_CID_PACKET_SERVICE, MBIM_CID_CMD_TYPE_QUERY, NULL, 0); + err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout); + mbim_check_err(err, pRequest, pCmdDone); + + if (le32toh(pCmdDone->InformationBufferLength)) { + MBIM_PACKET_SERVICE_INFO_T *pInfo = (MBIM_PACKET_SERVICE_INFO_T *)pCmdDone->InformationBuffer; + PacketServiceState = le32toh(pInfo->PacketServiceState); + mbim_update_state(); + + if (le32toh(pCmdDone->InformationBufferLength) == sizeof(MBIM_PACKET_SERVICE_INFO_V2_T)) { + MBIM_PACKET_SERVICE_INFO_V2_T *pInfo = (MBIM_PACKET_SERVICE_INFO_V2_T *)pCmdDone->InformationBuffer; + mbim_debug("CurrentDataClass = %s", MBIMDataClassStr(le32toh(pInfo->CurrentDataClass))); + } + } + mbim_free(pRequest); mbim_free(pCmdDone); + return err; +} + +static int mbim_packet_service_set(MBIM_PACKET_SERVICE_ACTION_E action) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_COMMAND_DONE_T *pCmdDone = NULL; + UINT32 value = htole32(action); + int err; + + mbim_debug("%s()", __func__); + pRequest = compose_basic_connect_command(MBIM_CID_PACKET_SERVICE, MBIM_CID_CMD_TYPE_SET, &value, sizeof(value)); + err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout); + mbim_check_err(err, pRequest, pCmdDone); + + if (le32toh(pCmdDone->InformationBufferLength)) { + MBIM_PACKET_SERVICE_INFO_T *pInfo = (MBIM_PACKET_SERVICE_INFO_T *)pCmdDone->InformationBuffer; + PacketServiceState = le32toh(pInfo->PacketServiceState); + mbim_update_state(); + } + mbim_free(pRequest); mbim_free(pCmdDone); + return err; +} + +#define _align_32(len) {len += (len % 4) ? (4 - (len % 4)) : 0;} +static int mbim_populate_connect_data(MBIM_SET_CONNECT_T **connect_req_ptr) { + int offset; + int buflen = 0; + + if (mbim_apn && strlen(mbim_apn) > 0) buflen += 2*strlen(mbim_apn) ; + _align_32(buflen); + if (mbim_user && strlen(mbim_user) > 0) buflen += 2*strlen(mbim_user); + _align_32(buflen); + if (mbim_passwd && strlen(mbim_passwd) > 0) buflen += 2*strlen(mbim_passwd); + _align_32(buflen); + + *connect_req_ptr = (MBIM_SET_CONNECT_T*)malloc(sizeof(MBIM_SET_CONNECT_T) + buflen); + if (! *connect_req_ptr) { + mbim_debug("not enough memory\n"); + return -1; + } + memset(*connect_req_ptr, 0, sizeof(MBIM_SET_CONNECT_T) + buflen); + + offset = 0; + if (mbim_apn && strlen(mbim_apn) > 0) { + (*connect_req_ptr)->AccessStringSize = htole32(2*strlen(mbim_apn)); + (*connect_req_ptr)->AccessStringOffset = htole32(offset + sizeof(MBIM_SET_CONNECT_T)); + offset = char2wchar(mbim_apn, strlen(mbim_apn), &(*connect_req_ptr)->DataBuffer[offset], buflen - offset); + _align_32(offset); + } + + if (mbim_user && strlen(mbim_user) > 0) { + (*connect_req_ptr)->UserNameSize = htole32(2*strlen(mbim_user)); + (*connect_req_ptr)->UserNameOffset = htole32(offset + sizeof(MBIM_SET_CONNECT_T)); + offset = char2wchar(mbim_user, strlen(mbim_user), &(*connect_req_ptr)->DataBuffer[offset], buflen - offset); + _align_32(offset); + } + + if (mbim_passwd && strlen(mbim_passwd) > 0) { + (*connect_req_ptr)->PasswordSize = htole32(2*strlen(mbim_passwd)); + (*connect_req_ptr)->PasswordOffset = htole32(offset + sizeof(MBIM_SET_CONNECT_T)); + offset = char2wchar(mbim_passwd, strlen(mbim_passwd), &(*connect_req_ptr)->DataBuffer[offset], buflen - offset); + } + + return buflen; +} + +static int mbim_set_connect(int onoff, int sessionID) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_COMMAND_DONE_T *pCmdDone = NULL; + MBIM_SET_CONNECT_T *set_connect = NULL; + int err; + + mbim_debug("%s(onoff=%d, sessionID=%d)", __func__, onoff, sessionID); + /* alloc memory then populate APN USERNAME PASSWORD */ + int buflen = mbim_populate_connect_data(&set_connect); + if (buflen < 0) { + return ENOMEM; + } + + set_connect->SessionId = htole32(sessionID); + if (onoff == 0) + set_connect->ActivationCommand = htole32(MBIMActivationCommandDeactivate); + else + set_connect->ActivationCommand = htole32(MBIMActivationCommandActivate); + + set_connect->Compression = htole32(MBIMCompressionNone); + set_connect->AuthProtocol = htole32(mbim_auth); + set_connect->IPType = htole32(mbim_iptype); + memcpy(set_connect->ContextType.uuid, str2uuid(UUID_MBIMContextTypeInternet), 16); + + pRequest = compose_basic_connect_command(MBIM_CID_CONNECT, MBIM_CID_CMD_TYPE_SET, set_connect, sizeof(MBIM_SET_CONNECT_T) + buflen); + mbim_free(set_connect); + err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout*10); + mbim_check_err(err, pRequest, pCmdDone); + + if (le32toh(pCmdDone->InformationBufferLength)) { + MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pCmdDone->InformationBuffer; + ActivationState = le32toh(pInfo->ActivationState); + mbim_update_state(); + } + + mbim_free(pRequest); mbim_free(pCmdDone); + return err; +} + +static int mbim_ip_config(PROFILE_T *profile, int sessionID) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_COMMAND_DONE_T *pCmdDone = NULL; + MBIM_IP_CONFIGURATION_INFO_T ip_info; + int err; + + if (profile->ipv4.Address == 0 || mbim_verbose) + mbim_debug("%s(sessionID=%d)", __func__, sessionID); + ip_info.SessionId = htole32(sessionID); + pRequest = compose_basic_connect_command(MBIM_CID_IP_CONFIGURATION, MBIM_CID_CMD_TYPE_QUERY, &ip_info, sizeof(ip_info)); + err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout); + mbim_check_err(err, pRequest, pCmdDone); + + if (le32toh(pCmdDone->InformationBufferLength)) { + UINT8 prefix, *ipv4=NULL, *ipv6=NULL, *gw=NULL, *dns1=NULL, *dns2=NULL; + UINT32 mtu = 1500; + MBIM_IP_CONFIGURATION_INFO_T *pInfo = (MBIM_IP_CONFIGURATION_INFO_T *)pCmdDone->InformationBuffer; + + /* IPv4 network configration */ + if (le32toh(pInfo->IPv4ConfigurationAvailable)&0x1) { + MBIM_IPV4_ELEMENT_T *pAddress = (MBIM_IPV4_ELEMENT_T *)(&pInfo->DataBuffer[le32toh(pInfo->IPv4AddressOffset)-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + prefix = le32toh(pAddress->OnLinkPrefixLength); + ipv4 = pAddress->IPv4Address; + + if (le32toh(pInfo->IPv4ConfigurationAvailable)&0x2) + gw = (UINT8 *)(&pInfo->DataBuffer[le32toh(pInfo->IPv4GatewayOffset)-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + + if (le32toh(pInfo->IPv4ConfigurationAvailable)&0x4) { + dns1 = (UINT8 *)(&pInfo->DataBuffer[le32toh(pInfo->IPv4DnsServerOffset)-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + if (le32toh(pInfo->IPv4DnsServerCount) == 2) + dns2 = dns1 + 4; + } + + if (le32toh(pInfo->IPv4ConfigurationAvailable)&0x8) + mtu = le32toh(pInfo->IPv4Mtu); + + if (profile->ipv4.Address != mbim2qmi_ipv4addr(*(uint32_t *)ipv4) || mbim_verbose) { + mbim_dump_ipconfig(pInfo, "<"); + profile->ipv4.Address = mbim2qmi_ipv4addr(*(uint32_t *)ipv4); + } + + if(gw != NULL) + profile->ipv4.Gateway = mbim2qmi_ipv4addr(*(uint32_t *)gw); + profile->ipv4.SubnetMask = mbim2qmi_ipv4addr(0xFFFFFFFF>>(32-prefix)<<(32-prefix)); + if(dns1 != NULL) + profile->ipv4.DnsPrimary = mbim2qmi_ipv4addr(*(uint32_t *)dns1); + if(dns2 != NULL) + profile->ipv4.DnsSecondary = mbim2qmi_ipv4addr(*(uint32_t *)dns2); + profile->ipv4.Mtu = mbim2qmi_ipv4addr(mtu); + } + + /* IPv6 network configration */ + if (le32toh(pInfo->IPv6ConfigurationAvailable)&0x1) { + gw = NULL; dns1 = NULL; dns2 = NULL; + MBIM_IPV6_ELEMENT_T *pAddress = (MBIM_IPV6_ELEMENT_T *)(&pInfo->DataBuffer[le32toh(pInfo->IPv6AddressOffset)-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + prefix = le32toh(pAddress->OnLinkPrefixLength); + ipv6 = pAddress->IPv6Address; + + if (le32toh(pInfo->IPv6ConfigurationAvailable)&0x2) + gw = (UINT8 *)(&pInfo->DataBuffer[le32toh(pInfo->IPv6GatewayOffset)-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + + if (le32toh(pInfo->IPv6ConfigurationAvailable)&0x4) { + dns1 = (UINT8 *)(&pInfo->DataBuffer[le32toh(pInfo->IPv6DnsServerOffset)-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + if (le32toh(pInfo->IPv6DnsServerCount) == 2) + dns2 = dns1 + 16; + } + + if (le32toh(pInfo->IPv6ConfigurationAvailable)&0x8) + mtu = le32toh(pInfo->IPv6Mtu); + + if(ipv6 != NULL) + mbim2qmi_ipv6addr(ipv6, profile->ipv6.Address); + if(gw != NULL) + mbim2qmi_ipv6addr(gw, profile->ipv6.Gateway); + if(dns1 != NULL) + mbim2qmi_ipv6addr(dns1, profile->ipv6.DnsPrimary); + if(dns2 != NULL) + mbim2qmi_ipv6addr(dns2, profile->ipv6.DnsSecondary); + profile->ipv6.PrefixLengthIPAddr = prefix; + profile->ipv6.PrefixLengthGateway = prefix; + profile->ipv6.Mtu = mbim2qmi_ipv4addr(mtu); + } + } + return err; +} + +int mbim_proxy_configure(const char *dev) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_COMMAND_DONE_T *pCmdDone = NULL; + MBIM_LIBQMI_PROXY_CONFIG_T *cfg; + int err; + + pRequest = compose_basic_connect_command( + MBIM_CID_PROXY_CONTROL_CONFIGURATION, + MBIM_CID_CMD_TYPE_SET, + NULL, + sizeof(*cfg) + strlen(dev)*2); + if (pRequest) { + memcpy(((MBIM_COMMAND_MSG_T *)pRequest)->DeviceServiceId.uuid, str2uuid(UUID_LIBMBIM_PROXY), 16); + cfg = (MBIM_LIBQMI_PROXY_CONFIG_T *)((MBIM_COMMAND_MSG_T *)pRequest)->InformationBuffer; + + cfg->DevicePathOffset = sizeof(*cfg); + cfg->DevicePathSize = char2wchar(dev, strlen(dev), cfg->DataBuffer, strlen(dev)*2); + cfg->Timeout = 15; + } + + err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout); + mbim_check_err(err, pRequest, pCmdDone); + + mbim_free(pRequest); mbim_free(pCmdDone); + return err; +} + +static int mbim_update_state(void) { + int chages = 0; + + if (oldReadyState != ReadyState) { + mbim_debug("SubscriberReadyState %s -> %s ", MBIMSubscriberReadyStateStr(oldReadyState), MBIMSubscriberReadyStateStr(ReadyState)); + oldReadyState = ReadyState; chages++; + } + if (oldRegisterState != RegisterState) { + mbim_debug("RegisterState %s -> %s ", MBIMRegisterStateStr(oldRegisterState), MBIMRegisterStateStr(RegisterState)); + oldRegisterState = RegisterState; chages++; + } + if (oldPacketServiceState != PacketServiceState) { + mbim_debug("PacketServiceState %s -> %s ", MBIMPacketServiceStateStr(oldPacketServiceState), MBIMPacketServiceStateStr(PacketServiceState)); + oldPacketServiceState = PacketServiceState; chages++; + } + if (oldActivationState != ActivationState) { + mbim_debug("ActivationState %s -> %s ", MBIMActivationStateStr(oldActivationState), MBIMActivationStateStr(ActivationState)); + oldActivationState = ActivationState; chages++; + } + + return chages; +} + +static int mbim_init(PROFILE_T *profile) { + int retval; + int t = 0; + + if (profile->proxy[0] && !strcmp(profile->proxy, LIBMBIM_PROXY)) { + retval = mbim_proxy_configure(profile->qmichannel); + if (retval) goto exit; + } + + while (t++ < 10) { + retval = mbim_open_device(4096); + if (retval != ETIMEDOUT) + break; + } + if (retval) goto exit; + retval = mbim_device_caps_query(profile); + if (retval) goto exit; + mbim_update_state(); + retval = mbim_device_services_query(); + if (retval) goto exit; + mbim_update_state(); + retval = mbim_set_radio_state(MBIMRadioOn); + if (retval) goto exit; + mbim_update_state(); + + if (qmi_over_mbim_support) { + if (!socketpair( AF_LOCAL, SOCK_STREAM, 0, qmi_over_mbim_sk)) { + qmidev_send = qmi_over_mbim_qmidev_send; +#ifdef CONFIG_CELLINFO //by now, only this function need QMI OVER MBIM + qmi_over_mbim_nas = qmi_over_mbim_get_client_id(QMUX_TYPE_NAS); +#endif + } + } + + return 0; + +exit: + return retval; +} + +static int mbim_deinit(void) { + if (qmi_over_mbim_nas) { + qmi_over_mbim_release_client_id(QMUX_TYPE_NAS, qmi_over_mbim_nas); + qmi_over_mbim_nas = 0; + } + + mbim_close_device(); + + if (qmi_over_mbim_sk[0] != -1) { + close(qmi_over_mbim_sk[0]); + close(qmi_over_mbim_sk[1]); + } + + return 0; +} + +const struct qmi_device_ops mbim_dev_ops = { + .init = mbim_init, + .deinit = mbim_deinit, + .read = mbim_read_thread, +}; + +static int requestBaseBandVersion(PROFILE_T *profile) { + (void)profile; + return 0; +} + +static int requestGetSIMStatus(SIM_Status *pSIMStatus) +{ + int retval; + + *pSIMStatus = SIM_ABSENT; + retval = mbim_subscriber_status_query(); + if (retval) + goto exit; + mbim_update_state(); + + switch(ReadyState) { + case MBIMSubscriberReadyStateNotInitialized: *pSIMStatus = SIM_NOT_READY; break; + case MBIMSubscriberReadyStateInitialized: *pSIMStatus = SIM_READY; break; + case MBIMSubscriberReadyStateSimNotInserted: *pSIMStatus = SIM_ABSENT; break; + case MBIMSubscriberReadyStateBadSim: *pSIMStatus = SIM_BAD; break; + case MBIMSubscriberReadyStateFailure: *pSIMStatus = SIM_ABSENT; break; + case MBIMSubscriberReadyStateNotActivated: *pSIMStatus = SIM_ABSENT; break; + case MBIMSubscriberReadyStateDeviceLocked: *pSIMStatus = SIM_PIN; break; + default: *pSIMStatus = SIM_ABSENT; break; + } + +exit: + return retval; +} + +static int requestRegistrationState(UCHAR *pPSAttachedState) { + int retval; + + *pPSAttachedState = 0; + retval = mbim_register_state_query(); + if (retval) + goto exit; + mbim_update_state(); + + switch (RegisterState) { + case MBIMRegisterStateUnknown: *pPSAttachedState = 0; break; + case MBIMRegisterStateDeregistered: *pPSAttachedState = 0; break; + case MBIMRegisterStateSearching: *pPSAttachedState = 0; break; + case MBIMRegisterStateHome: *pPSAttachedState = 1; break; + case MBIMRegisterStateRoaming: *pPSAttachedState = 1; break; + case MBIMRegisterStatePartner: *pPSAttachedState = 0; break; + case MBIMRegisterStateDenied: *pPSAttachedState = 0; break; + default: *pPSAttachedState = 0; break; + } + + if (*pPSAttachedState == 0) + goto exit; + + retval = mbim_packet_service_query(); + if (retval) + goto exit; + + switch (PacketServiceState) { + case MBIMPacketServiceStateUnknown: *pPSAttachedState = 0; break; + case MBIMPacketServiceStateAttaching: *pPSAttachedState = 0; break; + case MBIMPacketServiceStateAttached: *pPSAttachedState = 1; break; + case MBIMPacketServiceStateDetaching: *pPSAttachedState = 0; break; + case MBIMPacketServiceStateDetached: *pPSAttachedState = 0; break; + default: *pPSAttachedState = 0; break; + } + + if (*pPSAttachedState == 0) + mbim_packet_service_set(MBIMPacketServiceActionAttach); + +exit: + return retval; +} + +static int requestSetupDataCall(PROFILE_T *profile, int curIpFamily) { + int retval; + + (void)curIpFamily; + + if (profile->apn) + mbim_apn = profile->apn; + if (profile->user) + mbim_user = profile->user; + if (profile->password) + mbim_passwd = profile->password; + if (profile->auth) + mbim_auth = profile->auth; + if (profile->enable_ipv4) + mbim_iptype = MBIMContextIPTypeIPv4; + if (profile->enable_ipv6) + mbim_iptype = MBIMContextIPTypeIPv6; + if (profile->enable_ipv4 && profile->enable_ipv6) + mbim_iptype = MBIMContextIPTypeIPv4AndIPv6; + + retval = mbim_set_connect(1, mbim_sessionID); + if (retval) + goto exit; + +exit: + return retval; +} + +static int requestQueryDataCall(UCHAR *pConnectionStatus, int curIpFamily) { + int retval; + + (void)curIpFamily; + + *pConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; + + retval = mbim_query_connect(mbim_sessionID); + if (retval) + goto exit; + + switch(ActivationState) { + case MBIMActivationStateUnknown: *pConnectionStatus = QWDS_PKT_DATA_UNKNOW; break; + case MBIMActivationStateActivated: *pConnectionStatus = QWDS_PKT_DATA_CONNECTED; break; + case MBIMActivationStateActivating: *pConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; break; + case MBIMActivationStateDeactivated: *pConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; break; + case MBIMActivationStateDeactivating: *pConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; break; + default: *pConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; break; + } + +exit: + return retval; +} + +static int requestDeactivateDefaultPDP(PROFILE_T *profile, int curIpFamily) { + int retval; + + (void)profile; + (void)curIpFamily; + + retval = mbim_set_connect(0, mbim_sessionID); + if (retval) + goto exit; + +exit: + return retval; +} + +static int requestGetIPAddress(PROFILE_T *profile, int curIpFamily) { + int retval; + + (void)curIpFamily; + retval = mbim_ip_config(profile, mbim_sessionID); + if (retval) + goto exit; + +exit: + return retval; +} + +#ifdef CONFIG_CELLINFO +static int requestGetCellInfoList(void) { + if (qmi_over_mbim_nas) { + if (qmi_request_ops.requestGetCellInfoList) + return qmi_request_ops.requestGetCellInfoList(); + } + + return 0; +} +#endif + +const struct request_ops mbim_request_ops = { + .requestBaseBandVersion = requestBaseBandVersion, + .requestGetSIMStatus = requestGetSIMStatus, + .requestRegistrationState = requestRegistrationState, + .requestSetupDataCall = requestSetupDataCall, + .requestQueryDataCall = requestQueryDataCall, + .requestDeactivateDefaultPDP = requestDeactivateDefaultPDP, + .requestGetIPAddress = requestGetIPAddress, +#ifdef CONFIG_CELLINFO + .requestGetCellInfoList = requestGetCellInfoList, +#endif +}; + +int qmi_over_mbim_qmidev_send(PQCQMIMSG pQMI) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_COMMAND_DONE_T *pCmdDone = NULL; + int err; + size_t len = le16toh(pQMI->QMIHdr.Length) + 1; + + if (pQMI->QMIHdr.QMIType != QMUX_TYPE_CTL) { + if (pQMI->QMIHdr.QMIType == QMUX_TYPE_NAS) + pQMI->QMIHdr.ClientId = qmi_over_mbim_nas; + + if (pQMI->QMIHdr.ClientId == 0) { + dbg_time("QMIType %d has no clientID", pQMI->QMIHdr.QMIType); + return -ENODEV; + } + } + + pRequest = compose_qmi_over_mbim_command(1, MBIM_CID_CMD_TYPE_SET, pQMI, len); + err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout); + mbim_check_err(err, pRequest, pCmdDone); + + err = -1; + len = le32toh(pCmdDone->InformationBufferLength); + if (len) { + if (write(qmi_over_mbim_sk[0], pCmdDone->InformationBuffer, len) == (long)len) { + err = 0; + }; + } + + mbim_free(pRequest); mbim_free(pCmdDone); + return err; +} diff --git a/application/quectel_CM_5G/src/qendian.h b/application/quectel_CM_5G/src/qendian.h new file mode 100644 index 0000000..5e99d5f --- /dev/null +++ b/application/quectel_CM_5G/src/qendian.h @@ -0,0 +1,52 @@ +#ifndef __QUECTEL_ENDIAN_H__ +#define __QUECTEL_ENDIAN_H__ +#include + +#ifndef htole32 +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define htole16(x) (uint16_t)(x) +#define le16toh(x) (uint16_t)(x) +#define letoh16(x) (uint16_t)(x) +#define htole32(x) (uint32_t)(x) +#define le32toh(x) (uint32_t)(x) +#define letoh32(x) (uint32_t)(x) +#define htole64(x) (uint64_t)(x) +#define le64toh(x) (uint64_t)(x) +#define letoh64(x) (uint64_t)(x) +#else +static __inline uint16_t __bswap16(uint16_t __x) { + return (__x<<8) | (__x>>8); +} + +static __inline uint32_t __bswap32(uint32_t __x) { + return (__x>>24) | (__x>>8&0xff00) | (__x<<8&0xff0000) | (__x<<24); +} + +static __inline uint64_t __bswap64(uint64_t __x) { + return (__bswap32(__x)+0ULL<<32) | (__bswap32(__x>>32)); +} + +#define htole16(x) __bswap16(x) +#define le16toh(x) __bswap16(x) +#define letoh16(x) __bswap16(x) +#define htole32(x) __bswap32(x) +#define le32toh(x) __bswap32(x) +#define letoh32(x) __bswap32(x) +#define htole64(x) __bswap64(x) +#define le64toh(x) __bswap64(x) +#define letoh64(x) __bswap64(x) +#endif +#endif + +#define le16_to_cpu(x) le16toh((uint16_t)(x)) +#define le32_to_cpu(x) le32toh((uint32_t)(x)) +#define le64_to_cpu(x) le64toh((uint64_t)(x)) +#define cpu_to_le16(x) htole16((uint16_t)(x)) +#define cpu_to_le32(x) htole32((uint32_t)(x)) +#define cpu_to_le64(x) htole64((uint64_t)(x)) + +static __inline uint32_t ql_swap32(uint32_t __x) { + return (__x>>24) | (__x>>8&0xff00) | (__x<<8&0xff0000) | (__x<<24); +} +#endif //__QUECTEL_ENDIAN_H__ + diff --git a/application/quectel_CM_5G/src/qlist.h b/application/quectel_CM_5G/src/qlist.h new file mode 100644 index 0000000..c7f95cf --- /dev/null +++ b/application/quectel_CM_5G/src/qlist.h @@ -0,0 +1,38 @@ +#ifndef __QUECTEL_LIST_H__ +#define __QUECTEL_LIST_H__ +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) + +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; +} +#endif \ No newline at end of file diff --git a/application/quectel_CM_5G/src/qmap_bridge_mode.c b/application/quectel_CM_5G/src/qmap_bridge_mode.c new file mode 100644 index 0000000..1012bf0 --- /dev/null +++ b/application/quectel_CM_5G/src/qmap_bridge_mode.c @@ -0,0 +1,402 @@ +/****************************************************************************** + @file qmap_bridge_mode.c + @brief Connectivity bridge manager. + + DESCRIPTION + Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ +#include "QMIThread.h" + +static size_t ql_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=%zu, buf_size=%zu: (%s)", filename, n, size, strerror(errno)); + } + fclose(fp); + } + + return n > 0 ? n : 0; +} + +static size_t ql_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=%zu, buf_size=%zu: (%s)", filename, n, size, strerror(errno)); + } + fclose(fp); + } + + return n > 0 ? n : 0; +} + +int ql_bridge_mode_detect(PROFILE_T *profile) { + const char *ifname = profile->qmapnet_adapter[0] ? 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 = 0; + + 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_ipv4, R_OK)) { + if (errno != ENOENT) { + dbg_time("fail to access %s, errno: %d (%s)", bridge_mode, errno, strerror(errno)); + return 0; + } + + 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, R_OK)) { + if (errno != ENOENT) { + dbg_time("fail to access %s, errno: %d (%s)", bridge_mode, errno, strerror(errno)); + } + return 0; + } + } + + n = ql_fread(bridge_mode, buf, sizeof(buf)); + if (n > 0) { + in_bridge = (buf[0] != '0'); + } + if (!in_bridge) + 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); + ql_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); + ql_fwrite(bridge_ipv4, ipv4, strlen(ipv4)); + } + + return in_bridge; +} + +int ql_enable_qmi_wwan_rawip_mode(PROFILE_T *profile) { + char filename[256]; + char buf[4]; + size_t n; + FILE *fp; + + if (!qmidev_is_qmiwwan(profile->qmichannel)) + return 0; + + snprintf(filename, sizeof(filename), "/sys/class/net/%s/qmi/rawip", profile->usbnet_adapter); + n = ql_fread(filename, buf, sizeof(buf)); + + if (n == 0) + return 0; + + if (buf[0] == '1' || buf[0] == 'Y') + return 0; + + fp = fopen(filename , "w"); + if (fp == NULL) { + dbg_time("Fail to fopen(%s, \"w\"), errno: %d (%s)", filename, errno, strerror(errno)); + return 1; + } + + buf[0] = 'Y'; + n = fwrite(buf, 1, 1, fp); + if (n != 1) { + dbg_time("Fail to fwrite(%s), errno: %d (%s)", filename, errno, strerror(errno)); + fclose(fp); + return 1; + } + fclose(fp); + + return 0; +} + +int ql_driver_type_detect(PROFILE_T *profile) { + if (qmidev_is_gobinet(profile->qmichannel)) { + profile->qmi_ops = &gobi_qmidev_ops; + } + else { + profile->qmi_ops = &qmiwwan_qmidev_ops; + } + qmidev_send = profile->qmi_ops->send; + + return 0; +} + +void ql_set_driver_bridge_mode(PROFILE_T *profile) { + char enable[16]; + char filename[256]; + + if(profile->qmap_mode) + snprintf(filename, sizeof(filename), "/sys/class/net/%s/bridge_mode", profile->qmapnet_adapter); + else + snprintf(filename, sizeof(filename), "/sys/class/net/%s/bridge_mode", profile->usbnet_adapter); + snprintf(enable, sizeof(enable), "%02d\n", profile->enable_bridge); + ql_fwrite(filename, enable, sizeof(enable)); +} + +static int ql_qmi_qmap_mode_detect(PROFILE_T *profile) { + char buf[128]; + int n; + 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--; + strncpy(profile->driver_name, &pl->filename[n+1], sizeof(profile->driver_name) - 1); + + ql_get_driver_rmnet_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->muxid == 0)? profile->pdp - 1 : profile->muxid - 0x81; + + if (profile->qmap_mode == 1) + offset_id = 0; + profile->muxid = profile->rmnet_info.mux_id[offset_id]; + strncpy(profile->qmapnet_adapter, profile->rmnet_info.ifname[offset_id], sizeof(profile->qmapnet_adapter) - 1); + profile->qmap_size = profile->rmnet_info.rx_urb_size; + profile->qmap_version = profile->rmnet_info.qmap_version; + } + + goto _out; + } + + snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_mode", 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_mode", 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_mode", 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)) { + n = ql_fread(pl->filename, buf, sizeof(buf)); + if (n > 0) { + profile->qmap_mode = atoi(buf); + + if (profile->qmap_mode > 1) { + if(!profile->muxid) + profile->muxid = profile->pdp + 0x80; //muxis is 0x8X for PDN-X + snprintf(profile->qmapnet_adapter, sizeof(profile->qmapnet_adapter), + "%.16s.%d", profile->usbnet_adapter, profile->muxid - 0x80); + } if (profile->qmap_mode == 1) { + profile->muxid = 0x81; + strncpy(profile->qmapnet_adapter, profile->usbnet_adapter, sizeof(profile->qmapnet_adapter)); + } + } + } + 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 = ql_fread(pl->filename, buf, sizeof(buf)); + if (n >= 5) { + dbg_time("If use QMAP by /sys/class/net/%s/qmi/add_mux", profile->usbnet_adapter); + #if 1 + dbg_time("Please set mtu of wwan0 >= max dl qmap packet size"); + #else + dbg_time("File:%s Line:%d Please make sure add next patch to qmi_wwan.c", __func__, __LINE__); + /* + diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c + index 74bebbd..db8a777 100644 + --- a/drivers/net/usb/qmi_wwan.c + +++ b/drivers/net/usb/qmi_wwan.c + @@ -379,6 +379,24 @@ static ssize_t add_mux_store(struct device *d, struct device_attribute *attr, c + if (!ret) { + info->flags |= QMI_WWAN_FLAG_MUX; + ret = len; + +#if 1 //Add by Quectel + + if (le16_to_cpu(dev->udev->descriptor.idVendor) == 0x2c7c) { + + int idProduct = le16_to_cpu(dev->udev->descriptor.idProduct); + + + + if (idProduct == 0x0121 || idProduct == 0x0125 || idProduct == 0x0435) //MDM9x07 + + dev->rx_urb_size = 4*1024; + + else if (idProduct == 0x0306) //MDM9x40 + + dev->rx_urb_size = 16*1024; + + else if (idProduct == 0x0512) //SDX20 + + dev->rx_urb_size = 32*1024; + + else if (idProduct == 0x0620) //SDX24 + + dev->rx_urb_size = 32*1024; + + else if (idProduct == 0x0800) //SDX55 + + dev->rx_urb_size = 32*1024; + + else + + dev->rx_urb_size = 32*1024; + + } + +#endif + } + err: + rtnl_unlock(); + */ + #endif + profile->qmap_mode = n/5; //0x11\n0x22\n0x33\n + if (profile->qmap_mode > 1) { + //PDN-X map to qmimux-X + if(!profile->muxid) { + profile->muxid = (buf[5*(profile->pdp - 1) + 2] - '0')*16 + (buf[5*(profile->pdp - 1) + 3] - '0'); + snprintf(profile->qmapnet_adapter, sizeof(profile->qmapnet_adapter), "qmimux%d", profile->pdp - 1); + } else { + profile->muxid = (buf[5*(profile->muxid - 0x81) + 2] - '0')*16 + (buf[5*(profile->muxid - 0x81) + 3] - '0'); + snprintf(profile->qmapnet_adapter, sizeof(profile->qmapnet_adapter), "qmimux%d", profile->muxid - 0x81); + } + } else if (profile->qmap_mode == 1) { + profile->muxid = (buf[5*0 + 2] - '0')*16 + (buf[5*0 + 3] - '0'); + snprintf(profile->qmapnet_adapter, sizeof(profile->qmapnet_adapter), + "qmimux%d", 0); + } + } + } + +_out: + if (profile->qmap_mode) { + if (profile->qmap_size == 0) { + 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 = ql_fread(pl->filename, buf, sizeof(buf)); + if (n > 0) { + profile->qmap_size = atoi(buf); + } + } + } + + if (profile->qmap_version == 0) { + profile->qmap_version = WDA_DL_DATA_AGG_QMAP_ENABLED; + } + + dbg_time("qmap_mode = %d, qmap_version = %d, qmap_size = %d, muxid = 0x%02x, qmap_netcard = %s", + profile->qmap_mode, profile->qmap_version, profile->qmap_size, profile->muxid, profile->qmapnet_adapter); + } + ql_set_driver_bridge_mode(profile); + free(pl); + + return 0; +} + +static int ql_mbim_usb_vlan_mode_detect(PROFILE_T *profile) { + char tmp[128]; + + snprintf(tmp, sizeof(tmp), "/sys/class/net/%s.%d", profile->usbnet_adapter, profile->pdp); + if (!access(tmp, F_OK)) { + profile->qmap_mode = 4; + profile->muxid = profile->pdp; + no_trunc_strncpy(profile->qmapnet_adapter, tmp + strlen("/sys/class/net/"), sizeof(profile->qmapnet_adapter) - 1); + + dbg_time("mbim_qmap_mode = %d, vlan_id = 0x%02x, qmap_netcard = %s", + profile->qmap_mode, profile->muxid, profile->qmapnet_adapter); + } + + return 0; +} + +static int ql_mbim_mhi_qmap_mode_detect(PROFILE_T *profile) { + ql_get_driver_rmnet_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->pdp; + strcpy(profile->qmapnet_adapter, profile->rmnet_info.ifname[offset_id]); + profile->qmap_size = profile->rmnet_info.rx_urb_size; + profile->qmap_version = profile->rmnet_info.qmap_version; + + dbg_time("mbim_qmap_mode = %d, vlan_id = 0x%02x, qmap_netcard = %s", + profile->qmap_mode, profile->muxid, profile->qmapnet_adapter); + } + + goto _out; + } + +_out: + return 0; +} + +int ql_qmap_mode_detect(PROFILE_T *profile) { + if (profile->software_interface == SOFTWARE_MBIM) { + if (profile->hardware_interface == HARDWARE_USB) + return ql_mbim_usb_vlan_mode_detect(profile); + else if (profile->hardware_interface == HARDWARE_PCIE) + return ql_mbim_mhi_qmap_mode_detect(profile); + } else if (profile->software_interface == SOFTWARE_QMI) { + return ql_qmi_qmap_mode_detect(profile); + } +#ifdef CONFIG_QRTR + else if(profile->software_interface == SOFTWARE_QRTR) { + char tmp[128]; + + profile->qmap_mode = 4; + profile->qmap_version = WDA_DL_DATA_AGG_QMAP_V5_ENABLED; + profile->qmap_size = 31*1024; + profile->muxid = 0x80 | profile->pdp; + snprintf(profile->qmapnet_adapter, sizeof(profile->qmapnet_adapter), "rmnet_data%d", profile->muxid&0xF); + + snprintf(tmp, sizeof(tmp), "/sys/class/net/%s", profile->qmapnet_adapter); + if (access(tmp, F_OK)) { + rtrmnet_ctl_create_vnd(profile->usbnet_adapter, profile->qmapnet_adapter, + profile->muxid, profile->qmap_version, 11, 4096); + } + } +#endif + return 0; +} diff --git a/application/quectel_CM_5G/src/qrtr.c b/application/quectel_CM_5G/src/qrtr.c new file mode 100644 index 0000000..4ddf4a0 --- /dev/null +++ b/application/quectel_CM_5G/src/qrtr.c @@ -0,0 +1,657 @@ +//https://github.com/andersson/qrtr +/****************************************************************************** + @file QrtrCM.c + @brief GobiNet driver. + + DESCRIPTION + Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ +#include +#include +#include +#include +#include +#include "QMIThread.h" + +typedef struct { + uint32_t service; + uint32_t version; + uint32_t instance; + uint32_t node; + uint32_t port; +} QrtrService; + +#define QRTR_MAX (QMUX_TYPE_WDS_ADMIN + 1) +static QrtrService service_list[QRTR_MAX]; +static int qmiclientId[QRTR_MAX]; +static int get_client(UCHAR QMIType); +static uint32_t node_modem = 3; //IPQ ~ 3, QCM ~ 0 + +#ifdef USE_LINUX_MSM_IPC +#include + +struct xport_ipc_router_server_addr { + uint32_t service; + uint32_t instance; + uint32_t node_id; + uint32_t port_id; +}; + +union ctl_msg { + uint32_t cmd; + struct { + uint32_t cmd; + uint32_t service; + uint32_t instance; + uint32_t node_id; + uint32_t port_id; + } srv; + struct { + uint32_t cmd; + uint32_t node_id; + uint32_t port_id; + } cli; + }; +#define CTL_CMD_NEW_SERVER 4 +#define CTL_CMD_REMOVE_SERVER 5 + +#define VERSION_MASK 0xff +#define GET_VERSION(x) (x & 0xff) +#define GET_XPORT_SVC_INSTANCE(x) GET_VERSION(x) +#define GET_INSTANCE(x) ((x & 0xff00) >> 8) + +static int msm_ipc_socket(const char *name) +{ + int sock; + int flags; + + sock = socket(AF_MSM_IPC, SOCK_DGRAM, 0); + if (sock < 0) { + dbg_time("%s(%s) errno: %d (%s)\n", __func__, name, errno, strerror(errno)); + return -1; + } + + fcntl(sock, F_SETFD, FD_CLOEXEC); + flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); + + return sock; +} + +static uint32_t xport_lookup +( + int lookup_sock_fd, + uint32_t service_id, + uint32_t version +) +{ + uint32_t num_servers_found = 0; + uint32_t num_entries_to_fill = 4; + struct server_lookup_args *lookup_arg; + int i; + + lookup_arg = (struct server_lookup_args *)malloc(sizeof(*lookup_arg) + + (num_entries_to_fill * sizeof(struct msm_ipc_server_info))); + if (!lookup_arg) + { + dbg_time("%s: Malloc failed\n", __func__); + return 0; + } + + lookup_arg->port_name.service = service_id; + lookup_arg->port_name.instance = GET_XPORT_SVC_INSTANCE(version); + lookup_arg->num_entries_in_array = num_entries_to_fill; + lookup_arg->lookup_mask = VERSION_MASK; + lookup_arg->num_entries_found = 0; + if (ioctl(lookup_sock_fd, IPC_ROUTER_IOCTL_LOOKUP_SERVER, lookup_arg) < 0) + { + dbg_time("%s: Lookup failed for %08x: %08x\n", __func__, service_id, version); + free(lookup_arg); + return 0; + } + + dbg_time("%s: num_entries_found %d for type=%d instance=%d", __func__, + lookup_arg->num_entries_found, service_id, version); + num_servers_found = 0; + for (i = 0; ((i < (int)num_entries_to_fill) && (i < lookup_arg->num_entries_found)); i++) + { + QrtrService service_info[1]; + + if (lookup_arg->srv_info[i].node_id != node_modem) + continue; + num_servers_found++; + + service_info[0].service = lookup_arg->srv_info[i].service; + service_info[0].version = GET_VERSION(lookup_arg->srv_info[i].instance); + service_info[0].instance = GET_INSTANCE(lookup_arg->srv_info[i].instance); + service_info[0].node = lookup_arg->srv_info[i].node_id; + service_info[0].port = lookup_arg->srv_info[i].port_id; + + service_list[service_id] = service_info[0]; + qmiclientId[service_id] = get_client(service_id); + } + + free(lookup_arg); + return num_servers_found; +} + +static int xport_send(int sock, uint32_t node, uint32_t port, const void *data, unsigned int sz) +{ + struct sockaddr_msm_ipc addr = {}; + int rc; + + addr.family = AF_MSM_IPC; + addr.address.addrtype = MSM_IPC_ADDR_ID; + addr.address.addr.port_addr.node_id = node; + addr.address.addr.port_addr.port_id = port; + + rc = sendto(sock, data, sz, MSG_DONTWAIT, (void *)&addr, sizeof(addr)); + if (rc < 0) { + dbg_time("xport_send errno: %d (%s)\n", errno, strerror(errno)); + return -1; + } + + return 0; +} + +static int xport_recv(int sock, void *data, unsigned int sz, uint32_t *node, uint32_t *port) +{ + struct sockaddr_msm_ipc addr = {}; + socklen_t addr_size = sizeof(struct sockaddr_msm_ipc); + int rc; + + rc = recvfrom(sock, data, sz, MSG_DONTWAIT, (void *)&addr, &addr_size); + if (rc < 0) { + dbg_time("xport_recv errno: %d (%s)\n", errno, strerror(errno)); + } + else if (addr.address.addrtype != MSM_IPC_ADDR_ID) { + dbg_time("xport_recv addrtype is NOT MSM_IPC_ADDR_ID\n"); + rc = -1; + } + + *node = addr.address.addr.port_addr.node_id; + *port = addr.address.addr.port_addr.port_id; + return rc; +} +#define qmi_recv xport_recv + +static int xport_ctrl_init(void) +{ + int ctrl_sock; + int rc; + uint32_t instance = 1; //modem + uint32_t version; + + ctrl_sock = msm_ipc_socket("ctrl_port"); + if (ctrl_sock == -1) + return -1; + + rc = ioctl(ctrl_sock, IPC_ROUTER_IOCTL_GET_VERSION, &version); + if (rc < 0) { + dbg_time("%s: failed to get ipc version\n", __func__); + goto init_close_ctrl_fd; + } + dbg_time("%s ipc_version = %d", __func__, version); + + rc = ioctl(ctrl_sock, IPC_ROUTER_IOCTL_BIND_CONTROL_PORT, NULL); + if (rc < 0) { + dbg_time("%s: failed to bind as control port\n", __func__); + goto init_close_ctrl_fd; + } + + //cat /sys/kernel/debug/msm_ipc_router/dump_servers + rc = 0; + rc += xport_lookup(ctrl_sock, QMUX_TYPE_WDS, instance); + if (service_list[QMUX_TYPE_WDS].port) { + qmiclientId[QMUX_TYPE_WDS_IPV6] = get_client(QMUX_TYPE_WDS); + } + rc += xport_lookup(ctrl_sock, QMUX_TYPE_NAS, instance); + rc += xport_lookup(ctrl_sock, QMUX_TYPE_UIM, instance); + rc += xport_lookup(ctrl_sock, QMUX_TYPE_DMS, instance); + rc += xport_lookup(ctrl_sock, QMUX_TYPE_WDS_ADMIN, instance); + + if (rc == 0) { + dbg_time("%s: failed to lookup qmi service\n", __func__); + goto init_close_ctrl_fd; + } + + return ctrl_sock; + +init_close_ctrl_fd: + close(ctrl_sock); + return -1; +} + +static void handle_ctrl_pkt(int sock) { + union ctl_msg pkt; + uint32_t type; + int rc; + + rc = recvfrom(sock, &pkt, sizeof(pkt), 0, NULL, NULL); + if (rc < 0) + return; + + type = le32toh(pkt.cmd); + if (CTL_CMD_NEW_SERVER == type || CTL_CMD_REMOVE_SERVER == type) { + QrtrService s; + + s.service = le32toh(pkt.srv.service); + s.version = le32toh(pkt.srv.instance) & 0xff; + s.instance = le32toh(pkt.srv.instance) >> 8; + s.node = le32toh(pkt.srv.node_id); + s.port = le32toh(pkt.srv.port_id); + + if (debug_qmi) + dbg_time ("[qrtr] %s server on %u:%u -> service %u, version %u, instance %u", + CTL_CMD_NEW_SERVER == type ? "add" : "remove", + s.node, s.port, s.service, s.version, s.instance); + + if (CTL_CMD_NEW_SERVER == type) { + if (s.service < QRTR_MAX) { + service_list[s.service] = s; + } + } + else if (CTL_CMD_REMOVE_SERVER == type) { + if (s.service < QRTR_MAX) { + memset(&service_list[s.service], 0, sizeof(QrtrService)); + } + } + } +} +#else +#include +#include "qrtr.h" +#endif + +static int qrtr_socket(void) +{ + struct sockaddr_qrtr sq; + socklen_t sl = sizeof(sq); + int sock; + int rc; + + sock = socket(AF_QIPCRTR, SOCK_DGRAM, 0); + if (sock < 0) { + dbg_time("qrtr_socket errno: %d (%s)\n", errno, strerror(errno)); + return -1; + } + + rc = getsockname(sock, (void *)&sq, &sl); + if (rc || sq.sq_family != AF_QIPCRTR || sl != sizeof(sq)) { + dbg_time("getsockname: %d (%s)\n", errno, strerror(errno)); + close(sock); + return -1; + } + + return sock; +} + +static int qrtr_send(int sock, uint32_t node, uint32_t port, const void *data, unsigned int sz) +{ + struct sockaddr_qrtr sq = {}; + int rc; + + sq.sq_family = AF_QIPCRTR; + sq.sq_node = node; + sq.sq_port = port; + + rc = sendto(sock, data, sz, MSG_DONTWAIT, (void *)&sq, sizeof(sq)); + if (rc < 0) { + dbg_time("sendto errno: %d (%s)\n", errno, strerror(errno)); + return -1; + } + + return 0; +} + +static int qrtr_recv(int sock, void *data, unsigned int sz, uint32_t *node, uint32_t *port) +{ + struct sockaddr_qrtr sq = {}; + socklen_t sl = sizeof(sq); + int rc; + + rc = recvfrom(sock, data, sz, MSG_DONTWAIT, (void *)&sq, &sl); + if (rc < 0) { + dbg_time("qrtr_recv errno: %d (%s)\n", errno, strerror(errno)); + } + + *node = sq.sq_node; + *port = sq.sq_port; + return rc; + } +#define qmi_recv qrtr_recv + +static int qrtr_ctrl_init(void) { + int sock; + int rc; + struct qrtr_ctrl_pkt pkt; + struct sockaddr_qrtr sq; + socklen_t sl = sizeof(sq); + + sock = qrtr_socket(); + if (sock == -1) + return -1; + + memset(&pkt, 0, sizeof(pkt)); + pkt.cmd = htole32(QRTR_TYPE_NEW_LOOKUP); + + getsockname(sock, (void *)&sq, &sl); + rc = qrtr_send(sock, sq.sq_node, QRTR_PORT_CTRL, &pkt, sizeof(pkt)); + if (rc == -1) { + dbg_time("qrtr_send errno: %d (%s)\n", errno, strerror(errno)); + close(sock); + return -1; + } + + return sock; +} + +static void handle_server_change(uint32_t type, struct qrtr_ctrl_pkt *ppkt) { + struct qrtr_ctrl_pkt pkt = *ppkt; + QrtrService s; + + s.service = le32toh(pkt.server.service); + s.version = le32toh(pkt.server.instance) & 0xff; + s.instance = le32toh(pkt.server.instance) >> 8; + s.node = le32toh(pkt.server.node); + s.port = le32toh(pkt.server.port); + + if (debug_qmi) + dbg_time ("[qrtr] %s server on %u:%u -> service %u, version %u, instance %u", + QRTR_TYPE_NEW_SERVER == type ? "add" : "remove", + s.node, s.port, s.service, s.version, s.instance); + + if (s.node != node_modem) + return; //we only care modem + + if (QRTR_TYPE_NEW_SERVER == type) { + if (s.service < QRTR_MAX) { + service_list[s.service] = s; + } + } + else if (QRTR_TYPE_DEL_SERVER == type) { + if (s.service < QRTR_MAX) { + memset(&service_list[s.service], 0, sizeof(QrtrService)); + } + } + } + +static void handle_ctrl_pkt(int sock) { + struct qrtr_ctrl_pkt pkt; + struct sockaddr_qrtr sq; + socklen_t sl = sizeof(sq); + uint32_t type; + int rc; + + rc = recvfrom(sock, &pkt, sizeof(pkt), 0, (void *)&sq, &sl); + if (rc < 0) + return; + + type = le32toh(pkt.cmd); + if (debug_qmi) + dbg_time("type %u, node %u, sq.port %x, len: %d", type, sq.sq_node, sq.sq_port, rc); + + if (sq.sq_port != QRTR_PORT_CTRL) + return; + + if (QRTR_TYPE_NEW_SERVER == type || QRTR_TYPE_DEL_SERVER == type) { + handle_server_change(type, &pkt); + } +} + +static int get_client(UCHAR QMIType) { + int ClientId; + QrtrService *s = &service_list[QMIType]; + + if (!s ->service) { + dbg_time("%s service: %d for QMIType: %d", __func__, s ->service, QMIType); + return -ENODEV; + } + +#ifdef USE_LINUX_MSM_IPC + ClientId = msm_ipc_socket("xport"); +#else + ClientId = qrtr_socket(); +#endif + if (ClientId == -1) { + return 0; + } + + 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 void handle_alloc_client(PROFILE_T *profile) { + int srv_list[] = {QMUX_TYPE_WDS, QMUX_TYPE_NAS, QMUX_TYPE_UIM, QMUX_TYPE_DMS, QMUX_TYPE_WDS_ADMIN}; + size_t i = 0, srv_ready = 0; + static int report = -1; + + if (report != -1) + return; + + for(i = 0; i < sizeof(srv_list)/sizeof(srv_list[0]); i++) { + int srv = srv_list[i]; + + if (service_list[srv].service) + srv_ready++; + else + continue; + + if (qmiclientId[srv] == 0) { + qmiclientId[srv] = get_client(srv); + + if (qmiclientId[srv] != 0) { + if (srv == QMUX_TYPE_WDS) { + qmiclientId[QMUX_TYPE_WDS_IPV6] = get_client(QMUX_TYPE_WDS); + } + else if (srv == QMUX_TYPE_WDS_ADMIN) { + profile->wda_client = qmiclientId[QMUX_TYPE_WDS_ADMIN]; + } + } + } + } + + if (srv_ready == sizeof(srv_list)/sizeof(srv_list[0])) { + if (qmiclientId[QMUX_TYPE_WDS]) { + qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED); + } else { + qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED); + } + report = 1; + } +} + +static int qmi_send(PQCQMIMSG pRequest) { + uint8_t QMIType = pRequest->QMIHdr.QMIType; + int sock; + QrtrService *s = &service_list[QMIType == QMUX_TYPE_WDS_IPV6 ? QMUX_TYPE_WDS: QMIType]; + sock = qmiclientId[QMIType]; + + pRequest->QMIHdr.ClientId = 0xaa; + if (!s ->service || !sock) { + dbg_time("%s service: %d, sock: %d for QMIType: %d", __func__, s ->service, sock, QMIType); + return -ENODEV; + } + +#ifdef USE_LINUX_MSM_IPC + return xport_send(sock, s->node, s->port, &pRequest->MUXMsg, + le16_to_cpu(pRequest->QMIHdr.Length) + 1 - sizeof(QCQMI_HDR)); +#else + return qrtr_send(sock, s->node, s->port, &pRequest->MUXMsg, + le16_to_cpu(pRequest->QMIHdr.Length) + 1 - sizeof(QCQMI_HDR)); +#endif +} + +static int qmi_deinit(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 * qmi_read(void *pData) { + PROFILE_T *profile = (PROFILE_T *)pData; + int ctrl_sock; + int wait_for_request_quit = 0; + +#ifdef USE_LINUX_MSM_IPC + ctrl_sock = xport_ctrl_init(); + if (ctrl_sock != -1) + qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED); +#else + ctrl_sock = qrtr_ctrl_init(); +#endif + + if (ctrl_sock == -1) + goto _quit; + + while (1) { + struct pollfd pollfds[16] = {{qmidevice_control_fd[1], POLLIN, 0}, {ctrl_sock, POLLIN, 0}}; + int ne, ret, nevents = 2; + 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 _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 _quit; + break; + case SIG_EVENT_STOP: + wait_for_request_quit = 1; + break; + default: + break; + } + } + } + else if (fd == ctrl_sock) { + handle_ctrl_pkt(ctrl_sock); + handle_alloc_client(profile); + } + else + { + PQCQMIMSG pResponse = (PQCQMIMSG)cm_recv_buf; + int rc; + uint32_t sq_node = 0; + uint32_t sq_port = 0; + + rc = qmi_recv(fd, &pResponse->MUXMsg, sizeof(cm_recv_buf) - sizeof(QCQMI_HDR), &sq_node, &sq_port); + if (debug_qmi) + dbg_time("fd %d, node %u, port %x, len: %d", fd, sq_node, sq_port, rc); + + if (rc <= 0) + { + dbg_time("%s read=%d errno: %d (%s)", __func__, rc, errno, strerror(errno)); + break; + } + + for (i = 0; i < sizeof(qmiclientId)/sizeof(qmiclientId[0]); i++) + { + if (qmiclientId[i] == fd) + { + pResponse->QMIHdr.QMIType = i; + + if (service_list[i].node != sq_node || service_list[i].port != sq_port) { + continue; + } + } + } + + pResponse->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI; + pResponse->QMIHdr.Length = cpu_to_le16(rc + sizeof(QCQMI_HDR) - 1); + pResponse->QMIHdr.CtlFlags = 0x00; + pResponse->QMIHdr.ClientId = 0xaa; + + QmiThreadRecvQMI(pResponse); + } + } + } + +_quit: + qmi_deinit(); + close(ctrl_sock); + 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; +} + +const struct qmi_device_ops qrtr_qmidev_ops = { + .deinit = qmi_deinit, + .send = qmi_send, + .read = qmi_read, +}; + diff --git a/application/quectel_CM_5G/src/qrtr.h b/application/quectel_CM_5G/src/qrtr.h new file mode 100644 index 0000000..d1727a8 --- /dev/null +++ b/application/quectel_CM_5G/src/qrtr.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_QRTR_H +#define _LINUX_QRTR_H + +#include +#include + +#ifndef AF_QIPCRTR +#define AF_QIPCRTR 42 +#endif + +#define QRTR_NODE_BCAST 0xffffffffu +#define QRTR_PORT_CTRL 0xfffffffeu + +struct sockaddr_qrtr { + __kernel_sa_family_t sq_family; + __u32 sq_node; + __u32 sq_port; +}; + +enum qrtr_pkt_type { + QRTR_TYPE_DATA = 1, + QRTR_TYPE_HELLO = 2, + QRTR_TYPE_BYE = 3, + QRTR_TYPE_NEW_SERVER = 4, + QRTR_TYPE_DEL_SERVER = 5, + QRTR_TYPE_DEL_CLIENT = 6, + QRTR_TYPE_RESUME_TX = 7, + QRTR_TYPE_EXIT = 8, + QRTR_TYPE_PING = 9, + QRTR_TYPE_NEW_LOOKUP = 10, + QRTR_TYPE_DEL_LOOKUP = 11, +}; + +#define QRTR_TYPE_DEL_PROC 13 + +struct qrtr_ctrl_pkt { + __le32 cmd; + + union { + struct { + __le32 service; + __le32 instance; + __le32 node; + __le32 port; + } server; + + struct { + __le32 node; + __le32 port; + } client; + + struct { + __le32 rsvd; + __le32 node; + } proc; + + }; +} __attribute__ ((packed)); + +#define QRTR_PROTO_VER_1 1 + +struct qrtr_hdr_v1 { + __le32 version; + __le32 type; + __le32 src_node_id; + __le32 src_port_id; + __le32 confirm_rx; + __le32 size; + __le32 dst_node_id; + __le32 dst_port_id; +} __attribute__ ((packed)); + +#endif /* _LINUX_QRTR_H */ diff --git a/application/quectel_CM_5G/src/quectel-CM b/application/quectel_CM_5G/src/quectel-CM new file mode 100644 index 0000000..b7da70a Binary files /dev/null and b/application/quectel_CM_5G/src/quectel-CM differ diff --git a/application/quectel_CM_5G/src/quectel-atc-proxy b/application/quectel_CM_5G/src/quectel-atc-proxy new file mode 100644 index 0000000..ad431a2 Binary files /dev/null and b/application/quectel_CM_5G/src/quectel-atc-proxy differ diff --git a/application/quectel_CM_5G/src/quectel-atc-proxy.c b/application/quectel_CM_5G/src/quectel-atc-proxy.c new file mode 100644 index 0000000..9f7b329 --- /dev/null +++ b/application/quectel_CM_5G/src/quectel-atc-proxy.c @@ -0,0 +1,506 @@ +/****************************************************************************** + @file quectel-atc-proxy.c + @brief atc proxy. + + DESCRIPTION + Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qlist.h" +#include "QMIThread.h" +#include "atchannel.h" +#include "at_tok.h" + +#define dprintf(fmt, args...) do { fprintf(stdout, "%s " fmt, get_time(), ##args); } 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 safe_free(__x) do { if (__x) { free((void *)__x); __x = NULL;}} while(0) +#define safe_at_response_free(__x) { if (__x) { at_response_free(__x); __x = NULL;}} + +#define at_response_error(err, p_response) \ + (err \ + || p_response == NULL \ + || p_response->finalResponse == NULL \ + || p_response->success == 0) + +typedef struct { + struct qlistnode qnode; + int ClientFd; + unsigned AccessTime; +} ATC_PROXY_CONNECTION; + +static int atc_proxy_quit = 0; +static pthread_t thread_id = 0; +static int atc_dev_fd = -1; +static int atc_proxy_server_fd = -1; +static struct qlistnode atc_proxy_connection; +static int verbose_debug = 0; +static int modem_reset_flag = 0; +static uint8_t atc_buf[4096]; +static int asr_style_atc = 0; +extern int asprintf(char **s, const char *fmt, ...); +static ATC_PROXY_CONNECTION *current_client_fd = NULL; + +static void dump_atc(uint8_t *pATC, int fd,int size, const char flag) +{ + if (verbose_debug) { + printf("%c %d:\n", flag, fd); + printf("%.*s\n", size, pATC); + } +} + +static int send_atc_to_client(int clientFd, uint8_t *pATC, int size) { + struct pollfd pollfds[]= {{clientFd, POLLOUT, 0}}; + ssize_t ret = 0; + + do { + ret = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), 5000); + } while (ret == -1 && errno == EINTR && atc_proxy_quit == 0); + + if (pollfds[0].revents & POLLOUT) { + ret = write(clientFd, pATC, size); + } + + return ret; +} + +static void onUnsolicited (const char *s, const char *sms_pdu) +{ + struct qlistnode *con_node; + int ret; + char buf[1024]; + + if(s) { + strcpy(buf, s); + strcat(buf, "\r\n"); + } + if(sms_pdu) { + strcat(buf, sms_pdu); + strcat(buf, "\r\n"); + } + + if(current_client_fd) { + ATC_PROXY_CONNECTION *atc_con = current_client_fd; + ret = send_atc_to_client(atc_con->ClientFd, (uint8_t *)buf, strlen(buf)); + if(ret < 0) { + close(atc_con->ClientFd); + qlist_remove(&atc_con->qnode); + free(atc_con); + } + return; + } + + qlist_for_each(con_node, &atc_proxy_connection) { + ATC_PROXY_CONNECTION *atc_con = qnode_to_item(con_node, ATC_PROXY_CONNECTION, qnode); + if(atc_con && atc_con->ClientFd > 0) { + ret = send_atc_to_client(atc_con->ClientFd, (uint8_t *)buf, strlen(buf)); + if(ret < 0) { + close(atc_con->ClientFd); + con_node = con_node->prev; + qlist_remove(&atc_con->qnode); + free(atc_con); + continue; + } + } + } +} + +static void onTimeout(void) { + dprintf("%s", __func__); + //TODO +} + +static void onClose(void) { + dprintf("%s", __func__); +} + +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("bind %s errno: %d (%s)\n", 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_atc_connection(int serverfd) { + int clientfd = -1; + unsigned char addr[128]; + socklen_t alen = sizeof(addr); + ATC_PROXY_CONNECTION *atc_con; + + clientfd = accept(serverfd, (struct sockaddr *)addr, &alen); + + atc_con = (ATC_PROXY_CONNECTION *)malloc(sizeof(ATC_PROXY_CONNECTION)); + if (atc_con) { + qlist_init(&atc_con->qnode); + atc_con->ClientFd= clientfd; + atc_con->AccessTime = 0; + dprintf("+++ ClientFd=%d\n", atc_con->ClientFd); + qlist_add_tail(&atc_proxy_connection, &atc_con->qnode); + } + + cfmakenoblock(clientfd); +} + +static void cleanup_atc_connection(int clientfd) { + struct qlistnode *con_node; + + qlist_for_each(con_node, &atc_proxy_connection) { + ATC_PROXY_CONNECTION *atc_con = qnode_to_item(con_node, ATC_PROXY_CONNECTION, qnode); + if (atc_con->ClientFd == clientfd) { + dprintf("--- ClientFd=%d\n", atc_con->ClientFd); + close(atc_con->ClientFd); + qlist_remove(&atc_con->qnode); + free(atc_con); + if (current_client_fd == atc_con) + current_client_fd = NULL; + break; + } + } +} + +static int atc_proxy_init(void) { + int err; + char *cmd; + ATResponse *p_response = NULL; + + err = at_handshake(); + if (err) { + dprintf("handshake fail, TODO ... "); + goto exit; + } + + at_send_command_singleline("AT+QCFG=\"usbnet\"", "+QCFG:", NULL); + at_send_command_multiline("AT+QNETDEVCTL=?", "+QNETDEVCTL:", NULL); + at_send_command("AT+CGREG=2", NULL); //GPRS Network Registration Status + at_send_command("AT+CEREG=2", NULL); //EPS Network Registration Status + at_send_command("AT+C5GREG=2", NULL); //5GS Network Registration Status + + at_send_command_singleline("AT+QNETDEVSTATUS=?", "+QNETDEVSTATUS:", &p_response); + if (at_response_error(err, p_response)) + asr_style_atc = 1; //EC200T/EC100Y do not support this AT, but RG801/RG500U support + + safe_at_response_free(p_response); + + err = at_send_command_singleline("AT+QCFG=\"NAT\"", "+QCFG:", &p_response); + if (!at_response_error(err, p_response)) { + int old_nat, new_nat = asr_style_atc ? 1 : 0; + + err = at_tok_scanf(p_response->p_intermediates->line, "%s%d", NULL, &old_nat); + if (err == 2 && old_nat != new_nat) { + safe_at_response_free(p_response); + asprintf(&cmd, "AT+QCFG=\"NAT\",%d", new_nat); + err = at_send_command(cmd, &p_response); + safe_free(cmd); + if (!at_response_error(err, p_response)) { + err = at_send_command("at+cfun=1,1",NULL); + } + safe_at_response_free(p_response); + } + err = 0; + } + safe_at_response_free(p_response); + +exit: + return err; +} + +static void atc_start_server(const char* servername) { + atc_proxy_server_fd = create_local_server(servername); + dprintf("atc_proxy_server_fd = %d\n", atc_proxy_server_fd); + if (atc_proxy_server_fd == -1) { + dprintf("Failed to create %s, errno: %d (%s)\n", servername, errno, strerror(errno)); + } +} + +static void atc_close_server(const char* servername) { + if (atc_proxy_server_fd != -1) { + dprintf("%s %s close server\n", __func__, servername); + close(atc_proxy_server_fd); + atc_proxy_server_fd = -1; + } +} + +static void *atc_proxy_loop(void *param) +{ + uint8_t *pATC = atc_buf; + struct qlistnode *con_node; + ATC_PROXY_CONNECTION *atc_con; + + (void)param; + dprintf("%s enter thread_id %p\n", __func__, (void *)pthread_self()); + + qlist_init(&atc_proxy_connection); + while (atc_dev_fd > 0 && atc_proxy_quit == 0) { + struct pollfd pollfds[2+64]; + int ne, ret, nevents = 0; + ssize_t nreads; + + pollfds[nevents].fd = atc_dev_fd; + pollfds[nevents].events = POLLIN; + pollfds[nevents].revents= 0; + nevents++; + + if (atc_proxy_server_fd > 0) { + pollfds[nevents].fd = atc_proxy_server_fd; + pollfds[nevents].events = POLLIN; + pollfds[nevents].revents= 0; + nevents++; + } + + qlist_for_each(con_node, &atc_proxy_connection) { + atc_con = qnode_to_item(con_node, ATC_PROXY_CONNECTION, qnode); + + pollfds[nevents].fd = atc_con->ClientFd; + pollfds[nevents].events = POLLIN; + pollfds[nevents].revents= 0; + nevents++; + + if (nevents == (sizeof(pollfds)/sizeof(pollfds[0]))) + break; + } + + do { + ret = poll(pollfds, nevents, (atc_proxy_server_fd > 0) ? -1 : 200); + } while (ret == -1 && errno == EINTR && atc_proxy_quit == 0); + + if (ret < 0) { + dprintf("%s poll=%d, errno: %d (%s)\n", __func__, ret, errno, strerror(errno)); + goto atc_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 == atc_dev_fd) { + goto atc_proxy_loop_exit; + } else if(fd == atc_proxy_server_fd) { + + } else { + cleanup_atc_connection(fd); + } + continue; + } + + if (!(pollfds[ne].revents & POLLIN)) { + continue; + } + + if (fd == atc_proxy_server_fd) { + accept_atc_connection(fd); + } + else if (fd == atc_dev_fd) { + usleep(10*1000); //let atchannel.c read at response. + if (modem_reset_flag) + goto atc_proxy_loop_exit; + } + else { + memset(atc_buf, 0x0, sizeof(atc_buf)); + nreads = read(fd, pATC, sizeof(atc_buf)); + if (nreads <= 0) { + dprintf("%s read=%d errno: %d (%s)", __func__, (int)nreads, errno, strerror(errno)); + cleanup_atc_connection(fd); + break; + } + + dump_atc(pATC, fd, nreads, 'r'); + qlist_for_each(con_node, &atc_proxy_connection) { + atc_con = qnode_to_item(con_node, ATC_PROXY_CONNECTION, qnode); + if (atc_con->ClientFd == pollfds[nevents].fd) { + current_client_fd = atc_con; + break; + } + } + at_send_command ((const char *)pATC, NULL); + current_client_fd = NULL; + } + } + } + +atc_proxy_loop_exit: + at_close(); + while (!qlist_empty(&atc_proxy_connection)) { + ATC_PROXY_CONNECTION *atc_con = qnode_to_item(qlist_head(&atc_proxy_connection), ATC_PROXY_CONNECTION, qnode); + cleanup_atc_connection(atc_con->ClientFd); + } + dprintf("%s exit, thread_id %p\n", __func__, (void *)pthread_self()); + + return NULL; +} + +static void usage(void) { + dprintf(" -d A valid atc device\n" + " default /dev/ttyUSB2, but /dev/ttyUSB2 may be invalid\n" + " -i netcard name\n" + " -v Will show all details\n"); +} + +static void sig_action(int sig) { + if (atc_proxy_quit == 0) { + atc_proxy_quit = 1; + if (thread_id) + pthread_kill(thread_id, sig); + } +} + +int main(int argc, char *argv[]) { + int opt; + char atc_dev[32+1] = "/dev/ttyUSB2"; + int retry_times = 0; + char servername[64] = {0}; + + optind = 1; + signal(SIGINT, sig_action); + + while ( -1 != (opt = getopt(argc, argv, "d:i:vh"))) { + switch (opt) { + case 'd': + strcpy(atc_dev, optarg); + break; + case 'v': + verbose_debug = 1; + break; + default: + usage(); + return 0; + } + } + + if (access(atc_dev, R_OK | W_OK)) { + dprintf("Fail to access %s, errno: %d (%s). break\n", atc_dev, errno, strerror(errno)); + return -1; + } + + sprintf(servername, "quectel-atc-proxy%c", atc_dev[strlen(atc_dev) - 1]); + dprintf("Will use atc-dev='%s', proxy='%s'\n", atc_dev, servername); + + while (atc_proxy_quit == 0) { + if (access(atc_dev, R_OK | W_OK)) { + dprintf("Fail to access %s, errno: %d (%s). continue\n", atc_dev, errno, strerror(errno)); + // wait device + sleep(3); + continue; + } + + atc_dev_fd = open(atc_dev, O_RDWR | O_NONBLOCK | O_NOCTTY); + if (atc_dev_fd == -1) { + dprintf("Failed to open %s, errno: %d (%s). break\n", atc_dev, errno, strerror(errno)); + return -1; + } + cfmakenoblock(atc_dev_fd); + if (at_open(atc_dev_fd, onUnsolicited, 1)) { + close(atc_dev_fd); + atc_dev_fd = -1; + } + at_set_on_timeout(onTimeout); + at_set_on_reader_closed(onClose); + + /* no atc_proxy_loop lives, create one */ + pthread_create(&thread_id, NULL, atc_proxy_loop, NULL); + /* try to redo init if failed, init function must be successfully */ + while (atc_proxy_init() != 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(atc_dev, F_OK|R_OK|W_OK)) + break; + } + retry_times = 0; + atc_start_server(servername); + if (atc_proxy_server_fd == -1) + pthread_cancel(thread_id); + pthread_join(thread_id, NULL); + + /* close local server at last */ + atc_close_server(servername); + close(atc_dev_fd); + /* DO RESTART IN 20s IF MODEM RESET ITSELF */ + if (modem_reset_flag) { + unsigned int time_to_wait = 20; + while (time_to_wait) { + time_to_wait = sleep(time_to_wait); + } + modem_reset_flag = 0; + } + } + + return 0; +} \ No newline at end of file diff --git a/application/quectel_CM_5G/src/quectel-mbim-proxy b/application/quectel_CM_5G/src/quectel-mbim-proxy new file mode 100644 index 0000000..1d3d795 Binary files /dev/null and b/application/quectel_CM_5G/src/quectel-mbim-proxy differ diff --git a/application/quectel_CM_5G/src/quectel-mbim-proxy.c b/application/quectel_CM_5G/src/quectel-mbim-proxy.c new file mode 100644 index 0000000..5cf6f41 --- /dev/null +++ b/application/quectel_CM_5G/src/quectel-mbim-proxy.c @@ -0,0 +1,453 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qendian.h" + +#define QUECTEL_MBIM_PROXY "quectel-mbim-proxy" +#define safe_close(_fd) do { if (_fd > 0) { close(_fd); _fd = -1; } } while(0) + +#define CM_MAX_CLIENT 32 +#define TID_MASK (0xFFFFFF) +#define TID_SHIFT (24) + +typedef enum { + MBIM_OPEN_MSG = 1, + MBIM_CLOSE_MSG = 2, + MBIM_OPEN_DONE = 0x80000001, + MBIM_CLOSE_DONE = 0x80000002, +} MBIM_MSG; + +typedef struct { + unsigned int MessageType; + unsigned int MessageLength; + unsigned int TransactionId; +} MBIM_MESSAGE_HEADER; + +typedef struct { + MBIM_MESSAGE_HEADER MessageHeader; + unsigned int MaxControlTransfer; +} MBIM_OPEN_MSG_T; + +typedef struct { + MBIM_MESSAGE_HEADER MessageHeader; + unsigned int Status; +} MBIM_OPEN_DONE_T; + +typedef struct { + int client_fd; + int client_idx; +} CM_CLIENT_T; + +static unsigned char cm_recv_buffer[4096]; +static CM_CLIENT_T cm_clients[CM_MAX_CLIENT]; +static int verbose = 0; + +const char * get_time(void) { + static char time_buf[128]; + 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; +} + +#define mbim_debug(fmt, args...) do { fprintf(stdout, "%s " fmt, get_time(), ##args); } while(0); + +static int non_block_write(int fd, void *data, int len) +{ + int ret; + struct pollfd pollfd = {fd, POLLOUT, 0}; + ret = poll(&pollfd, 1, 3000); + + if (ret <= 0) { + mbim_debug("%s poll ret=%d, errno: %d(%s)\n", __func__, ret, errno, strerror(errno)); + } + + ret = write (fd, data, len); + if (ret != len) + mbim_debug("%s write ret=%d, errno: %d(%s)\n", __func__, ret, errno, strerror(errno)); + + return len; +} + +static int mbim_send_open_msg(int mbim_dev_fd, uint32_t MaxControlTransfer) { + MBIM_OPEN_MSG_T open_msg; + MBIM_OPEN_MSG_T *pRequest = &open_msg; + + pRequest->MessageHeader.MessageType = htole32(MBIM_OPEN_MSG); + pRequest->MessageHeader.MessageLength = htole32(sizeof(MBIM_OPEN_MSG_T)); + pRequest->MessageHeader.TransactionId = htole32(1); + pRequest->MaxControlTransfer = htole32(MaxControlTransfer); + + mbim_debug("%s()\n", __func__); + return non_block_write(mbim_dev_fd, pRequest, sizeof(MBIM_OPEN_MSG_T)); +} + +/* + * parameter: proxy name + * return: local proxy server fd or -1 +*/ +static int proxy_make_server(const char *proxy_name) +{ + int len, flag; + struct sockaddr_un sockaddr; + int mbim_server_fd; + + mbim_server_fd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (mbim_server_fd < 0) { + mbim_debug("socket failed: %s\n", strerror(errno)); + return -1; + } + if (fcntl(mbim_server_fd, F_SETFL, fcntl(mbim_server_fd, F_GETFL) | O_NONBLOCK) < 0) + mbim_debug("fcntl set server(%d) NONBLOCK attribute failed: %s\n", mbim_server_fd, strerror(errno)); + + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sun_family = AF_LOCAL; + sockaddr.sun_path[0] = 0; + snprintf(sockaddr.sun_path, UNIX_PATH_MAX, "0%s", proxy_name); + sockaddr.sun_path[0] = '\0'; // string starts with leading '\0' + flag = 1; + if (setsockopt(mbim_server_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0) { + safe_close(mbim_server_fd); + mbim_debug("setsockopt failed\n"); + } + + len = strlen(proxy_name) + offsetof(struct sockaddr_un, sun_path) + 1; + if (bind(mbim_server_fd, (struct sockaddr*)&sockaddr, len) < 0) { + safe_close(mbim_server_fd); + mbim_debug("bind failed: %s\n", strerror(errno)); + return -1; + } + + listen(mbim_server_fd, 4); + return mbim_server_fd; +} + +static int handle_client_connect(int server_fd) +{ + int i, client_fd; + struct sockaddr_in cli_addr; + socklen_t len = sizeof(cli_addr); + + client_fd = accept(server_fd, (struct sockaddr *)&cli_addr, &len); + if (client_fd < 0) { + mbim_debug("proxy accept failed: %s\n", strerror(errno)); + return -1; + } + + if (fcntl(client_fd, F_SETFL, fcntl(client_fd, F_GETFL) | O_NONBLOCK) < 0) + mbim_debug("fcntl set client(%d) NONBLOCK attribute failed: %s\n", client_fd, strerror(errno)); + + for (i = 0; i < CM_MAX_CLIENT; i++) { + if (cm_clients[i].client_fd <= 0) { + cm_clients[i].client_fd = client_fd; + cm_clients[i].client_idx= i+1; + mbim_debug("%s client_fd=%d, client_idx=%d\n", __func__, cm_clients[i].client_fd, cm_clients[i].client_idx); + return 0; + } + } + + close(client_fd); + return -1; +} + +static void handle_client_disconnect(int client_fd) +{ + int i; + + for (i = 0; i < CM_MAX_CLIENT; i++) { + if (cm_clients[i].client_fd == client_fd) { + mbim_debug("%s client_fd=%d, client_idx=%d\n", __func__, cm_clients[i].client_fd, cm_clients[i].client_idx); + safe_close(cm_clients[i].client_fd); + return; + } + } +} + +static int handle_client_request(int mbim_dev_fd, int client_fd, void *pdata, int len) +{ + int i; + int client_idx = -1; + int ret; + MBIM_MESSAGE_HEADER *pRequest = (MBIM_MESSAGE_HEADER *)pdata; + unsigned int TransactionId = le32toh(pRequest->TransactionId); + + for (i = 0; i < CM_MAX_CLIENT; i++) { + if (cm_clients[i].client_fd == client_fd) { + client_idx = cm_clients[i].client_idx; + break; + } + } + + if (client_idx == -1) { + goto error; + } + + if (le32toh(pRequest->MessageType) == MBIM_OPEN_MSG + || le32toh(pRequest->MessageType) == MBIM_CLOSE_MSG) { + MBIM_OPEN_DONE_T OpenDone; + OpenDone.MessageHeader.MessageType = htole32(le32toh(pRequest->MessageType) | 0x80000000); + OpenDone.MessageHeader.MessageLength = htole32(sizeof(OpenDone)); + OpenDone.MessageHeader.TransactionId = htole32(TransactionId); + OpenDone.Status = htole32(0); + non_block_write (client_fd, &OpenDone, sizeof(OpenDone)); + return 0; + } + + /* transfer TransicationID to proxy transicationID and record in sender list */ + pRequest->TransactionId = htole32(TransactionId | (client_idx << TID_SHIFT)); + if (verbose) mbim_debug("REQ client_fd=%d, client_idx=%d, tid=%u\n", + cm_clients[client_idx].client_fd, cm_clients[client_idx].client_idx, TransactionId); + ret = non_block_write (mbim_dev_fd, pRequest, len); + if (ret == len) + return 0; + +error: + return -1; +} + +/* + * Will read message from device and transfer it to clients/client + * Notice: + * unsocial message will be send to all clients + */ +static int handle_device_response(void *pdata, int len) +{ + int i; + MBIM_MESSAGE_HEADER *pResponse = (MBIM_MESSAGE_HEADER *)pdata; + unsigned int TransactionId = le32toh(pResponse->TransactionId); + + /* unsocial/function error message */ + if (TransactionId == 0) { + for (i = 0; i < CM_MAX_CLIENT; i++) { + if (cm_clients[i].client_fd > 0) { + non_block_write(cm_clients[i].client_fd, pResponse, len); + } + } + } + else { + /* try to find the sender */ + int client_idx = (TransactionId >> TID_SHIFT); + + for (i = 0; i < CM_MAX_CLIENT; i++) { + if (cm_clients[i].client_idx == client_idx && cm_clients[i].client_fd > 0) { + TransactionId &= TID_MASK; + pResponse->TransactionId = htole32(TransactionId); + if (verbose) mbim_debug("RSP client_fd=%d, client_idx=%d, tid=%u\n", + cm_clients[i].client_fd, cm_clients[i].client_idx, TransactionId); + non_block_write(cm_clients[i].client_fd, pResponse, len); + break; + } + } + + if (i == CM_MAX_CLIENT) { + mbim_debug("%s nobody care tid=%u\n", __func__, TransactionId); + } + } + + return 0; +} + +static int proxy_loop(int mbim_dev_fd) +{ + int i; + int mbim_server_fd = -1; + + while (mbim_dev_fd > 0) { + struct pollfd pollfds[2+CM_MAX_CLIENT]; + int ne, ret, nevents = 0; + + pollfds[nevents].fd = mbim_dev_fd; + pollfds[nevents].events = POLLIN; + pollfds[nevents].revents= 0; + nevents++; + + if (mbim_server_fd > 0) { + pollfds[nevents].fd = mbim_server_fd; + pollfds[nevents].events = POLLIN; + pollfds[nevents].revents= 0; + nevents++; + + for (i = 0; i < CM_MAX_CLIENT; i++) { + if (cm_clients[i].client_fd > 0) { + pollfds[nevents].fd = cm_clients[i].client_fd; + pollfds[nevents].events = POLLIN; + pollfds[nevents].revents= 0; + nevents++; + } + } + } + + ret = poll(pollfds, nevents, (mbim_server_fd > 0) ? -1 : (10*1000)); + if (ret <= 0) { + goto error; + } + + for (ne = 0; ne < nevents; ne++) { + int fd = pollfds[ne].fd; + short revents = pollfds[ne].revents; + + if (revents & (POLLERR | POLLHUP | POLLNVAL)) { + mbim_debug("%s poll fd = %d, revents = %04x\n", __func__, fd, revents); + if (fd == mbim_dev_fd) { + goto error; + } else if(fd == mbim_server_fd) { + + } else { + handle_client_disconnect(fd); + } + continue; + } + + if (!(pollfds[ne].revents & POLLIN)) { + continue; + } + + if (fd == mbim_server_fd) { + handle_client_connect(fd); + } + else { + int len = read(fd, cm_recv_buffer, sizeof(cm_recv_buffer)); + + if (len <= 0) { + mbim_debug("%s read fd=%d, len=%d, errno: %d(%s)\n", __func__, fd, len, errno, strerror(errno)); + if (fd == mbim_dev_fd) + goto error; + else + handle_client_disconnect(fd); + + return len; + } + + if (fd == mbim_dev_fd) { + if (mbim_server_fd == -1) { + MBIM_OPEN_DONE_T *pOpenDone = (MBIM_OPEN_DONE_T *)cm_recv_buffer; + + if (le32toh(pOpenDone->MessageHeader.MessageType) == MBIM_OPEN_DONE) { + mbim_debug("receive MBIM_OPEN_DONE, status=%d\n", htole32(pOpenDone->Status)); + if (htole32(pOpenDone->Status)) + goto error; + mbim_server_fd = proxy_make_server(QUECTEL_MBIM_PROXY); + mbim_debug("mbim_server_fd=%d\n", mbim_server_fd); + } + } + else { + handle_device_response(cm_recv_buffer, len); + } + } + else { + handle_client_request(mbim_dev_fd, fd, cm_recv_buffer, len); + } + } + } + } + +error: + safe_close(mbim_server_fd); + for (i = 0; i < CM_MAX_CLIENT; i++) { + safe_close(cm_clients[i].client_fd); + } + + mbim_debug("%s exit\n", __func__); + return 0; +} + +/* + * How to use this proxy? + * 1. modprobe -a 8021q + * 2. Create network interface for channels: + * ip link add link wwan0 name wwan0.1 type vlan id 1 + * ip link add link wwan0 name wwan0.2 type vlan id 2 + * 3. Start './mbim-proxy' with -d 'device' + * 4. Start Clients: ./quectel-CM -n id1 + * 5. Start Clients: ./quectel-CM -n id2 + * ... + * Notice: + * mbim-proxy can work in backgroud as a daemon + * '-n' sessionID + * The modem may not support multi-PDN mode or how many PDN it supports is undefined. It depends!!! + * Besides, some modem also may not support some sessionID. For instance EC20 doesn't support SessionId 1... + */ +int main(int argc, char **argv) +{ + int optidx = 0; + int opt; + char *optstr = "d:vh"; + const char *device = "/dev/cdc-wdm0"; + + struct option options[] = { + {"verbose", no_argument, NULL, 'v'}, + {"device", required_argument, NULL, 'd'}, + {0, 0, 0, 0}, + }; + while ((opt = getopt_long(argc, argv, optstr, options, &optidx)) != -1) { + switch (opt) { + case 'v': + verbose = 1; + break; + case 'd': + device = optarg; + break; + case 'h': + mbim_debug("-h Show this message\n"); + mbim_debug("-v Verbose\n"); + mbim_debug("-d [device] MBIM device\n"); + return 0; + default: + mbim_debug("illegal argument\n"); + return -1; + } + } + + if (!device) { + mbim_debug("Missing parameter: device\n"); + return -1; + } + + while (1) { + int mbim_dev_fd = open(device, O_RDWR | O_NONBLOCK | O_NOCTTY); + if (mbim_dev_fd < 0) { + mbim_debug("cannot open mbim_device %s: %s\n", device, strerror(errno)); + sleep(2); + continue; + } + mbim_debug ("mbim_dev_fd=%d\n", mbim_dev_fd); + + memset(cm_clients, 0, sizeof(cm_clients)); + mbim_send_open_msg(mbim_dev_fd, sizeof(cm_recv_buffer)); + proxy_loop(mbim_dev_fd); + safe_close(mbim_dev_fd); + } + + return -1; +} diff --git a/application/quectel_CM_5G/src/quectel-qmi-proxy b/application/quectel_CM_5G/src/quectel-qmi-proxy new file mode 100644 index 0000000..a058946 Binary files /dev/null and b/application/quectel_CM_5G/src/quectel-qmi-proxy differ diff --git a/application/quectel_CM_5G/src/quectel-qmi-proxy.c b/application/quectel_CM_5G/src/quectel-qmi-proxy.c new file mode 100644 index 0000000..828f1b9 --- /dev/null +++ b/application/quectel_CM_5G/src/quectel-qmi-proxy.c @@ -0,0 +1,700 @@ +/****************************************************************************** + @file quectel-qmi-proxy.c + @brief The qmi proxy. + + DESCRIPTION + Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qendian.h" +#include "qlist.h" +#include "QCQMI.h" +#include "QCQCTL.h" +#include "QCQMUX.h" + +#ifndef MIN +#define MIN(a, b) ((a) < (b)? (a): (b)) +#endif + +const char * get_time(void) { + static char time_buf[128]; + 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; +} + +#define dprintf(fmt, args...) do { fprintf(stdout, "%s " fmt, get_time(), ##args); } 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) + +typedef struct { + struct qlistnode qnode; + int 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; + int ClientFd; + unsigned AccessTime; +} QMI_PROXY_CONNECTION; + +#ifdef QUECTEL_QMI_MERGE +#define MERGE_PACKET_IDENTITY 0x2c7c +#define MERGE_PACKET_VERSION 0x0001 +#define MERGE_PACKET_MAX_PAYLOAD_SIZE 56 +typedef struct __QMI_MSG_HEADER { + uint16_t idenity; + uint16_t version; + uint16_t cur_len; + uint16_t total_len; +} QMI_MSG_HEADER; + +typedef struct __QMI_MSG_PACKET { + QMI_MSG_HEADER header; + uint16_t len; + char buf[4096]; +} QMI_MSG_PACKET; +#endif + +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 int verbose_debug = 0; +static int modem_reset_flag = 0; +static int qmi_sync_done = 0; +static uint8_t qmi_buf[4096]; + +static int send_qmi_to_cdc_wdm(PQCQMIMSG pQMI); + +#ifdef QUECTEL_QMI_MERGE +static int merge_qmi_rsp_packet(void *buf, ssize_t *src_size) { + static QMI_MSG_PACKET s_QMIPacket; + QMI_MSG_HEADER *header = NULL; + ssize_t size = *src_size; + + if((uint16_t)size < sizeof(QMI_MSG_HEADER)) + return -1; + + header = (QMI_MSG_HEADER *)buf; + if(le16toh(header->idenity) != MERGE_PACKET_IDENTITY || le16toh(header->version) != MERGE_PACKET_VERSION || le16toh(header->cur_len) > le16toh(header->total_len)) + return -1; + + if(le16toh(header->cur_len) == le16toh(header->total_len)) { + *src_size = le16toh(header->total_len); + memcpy(buf, buf + sizeof(QMI_MSG_HEADER), *src_size); + s_QMIPacket.len = 0; + return 0; + } + + memcpy(s_QMIPacket.buf + s_QMIPacket.len, buf + sizeof(QMI_MSG_HEADER), le16toh(header->cur_len)); + s_QMIPacket.len += le16toh(header->cur_len); + + if (le16toh(header->cur_len) < MERGE_PACKET_MAX_PAYLOAD_SIZE || s_QMIPacket.len >= le16toh(header->total_len)) { + memcpy(buf, s_QMIPacket.buf, s_QMIPacket.len); + *src_size = s_QMIPacket.len; + s_QMIPacket.len = 0; + return 0; + } + + return -1; +} +#endif + +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) { + dprintf("bind %s errno: %d (%s)\n", name, errno, strerror(errno)); + close(sockfd); + 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, int clientDisconnect) { + 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); + + if (clientDisconnect) { + int size = 17; + QMI_PROXY_MSG *qmi_msg = malloc(sizeof(QMI_PROXY_MSG) + size); + PQCQMIMSG pQMI = &qmi_msg->qmi[0]; + + dprintf("xxx ClientFd=%d QMIType=%d ClientId=%d\n", qmi_con->ClientFd, qmi_client->QMIType, qmi_client->ClientId); + qlist_init(&qmi_msg->qnode); + qmi_msg->ClientFd = qmi_proxy_server_fd; + pQMI->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI; + pQMI->QMIHdr.Length = htole16(16); + pQMI->QMIHdr.CtlFlags = 0x00; + pQMI->QMIHdr.QMIType = QMUX_TYPE_CTL; + pQMI->QMIHdr.ClientId= 0x00; + pQMI->CTLMsg.ReleaseClientIdReq.CtlFlags = QMICTL_FLAG_REQUEST; + pQMI->CTLMsg.ReleaseClientIdReq.TransactionId = 255; + pQMI->CTLMsg.ReleaseClientIdReq.QMICTLType = htole16(QMICTL_RELEASE_CLIENT_ID_REQ); + pQMI->CTLMsg.ReleaseClientIdReq.Length = htole16(5); + pQMI->CTLMsg.ReleaseClientIdReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER; + pQMI->CTLMsg.ReleaseClientIdReq.TLVLength = htole16(2); + pQMI->CTLMsg.ReleaseClientIdReq.QMIType = qmi_client->QMIType; + pQMI->CTLMsg.ReleaseClientIdReq.ClientId = qmi_client->ClientId; + + if (qlist_empty(&qmi_proxy_ctl_msg)) + send_qmi_to_cdc_wdm(pQMI); + qlist_add_tail(&qmi_proxy_ctl_msg, &qmi_msg->qnode); + } + + 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 void dump_qmi(PQCQMIMSG pQMI, int fd, const char flag) +{ + if (verbose_debug) + { + unsigned i; + unsigned size = le16toh(pQMI->QMIHdr.Length) + 1; + char buf[128]; + int cnt = 0; + + cnt += snprintf(buf + cnt, sizeof(buf) - cnt, "%c %d %u: ", flag, fd, size); + for (i = 0; i < size && i < 24; i++) + cnt += snprintf(buf + cnt, sizeof(buf) - cnt, "%02x ", ((uint8_t *)pQMI)[i]); + dprintf("%s\n", buf) + } +} + +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 == -1 && errno == EINTR && qmi_proxy_quit == 0); + + if (pollfds[0].revents & POLLOUT) { + ssize_t size = le16toh(pQMI->QMIHdr.Length) + 1; + ret = write(cdc_wdm_fd, pQMI, size); + dump_qmi(pQMI, cdc_wdm_fd, 'w'); + } + + 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 == -1 && errno == EINTR && qmi_proxy_quit == 0); + + if (pollfds[0].revents & POLLOUT) { + ssize_t size = le16toh(pQMI->QMIHdr.Length) + 1; + ret = write(clientFd, pQMI, size); + dump_qmi(pQMI, clientFd, 'w'); + } + + return ret; +} + +static void recv_qmi_from_dev(PQCQMIMSG pQMI) { + struct qlistnode *con_node, *client_node; + + if (qmi_proxy_server_fd == -1) { + qmi_sync_done = 1; + } + 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); + + if (qmi_msg->qmi[0].CTLMsg.QMICTLMsgHdrRsp.TransactionId != pQMI->CTLMsg.QMICTLMsgHdrRsp.TransactionId + || qmi_msg->qmi[0].CTLMsg.QMICTLMsgHdrRsp.QMICTLType != pQMI->CTLMsg.QMICTLMsgHdrRsp.QMICTLType) { + dprintf("ERROR: ctl rsp tid:%d, type:%d - ctl req %d, %d\n", + pQMI->CTLMsg.QMICTLMsgHdrRsp.TransactionId, pQMI->CTLMsg.QMICTLMsgHdrRsp.QMICTLType, + qmi_msg->qmi[0].CTLMsg.QMICTLMsgHdrRsp.TransactionId, qmi_msg->qmi[0].CTLMsg.QMICTLMsgHdrRsp.QMICTLType); + } + else if (qmi_msg->ClientFd == qmi_proxy_server_fd) { + if (le16toh(pQMI->CTLMsg.QMICTLMsgHdrRsp.QMICTLType) == QMICTL_RELEASE_CLIENT_ID_RESP) { + dprintf("--- ClientFd=%d QMIType=%d ClientId=%d\n", qmi_proxy_server_fd, + pQMI->CTLMsg.ReleaseClientIdRsp.QMIType, pQMI->CTLMsg.ReleaseClientIdRsp.ClientId); + } + } + else { + 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 (le16toh(pQMI->CTLMsg.QMICTLMsgHdrRsp.QMICTLType) == QMICTL_GET_CLIENT_ID_RESP) { + get_client_id(qmi_con, &pQMI->CTLMsg.GetClientIdRsp); + } + else if (le16toh(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); + + send_qmi_to_cdc_wdm(qmi_msg->qmi); + } + } + } + else if (pQMI->QMIHdr.QMIType == QMICTL_CTL_FLAG_IND) { + if (le16toh(pQMI->CTLMsg.QMICTLMsgHdrRsp.QMICTLType) == QMICTL_REVOKE_CLIENT_ID_IND) { + modem_reset_flag = 1; + } + } + } + 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 recv_qmi_from_client(PQCQMIMSG pQMI, unsigned size, int clientfd) { + if (qmi_proxy_server_fd == -1) + return -1; + + if (pQMI->QMIHdr.QMIType == QMUX_TYPE_CTL) { + QMI_PROXY_MSG *qmi_msg; + + if (pQMI->CTLMsg.QMICTLMsgHdr.QMICTLType == QMICTL_SYNC_REQ) { + dprintf("do not allow client send QMICTL_SYNC_REQ\n"); + return 0; + } + + qmi_msg = malloc(sizeof(QMI_PROXY_MSG) + size); + qlist_init(&qmi_msg->qnode); + qmi_msg->ClientFd = clientfd; + memcpy(qmi_msg->qmi, pQMI, size); + + if (qlist_empty(&qmi_proxy_ctl_msg)) + send_qmi_to_cdc_wdm(pQMI); + qlist_add_tail(&qmi_proxy_ctl_msg, &qmi_msg->qnode); + } + else { + send_qmi_to_cdc_wdm(pQMI); + } + + return 0; +} + +static int qmi_proxy_init(unsigned retry) { + unsigned i; + QCQMIMSG _QMI; + PQCQMIMSG pQMI = &_QMI; + + 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; + + qmi_sync_done = 0; + for (i = 0; i < retry; i++) { + pQMI->CTLMsg.SyncReq.TransactionId = i+1; + pQMI->CTLMsg.SyncReq.QMICTLType = htole16(QMICTL_SYNC_REQ); + pQMI->CTLMsg.SyncReq.Length = htole16(0); + + pQMI->QMIHdr.Length = + htole16(le16toh(pQMI->CTLMsg.QMICTLMsgHdr.Length) + sizeof(QCQMI_HDR) + sizeof(QCQMICTL_MSG_HDR) - 1); + + if (send_qmi_to_cdc_wdm(pQMI) <= 0) + break; + + sleep(1); + if (qmi_sync_done) + break; + } + + dprintf("%s %s\n", __func__, qmi_sync_done ? "succful" : "fail"); + return qmi_sync_done ? 0 : -1; +} + +static void *qmi_proxy_loop(void *param) +{ + PQCQMIMSG pQMI = (PQCQMIMSG)qmi_buf; + struct qlistnode *con_node; + QMI_PROXY_CONNECTION *qmi_con; + + (void)param; + dprintf("%s enter thread_id %p\n", __func__, (void *)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; + } + +#if 0 + dprintf("poll "); + for (ne = 0; ne < nevents; ne++) { + dprintf("%d ", pollfds[ne].fd); + } + dprintf("\n"); +#endif + + do { + //ret = poll(pollfds, nevents, -1); + ret = poll(pollfds, nevents, (qmi_proxy_server_fd > 0) ? -1 : 200); + } while (ret == -1 && 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, 1); + } + + 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 (nreads <= 0) { + dprintf("%s read=%d errno: %d (%s)\n", __func__, (int)nreads, errno, strerror(errno)); + goto qmi_proxy_loop_exit; + } +#ifdef QUECTEL_QMI_MERGE + if(merge_qmi_rsp_packet(pQMI, &nreads)) + continue; +#endif + if (nreads != (le16toh(pQMI->QMIHdr.Length) + 1)) { + dprintf("%s nreads=%d, pQCQMI->QMIHdr.Length = %d\n", __func__, (int)nreads, le16toh(pQMI->QMIHdr.Length)); + continue; + } + + dump_qmi(pQMI, fd, 'r'); + recv_qmi_from_dev(pQMI); + if (modem_reset_flag) + goto qmi_proxy_loop_exit; + } + else { + nreads = read(fd, pQMI, sizeof(qmi_buf)); + + if (nreads <= 0) { + dprintf("%s read=%d errno: %d (%s)", __func__, (int)nreads, errno, strerror(errno)); + cleanup_qmi_connection(fd, 1); + break; + } + + if (nreads != (le16toh(pQMI->QMIHdr.Length) + 1)) { + dprintf("%s nreads=%d, pQCQMI->QMIHdr.Length = %d\n", __func__, (int)nreads, le16toh(pQMI->QMIHdr.Length)); + continue; + } + + dump_qmi(pQMI, fd, 'r'); + recv_qmi_from_client(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, 0); + } + + dprintf("%s exit, thread_id %p\n", __func__, (void *)pthread_self()); + + return NULL; +} + +static void usage(void) { + dprintf(" -d A valid qmi device\n" + " default /dev/cdc-wdm0, but cdc-wdm0 may be invalid\n" + " -i netcard name\n" + " -v Will show all details\n"); +} + +static void sig_action(int sig) { + if (qmi_proxy_quit++ == 0) { + if (thread_id) + pthread_kill(thread_id, sig); + } +} + +int main(int argc, char *argv[]) { + int opt; + char cdc_wdm[32+1] = "/dev/cdc-wdm0"; + char servername[64] = {0}; + + optind = 1; + + signal(SIGINT, sig_action); + + while ( -1 != (opt = getopt(argc, argv, "d:i:vh"))) { + switch (opt) { + case 'd': + strcpy(cdc_wdm, optarg); + break; + case 'v': + verbose_debug = 1; + break; + default: + usage(); + return 0; + } + } + + sprintf(servername, "quectel-qmi-proxy%c", cdc_wdm[strlen(cdc_wdm)-1]); + dprintf("Will use cdc-wdm='%s', proxy='%s'\n", cdc_wdm, servername); + + while (qmi_proxy_quit == 0) { + 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)\n", cdc_wdm, errno, strerror(errno)); + sleep(3); + continue; + } + cfmakenoblock(cdc_wdm_fd); + + /* no qmi_proxy_loop lives, create one */ + pthread_create(&thread_id, NULL, qmi_proxy_loop, NULL); + + if (qmi_proxy_init(60) == 0) { + qmi_proxy_server_fd = create_local_server(servername); + dprintf("qmi_proxy_server_fd = %d\n", qmi_proxy_server_fd); + if (qmi_proxy_server_fd == -1) { + dprintf("Failed to create %s, errno: %d (%s)\n", servername, errno, strerror(errno)); + pthread_cancel(thread_id); + } + } + else { + pthread_cancel(thread_id); + } + + pthread_join(thread_id, NULL); + thread_id = 0; + + if (qmi_proxy_server_fd != -1) { + dprintf("close server %s\n", servername); + close(qmi_proxy_server_fd); + qmi_proxy_server_fd = -1; + } + close(cdc_wdm_fd); + cdc_wdm_fd = -1; + + if (qmi_proxy_quit == 0) + sleep(modem_reset_flag ? 30 : 3); + modem_reset_flag = 0; + } + + return 0; +} diff --git a/application/quectel_CM_5G/src/quectel-qrtr-proxy.c b/application/quectel_CM_5G/src/quectel-qrtr-proxy.c new file mode 100644 index 0000000..67ddc16 --- /dev/null +++ b/application/quectel_CM_5G/src/quectel-qrtr-proxy.c @@ -0,0 +1,894 @@ +/****************************************************************************** + @file quectel-qrtr-proxy.c + @brief The qrtr proxy. + + DESCRIPTION + Connectivity Management Tool for USB/PCIE network adapter of Quectel wireless cellular modules. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qrtr.h" + +#include "qendian.h" +#include "qlist.h" +#include "QCQMI.h" +#include "QCQCTL.h" +#include "QCQMUX.h" + +static const char * get_time(void) { + static char time_buf[128]; + 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; +} + +#define dprintf(fmt, args...) do { fprintf(stdout, "%s " fmt, get_time(), ##args); } 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 align_4(_len) (((_len) + 3) & ~3) + +typedef struct { + struct qlistnode qnode; + int ClientFd; + QCQMIMSG qrtr[0]; +} QRTR_PROXY_MSG; + +typedef struct { + struct qlistnode qnode; + uint8_t QMIType; + uint8_t ClientId; + uint32_t node_id; + uint32_t port_id; + unsigned AccessTime; +} QRTR_PROXY_CLINET; + +typedef struct { + struct qlistnode qnode; + struct qlistnode client_qnode; + int ClientFd; + unsigned AccessTime; +} QRTR_PROXY_CONNECTION; + +typedef struct { + struct qlistnode qnode; + uint32_t service; + uint32_t version; + uint32_t instance; + uint32_t node; + uint32_t port; + + __le32 src_node_id; + __le32 src_port_id; +} QRTR_SERVICE; + +static int qrtr_proxy_quit = 0; +static pthread_t thread_id = 0; +static int cdc_wdm_fd = -1; +static int qrtr_proxy_server_fd = -1; +static struct qlistnode qrtr_proxy_connection; +static struct qlistnode qrtr_server_list; +static int verbose_debug = 0; +static uint32_t node_modem = 3; //IPQ ~ 3, QCM ~ 0 +static uint32_t node_myself = 1; + +static QRTR_SERVICE *find_qrtr_service(uint8_t QMIType) +{ + struct qlistnode *node; + + qlist_for_each (node, &qrtr_server_list) { + QRTR_SERVICE *srv = qnode_to_item(node, QRTR_SERVICE, qnode); + if (srv->service == QMIType) + return srv; + } + + return NULL; +} + +static uint8_t client_bitmap[0xf0]; +static uint8_t port_bitmap[0xff0]; +static int alloc_client_id(void) { + int id = 1; + + for (id = 1; id < (int)sizeof(client_bitmap); id++) { + if (client_bitmap[id] == 0) { + client_bitmap[id] = id; + return id; + } + } + + dprintf("NOT find %s()\n", __func__); + return 0; +} + +static void free_client_id(int id) { + if (id < (int)sizeof(client_bitmap) && client_bitmap[id] == id) { + client_bitmap[id] = 0; + return; + } + dprintf("NOT find %s(id=%d)\n", __func__, id); +} + +static int alloc_port_id(void) { + int id = 1; + + for (id = 1; id < (int)sizeof(port_bitmap); id++) { + if (port_bitmap[id] == 0) { + port_bitmap[id] = id; + return id; + } + } + + dprintf("NOT find %s()\n", __func__); + return 0; +} + +static void free_port_id(int id) { + if (id < (int)sizeof(port_bitmap) && port_bitmap[id] == id) { + port_bitmap[id] = 0; + return; + } + dprintf("NOT find %s(id=%d)\n", __func__, id); +} + +static void dump_qrtr(void *buf, size_t len, char flag) +{ + size_t i; + static char printf_buf[1024]; + int cnt = 0, limit=1024; + unsigned char *d = (unsigned char *)buf; + struct qrtr_hdr_v1 *hdr = (struct qrtr_hdr_v1 *)buf; + const char *ctrl_pkt_strings[] = { + [QRTR_TYPE_DATA] = "data", + [QRTR_TYPE_HELLO] = "hello", + [QRTR_TYPE_BYE] = "bye", + [QRTR_TYPE_NEW_SERVER] = "new-server", + [QRTR_TYPE_DEL_SERVER] = "del-server", + [QRTR_TYPE_DEL_CLIENT] = "del-client", + [QRTR_TYPE_RESUME_TX] = "resume-tx", + [QRTR_TYPE_EXIT] = "exit", + [QRTR_TYPE_PING] = "ping", + [QRTR_TYPE_NEW_LOOKUP] = "new-lookup", + [QRTR_TYPE_DEL_LOOKUP] = "del-lookup", + }; + + for (i = 0; i < len && i < 64; i++) { + if (i%4 == 0) + cnt += snprintf(printf_buf+cnt, limit-cnt, " "); + cnt += snprintf(printf_buf+cnt, limit-cnt, "%02x", d[i]); + } + dprintf("%s\n", printf_buf); + + dprintf("%c ver=%d, type=%d(%s), %x,%x -> %x,%x, confirm_rx=%d, size=%u\n", + flag, + le32toh(hdr->version), le32toh(hdr->type), ctrl_pkt_strings[le32toh(hdr->type)], + le32toh(hdr->src_node_id), le32toh(hdr->src_port_id), le32toh(hdr->dst_node_id), le32toh(hdr->dst_port_id), + le32toh(hdr->confirm_rx), le32toh(hdr->size)); +} + +static int send_qmi_to_client(PQCQMIMSG pQMI, int fd) { + struct pollfd pollfds[]= {{fd, POLLOUT, 0}}; + ssize_t ret = 0; + ssize_t size = le16toh(pQMI->QMIHdr.Length) + 1; + + do { + ret = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), 5000); + } while (ret == -1 && errno == EINTR && qrtr_proxy_quit == 0); + + if (pollfds[0].revents & POLLOUT) { + ret = write(fd, pQMI, size); + } + + return ret == size ? 0 : -1; +} + +static int send_qrtr_to_dev(struct qrtr_hdr_v1 *hdr, int fd) { + struct pollfd pollfds[]= {{fd, POLLOUT, 0}}; + ssize_t ret = 0; + ssize_t size = align_4(le32toh(hdr->size) + sizeof(*hdr)); + + do { + ret = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), 5000); + } while (ret == -1 && errno == EINTR && qrtr_proxy_quit == 0); + + if (pollfds[0].revents & POLLOUT) { + ret = write(fd, hdr, size); + } + + return ret == size ? 0 : -1; +} + +static int qrtr_node_enqueue(const void *data, size_t len, + int type, struct sockaddr_qrtr *from, + struct sockaddr_qrtr *to, unsigned int confirm_rx) +{ + int rc = -1; + size_t size = sizeof(struct qrtr_hdr_v1) + len; + struct qrtr_hdr_v1 *hdr = (struct qrtr_hdr_v1 *)malloc(align_4(size)); + + if (hdr) { + hdr->version = htole32(QRTR_PROTO_VER_1); + hdr->type = htole32(type); + hdr->src_node_id = htole32(from->sq_node); + hdr->src_port_id = htole32(from->sq_port); + hdr->dst_node_id = htole32(to->sq_node); + hdr->dst_port_id = htole32(to->sq_port); + hdr->size = htole32(len); + hdr->confirm_rx = htole32(!!confirm_rx); + + memcpy(hdr + 1, data, len); + dump_qrtr(hdr, size, '>'); + send_qrtr_to_dev(hdr, cdc_wdm_fd); + free(hdr); + } + + return rc; +} + +static int send_ctrl_hello(__u32 sq_node, __u32 sq_port) +{ + struct qrtr_ctrl_pkt pkt; + int rc; + struct sockaddr_qrtr to = {AF_QIPCRTR, sq_node, sq_port}; + struct sockaddr_qrtr from = {AF_QIPCRTR, node_myself, QRTR_PORT_CTRL}; + + memset(&pkt, 0, sizeof(pkt)); + pkt.cmd = htole32(QRTR_TYPE_HELLO); + + rc = qrtr_node_enqueue(&pkt, sizeof(pkt), QRTR_TYPE_HELLO, &from, &to, 0); + if (rc < 0) + return rc; + + return 0; +} + +static int ctrl_cmd_del_client(__u32 sq_node, __u32 sq_port, uint8_t QMIType) +{ + struct qrtr_ctrl_pkt pkt; + int rc; + struct sockaddr_qrtr to = {AF_QIPCRTR, QRTR_NODE_BCAST, QRTR_PORT_CTRL}; + struct sockaddr_qrtr from = {AF_QIPCRTR, sq_node, sq_port}; + QRTR_SERVICE *srv = find_qrtr_service(QMIType); + + if (srv) { + to.sq_node = srv->src_node_id; + } + + memset(&pkt, 0, sizeof(pkt)); + pkt.cmd = htole32(QRTR_TYPE_DEL_CLIENT); + pkt.client.node = htole32(sq_node); + pkt.client.port = htole32(sq_port); + + rc = qrtr_node_enqueue(&pkt, sizeof(pkt), QRTR_TYPE_DATA, &from, &to, 0); + if (rc < 0) + return rc; + + return 0; +} + +static void handle_server_change(struct qrtr_hdr_v1 *hdr) { + struct qrtr_ctrl_pkt *pkt = (struct qrtr_ctrl_pkt *)(hdr + 1); + QRTR_SERVICE *s; + + dprintf ("[qrtr] %s server on %u:%u(%u:%u) -> service %u, instance %x\n", + QRTR_TYPE_NEW_SERVER == hdr->type ? "add" : "remove", + le32toh(pkt->server.node), le32toh(pkt->server.port), + le32toh(hdr->src_node_id), le32toh(hdr->src_port_id), + le32toh(pkt->server.service), le32toh(pkt->server.instance)); + + if (le32toh(pkt->server.node) != node_modem) { + return; //we only care modem + } + + s = (QRTR_SERVICE *)malloc(sizeof(QRTR_SERVICE)); + if (!s) + return; + + qlist_init(&s->qnode); + s->service = le32toh(pkt->server.service); + s->version = le32toh(pkt->server.instance) & 0xff; + s->instance = le32toh(pkt->server.instance) >> 8; + s->node = le32toh(pkt->server.node); + s->port = le32toh(pkt->server.port); + + s->src_node_id = le32toh(hdr->src_node_id); + s->src_port_id = le32toh(hdr->src_port_id); + + if (QRTR_TYPE_NEW_SERVER == hdr->type) { + qlist_add_tail(&qrtr_server_list, &s->qnode); + } + else if (QRTR_TYPE_DEL_SERVER == hdr->type) { + qlist_remove(&s->qnode); + } +} + +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("bind %s errno: %d (%s)\n", name, errno, strerror(errno)); + return -1; + } + + dprintf("local server: %s sockfd = %d\n", name, sockfd); + cfmakenoblock(sockfd); + listen(sockfd, 1); + + return sockfd; +} + +static uint8_t alloc_qrtr_client_id(QRTR_PROXY_CONNECTION *qrtr_con, uint8_t QMIType) { + QRTR_PROXY_CLINET *qrtr_client = (QRTR_PROXY_CLINET *)malloc(sizeof(QRTR_PROXY_CLINET)); + + qlist_init(&qrtr_client->qnode); + qrtr_client->QMIType = QMIType; + qrtr_client->ClientId = alloc_client_id(); + qrtr_client->node_id = 1; + qrtr_client->port_id = alloc_port_id(); + qrtr_client->AccessTime = 0; + + dprintf("+++ ClientFd=%d QMIType=%d ClientId=%d, node_id=%d, port_id=%d\n", + qrtr_con->ClientFd, qrtr_client->QMIType, qrtr_client->ClientId, + qrtr_client->node_id, qrtr_client->port_id); + qlist_add_tail(&qrtr_con->client_qnode, &qrtr_client->qnode); + + return qrtr_client->ClientId; +} + +static void release_qrtr_client_id(QRTR_PROXY_CONNECTION *qrtr_con, uint8_t QMIType, uint8_t ClientId) { + struct qlistnode *client_node; + int find = 0; + + qlist_for_each (client_node, &qrtr_con->client_qnode) { + QRTR_PROXY_CLINET *qrtr_client = qnode_to_item(client_node, QRTR_PROXY_CLINET, qnode); + + if (QMIType == qrtr_client->QMIType && ClientId == qrtr_client->ClientId) { + dprintf("--- ClientFd=%d QMIType=%d ClientId=%d, node_id=%d, port_id=%d\n", + qrtr_con->ClientFd, qrtr_client->QMIType, qrtr_client->ClientId, + qrtr_client->node_id, qrtr_client->port_id); + ctrl_cmd_del_client(qrtr_client->node_id, qrtr_client->port_id, qrtr_client->QMIType); + free_client_id(qrtr_client->ClientId); + free_port_id(qrtr_client->port_id); + qlist_remove(&qrtr_client->qnode); + free(qrtr_client); + find++; + break; + } + } + + if (!find) { + dprintf("NOT find on %s(ClientFd=%d, QMIType=%d, ClientId=%d)\n", + __func__, qrtr_con->ClientFd, QMIType, ClientId); + } +} + +static void accept_qrtr_connection(int serverfd) { + int clientfd = -1; + unsigned char addr[128]; + socklen_t alen = sizeof(addr); + QRTR_PROXY_CONNECTION *qrtr_con; + + clientfd = accept(serverfd, (struct sockaddr *)addr, &alen); + + qrtr_con = (QRTR_PROXY_CONNECTION *)malloc(sizeof(QRTR_PROXY_CONNECTION)); + if (qrtr_con) { + qlist_init(&qrtr_con->qnode); + qlist_init(&qrtr_con->client_qnode); + qrtr_con->ClientFd= clientfd; + qrtr_con->AccessTime = 0; + dprintf("+++ ClientFd=%d\n", qrtr_con->ClientFd); + qlist_add_tail(&qrtr_proxy_connection, &qrtr_con->qnode); + } + + cfmakenoblock(clientfd); +} + +static void cleanup_qrtr_connection(int clientfd) { + struct qlistnode *con_node; + int find = 0; + + qlist_for_each(con_node, &qrtr_proxy_connection) { + QRTR_PROXY_CONNECTION *qrtr_con = qnode_to_item(con_node, QRTR_PROXY_CONNECTION, qnode); + + if (qrtr_con->ClientFd == clientfd) { + while (!qlist_empty(&qrtr_con->client_qnode)) { + QRTR_PROXY_CLINET *qrtr_client = qnode_to_item(qlist_head(&qrtr_con->client_qnode), QRTR_PROXY_CLINET, qnode); + + release_qrtr_client_id(qrtr_con, qrtr_client->QMIType, qrtr_client->ClientId); + } + + dprintf("--- ClientFd=%d\n", qrtr_con->ClientFd); + close(qrtr_con->ClientFd); + qlist_remove(&qrtr_con->qnode); + free(qrtr_con); + find = 1; + break; + } + } + + if (!find) { + dprintf("NOT find on %s(ClientFd=%d)\n", __func__, clientfd); + } +} + +static void recv_qrtr_from_dev(struct qrtr_hdr_v1 *hdr) { + int find = 0; + uint32_t type = le32toh(hdr->type); + + if (type == QRTR_TYPE_HELLO) { + send_ctrl_hello(le32toh(hdr->src_node_id), le32toh(hdr->src_port_id)); + find++; + } + else if (type == QRTR_TYPE_NEW_SERVER || type == QRTR_TYPE_DEL_SERVER) { + handle_server_change(hdr); + find++; + } + else if (type == QRTR_TYPE_DATA) { + struct qlistnode *con_node, *client_node; + + qlist_for_each(con_node, &qrtr_proxy_connection) { + QRTR_PROXY_CONNECTION *qrtr_con = qnode_to_item(con_node, QRTR_PROXY_CONNECTION, qnode); + + qlist_for_each(client_node, &qrtr_con->client_qnode) { + QRTR_PROXY_CLINET *qrtr_client = qnode_to_item(client_node, QRTR_PROXY_CLINET, qnode); + + if (qrtr_client->node_id == le32toh(hdr->dst_node_id) && qrtr_client->port_id == le32toh(hdr->dst_port_id)) { + PQCQMIMSG pQMI = (PQCQMIMSG)malloc(hdr->size + sizeof(QCQMI_HDR)); + + if (pQMI) { + pQMI->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI; + pQMI->QMIHdr.Length = htole16(hdr->size + sizeof(QCQMI_HDR) - 1); + pQMI->QMIHdr.CtlFlags = 0x00; + pQMI->QMIHdr.QMIType = qrtr_client->QMIType; + pQMI->QMIHdr.ClientId = qrtr_client->ClientId; + memcpy(&pQMI->MUXMsg, hdr + 1, hdr->size); + send_qmi_to_client(pQMI, qrtr_con->ClientFd); + free(pQMI); + find++; + } + } + } + } + + if (hdr->confirm_rx) { + struct qrtr_ctrl_pkt pkt; + struct sockaddr_qrtr from = {AF_QIPCRTR, le32toh(hdr->dst_node_id), le32toh(hdr->dst_port_id)}; + struct sockaddr_qrtr to = {AF_QIPCRTR, le32toh(hdr->src_node_id), le32toh(hdr->src_port_id)}; + + memset(&pkt, 0, sizeof(pkt)); + pkt.cmd = htole32(QRTR_TYPE_RESUME_TX); + pkt.client.node = hdr->dst_node_id; + pkt.client.port = hdr->dst_port_id; + + qrtr_node_enqueue(&pkt, sizeof(pkt), QRTR_TYPE_RESUME_TX, &from, &to, 0); + } + } + else if (type == QRTR_TYPE_RESUME_TX) { + } + + if (!find) { + dprintf("NOT find on %s()\n", __func__); + } +} + +static int recv_qmi_from_client(PQCQMIMSG pQMI, int clientfd) { + QRTR_PROXY_CONNECTION *qrtr_con; + struct qlistnode *con_node, *client_node; + int find = 0; + + qlist_for_each(con_node, &qrtr_proxy_connection) { + qrtr_con = qnode_to_item(con_node, QRTR_PROXY_CONNECTION, qnode); + if (qrtr_con->ClientFd == clientfd) + break; + qrtr_con = NULL; + } + + if (!qrtr_con) { + return -1; + } + + if (le16toh(pQMI->QMIHdr.QMIType) == QMUX_TYPE_CTL) { + if (pQMI->CTLMsg.QMICTLMsgHdr.QMICTLType == QMICTL_SYNC_REQ) { + dprintf("do not allow client send QMICTL_SYNC_REQ\n"); + return 0; + } + else if (le16toh(pQMI->CTLMsg.QMICTLMsgHdr.QMICTLType) == QMICTL_GET_CLIENT_ID_REQ) { + uint8_t QMIType = pQMI->CTLMsg.GetClientIdReq.QMIType; + PQCQMIMSG pRsp = (PQCQMIMSG)malloc(256); + + if (pRsp) { + uint8_t ClientId = 0; + + if (find_qrtr_service(QMIType)) { + ClientId = alloc_qrtr_client_id(qrtr_con, QMIType); + } + + pRsp->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI; + pRsp->QMIHdr.Length = htole16(sizeof(pRsp->CTLMsg.GetClientIdRsp) + sizeof(pRsp->QMIHdr) - 1); + pRsp->QMIHdr.CtlFlags = 0x00; + pRsp->QMIHdr.QMIType = QMUX_TYPE_CTL; + pRsp->QMIHdr.ClientId = 0; + + pRsp->CTLMsg.QMICTLMsgHdrRsp.CtlFlags = QMICTL_FLAG_RESPONSE; + pRsp->CTLMsg.QMICTLMsgHdrRsp.TransactionId = pQMI->CTLMsg.QMICTLMsgHdr.TransactionId; + pRsp->CTLMsg.QMICTLMsgHdrRsp.QMICTLType = pQMI->CTLMsg.QMICTLMsgHdr.QMICTLType; + pRsp->CTLMsg.QMICTLMsgHdrRsp.Length = htole16(sizeof(pRsp->CTLMsg.GetClientIdRsp) - sizeof(pRsp->CTLMsg.QMICTLMsgHdr)); + pRsp->CTLMsg.QMICTLMsgHdrRsp.TLVType = QCTLV_TYPE_RESULT_CODE; + pRsp->CTLMsg.QMICTLMsgHdrRsp.TLVLength = htole16(4); + pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXResult = htole16(ClientId ? 0 : QMI_RESULT_FAILURE); + pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXError = htole16(ClientId ? 0 : QMI_ERR_INTERNAL); + pRsp->CTLMsg.GetClientIdRsp.TLV2Type = QCTLV_TYPE_REQUIRED_PARAMETER; + pRsp->CTLMsg.GetClientIdRsp.TLV2Length = htole16(2); + pRsp->CTLMsg.GetClientIdRsp.QMIType = QMIType; + pRsp->CTLMsg.GetClientIdRsp.ClientId = ClientId; + + send_qmi_to_client(pRsp, clientfd); + free(pRsp); + find++; + } + } + else if (le16toh(pQMI->CTLMsg.QMICTLMsgHdr.QMICTLType) == QMICTL_RELEASE_CLIENT_ID_REQ) { + PQCQMIMSG pRsp = (PQCQMIMSG)malloc(256); + release_qrtr_client_id(qrtr_con, pQMI->CTLMsg.ReleaseClientIdReq.QMIType, pQMI->CTLMsg.ReleaseClientIdReq.ClientId); + + if (pRsp) { + pRsp->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI; + pRsp->QMIHdr.Length = htole16(sizeof(pRsp->CTLMsg.ReleaseClientIdRsp) + sizeof(pRsp->QMIHdr) - 1); + pRsp->QMIHdr.CtlFlags = 0x00; + pRsp->QMIHdr.QMIType = QMUX_TYPE_CTL; + pRsp->QMIHdr.ClientId = 0; + + pRsp->CTLMsg.QMICTLMsgHdrRsp.CtlFlags = QMICTL_FLAG_RESPONSE; + pRsp->CTLMsg.QMICTLMsgHdrRsp.TransactionId = pQMI->CTLMsg.QMICTLMsgHdr.TransactionId; + pRsp->CTLMsg.QMICTLMsgHdrRsp.QMICTLType = pQMI->CTLMsg.QMICTLMsgHdr.QMICTLType; + pRsp->CTLMsg.QMICTLMsgHdrRsp.Length = htole16(sizeof(pRsp->CTLMsg.ReleaseClientIdRsp) - sizeof(pRsp->CTLMsg.QMICTLMsgHdr)); + pRsp->CTLMsg.QMICTLMsgHdrRsp.TLVType = QCTLV_TYPE_RESULT_CODE; + pRsp->CTLMsg.QMICTLMsgHdrRsp.TLVLength = htole16(4); + pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXResult = htole16(0); + pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXError = htole16(0); + pRsp->CTLMsg.ReleaseClientIdRsp.TLV2Type = QCTLV_TYPE_REQUIRED_PARAMETER; + pRsp->CTLMsg.ReleaseClientIdRsp.TLV2Length = htole16(2); + pRsp->CTLMsg.ReleaseClientIdRsp.QMIType = pQMI->CTLMsg.ReleaseClientIdReq.QMIType; + pRsp->CTLMsg.ReleaseClientIdRsp.ClientId = pQMI->CTLMsg.ReleaseClientIdReq.ClientId; + + send_qmi_to_client(pRsp, clientfd); + free(pRsp); + find++; + } + } + else if (le16toh(pQMI->CTLMsg.QMICTLMsgHdr.QMICTLType) == QMICTL_GET_VERSION_REQ) { + PQCQMIMSG pRsp = (PQCQMIMSG)malloc(256); + + if (pRsp) { + pRsp->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI; + pRsp->QMIHdr.Length = htole16(sizeof(pRsp->CTLMsg.GetVersionRsp) + sizeof(pRsp->QMIHdr) - 1); + pRsp->QMIHdr.CtlFlags = 0x00; + pRsp->QMIHdr.QMIType = QMUX_TYPE_CTL; + pRsp->QMIHdr.ClientId = 0; + + pRsp->CTLMsg.QMICTLMsgHdrRsp.CtlFlags = QMICTL_FLAG_RESPONSE; + pRsp->CTLMsg.QMICTLMsgHdrRsp.TransactionId = pQMI->CTLMsg.QMICTLMsgHdr.TransactionId; + pRsp->CTLMsg.QMICTLMsgHdrRsp.QMICTLType = pQMI->CTLMsg.QMICTLMsgHdr.QMICTLType; + pRsp->CTLMsg.QMICTLMsgHdrRsp.Length = htole16(sizeof(pRsp->CTLMsg.GetVersionRsp) - sizeof(pRsp->CTLMsg.QMICTLMsgHdr)); + pRsp->CTLMsg.QMICTLMsgHdrRsp.TLVType = QCTLV_TYPE_RESULT_CODE; + pRsp->CTLMsg.QMICTLMsgHdrRsp.TLVLength = htole16(4); + pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXResult = htole16(0); + pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXError = htole16(0); + pRsp->CTLMsg.GetVersionRsp.TLV2Type = QCTLV_TYPE_REQUIRED_PARAMETER; + pRsp->CTLMsg.GetVersionRsp.TLV2Length = htole16(1); + pRsp->CTLMsg.GetVersionRsp.NumElements = 0; + + send_qmi_to_client(pRsp, clientfd); + free(pRsp); + find++; + } + } + } + else { + qlist_for_each (client_node, &qrtr_con->client_qnode) { + QRTR_PROXY_CLINET *qrtr_client = qnode_to_item(client_node, QRTR_PROXY_CLINET, qnode); + + if (pQMI->QMIHdr.QMIType == qrtr_client->QMIType && pQMI->QMIHdr.ClientId == qrtr_client->ClientId) { + QRTR_SERVICE *srv = find_qrtr_service(pQMI->QMIHdr.QMIType); + + if (srv && srv->service) { + struct sockaddr_qrtr from = {AF_QIPCRTR, qrtr_client->node_id, qrtr_client->port_id}; + struct sockaddr_qrtr to = {AF_QIPCRTR, srv->node, srv->port}; + + qrtr_node_enqueue(&pQMI->MUXMsg, le16toh(pQMI->QMIHdr.Length) + 1 - sizeof(QCQMI_HDR), + QRTR_TYPE_DATA, &from, &to, 0); + find++; + } + break; + } + } + } + + if (!find) { + dprintf("NOT find on %s()\n", __func__); + } + + return 0; +} + +static int qrtr_proxy_init(void) { + unsigned i; + int qrtr_sync_done = 0; + + dprintf("%s enter\n", __func__); + send_ctrl_hello(QRTR_NODE_BCAST, QRTR_PORT_CTRL); + + for (i = 0; i < 10; i++) { + sleep(1); + qrtr_sync_done = !qlist_empty(&qrtr_server_list); + if (qrtr_sync_done) + break; + } + + dprintf("%s %s\n", __func__, qrtr_sync_done ? "succful" : "fail"); + return qrtr_sync_done ? 0 : -1; +} + +static void qrtr_start_server(const char* servername) { + qrtr_proxy_server_fd = create_local_server(servername); + dprintf("qrtr_proxy_server_fd = %d\n", qrtr_proxy_server_fd); + if (qrtr_proxy_server_fd == -1) { + dprintf("Failed to create %s, errno: %d (%s)\n", servername, errno, strerror(errno)); + } +} + +static void qrtr_close_server(const char* servername) { + if (qrtr_proxy_server_fd != -1) { + dprintf("%s %s\n", __func__, servername); + close(qrtr_proxy_server_fd); + qrtr_proxy_server_fd = -1; + } +} + +static void *qrtr_proxy_loop(void *param) +{ + void *rx_buf; + struct qlistnode *con_node; + QRTR_PROXY_CONNECTION *qrtr_con; + + (void)param; + dprintf("%s enter thread_id %p\n", __func__, (void *)pthread_self()); + + rx_buf = malloc(8192); + if (!rx_buf) + return NULL; + + while (cdc_wdm_fd > 0 && qrtr_proxy_quit == 0) { + struct pollfd pollfds[32]; + int ne, ret, nevents = 0; + ssize_t nreads; + + pollfds[nevents].fd = cdc_wdm_fd; + pollfds[nevents].events = POLLIN; + pollfds[nevents].revents= 0; + nevents++; + + if (qrtr_proxy_server_fd > 0) { + pollfds[nevents].fd = qrtr_proxy_server_fd; + pollfds[nevents].events = POLLIN; + pollfds[nevents].revents= 0; + nevents++; + } + + qlist_for_each(con_node, &qrtr_proxy_connection) { + qrtr_con = qnode_to_item(con_node, QRTR_PROXY_CONNECTION, qnode); + + pollfds[nevents].fd = qrtr_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, (qrtr_proxy_server_fd > 0) ? -1 : 200); + } while (ret == -1 && errno == EINTR && qrtr_proxy_quit == 0); + + if (ret < 0) { + dprintf("%s poll=%d, errno: %d (%s)\n", __func__, ret, errno, strerror(errno)); + goto qrtr_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 qrtr_proxy_loop_exit; + } + else if (fd == qrtr_proxy_server_fd) { + + } + else { + cleanup_qrtr_connection(fd); + } + + continue; + } + + if (!(pollfds[ne].revents & POLLIN)) { + continue; + } + + if (fd == qrtr_proxy_server_fd) { + accept_qrtr_connection(fd); + } + else if (fd == cdc_wdm_fd) { + struct qrtr_hdr_v1 *hdr = (struct qrtr_hdr_v1 *)rx_buf; + + nreads = read(fd, rx_buf, 8192); + if (nreads <= 0) { + dprintf("%s read=%d errno: %d (%s)\n", __func__, (int)nreads, errno, strerror(errno)); + goto qrtr_proxy_loop_exit; + } + else if (nreads != (int)align_4(le32toh(hdr->size) + sizeof(*hdr))) { + dprintf("%s nreads=%d, hdr->size = %d\n", __func__, (int)nreads, le32toh(hdr->size)); + continue; + } + + dump_qrtr(hdr, nreads, '<'); + recv_qrtr_from_dev(hdr); + } + else { + PQCQMIMSG pQMI = (PQCQMIMSG)rx_buf; + + nreads = read(fd, rx_buf, 8192); + if (nreads <= 0) { + dprintf("%s read=%d errno: %d (%s)", __func__, (int)nreads, errno, strerror(errno)); + cleanup_qrtr_connection(fd); + break; + } + else if (nreads != (le16toh(pQMI->QMIHdr.Length) + 1)) { + dprintf("%s nreads=%d, pQCQMI->QMIHdr.Length = %d\n", __func__, (int)nreads, le16toh(pQMI->QMIHdr.Length)); + continue; + } + + recv_qmi_from_client(pQMI, fd); + } + } + } + +qrtr_proxy_loop_exit: + while (!qlist_empty(&qrtr_proxy_connection)) { + QRTR_PROXY_CONNECTION *qrtr_con = qnode_to_item(qlist_head(&qrtr_proxy_connection), QRTR_PROXY_CONNECTION, qnode); + + cleanup_qrtr_connection(qrtr_con->ClientFd); + } + + dprintf("%s exit, thread_id %p\n", __func__, (void *)pthread_self()); + free(rx_buf); + + return NULL; +} + +static void usage(void) { + dprintf(" -d A valid qrtr device\n" + " default /dev/mhi_IPCR, but mhi_IPCR may be invalid\n" + " -i netcard name\n" + " -v Will show all details\n"); +} + +static void sig_action(int sig) { + if (qrtr_proxy_quit == 0) { + qrtr_proxy_quit = 1; + if (thread_id) + pthread_kill(thread_id, sig); + } +} + +int main(int argc, char *argv[]) { + int opt; + char cdc_wdm[32+1] = "/dev/mhi_IPCR"; + char servername[64] = {0}; + + signal(SIGINT, sig_action); + signal(SIGTERM, sig_action); + + optind = 1; + while ( -1 != (opt = getopt(argc, argv, "d:i:vh"))) { + switch (opt) { + case 'd': + strcpy(cdc_wdm, optarg); + break; + case 'v': + verbose_debug = 1; + break; + default: + usage(); + return 0; + } + } + + sprintf(servername, "quectel-qrtr-proxy%c", cdc_wdm[strlen(cdc_wdm)-1]); + dprintf("Will use cdc-wdm='%s', proxy='%s'\n", cdc_wdm, servername); + + while (qrtr_proxy_quit == 0) { + 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)\n", cdc_wdm, errno, strerror(errno)); + sleep(5); + continue; + } + cfmakenoblock(cdc_wdm_fd); + qlist_init(&qrtr_proxy_connection); + qlist_init(&qrtr_server_list); + pthread_create(&thread_id, NULL, qrtr_proxy_loop, NULL); + + if (qrtr_proxy_init() == 0) { + qrtr_start_server(servername); + pthread_join(thread_id, NULL); + qrtr_close_server(servername); + } + else { + pthread_cancel(thread_id); + pthread_join(thread_id, NULL); + } + + close(cdc_wdm_fd); + } + + return 0; +} diff --git a/application/quectel_CM_5G/src/rmnetctl.c b/application/quectel_CM_5G/src/rmnetctl.c new file mode 100644 index 0000000..5b2fb0f --- /dev/null +++ b/application/quectel_CM_5G/src/rmnetctl.c @@ -0,0 +1,342 @@ +//https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource/dataservices/tree/rmnetctl +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RMNETCTL_SUCCESS 0 +#define RMNETCTL_LIB_ERR 1 +#define RMNETCTL_KERNEL_ERR 2 +#define RMNETCTL_INVALID_ARG 3 + +enum rmnetctl_error_codes_e { + RMNETCTL_API_SUCCESS = 0, + + RMNETCTL_API_FIRST_ERR = 1, + RMNETCTL_API_ERR_MESSAGE_SEND = 3, + RMNETCTL_API_ERR_MESSAGE_RECEIVE = 4, + + RMNETCTL_INIT_FIRST_ERR = 5, + RMNETCTL_INIT_ERR_PROCESS_ID = RMNETCTL_INIT_FIRST_ERR, + RMNETCTL_INIT_ERR_NETLINK_FD = 6, + RMNETCTL_INIT_ERR_BIND = 7, + + RMNETCTL_API_SECOND_ERR = 9, + RMNETCTL_API_ERR_HNDL_INVALID = RMNETCTL_API_SECOND_ERR, + RMNETCTL_API_ERR_RETURN_TYPE = 13, +}; + +struct rmnetctl_hndl_s { + uint32_t pid; + uint32_t transaction_id; + int netlink_fd; + struct sockaddr_nl src_addr, dest_addr; +}; +typedef struct rmnetctl_hndl_s rmnetctl_hndl_t; + +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((char *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +struct nlmsg { + struct nlmsghdr nl_addr; + struct ifinfomsg ifmsg; + char data[500]; +}; + +#define MIN_VALID_PROCESS_ID 0 +#define MIN_VALID_SOCKET_FD 0 +#define KERNEL_PROCESS_ID 0 +#define UNICAST 0 + +enum { + IFLA_RMNET_UL_AGG_PARAMS = __IFLA_RMNET_MAX, + __IFLA_RMNET_EXT_MAX, +}; + +struct rmnet_egress_agg_params { + uint16_t agg_size; + uint16_t agg_count; + uint32_t agg_time; +}; + +static int rmnet_get_ack(rmnetctl_hndl_t *hndl, uint16_t *error_code) +{ + struct nlack { + struct nlmsghdr ackheader; + struct nlmsgerr ackdata; + char data[256]; + + } ack; + int i; + + if (!hndl || !error_code) + return RMNETCTL_INVALID_ARG; + + if ((i = recv(hndl->netlink_fd, &ack, sizeof(ack), 0)) < 0) { + *error_code = errno; + return RMNETCTL_API_ERR_MESSAGE_RECEIVE; + } + + /*Ack should always be NLMSG_ERROR type*/ + if (ack.ackheader.nlmsg_type == NLMSG_ERROR) { + if (ack.ackdata.error == 0) { + *error_code = RMNETCTL_API_SUCCESS; + return RMNETCTL_SUCCESS; + } else { + *error_code = -ack.ackdata.error; + return RMNETCTL_KERNEL_ERR; + } + } + + *error_code = RMNETCTL_API_ERR_RETURN_TYPE; + return RMNETCTL_API_FIRST_ERR; +} + +static int rtrmnet_ctl_init(rmnetctl_hndl_t **hndl, uint16_t *error_code) +{ + struct sockaddr_nl __attribute__((__may_alias__)) *saddr_ptr; + int netlink_fd = -1; + pid_t pid = 0; + + if (!hndl || !error_code) + return RMNETCTL_INVALID_ARG; + + *hndl = (rmnetctl_hndl_t *)malloc(sizeof(rmnetctl_hndl_t)); + if (!*hndl) { + *error_code = RMNETCTL_API_ERR_HNDL_INVALID; + return RMNETCTL_LIB_ERR; + } + + memset(*hndl, 0, sizeof(rmnetctl_hndl_t)); + + pid = getpid(); + if (pid < MIN_VALID_PROCESS_ID) { + free(*hndl); + *error_code = RMNETCTL_INIT_ERR_PROCESS_ID; + return RMNETCTL_LIB_ERR; + } + (*hndl)->pid = KERNEL_PROCESS_ID; + netlink_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (netlink_fd < MIN_VALID_SOCKET_FD) { + free(*hndl); + *error_code = RMNETCTL_INIT_ERR_NETLINK_FD; + return RMNETCTL_LIB_ERR; + } + + (*hndl)->netlink_fd = netlink_fd; + + memset(&(*hndl)->src_addr, 0, sizeof(struct sockaddr_nl)); + + (*hndl)->src_addr.nl_family = AF_NETLINK; + (*hndl)->src_addr.nl_pid = (*hndl)->pid; + + saddr_ptr = &(*hndl)->src_addr; + if (bind((*hndl)->netlink_fd, + (struct sockaddr *)saddr_ptr, + sizeof(struct sockaddr_nl)) < 0) { + close((*hndl)->netlink_fd); + free(*hndl); + *error_code = RMNETCTL_INIT_ERR_BIND; + return RMNETCTL_LIB_ERR; + } + + memset(&(*hndl)->dest_addr, 0, sizeof(struct sockaddr_nl)); + + (*hndl)->dest_addr.nl_family = AF_NETLINK; + (*hndl)->dest_addr.nl_pid = KERNEL_PROCESS_ID; + (*hndl)->dest_addr.nl_groups = UNICAST; + + return RMNETCTL_SUCCESS; +} + +static int rtrmnet_ctl_deinit(rmnetctl_hndl_t *hndl) +{ + if (!hndl) + return RMNETCTL_SUCCESS; + + close(hndl->netlink_fd); + free(hndl); + + return RMNETCTL_SUCCESS; +} + +static int rtrmnet_ctl_newvnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname, + uint16_t *error_code, uint8_t index, + uint32_t flagconfig, uint32_t ul_agg_cnt, uint32_t ul_agg_size) +{ + struct rtattr *attrinfo, *datainfo, *linkinfo; + struct ifla_vlan_flags flags; + int devindex = 0, val = 0; + char *kind = "rmnet"; + struct nlmsg req; + short id; + + if (!hndl || !devname || !vndname || !error_code) + return RMNETCTL_INVALID_ARG; + + memset(&req, 0, sizeof(req)); + req.nl_addr.nlmsg_type = RTM_NEWLINK; + req.nl_addr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.nl_addr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | + NLM_F_ACK; + req.nl_addr.nlmsg_seq = hndl->transaction_id; + hndl->transaction_id++; + + /* Get index of devname*/ + devindex = if_nametoindex(devname); + if (devindex < 0) { + *error_code = errno; + return RMNETCTL_KERNEL_ERR; + } + + /* Setup link attr with devindex as data */ + val = devindex; + attrinfo = (struct rtattr *)(((char *)&req) + + NLMSG_ALIGN(req.nl_addr.nlmsg_len)); + attrinfo->rta_type = IFLA_LINK; + attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(sizeof(val))); + memcpy(RTA_DATA(attrinfo), &val, sizeof(val)); + req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) + + RTA_ALIGN(RTA_LENGTH(sizeof(val))); + + /* Set up IFLA info kind RMNET that has linkinfo and type */ + attrinfo = (struct rtattr *)(((char *)&req) + + NLMSG_ALIGN(req.nl_addr.nlmsg_len)); + attrinfo->rta_type = IFLA_IFNAME; + attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1)); + memcpy(RTA_DATA(attrinfo), vndname, strlen(vndname) + 1); + req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) + + RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1)); + + linkinfo = (struct rtattr *)(((char *)&req) + + NLMSG_ALIGN(req.nl_addr.nlmsg_len)); + linkinfo->rta_type = IFLA_LINKINFO; + linkinfo->rta_len = RTA_ALIGN(RTA_LENGTH(0)); + req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) + + RTA_ALIGN(RTA_LENGTH(0)); + + attrinfo = (struct rtattr *)(((char *)&req) + + NLMSG_ALIGN(req.nl_addr.nlmsg_len)); + attrinfo->rta_type = IFLA_INFO_KIND; + attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(kind))); + memcpy(RTA_DATA(attrinfo), kind, strlen(kind)); + req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) + + RTA_ALIGN(RTA_LENGTH(strlen(kind))); + + datainfo = (struct rtattr *)(((char *)&req) + + NLMSG_ALIGN(req.nl_addr.nlmsg_len)); + datainfo->rta_type = IFLA_INFO_DATA; + datainfo->rta_len = RTA_ALIGN(RTA_LENGTH(0)); + req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) + + RTA_ALIGN(RTA_LENGTH(0)); + + id = index; + attrinfo = (struct rtattr *)(((char *)&req) + + NLMSG_ALIGN(req.nl_addr.nlmsg_len)); + attrinfo->rta_type = IFLA_VLAN_ID; + attrinfo->rta_len = RTA_LENGTH(sizeof(id)); + memcpy(RTA_DATA(attrinfo), &id, sizeof(id)); + req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) + + RTA_ALIGN(RTA_LENGTH(sizeof(id))); + + if (flagconfig != 0) { + flags.mask = flagconfig; + flags.flags = flagconfig; + + attrinfo = (struct rtattr *)(((char *)&req) + + NLMSG_ALIGN(req.nl_addr.nlmsg_len)); + attrinfo->rta_type = IFLA_VLAN_FLAGS; + attrinfo->rta_len = RTA_LENGTH(sizeof(flags)); + memcpy(RTA_DATA(attrinfo), &flags, sizeof(flags)); + req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) + + RTA_ALIGN(RTA_LENGTH(sizeof(flags))); + } + + if (ul_agg_cnt > 1) { + struct rmnet_egress_agg_params agg_params; + + agg_params.agg_size = ul_agg_size; + agg_params.agg_count = ul_agg_cnt; + agg_params.agg_time = 3000000; + + attrinfo = (struct rtattr *)(((char *)&req) + + NLMSG_ALIGN(req.nl_addr.nlmsg_len)); + attrinfo->rta_type = IFLA_RMNET_UL_AGG_PARAMS; + attrinfo->rta_len = RTA_LENGTH(sizeof(agg_params)); + memcpy(RTA_DATA(attrinfo), &agg_params, sizeof(agg_params)); + req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) + + RTA_ALIGN(RTA_LENGTH(sizeof(agg_params))); + } + + datainfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)datainfo; + + linkinfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)linkinfo; + + if (send(hndl->netlink_fd, &req, req.nl_addr.nlmsg_len, 0) < 0) { + *error_code = RMNETCTL_API_ERR_MESSAGE_SEND; + return RMNETCTL_LIB_ERR; + } + + return rmnet_get_ack(hndl, error_code); +} + +int rtrmnet_ctl_create_vnd(char *devname, char *vndname, uint8_t muxid, + uint32_t qmap_version, uint32_t ul_agg_cnt, uint32_t ul_agg_size) +{ + struct rmnetctl_hndl_s *handle; + uint16_t error_code; + int return_code; + uint32_t flagconfig = RMNET_FLAGS_INGRESS_DEAGGREGATION; + + printf("%s devname: %s, vndname: %s, muxid: %d, qmap_version: %d\n", + __func__, devname, vndname, muxid, qmap_version); + + ul_agg_cnt = 0; //TODO + + if (ul_agg_cnt > 1) + flagconfig |= RMNET_EGRESS_FORMAT_AGGREGATION; + + if (qmap_version == 9) { //QMAPV5 +#ifdef RMNET_FLAGS_INGRESS_MAP_CKSUMV5 + flagconfig |= RMNET_FLAGS_INGRESS_MAP_CKSUMV5; + flagconfig |= RMNET_FLAGS_EGRESS_MAP_CKSUMV5; +#else + return -1001; +#endif + } + else if (qmap_version == 8) { //QMAPV4 + flagconfig |= RMNET_FLAGS_INGRESS_MAP_CKSUMV4; + flagconfig |= RMNET_FLAGS_EGRESS_MAP_CKSUMV4; + } + else if (qmap_version == 5) { //QMAPV1 + } + else { + flagconfig = 0; + } + + return_code = rtrmnet_ctl_init(&handle, &error_code); + if (return_code) { + printf("rtrmnet_ctl_init error_code: %d, return_code: %d, errno: %d (%s)\n", + error_code, return_code, errno, strerror(errno)); + } + if (return_code == RMNETCTL_SUCCESS) { + return_code = rtrmnet_ctl_newvnd(handle, devname, vndname, &error_code, + muxid, flagconfig, ul_agg_cnt, ul_agg_size); + if (return_code) { + printf("rtrmnet_ctl_newvnd error_code: %d, return_code: %d, errno: %d (%s)\n", + error_code, return_code, errno, strerror(errno)); + } + rtrmnet_ctl_deinit(handle); + } + + return return_code; +} diff --git a/application/quectel_CM_5G/src/udhcpc.c b/application/quectel_CM_5G/src/udhcpc.c new file mode 100644 index 0000000..5c2c12a --- /dev/null +++ b/application/quectel_CM_5G/src/udhcpc.c @@ -0,0 +1,745 @@ +/****************************************************************************** + @file udhcpc.c + @brief call DHCP tools to obtain IP address. + + DESCRIPTION + Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "QMIThread.h" +extern int ql_get_netcard_carrier_state(const char *devname); + +static __inline in_addr_t qmi2addr(uint32_t __x) { + return (__x>>24) | (__x>>8&0xff00) | (__x<<8&0xff0000) | (__x<<24); +} + +static int ql_system(const char *shell_cmd) { + dbg_time("%s", shell_cmd); + return system(shell_cmd); +} + +static void ifc_init_ifr(const char *name, struct ifreq *ifr) +{ + memset(ifr, 0, sizeof(struct ifreq)); + no_trunc_strncpy(ifr->ifr_name, name, IFNAMSIZ); + ifr->ifr_name[IFNAMSIZ - 1] = 0; +} + +static void ql_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) { + ifc_init_ifr(ifname, &ifr); + + 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 int ifc_get_addr(const char *name, in_addr_t *addr) +{ + int inet_sock; + struct ifreq ifr; + int ret = 0; + + inet_sock = socket(AF_INET, SOCK_DGRAM, 0); + + ifc_init_ifr(name, &ifr); + if (addr != NULL) { + ret = ioctl(inet_sock, SIOCGIFADDR, &ifr); + if (ret < 0) { + *addr = 0; + } else { + *addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr; + } + } + close(inet_sock); + return ret; +} + +static short ifc_get_flags(const char *ifname) +{ + int inet_sock; + struct ifreq ifr; + int ret = 0; + + inet_sock = socket(AF_INET, SOCK_DGRAM, 0); + + if (inet_sock > 0) { + ifc_init_ifr(ifname, &ifr); + + if (!ioctl(inet_sock, SIOCGIFFLAGS, &ifr)) { + ret = ifr.ifr_ifru.ifru_flags; + } + + close(inet_sock); + } + + return ret; +} + +static void ifc_set_state(const char *ifname, int state) { + char shell_cmd[128]; + + if (!access("/sbin/ip", X_OK)) { + snprintf(shell_cmd, sizeof(shell_cmd), "ip link set dev %s %s", ifname, state ? "up" : "down"); + } else { + snprintf(shell_cmd, sizeof(shell_cmd), "ifconfig %s %s", ifname, state ? "up" : "down"); + } + ql_system(shell_cmd); +} + +static int ql_netcard_ipv4_address_check(const char *ifname, in_addr_t ip) { + in_addr_t addr = 0; + + ifc_get_addr(ifname, &addr); + return addr == ip; +} + +static int ql_raw_ip_mode_check(const char *ifname, uint32_t ip) { + int fd; + char raw_ip[128]; + char mode[2] = "X"; + int mode_change = 0; + + if (ql_netcard_ipv4_address_check(ifname, qmi2addr(ip))) + return 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; + } + + if (read(fd, mode, 2) == -1) {}; + if (mode[0] == '0' || mode[0] == 'N') { + dbg_time("File:%s Line:%d udhcpc fail to get ip address, try next:", __func__, __LINE__); + ifc_set_state(ifname, 0); + dbg_time("echo Y > /sys/class/net/%s/qmi/raw_ip", ifname); + mode[0] = 'Y'; + if (write(fd, mode, 2) == -1) {}; + mode_change = 1; + ifc_set_state(ifname, 1); + } + + close(fd); + return mode_change; +} + +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; +} + +//#define USE_DHCLIENT +#ifdef USE_DHCLIENT +static int dhclient_alive = 0; +#endif +static int dibbler_client_alive = 0; + +void ql_set_driver_link_state(PROFILE_T *profile, int link_state) { + char link_file[128]; + int fd; + int new_state = 0; + + 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->muxid & 0x7F); + } + + snprintf(link_file, sizeof(link_file), "%d\n", new_state); + if (write(fd, link_file, sizeof(link_file)) == -1) {}; + + if (link_state == 0 && profile->qmapnet_adapter[0] + && strcmp(profile->qmapnet_adapter, profile->usbnet_adapter)) { + size_t rc; + + lseek(fd, 0, SEEK_SET); + rc = read(fd, link_file, sizeof(link_file)); + if (rc > 1 && (!strncasecmp(link_file, "0\n", 2) || !strncasecmp(link_file, "0x0\n", 4))) { + ifc_set_state(profile->usbnet_adapter, 0); + } + } + + close(fd); +} + +static const char *ipv4Str(const uint32_t Address) { + static char str[] = {"255.225.255.255"}; + uint8_t *ip = (uint8_t *)&Address; + + snprintf(str, sizeof(str), "%d.%d.%d.%d", ip[3], ip[2], ip[1], ip[0]); + return str; +} + +static const char *ipv6Str(const UCHAR Address[16]) { + static char str[64]; + uint16_t ip[8]; + int i; + for (i = 0; i < 8; i++) { + ip[i] = (Address[i*2]<<8) + Address[i*2+1]; + } + + snprintf(str, sizeof(str), "%x:%x:%x:%x:%x:%x:%x:%x", + ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7]); + + return str; +} + +void update_ipv4_address(const char *ifname, const char *ip, const char *gw, unsigned prefix) +{ + char shell_cmd[128]; + + if (!ifname) + return; + + if (!access("/sbin/ip", X_OK)) { + snprintf(shell_cmd, sizeof(shell_cmd), "ip -%d address flush dev %s", 4, ifname); + ql_system(shell_cmd); + + snprintf(shell_cmd, sizeof(shell_cmd), "ip -%d address add %s/%u dev %s", 4, ip, prefix, ifname); + ql_system(shell_cmd); + + //ping6 www.qq.com + snprintf(shell_cmd, sizeof(shell_cmd), "ip -%d route add default via %s dev %s", 4, gw, ifname); + ql_system(shell_cmd); + } else { + unsigned n = (0xFFFFFFFF >> (32 - prefix)) << (32 - prefix); + // n = (n>>24) | (n>>8&0xff00) | (n<<8&0xff0000) | (n<<24); + + snprintf(shell_cmd, sizeof(shell_cmd), "ifconfig %s %s netmask %s", ifname, ip, ipv4Str(n)); + ql_system(shell_cmd); + + //Resetting default routes + snprintf(shell_cmd, sizeof(shell_cmd), "route del default dev %s", ifname); + while(!system(shell_cmd)); + + snprintf(shell_cmd, sizeof(shell_cmd), "route add default gw %s dev %s", gw, ifname); + ql_system(shell_cmd); + } +} + +void update_ipv6_address(const char *ifname, const char *ip, const char *gw, unsigned prefix) { + char shell_cmd[128]; + + (void)gw; + if (!access("/sbin/ip", X_OK)) { + snprintf(shell_cmd, sizeof(shell_cmd), "ip -%d address flush dev %s", 6, ifname); + ql_system(shell_cmd); + + snprintf(shell_cmd, sizeof(shell_cmd), "ip -%d address add %s/%u dev %s", 6, ip, prefix, ifname); + ql_system(shell_cmd); + + //ping6 www.qq.com + snprintf(shell_cmd, sizeof(shell_cmd), "ip -%d route add default dev %s", 6, ifname); + ql_system(shell_cmd); + } else { + snprintf(shell_cmd, sizeof(shell_cmd), "ifconfig %s %s/%d", ifname, ip, prefix); + ql_system(shell_cmd); + + snprintf(shell_cmd, sizeof(shell_cmd), "route -A inet6 add default dev %s", ifname); + ql_system(shell_cmd); + } +} + +static void update_ip_address_by_qmi(const char *ifname, const IPV4_T *ipv4, const IPV6_T *ipv6) { + char *d1, *d2; + + if (ipv4 && ipv4->Address) { + d1 = strdup(ipv4Str(ipv4->Address)); + d2 = strdup(ipv4Str(ipv4->Gateway)); + unsigned prefix = 0; + unsigned n = 0; + + for (n = 0; n < 32; n++) { + if (ipv4->SubnetMask&((unsigned)1<DnsPrimary) { + d1 = strdup(ipv4Str(ipv4->DnsPrimary)); + d2 = strdup(ipv4Str(ipv4->DnsSecondary ? ipv4->DnsSecondary : ipv4->DnsPrimary)); + update_resolv_conf(4, ifname, d1, d2); + free(d1); free(d2); + } + } + + if (ipv6 && ipv6->Address[0] && ipv6->PrefixLengthIPAddr) { + d1 = strdup(ipv6Str(ipv6->Address)); + d2 = strdup(ipv6Str(ipv6->Gateway)); + + update_ipv6_address(ifname, d1, d2, ipv6->PrefixLengthIPAddr); + free(d1); free(d2); + + //Adding DNS + if (ipv6->DnsPrimary[0]) { + d1 = strdup(ipv6Str(ipv6->DnsPrimary)); + d2 = strdup(ipv6Str(ipv6->DnsSecondary[0] ? ipv6->DnsSecondary : ipv6->DnsPrimary)); + update_resolv_conf(6, ifname, d1, d2); + free(d1); free(d2); + } + } +} + +//#define QL_OPENWER_NETWORK_SETUP +#ifdef QL_OPENWER_NETWORK_SETUP +static const char *openwrt_lan = "br-lan"; +static const char *openwrt_wan = "wwan0"; + +static int ql_openwrt_system(const char *cmd) { + int i; + int ret = 1; + char shell_cmd[128]; + + snprintf(shell_cmd, sizeof(shell_cmd), "%s 2>1 > /dev/null", cmd); + + for (i = 0; i < 15; i++) { + dbg_time("%s", cmd); + ret = system(shell_cmd); + if (!ret) + break; + sleep(1); + } + + return ret; +} + +static int ql_openwrt_is_wan(const char *ifname) { + if (openwrt_lan == NULL) { + system("uci show network.wan.ifname"); + } + + if (strcmp(ifname, openwrt_wan)) + return 0; + + return 1; +} + +static void ql_openwrt_setup_wan(const char *ifname, const IPV4_T *ipv4) { + FILE *fp = NULL; + char config[64]; + + snprintf(config, sizeof(config), "/tmp/rmnet_%s_ipv4config", ifname); + + if (ipv4 == NULL) { + if (ql_openwrt_is_wan(ifname)) + ql_openwrt_system("ifdown wan"); + return; + } + + fp = fopen(config, "w"); + if (fp == NULL) + return; + + fprintf(fp, "IFNAME=\"%s\"\n", ifname); + fprintf(fp, "PUBLIC_IP=\"%s\"\n", ipv4Str(ipv4->Address)); + fprintf(fp, "NETMASK=\"%s\"\n", ipv4Str(ipv4->SubnetMask)); + fprintf(fp, "GATEWAY=\"%s\"\n", ipv4Str(ipv4->Gateway)); + fprintf(fp, "DNSSERVERS=\"%s", ipv4Str(ipv4->DnsPrimary)); + if (ipv4->DnsSecondary != 0) + fprintf(fp, " %s", ipv4Str(ipv4->DnsSecondary)); + fprintf(fp, "\"\n"); + + fclose(fp); + + if (!ql_openwrt_is_wan(ifname)) + return; + + ql_openwrt_system("ifup wan"); +} + +static void ql_openwrt_setup_wan6(const char *ifname, const IPV6_T *ipv6) { + FILE *fp = NULL; + char config[64]; + int first_ifup; + + snprintf(config, sizeof(config), "/tmp/rmnet_%s_ipv6config", ifname); + + if (ipv6 == NULL) { + if (ql_openwrt_is_wan(ifname)) + ql_openwrt_system("ifdown wan6"); + return; + } + + first_ifup = (access(config, F_OK) != 0); + + fp = fopen(config, "w"); + if (fp == NULL) + return; + + fprintf(fp, "IFNAME=\"%s\"\n", ifname); + fprintf(fp, "PUBLIC_IP=\"%s\"\n", ipv6Str(ipv6->Address)); + fprintf(fp, "NETMASK=\"%s\"\n", ipv6Str(ipv6->SubnetMask)); + fprintf(fp, "GATEWAY=\"%s\"\n", ipv6Str(ipv6->Gateway)); + fprintf(fp, "PrefixLength=\"%d\"\n", ipv6->PrefixLengthIPAddr); + fprintf(fp, "DNSSERVERS=\"%s", ipv6Str(ipv6->DnsPrimary)); + if (ipv6->DnsSecondary[0]) + fprintf(fp, " %s", ipv6Str(ipv6->DnsSecondary)); + fprintf(fp, "\"\n"); + + fclose(fp); + + if (!ql_openwrt_is_wan(ifname)) + return; + + if (first_ifup) + ql_openwrt_system("ifup wan6"); + else + ql_openwrt_system("/etc/init.d/network restart"); //make PC to release old IPV6 address, and RS new IPV6 address + +#if 1 //TODO? why need this? + if (openwrt_lan) { + int i; + char shell_cmd[128]; + UCHAR Address[16] = {0}; + + ql_openwrt_system(("ifstatus lan")); + + for (i = 0; i < (ipv6->PrefixLengthIPAddr/8); i++) + Address[i] = ipv6->Address[i]; + + snprintf(shell_cmd, sizeof(shell_cmd), "ip route del %s/%u dev %s", ipv6Str(Address), ipv6->PrefixLengthIPAddr, ifname); + ql_openwrt_system(shell_cmd); + + snprintf(shell_cmd, sizeof(shell_cmd), "ip route add %s/%u dev %s", ipv6Str(Address), ipv6->PrefixLengthIPAddr, openwrt_lan); + ql_system(shell_cmd); + } +#endif +} +#endif + +void udhcpc_start(PROFILE_T *profile) { + char *ifname = profile->usbnet_adapter; + + ql_set_driver_link_state(profile, 1); + + if (profile->qmapnet_adapter[0]) { + ifname = profile->qmapnet_adapter; + } + + if (profile->rawIP && profile->ipv4.Address && profile->ipv4.Mtu) { + ql_set_mtu(ifname, (profile->ipv4.Mtu)); + } + + if (strcmp(ifname, profile->usbnet_adapter)) { + ifc_set_state(profile->usbnet_adapter, 1); + if (ifc_get_flags(ifname)&IFF_UP) { + ifc_set_state(ifname, 0); + } + } + + ifc_set_state(ifname, 1); + if (profile->ipv4.Address) { + if (profile->PCSCFIpv4Addr1) + dbg_time("pcscf1: %s", ipv4Str(profile->PCSCFIpv4Addr1)); + if (profile->PCSCFIpv4Addr2) + dbg_time("pcscf2: %s", ipv4Str(profile->PCSCFIpv4Addr2)); + } + + if (profile->ipv6.Address[0] && profile->ipv6.PrefixLengthIPAddr) { + if (profile->PCSCFIpv6Addr1[0]) + dbg_time("pcscf1: %s", ipv6Str(profile->PCSCFIpv6Addr1)); + if (profile->PCSCFIpv6Addr2[0]) + dbg_time("pcscf2: %s", ipv6Str(profile->PCSCFIpv6Addr2)); + } + +#if 1 //for bridge mode, only one public IP, so do udhcpc manually + if (ql_bridge_mode_detect(profile)) { + return; + } +#endif + +//because must use udhcpc to obtain IP when working on ETH mode, +//so it is better also use udhcpc to obtain IP when working on IP mode. +//use the same policy for all modules +#if 0 + if (profile->rawIP != 0) //mdm9x07/ec25,ec20 R2.0 + { + update_ip_address_by_qmi(ifname, &profile->ipv4, &profile->ipv6); + return; + } +#endif + + if (profile->ipv4.Address == 0) + goto set_ipv6; + + if (profile->no_dhcp || profile->request_ops == &mbim_request_ops) { //lots of mbim modem do not support DHCP + update_ip_address_by_qmi(ifname, &profile->ipv4, NULL); + } + else +/* 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); + +#ifdef USE_DHCLIENT + snprintf(udhcpc_cmd, sizeof(udhcpc_cmd), "dhclient -4 -d --no-pid %s", ifname); + dhclient_alive++; +#else + if (access("/usr/share/udhcpc/default.script", X_OK) + && access("/etc//udhcpc/default.script", X_OK)) { + dbg_time("No default.script found, it should be in '/usr/share/udhcpc/' or '/etc//udhcpc' depend on your udhcpc version!"); + } + + //-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); +#endif + +#if 1 //for OpenWrt + if (!access("/lib/netifd/dhcp.script", X_OK) && !access("/sbin/ifup", X_OK) && !access("/sbin/ifstatus", X_OK)) { +#if 0 //20210415 do not promot these message + 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); +#endif + } +#endif + +#ifdef USE_DHCLIENT + pthread_create(&udhcpc_thread_id, &udhcpc_thread_attr, udhcpc_thread_function, (void*)strdup(udhcpc_cmd)); + sleep(1); +#else + pthread_create(&udhcpc_thread_id, NULL, udhcpc_thread_function, (void*)strdup(udhcpc_cmd)); + pthread_join(udhcpc_thread_id, NULL); + + if (profile->request_ops == &atc_request_ops) { + profile->udhcpc_ip = 0; + ifc_get_addr(ifname, &profile->udhcpc_ip); + if (profile->udhcpc_ip != profile->ipv4.Address) { + unsigned char *l = (unsigned char *)&profile->udhcpc_ip; + unsigned char *r = (unsigned char *)&profile->ipv4.Address; + dbg_time("ERROR: IP from udhcpc (%d.%d.%d.%d) is different to IP from ATC (%d.%d.%d.%d)!", + l[0], l[1], l[2], l[3], r[0], r[1], r[2], r[3]); + ql_get_netcard_carrier_state(ifname); //miss udhcpc default.script or modem not report usb-net-cdc-linkup + } + } + + if (profile->request_ops != &qmi_request_ops) { //only QMI modem support next fixup! + goto set_ipv6; + } + + if (ql_raw_ip_mode_check(ifname, profile->ipv4.Address)) { + pthread_create(&udhcpc_thread_id, NULL, udhcpc_thread_function, (void*)strdup(udhcpc_cmd)); + pthread_join(udhcpc_thread_id, NULL); + } + + if (!ql_netcard_ipv4_address_check(ifname, qmi2addr(profile->ipv4.Address))) { + //no udhcpc's default.script exist, directly set ip and dns + update_ip_address_by_qmi(ifname, &profile->ipv4, NULL); + } + //Add by Demon. check default route + FILE *rt_fp = NULL; + char rt_cmd[128] = {0}; + + //Check if there is a default route. + snprintf(rt_cmd, sizeof(rt_cmd), "route -n | grep %s | awk '{print $1}' | grep 0.0.0.0", ifname); + rt_fp = popen((const char *)rt_cmd, "r"); + if (rt_fp != NULL) { + char buf[20] = {0}; + int found_default_rt = 0; + + if (fgets(buf, sizeof(buf), rt_fp) != NULL) { + //Find the specified interface + found_default_rt = 1; + } + + if (1 == found_default_rt) { + //dbg_time("Route items found for %s", ifname); + } + else { + dbg_time("Warning: No route items found for %s", ifname); + } + + pclose(rt_fp); + } + //End by Demon. +#endif + } + +#ifdef QL_OPENWER_NETWORK_SETUP + ql_openwrt_setup_wan(ifname, &profile->ipv4); +#endif + +set_ipv6: + if (profile->ipv6.Address[0] && profile->ipv6.PrefixLengthIPAddr) { +#if 1 + //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]; + if (read(forward_fd, forward_state, 2) == -1) {}; + if (forward_state[0] == '1') { + //dbg_time("%s enabled, kernel maybe donot send 'Router Solicit'", forward_file); + } + close(forward_fd); + } + + update_ip_address_by_qmi(ifname, NULL, &profile->ipv6); + + if (profile->ipv6.DnsPrimary[0] || profile->ipv6.DnsSecondary[0]) { + char dns1str[64], dns2str[64]; + + if (profile->ipv6.DnsPrimary[0]) { + strcpy(dns1str, ipv6Str(profile->ipv6.DnsPrimary)); + } + + if (profile->ipv6.DnsSecondary[0]) { + strcpy(dns2str, ipv6Str(profile->ipv6.DnsSecondary)); + } + + update_resolv_conf(6, ifname, profile->ipv6.DnsPrimary[0] ? dns1str : NULL, + profile->ipv6.DnsSecondary[0] != '\0' ? dns2str : NULL); + } + +#ifdef QL_OPENWER_NETWORK_SETUP + ql_openwrt_setup_wan6(ifname, &profile->ipv6); +#endif +#else +#ifdef USE_DHCLIENT + snprintf(udhcpc_cmd, sizeof(udhcpc_cmd), "dhclient -6 -d --no-pid %s", ifname); + dhclient_alive++; +#else + /* + DHCPv6: Dibbler - a portable DHCPv6 + 1. download from http://klub.com.pl/dhcpv6/ + 2. cross-compile + 2.1 ./configure --host=arm-linux-gnueabihf + 2.2 copy dibbler-client to your board + 3. mkdir -p /var/log/dibbler/ /var/lib/ on your board + 4. create /etc/dibbler/client.conf on your board, the content is + log-mode short + log-level 7 + iface wwan0 { + ia + option dns-server + } + 5. run "dibbler-client start" to get ipV6 address + 6. run "route -A inet6 add default dev wwan0" to add default route + */ + snprintf(shell_cmd, sizeof(shell_cmd), "route -A inet6 add default %s", ifname); + ql_system(shell_cmd); + snprintf(udhcpc_cmd, sizeof(udhcpc_cmd), "dibbler-client run"); + dibbler_client_alive++; +#endif + + pthread_create(&udhcpc_thread_id, &udhcpc_thread_attr, udhcpc_thread_function, (void*)strdup(udhcpc_cmd)); +#endif + } +} + +void udhcpc_stop(PROFILE_T *profile) { + char *ifname = profile->usbnet_adapter; + char shell_cmd[128]; + + ql_set_driver_link_state(profile, 0); + + if (profile->qmapnet_adapter[0]) { + ifname = profile->qmapnet_adapter; + } + +#ifdef USE_DHCLIENT + if (dhclient_alive) { + system("killall dhclient"); + dhclient_alive = 0; + } +#endif + if (dibbler_client_alive) { + if (system("killall dibbler-client")) {}; + dibbler_client_alive = 0; + } + + profile->udhcpc_ip = 0; +//it seems when call netif_carrier_on(), and netcard 's IP is "0.0.0.0", will cause netif_queue_stopped() + if (!access("/sbin/ip", X_OK)) + snprintf(shell_cmd, sizeof(shell_cmd), "ip addr flush dev %s", ifname); + else + snprintf(shell_cmd, sizeof(shell_cmd), "ifconfig %s 0.0.0.0", ifname); + ql_system(shell_cmd); + ifc_set_state(ifname, 0); + +#ifdef QL_OPENWER_NETWORK_SETUP + ql_openwrt_setup_wan(ifname, NULL); + ql_openwrt_setup_wan6(ifname, NULL); +#endif +} diff --git a/application/quectel_CM_5G/src/udhcpc_netlink.c b/application/quectel_CM_5G/src/udhcpc_netlink.c new file mode 100644 index 0000000..56209f8 --- /dev/null +++ b/application/quectel_CM_5G/src/udhcpc_netlink.c @@ -0,0 +1,179 @@ +#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 ql_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; +} + +void ql_set_driver_link_state(PROFILE_T *profile, int link_state) +{ + char link_file[128]; + int fd; + int new_state = 0; + + 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) +{ + char *ifname = profile->usbnet_adapter; + + ql_set_driver_link_state(profile, 1); + ql_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); + } + + if_link_up(ifname); + +#if 1 //for bridge mode, only one public IP, so do udhcpc manually + if (ql_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)); + } + + 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); + } + + dbg_time("IPv6 MTU: %d", profile->ipv6.Mtu); + dbg_time("IPv6 Address: %s", ipaddr_to_string_v6(profile->ipv6.Address)); + dbg_time("IPv6 Netmask: %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); + } +} + +void udhcpc_stop(PROFILE_T *profile) +{ + char *ifname = profile->usbnet_adapter; + + ql_set_driver_link_state(profile, 0); + + if (profile->qmapnet_adapter) + { + ifname = profile->qmapnet_adapter; + } + + if_link_down(ifname); + if_flush_v4_addr(ifname); + if_flush_v6_addr(ifname); +} diff --git a/application/quectel_CM_5G/src/udhcpc_script.c b/application/quectel_CM_5G/src/udhcpc_script.c new file mode 100644 index 0000000..3282316 --- /dev/null +++ b/application/quectel_CM_5G/src/udhcpc_script.c @@ -0,0 +1,132 @@ +/****************************************************************************** + @file udhcpc.c + @brief call DHCP tools to obtain IP address. + + DESCRIPTION + Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "QMIThread.h" + +#define IFDOWN_SCRIPT "/etc/quectel/ifdown.sh" +#define IFUP_SCRIPT "/etc/quectel/ifup.sh" + +static int ql_system(const char *shell_cmd) +{ + dbg_time("%s", shell_cmd); + return system(shell_cmd); +} + +uint32_t mask_to_prefix_v4(uint32_t mask) +{ + uint32_t prefix = 0; + while (mask) + { + mask = mask & (mask - 1); + prefix++; + } + return prefix; +} + +uint32_t mask_from_prefix_v4(uint32_t prefix) +{ + return ~((1 << (32 - prefix)) - 1); +} + +/* mask in int */ +uint32_t broadcast_from_mask(uint32_t ip, uint32_t mask) +{ + return (ip & mask) | (~mask); +} + +const char *ipaddr_to_string_v4(in_addr_t ipaddr, char *buf, size_t size) +{ + // static char buf[INET6_ADDRSTRLEN] = {'\0'}; + buf[0] = '\0'; + uint32_t addr = ipaddr; + return inet_ntop(AF_INET, &addr, buf, size); +} + +const char *ipaddr_to_string_v6(uint8_t *ipaddr, char *buf, size_t size) +{ + buf[0] = '\0'; + return inet_ntop(AF_INET6, ipaddr, buf, size); +} + +/** + * For more details see default.script + * + * The main aim of this function is offload ip management to script, CM has not interest in manage IP address. + * just tell script all the info about ip, mask, router, dns... + */ +void udhcpc_start(PROFILE_T *profile) +{ + char shell_cmd[1024]; + char ip[128]; + char subnet[128]; + char broadcast[128]; + char router[128]; + char domain1[128]; + char domain2[128]; + + if (NULL == getenv(IFUP_SCRIPT)) + return; + + // manage IPv4??? + // check rawip ??? + snprintf(shell_cmd, sizeof(shell_cmd), + " netiface=%s interface=%s mtu=%u ip=%s subnet=%s broadcast=%s router=%s" + " domain=\"%s %s\" %s", + profile->usbnet_adapter, + profile->qmapnet_adapter ? profile->qmapnet_adapter : profile->usbnet_adapter, + profile->ipv4.Mtu, + ipaddr_to_string_v4(ntohl(profile->ipv4.Address), ip, sizeof(ip)), + ipaddr_to_string_v4(ntohl(profile->ipv4.SubnetMask), subnet, sizeof(subnet)), + ipaddr_to_string_v4(ntohl(broadcast_from_mask(profile->ipv4.Address, profile->ipv4.SubnetMask)), + broadcast, sizeof(broadcast)), + ipaddr_to_string_v4(ntohl(profile->ipv4.Gateway), router, sizeof(router)), + ipaddr_to_string_v4(ntohl(profile->ipv4.DnsPrimary), domain1, sizeof(domain1)), + ipaddr_to_string_v4(ntohl(profile->ipv4.DnsSecondary), domain2, sizeof(domain2)), + getenv(IFUP_SCRIPT)); + ql_system(shell_cmd); + + // manage IPv6??? +} + +/** + * For more details see default.script + * + * The main aim of this function is offload ip management to script, CM has not interest in manage IP address. + * just tell script all the info about ip, mask, router, dns... + */ +void udhcpc_stop(PROFILE_T *profile) +{ + char shell_cmd[1024]; + + if (NULL == getenv(IFDOWN_SCRIPT)) + return; + + snprintf(shell_cmd, sizeof(shell_cmd), + "netiface=%s interface=%s %s", + profile->usbnet_adapter, + profile->qmapnet_adapter ? profile->qmapnet_adapter : profile->usbnet_adapter, + getenv(IFDOWN_SCRIPT)); + ql_system(shell_cmd); +} diff --git a/application/quectel_CM_5G/src/util.c b/application/quectel_CM_5G/src/util.c new file mode 100644 index 0000000..ed99117 --- /dev/null +++ b/application/quectel_CM_5G/src/util.c @@ -0,0 +1,361 @@ +/****************************************************************************** + @file util.c + @brief some utils for this QCM tool. + + DESCRIPTION + Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules. + + INITIALIZATION AND SEQUENCING REQUIREMENTS + None. + + --------------------------------------------------------------------------- + Copyright (c) 2016 - 2023 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. + Quectel Wireless Solution Proprietary and Confidential. + --------------------------------------------------------------------------- +******************************************************************************/ + +#include +#include +typedef unsigned short sa_family_t; +#include + +#if defined(__STDC__) +#include +#define __V(x) x +#else +#include +#define __V(x) (va_alist) va_dcl +#define const +#define volatile +#endif + +#include + +#include "QMIThread.h" + +pthread_mutex_t cm_command_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t cm_command_cond = PTHREAD_COND_INITIALIZER; +unsigned int cm_recv_buf[1024]; + +int cm_open_dev(const char *dev) { + int fd; + + fd = open(dev, O_RDWR | O_NONBLOCK | O_NOCTTY); + if (fd != -1) { + fcntl(fd, F_SETFL, fcntl(fd,F_GETFL) | O_NONBLOCK); + fcntl(fd, F_SETFD, FD_CLOEXEC); + + if (!strncmp(dev, "/dev/tty", strlen("/dev/tty"))) + { + //disable echo on serial ports + struct termios ios; + + memset(&ios, 0, sizeof(ios)); + tcgetattr( fd, &ios ); + cfmakeraw(&ios); + cfsetispeed(&ios, B115200); + cfsetospeed(&ios, B115200); + tcsetattr( fd, TCSANOW, &ios ); + tcflush(fd, TCIOFLUSH); + } + } else { + dbg_time("Failed to open %s, errno: %d (%s)", dev, errno, strerror(errno)); + } + + return fd; +} + +int cm_open_proxy(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("connect %s errno: %d (%s)", name, errno, strerror(errno)); + return -1; + } + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,sizeof(reuse_addr)); + fcntl(sockfd, F_SETFL, fcntl(sockfd,F_GETFL) | O_NONBLOCK); + fcntl(sockfd, F_SETFD, FD_CLOEXEC); + + dbg_time("connect to %s sockfd = %d", name, sockfd); + + return sockfd; +} + +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; + if ((unsigned long)p_ts->tv_nsec >= 1000000000UL) { + p_ts->tv_sec += 1; + p_ts->tv_nsec -= 1000000000UL; + } +} + +int pthread_cond_timeout_np(pthread_cond_t *cond, pthread_mutex_t * mutex, unsigned msecs) { + if (msecs != 0) { + unsigned i; + unsigned t = msecs/4; + int ret = 0; + + if (t == 0) + t = 1; + + for (i = 0; i < msecs; i += t) { + struct timespec ts; + setTimespecRelative(&ts, t); +//very old uclibc do not support pthread_condattr_setclock(CLOCK_MONOTONIC) + ret = pthread_cond_timedwait(cond, mutex, &ts); //to advoid system time change + if (ret != ETIMEDOUT) { + if(ret) dbg_time("ret=%d, msecs=%u, t=%u", ret, msecs, t); + break; + } + } + + return ret; + } else { + return pthread_cond_wait(cond, mutex); + } +} + +const char * get_time(void) { + static char time_buf[128]; + 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; +} + +unsigned long clock_msec(void) +{ + struct timespec tm; + clock_gettime( CLOCK_MONOTONIC, &tm); + return (unsigned long)(tm.tv_sec*1000 + (tm.tv_nsec/1000000)); +} + +FILE *logfilefp = NULL; + +void update_resolv_conf(int iptype, const char *ifname, const char *dns1, const char *dns2) { + const char *dns_file = "/etc/resolv.conf"; + FILE *dns_fp; + char dns_line[256]; + #define MAX_DNS 16 + char *dns_info[MAX_DNS]; + char dns_tag[64]; + int dns_match = 0; + int i; + + snprintf(dns_tag, sizeof(dns_tag), "# IPV%d %s", iptype, ifname); + + for (i = 0; i < MAX_DNS; i++) + dns_info[i] = NULL; + + dns_fp = fopen(dns_file, "r"); + if (dns_fp) { + i = 0; + dns_line[sizeof(dns_line)-1] = '\0'; + + while((fgets(dns_line, sizeof(dns_line)-1, dns_fp)) != NULL) { + if ((strlen(dns_line) > 1) && (dns_line[strlen(dns_line) - 1] == '\n')) + dns_line[strlen(dns_line) - 1] = '\0'; + //dbg_time("%s", dns_line); + if (strstr(dns_line, dns_tag)) { + dns_match++; + continue; + } + dns_info[i++] = strdup(dns_line); + if (i == MAX_DNS) + break; + } + + fclose(dns_fp); + } + else if (errno != ENOENT) { + dbg_time("fopen %s fail, errno:%d (%s)", dns_file, errno, strerror(errno)); + return; + } + + if (dns1 == NULL && dns_match == 0) + return; + + dns_fp = fopen(dns_file, "w"); + if (dns_fp) { + if (dns1) + fprintf(dns_fp, "nameserver %s %s\n", dns1, dns_tag); + if (dns2) + fprintf(dns_fp, "nameserver %s %s\n", dns2, dns_tag); + + for (i = 0; i < MAX_DNS && dns_info[i]; i++) + fprintf(dns_fp, "%s\n", dns_info[i]); + fclose(dns_fp); + } + else { + dbg_time("fopen %s fail, errno:%d (%s)", dns_file, errno, strerror(errno)); + } + + for (i = 0; i < MAX_DNS && dns_info[i]; i++) + free(dns_info[i]); +} + +pid_t getpid_by_pdp(int pdp, const char* program_name) +{ + glob_t gt; + int ret; + char filter[16]; + pid_t pid; + + snprintf(filter, sizeof(filter), "-n %d", pdp); + ret = glob("/proc/*/cmdline", GLOB_NOSORT, NULL, >); + if (ret != 0) { + dbg_time("glob error, errno = %d(%s)", errno, strerror(errno)); + return -1; + } else { + int i = 0, fd = -1; + ssize_t nreads; + char cmdline[512] = {0}; + + for (i = 0; i < (int)gt.gl_pathc; i++) { + fd = open(gt.gl_pathv[i], O_RDONLY); + if (fd == -1) { + dbg_time("open %s failed, errno = %d(%s)", gt.gl_pathv[i], errno, strerror(errno)); + globfree(>); + return -1; + } + + nreads = read(fd, cmdline, sizeof(cmdline)); + if (nreads > 0) { + int pos = 0; + while (pos < nreads-1) { + if (cmdline[pos] == '\0') + cmdline[pos] = ' '; // space + pos++; + } + // printf("%s\n", cmdline); + } + + if (strstr(cmdline, program_name) && strstr(cmdline, filter)) { + char path[64] = {0}; + char pidstr[64] = {0}; + char *p; + + dbg_time("%s: %s", gt.gl_pathv[i], cmdline); + strcpy(path, gt.gl_pathv[i]); + p = strstr(gt.gl_pathv[i], "/cmdline"); + *p = '\0'; + while (*(--p) != '/') ; + + strcpy(pidstr, p+1); + pid = atoi(pidstr); + globfree(>); + + return pid; + } + } + } + + globfree(>); + return -1; +} + +void ql_get_driver_rmnet_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) { + if (errno != ENOTSUP) + dbg_time("ioctl(0x%x, qmap_settings) errno:%d (%s), rc=%d", request, errno, strerror(errno), rc); + } + else { + memcpy(rmnet_info, data, sizeof(*rmnet_info)); + } + + close(ifc_ctl_sock); +} + +void ql_set_driver_qmap_setting(PROFILE_T *profile, QMAP_SETTING *qmap_settings) { + int ifc_ctl_sock; + struct ifreq ifr; + int rc; + int request = 0x89F2; + + 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 *)qmap_settings; + + 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); + } + + close(ifc_ctl_sock); +} + +void no_trunc_strncpy(char *dest, const char *src, size_t dest_size) +{ + size_t i = 0; + + for (i = 0; i < dest_size && *src; i++) { + *dest++ = *src++; + } + + *dest = 0; +} diff --git a/application/quectel_CM_5G/src/util.h b/application/quectel_CM_5G/src/util.h new file mode 100644 index 0000000..4354bbc --- /dev/null +++ b/application/quectel_CM_5G/src/util.h @@ -0,0 +1,52 @@ +/** + @file + util.h + + @brief + This file provides the definitions, and declares some common APIs for list-algorithm. + + */ + +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#include +#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 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); +const char * get_time(void); +unsigned long clock_msec(void); +pid_t getpid_by_pdp(int, const char*); + +#endif