From d0d06431799dc68fce57c940a8fbd37598b1e3eb Mon Sep 17 00:00:00 2001 From: fujr Date: Thu, 24 Oct 2024 15:38:17 +0800 Subject: [PATCH] add quectel-CM with metric option --- application/quectel_CM_5G/Makefile | 41 + application/quectel_CM_5G/files/dhcp | 48 + application/quectel_CM_5G/files/rmnet.script | 65 + application/quectel_CM_5G/files/rmnet.sh | 31 + application/quectel_CM_5G/files/rmnet6.script | 60 + application/quectel_CM_5G/files/rmnet6.sh | 31 + application/quectel_CM_5G/files/rmnet_init.sh | 31 + application/quectel_CM_5G/src/CMakeLists.txt | 36 + application/quectel_CM_5G/src/GobiNetCM.c | 246 + application/quectel_CM_5G/src/Makefile | 46 + application/quectel_CM_5G/src/Makefile.am | 22 + application/quectel_CM_5G/src/NOTICE | 7 + application/quectel_CM_5G/src/QCQCTL.h | 394 ++ application/quectel_CM_5G/src/QCQMI.h | 320 ++ application/quectel_CM_5G/src/QCQMUX.c | 477 ++ application/quectel_CM_5G/src/QCQMUX.h | 4310 +++++++++++++++++ application/quectel_CM_5G/src/QMIThread.c | 3037 ++++++++++++ application/quectel_CM_5G/src/QMIThread.h | 424 ++ application/quectel_CM_5G/src/QmiWwanCM.c | 459 ++ application/quectel_CM_5G/src/ReleaseNote.txt | 339 ++ application/quectel_CM_5G/src/at_tok.c | 283 ++ application/quectel_CM_5G/src/at_tok.h | 33 + application/quectel_CM_5G/src/atc.c | 1055 ++++ application/quectel_CM_5G/src/atchannel.c | 1037 ++++ application/quectel_CM_5G/src/atchannel.h | 152 + application/quectel_CM_5G/src/configure.ac | 48 + application/quectel_CM_5G/src/default.script | 63 + .../quectel_CM_5G/src/default.script_ip | 61 + application/quectel_CM_5G/src/device.c | 746 +++ application/quectel_CM_5G/src/ethtool-copy.h | 1100 +++++ .../quectel_CM_5G/src/log/cdc_mbim.txt | 71 + .../quectel_CM_5G/src/log/cdc_mbim_vlan.txt | 168 + .../quectel_CM_5G/src/log/ecm_ncm_rndis.txt | 129 + application/quectel_CM_5G/src/log/gobinet.txt | 62 + .../quectel_CM_5G/src/log/gobinet_bridge.txt | 60 + .../quectel_CM_5G/src/log/gobinet_qmap=1.txt | 45 + .../src/log/gobinet_qmap=1_bridge.txt | 62 + .../quectel_CM_5G/src/log/gobinet_qmap=4.txt | 146 + .../src/log/gobinet_qmap=4_bridge.txt | 114 + .../quectel_CM_5G/src/log/pcie_mhi_mbim.txt | 80 + .../src/log/pcie_mhi_mbim_qmap=4.txt | 170 + .../quectel_CM_5G/src/log/pcie_mhi_qmap=1.txt | 127 + .../src/log/pcie_mhi_qmap=1_bridge.txt | 76 + .../quectel_CM_5G/src/log/pcie_mhi_qmap=4.txt | 138 + .../src/log/pcie_mhi_qmap=4_bridge.txt | 147 + .../quectel_CM_5G/src/log/qmi_wwan_q.txt | 65 + .../src/log/qmi_wwan_q_bridge.txt | 57 + .../src/log/qmi_wwan_q_qmap=1.txt | 54 + .../src/log/qmi_wwan_q_qmap=1_bridge.txt | 86 + .../src/log/qmi_wwan_q_qmap=4.txt | 185 + .../src/log/qmi_wwan_q_qmap=4_bridge.txt | 132 + .../quectel_CM_5G/src/log/qmi_wwan_qmap=4.txt | 55 + .../src/log/usage_of_argument/6.txt | 68 + .../src/log/usage_of_argument/m.txt | 58 + application/quectel_CM_5G/src/main.c | 961 ++++ application/quectel_CM_5G/src/mbim-cm.c | 2426 ++++++++++ application/quectel_CM_5G/src/qendian.h | 52 + application/quectel_CM_5G/src/qlist.h | 38 + .../quectel_CM_5G/src/qmap_bridge_mode.c | 402 ++ application/quectel_CM_5G/src/qrtr.c | 657 +++ application/quectel_CM_5G/src/qrtr.h | 74 + application/quectel_CM_5G/src/quectel-CM | Bin 0 -> 216288 bytes .../quectel_CM_5G/src/quectel-atc-proxy | Bin 0 -> 44024 bytes .../quectel_CM_5G/src/quectel-atc-proxy.c | 506 ++ .../quectel_CM_5G/src/quectel-mbim-proxy | Bin 0 -> 18064 bytes .../quectel_CM_5G/src/quectel-mbim-proxy.c | 453 ++ .../quectel_CM_5G/src/quectel-qmi-proxy | Bin 0 -> 23128 bytes .../quectel_CM_5G/src/quectel-qmi-proxy.c | 700 +++ .../quectel_CM_5G/src/quectel-qrtr-proxy.c | 894 ++++ application/quectel_CM_5G/src/rmnetctl.c | 342 ++ application/quectel_CM_5G/src/udhcpc.c | 745 +++ .../quectel_CM_5G/src/udhcpc_netlink.c | 179 + application/quectel_CM_5G/src/udhcpc_script.c | 132 + application/quectel_CM_5G/src/util.c | 361 ++ application/quectel_CM_5G/src/util.h | 52 + 75 files changed, 26331 insertions(+) create mode 100644 application/quectel_CM_5G/Makefile create mode 100755 application/quectel_CM_5G/files/dhcp create mode 100755 application/quectel_CM_5G/files/rmnet.script create mode 100755 application/quectel_CM_5G/files/rmnet.sh create mode 100755 application/quectel_CM_5G/files/rmnet6.script create mode 100755 application/quectel_CM_5G/files/rmnet6.sh create mode 100755 application/quectel_CM_5G/files/rmnet_init.sh create mode 100644 application/quectel_CM_5G/src/CMakeLists.txt create mode 100644 application/quectel_CM_5G/src/GobiNetCM.c create mode 100644 application/quectel_CM_5G/src/Makefile create mode 100644 application/quectel_CM_5G/src/Makefile.am create mode 100644 application/quectel_CM_5G/src/NOTICE create mode 100644 application/quectel_CM_5G/src/QCQCTL.h create mode 100644 application/quectel_CM_5G/src/QCQMI.h create mode 100644 application/quectel_CM_5G/src/QCQMUX.c create mode 100644 application/quectel_CM_5G/src/QCQMUX.h create mode 100644 application/quectel_CM_5G/src/QMIThread.c create mode 100644 application/quectel_CM_5G/src/QMIThread.h create mode 100644 application/quectel_CM_5G/src/QmiWwanCM.c create mode 100644 application/quectel_CM_5G/src/ReleaseNote.txt create mode 100644 application/quectel_CM_5G/src/at_tok.c create mode 100644 application/quectel_CM_5G/src/at_tok.h create mode 100644 application/quectel_CM_5G/src/atc.c create mode 100644 application/quectel_CM_5G/src/atchannel.c create mode 100644 application/quectel_CM_5G/src/atchannel.h create mode 100644 application/quectel_CM_5G/src/configure.ac create mode 100644 application/quectel_CM_5G/src/default.script create mode 100644 application/quectel_CM_5G/src/default.script_ip create mode 100644 application/quectel_CM_5G/src/device.c create mode 100644 application/quectel_CM_5G/src/ethtool-copy.h create mode 100644 application/quectel_CM_5G/src/log/cdc_mbim.txt create mode 100644 application/quectel_CM_5G/src/log/cdc_mbim_vlan.txt create mode 100644 application/quectel_CM_5G/src/log/ecm_ncm_rndis.txt create mode 100644 application/quectel_CM_5G/src/log/gobinet.txt create mode 100644 application/quectel_CM_5G/src/log/gobinet_bridge.txt create mode 100644 application/quectel_CM_5G/src/log/gobinet_qmap=1.txt create mode 100644 application/quectel_CM_5G/src/log/gobinet_qmap=1_bridge.txt create mode 100644 application/quectel_CM_5G/src/log/gobinet_qmap=4.txt create mode 100644 application/quectel_CM_5G/src/log/gobinet_qmap=4_bridge.txt create mode 100644 application/quectel_CM_5G/src/log/pcie_mhi_mbim.txt create mode 100644 application/quectel_CM_5G/src/log/pcie_mhi_mbim_qmap=4.txt create mode 100644 application/quectel_CM_5G/src/log/pcie_mhi_qmap=1.txt create mode 100644 application/quectel_CM_5G/src/log/pcie_mhi_qmap=1_bridge.txt create mode 100644 application/quectel_CM_5G/src/log/pcie_mhi_qmap=4.txt create mode 100644 application/quectel_CM_5G/src/log/pcie_mhi_qmap=4_bridge.txt create mode 100644 application/quectel_CM_5G/src/log/qmi_wwan_q.txt create mode 100644 application/quectel_CM_5G/src/log/qmi_wwan_q_bridge.txt create mode 100644 application/quectel_CM_5G/src/log/qmi_wwan_q_qmap=1.txt create mode 100644 application/quectel_CM_5G/src/log/qmi_wwan_q_qmap=1_bridge.txt create mode 100644 application/quectel_CM_5G/src/log/qmi_wwan_q_qmap=4.txt create mode 100644 application/quectel_CM_5G/src/log/qmi_wwan_q_qmap=4_bridge.txt create mode 100644 application/quectel_CM_5G/src/log/qmi_wwan_qmap=4.txt create mode 100644 application/quectel_CM_5G/src/log/usage_of_argument/6.txt create mode 100644 application/quectel_CM_5G/src/log/usage_of_argument/m.txt create mode 100644 application/quectel_CM_5G/src/main.c create mode 100644 application/quectel_CM_5G/src/mbim-cm.c create mode 100644 application/quectel_CM_5G/src/qendian.h create mode 100644 application/quectel_CM_5G/src/qlist.h create mode 100644 application/quectel_CM_5G/src/qmap_bridge_mode.c create mode 100644 application/quectel_CM_5G/src/qrtr.c create mode 100644 application/quectel_CM_5G/src/qrtr.h create mode 100644 application/quectel_CM_5G/src/quectel-CM create mode 100644 application/quectel_CM_5G/src/quectel-atc-proxy create mode 100644 application/quectel_CM_5G/src/quectel-atc-proxy.c create mode 100644 application/quectel_CM_5G/src/quectel-mbim-proxy create mode 100644 application/quectel_CM_5G/src/quectel-mbim-proxy.c create mode 100644 application/quectel_CM_5G/src/quectel-qmi-proxy create mode 100644 application/quectel_CM_5G/src/quectel-qmi-proxy.c create mode 100644 application/quectel_CM_5G/src/quectel-qrtr-proxy.c create mode 100644 application/quectel_CM_5G/src/rmnetctl.c create mode 100644 application/quectel_CM_5G/src/udhcpc.c create mode 100644 application/quectel_CM_5G/src/udhcpc_netlink.c create mode 100644 application/quectel_CM_5G/src/udhcpc_script.c create mode 100644 application/quectel_CM_5G/src/util.c create mode 100644 application/quectel_CM_5G/src/util.h 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 0000000000000000000000000000000000000000..b7da70a1ca4a38955dda7a3066d3375b482cd1e0 GIT binary patch literal 216288 zcmbS!3tUu1`~O)4#p@wcBhx~|q>F~81?482Ad6yAYSEjehL9F2=~9$8Y!~IUt!Y=^ zva;*T+tqpt$x=}i%&g3AWc6ltn-i91siYNtQl&-v_4AHCrws z>fdjZGWa;(PP{7-Pu>y&|I6P5dH*VzUNpNL=q&`DZ?YqO&M%&ECVrC5zI-By8{}_k3Vy$b#OVIW(j-=Me#dXl?LO#&O}XLi zwL@ROpJb=upW`=NB9Oo_x;(q@Jx5Aliu_Aa^t>aA{8drp-;9#(<5BEZ7)9O}h5z0t`n(r~=TH>+Wl`{+QPO=b ziv0E{`KpY*NSu9EJaO z+&fYInja8V&kVjmbF^ z#!a49IH&NY8FK@Lb0&-%RXlrE;e@F-78i{;&JnWcfkGJ~MMcG> zbEg|~19J*W?m*mT#7_C3-CB}3De=rDMM5+Wtp8h6?KKYiCh*Mj;W07 z+?>lsO&&UU=-}b*^#zy^I`{?d)zFLF%hszQ!(4F4h3;i5c){RdXdPoPm!yBO_~-nk z`DGjr6GC^|%aDT#SrMlLzem@t9FBYppQW}l;$?inC*+8uqtS_{E_86xX}vEvJ44N{9n7DXGwJ)@(6G4Wv3q$}e5<5S9r#8yCs&Dg1s%<1!h3fZvZda%K1-9`0aV zBf~woe4KHk42$&J@$^Zb0M{?ZD8_yIIO{7;zfm$q;E8hm#lcf0o}}Tkt{3tt8h&fO zz*9B+ud4)}rs10;zhA?Dnk?k=G@KO+JYT~vqzOAd(=~jSmwas)Wg33B#49!YUlL!b z;dK(P((pYJuhH;7CB99=Prg*>)2QL+N}OrHH=?cXHvL=BJ65Ozq>@UtYIrs0=J+^^wRN<3e~izPl? z!z(3Tsp0w3Un@1dT;f$4UM2Aw4c{s8Z5rMz@kR|#x?I?SX}DkF%^F@J@%YBJc3vs* zL=CTzc#4Mak+@&OpLFc0;p-iHYWQx6mudJx$Icqwo+gI)tkm$+BwnTAsS>Zz@X-?A zrs4mPc%z2TmN?V!3W+ys_$rCVf8N&4FH1a8!;LFMzEU(iN#bc5zDe@X_9}XhMyzl zt28`Q;x!sRMdI5uJYVuRYIwQCnTA(MyjjC{N<99Hw)St9c%p_UT`BULqTzmtr)hYJ z#Qhq+QsVg#{d-G1QNssIJVnDtOWd#FlO>+7;qxUvUBe%dc$tQ;m3XCw z?~?dR4KH!*tl`TgUZdf^NSEko~Ge-68CHPR}#mOPaj-gC6D&q@uyK;l&zo+I%Z4R4m| zZqx9j@zVYp?w2^z@DhnPYxoqIZv21S+W$_8Cu;bE5>L_aH4;zL@EsEOYxoZm&)0BI zw#e6X4PPnsFVpZEiC1d)9*M8i@G~TTm4?Sp5b4%vxL3+=)9@52->Bh!i8BqKF7aj! zzfSVUf8Eyp%cXpxhTkFOQ#AYmiKl7!8j1Tg{2htsYxuVkpRVDCU)ZNi!`DbXD>b}P z;wv@$bjeet;b{`D(eUvS-=^WWNW4+QLlS2i{+Ps@HGHkaZzz#H%#CR^l}pzDMHQG~Bpa=-H^@ zDH3NIo-Xs-tl@c5KK`4wb}o^4qK2=Oc#4MCN<2-&_ek8Y;qli9{qr?EMdH&nJWt|f z8eSstN)5k8=69ur&z5+VhF40wM#G<$_%;pSDDg%OZ*6@{5|MZ8`KN36jZ(f$!|#xIrG_t;c$J3#OX4*ezFFeiH2l93 zZ`ANZ5@#CTl@1c%)2!iVNj(1Bw)V`Bc%p`1FYy!&pC|D&4S!hTehq(J;`ti>fyAe4 zc$37-H2j#vD>b}JuE_6d4S)O(ZU&=D!~gw@z}IN_75fBUqv7p-6nL$MpZ>kTw`q9S zZv?(m!=G;yc%z2Td{^LmH2hy%1kN;k+$Mn^(C~*g2)tRto8A<7(synB-nClbehu%C zFYKAG;WZMUuHow?UZ&yOBwnfEuStBRhHsX5m4+Xbc#VcHyjJMBO~WfB-l*a4Ngk%* zA4$Af!*@$O{` zhVLv9{yk5_N6i%YFb#L)Gc^1_nUMEuxFbJ7!}BHoBn@}ur)&63DPN-Dj{JNLzf;PG zG~AJ2uHj)Rzf!{;`DZlzDJlP=hCA{#8op7=*J`*UzeB@!OZksA+>zg-;RmHW({M-r zcMb0}OZe+g4R_?@f9%i8i?ffGB;^w{+>!62;X|Z+vW7eIsTzKTluy%eM?O=-Z;|r3 z8t%yFYj}l}pQ7Q8e6fZn-XZK0&~Qh-T*Dui{FNH+$UmUrZ%X-98t%wfY52!devO7Z z@^5SSJ}JLN!yWmZ8lEBZ+o<8qvVHhY!>363eH!k_H*0u@*}|R%Yg?`y`7RnhK+5;f za7R8#!$(Q^6b*OehiUj!DL+EP9eKZo-zMerG~AJ&q~Vp)KGQVZkuTBk6_US9!yWmM zhQBQ3@6m8aex-(Qkn*cF+>w7#!>gqJuWPs?U#sEUr2IAwcjQ0T@Xw?^Uuw7`&oq3W zls};1j{Kh*-Xi5=?Y8C3m@CR*f`*?g%Xy-PJMzgIp5pYI8t%xaY4`zYp9~FmaT8#TQ82O+;l!&AQ%IMeVq_6YodhIfHT>=E0?*g*Q+5b^iiTevmVVdp2@)^S@PA0WOv9&0 zyj;U?l6a+t&y@Ib4Zlp{D>eKMiLci1ki@Gre3``8X!s)%k3Z1XFHSf~!=3&vMZ>2~ z7j{e4aHl;<)9`?l&(QFsTLq6_!^2WOPs9CEK3~Hhkn&SByhO@R*YL-se2Ipyl=5X7 z{*sh0*YH{?U#a2MQhvFHGbz7P!)vAdY7I}ABkWV9;qOTKH5#5KDYE}c)bRdNK1sv#qdHl+V}jJsr;E`PsQ&UoLhST|! zDR@r@#CN%Z_fqgm1wTc>mn(Rpg0EEYQx$x*g3Cu$`Q0i7KTVNequ}ZTq%{iOSCOw( z@Y5B1n}VOA;5!vONx>Txyq|*aQE*XG#Z9K*qUH+xfPyD0Jk1I|K*5b)TFd`g3LdZE z0~I_$!OvFkL2l{YUBQPtAihf!{6YmUQ}Bxvyj;Q46ueTw(-nNVf?urQD;4|_ z1z)Y;BNV(!!MzH;M!_#t@EQdlso=E=o}u8|6x^raI~Dvg1#eXFQ3}3C!7~+{DR`EG zA5ieo3f`>X*$QsR{)pOgzk@rwKu1)rec(-mAi!7px>DEQTie3^n@qu}KV zeyxI6D)>JXe7S;Or{F6UTs{)O@2*zx8x;8}1(!$s`Q0@NK1GqQQShk>UaR0YD)=@9 zFHmrQa9_MX99QvTf5Tr<8;EJy=?`v+uX80@hX3Q={f(9ZlknF(BLi0yo=*4eJuUbQ zxQfCw6tZ{n@E8imP`H+dFQaff3a{beiz!S)9(y$p52Y{-b?oIld=7 zQ<#Ppb_NgMMPV9J*r_~xCxvM!VJGqM917DA!cO4fn<-2~2ixG`8!1df2K&HYl>dKF zIEli0c=##`(-6Vl$-`qPOr5x0%fpvZ_)H3~;o*xZOhW;CH4hJ^Fbx6hBJH z;r1FHUPoc-y6x3G{2GO+%eI&E@beU=uG%i=;io7}U9?@o!;esyx@LO{58p>&>XPj| z9=@Bx)D_zqJbV|0sSCDKdH7BWQ`c)J@$ei9QS!qgSo89W?AVd?_yR31M19KzJ~*-1Qn zkiyjE*$F)S6NRa(vke~JOJVBb>;p%+|0zseo4tpJcTt$SGKPIWp*tOZ>BJH zVfGpxUPoc-y6n|F{2GO+%d(gA@beU=uF5Xw;io7}U6ftI!;ernpTbjk_&y3#S7qn% z@ZA)qF3Qf};kzhIU6Y;4!*^165`~j^cn*cBi?S2k_(jVR=AR)y=U5-!;J5brgWny< zn~)ocJ7=%Q@JG7-=NtO+ZilB@dR_{S-_0}NRY32+;1m6kxJ~%>r#Jhp?*pfzi|Gy- z>SDTH-?FD@uz5y^^Iu;_y-I5s@hyrM8GaY1Ir})``!^kL=eG{{H?fg^Prd)c_UXm_{zOkg2yO5-yih8f(XzI`gHM!z3UKSUExvY$DjJRvmxgpb;?Px-|9e# z{E+zdb`eQrxAzn^WOwiw_1PU;YN&^)YsJj!_LbQk+oCasK+WpWp33Y_HPi{!`L6L@ z?VISEFmXJ(-h97>{LN2JnC1`14xmQOZ)GO?ht&EbPuxqPhD}Jh#czFm@MZsz56$Z0 z&d19arWj^u5PIC)NKwi<>Um(TDy&22T0gRV%`GhzwPxr|g2S1~3I0e~3j6d8!?5CZ zKGWX_5895u!M|F}5WRwdHN9pSK8yLSP5wyFM?tayC}Mg~hP!HTX|QxOg8*Uxu*Xm( zV97)Ofh7Zj!+jTkjQxvqGzyMu6pjxdCO8hEDcKlq%5LNc>9Zw)XSKWx zmZ@kj490V2#eMm7f5YwoXdQL&Fw{dJf<0LsVxlyTQ{=?C;dHj zk*}^YeS03I>eyRl7$z8cM`L*Ru*iX(4hF{gtgYcR_AM-(V{Oe07aDBzt8fy^Opf(A zOtFb7Aotfu#nuJzgwV+%ABDYFk!ybbL)s_k^}R+u%M33t*iz2DB|H=UUjyn169w~4 zF7x+L*C`1d5m7LoPt36P#&+b%IhPs6Mi|v#*C3UO+R_fe`S5FLpNgN&Wy@m?V?Eu> zjSNp%*WbvozO?Dx5+Xv*%V=&oL4@6c^pU5ZsWstw8uZ`Rl<$S}M9OcVX+oYB0$``| zJb7<@mGW~QJ6kZk;4(B}i^b7>9E1eJTrij$>rlqJu*0w5MP}jL_2~_=%i453MrB7pHTL2U8)&+F2*pLhOI-MQM&@kiJ8`Z z*1~z!WZDx__!rqGjuwA_9de7k*I*;vzOTaMAWvKnK)r{@k{G0~{;lmpd zpDHrdN1Jw|*0ra+MTV!>^fxGX{U~=%LgFkYt`b;XsXF+#rRkN&b zkcB7V2w{XL6eEm+4TVc)kSD|5p#a!>vi8BV{a%4*%}@a8Q5`AZ{9B2>;U%hml6{oR z?o-&mf7xO0%*)A^1p8bNv3n4eS_9NxUU%c%*k>xS|FznQ{T?|b?0pCT8`7#<1?Y`H z77X}hHH>Sno$g^nH0tM3aYVMaDW1!96`LcQDt~Zmyg&F?f*D!@e~k_o4={Y@+CJwW zu1hm3iqTFVuJZ(TXQj`IHACaj^k=3Idie4FMrni6#-48XHidFy&6vtJg?E1Z6MjKZuA#?gfZM~MCmQ=)|TU5C;A4;mqXrC?zly6`|r}PjIWpU+_t;^+VWO0BzX&kj$~RSas=J&Cq?I zW#<7A6*vaDbt-Tn3PNiI-U&kCgR2!g-33bsJ5`b0!`@o~urAaf2p>#&f%~8flA#Lx z6r|u{s=)1u-l@QIT=r(PKTb!HjL3q0jP$BqM-}+pfAeP1>qc6Orp}49A9)m!dJ2{P zOPRg{j(h88_zdM@$MamLGNLLMR0okKk-K7r>KO;olOz0Km?y9$5m%4%HHm# z`xs?E>@A{nJxaP0M7jp|O!cJXSQev_eTq{NPA1NAQ_ce!SKQu)npB@1>#+}^0bpgn zAQ9f^UJeQAvrKd2wB+{eg(}&=@je1u)v zgUZWw*ZU^?4{>;?bwfkEL`#0$E^kH!+$!iI6{>^?5zdB zuA@YP-v7|7EU<#zA{btB8A=p}dm$v8`34yHdLR2@AI0C7sF)FhkRi1hmB@TXWgxkO zKQaMkhzu`90oZsZG-u_!&*?e^iL6U4MWY4~3OBIZF%Q;CM<<>jib!G|n~k*iJT0CV z0)J$X7g=cP>JQd={ELr~Qvw66ug&T}Un3)QIPhbJS$#No>?H0eGqahENBODG>PG)spuZuwy=qt)U>?O;;SwZjOK?YNxiCLY0O8L?Lx9>Kq zcY*)%PvFRc*cE=yww66+brak13^hS=diXQW7CEiP?+-h!=}is|d%p$17NXgMb3dht zc3=rxrpA6niCql=;n*)x?d)q|CQ9uFReq%+zr;=LMQV$}-cm{}L8LaFQZqw6K*IW{ z3|A`*nQn?VQ;I*LL{N(Ro>uZVQjzcBrg*VPu^pxOI4^wO!v+E!f7qX zLc81vSu^ysOJ$08s`5W5@=rbF*kl{!E9_kXfSo5&42cwJ1(&6%49_SGQ`{7nP>Stj zit(y^M3GN*Q@mQFcm}1ou8I4wyGU^ZbpZ8GDOMV*F#O_9m3mN$DlrOaZ0GMB-xEf@zYA&TXL1j3&v(>8e-4ve!gMEffF{&fDh!?66jLe1mmo}ed`*?VOOfCGfRjfAk)Q6SxQr|r_Fh9N9<+HL$A}aU(@6OEY9){C zgI24a<))ZRDZWF(bACjJ=qywvV))pmsX~iezK9m0j#5(^xjp4g^a!}{4t(EZ{Pvvr zaBUw0W6inifYK~s18lLeAFkPNG(ke26X{xolHs;y`>^Ec3%;F93o)P_fG>Y|M6$mE zFT`XIz#q>IGqXA3SeP>;^>6i9C>euSoS=q_xpNJIxljsrFV= zy$z{iqEK-i7~JG87imy(qr@d8mxfC==zAEYe10-5H1(sw0p&V3GUzL~gJ+d@EgNaO zf^|ZYxt6_-rYCNX6Nd!(+$6LY)pH{_4Hl2G5p435Zm^$45Z!%RcCXt~&FY>0g6;lD z7pr`}c}nlnAB%$X&E5&-!<*4o`>Y3&PeJ6S52e6LN!-PhkBT_+l) zGdIRiLLrXN1HKVD87#oV#T2HsNj8m#r%|{!g(vWE0m5c=t){coH8} z^$~-rWa=@+psG&f>J%QiFCsTBUJp~XG;L?6!+c_Kth$9ACYJ=U*(j02=sjCll7otl z*B|No4k-MQ7yg9GO&!to#`@En0w;$rsb~rO%2t7~fihz>eW!M@ZI)T%QF^UI?DNNA z`#L@&dKVEg&Fb%I+W1WvaD(WM$iGw`rXZsk<$uLtJs?(IEu9Y=0>c8$T}Y5$E!i*R z+L@uR1ctSolYz1OAET-!$L=lfwpO*bmOIVEH>hB!tj_~rSwE01ypLf*hVuL&8UIR^ zVZFj|2ZV&SPk_Njk7uw?_98wXqQ3ylnO1cJ1sT_Ob(Uq1v-XHF+6vTAr;{4wYBOGF z(}%QK%iY8$ww+0%z$hA&U&)#IgzAk|k{Dz0R^l{;_+xjrc9dsrO9G4mXFKC=-{d-$ z1f^+)mc|Z4CdX>X3YQw}T8I0ZEbG@SN9kCFyX;=a0FM)Q*n1NI7SpQq)kqx6B|g&n zQBGZ-Mf1Spk1FLNVu~Z8Xv>UUT1Sj z5RM1d+YG%du>M#_B+^q<(jOJlk*->WLao81);q`^)aobHnn(}+6X0DeZ;ZHKC(gB`vw4(Clam#C+zVh_4|`lhNIhBEgEtYejW_= zD%fk0k)StSBsY^7e*UMD&w&a0diRA7j22*8tKw-GZP40S1?nV;cyHYX5KNaClZgQz2xj({4?tyQ(G529U zgxt|iGFz=OT%#~-b0>noKuP;I8ePm)g|AnH9|gNG#U{#8*n2l6H$Wt}KqU7nF$_@| zPE#1Jb(32}$%P=S^ahLf(c*^R`b!zkgpw&mch^lcj#x-si!RKH8wUfFFPMlPe+XK3 zca`xGesdUZZlL~$79N*!XaGUfpAtk}tQ9vDP;fmT+5|e5uRk626>lEh&|3;!!G&@l zWK|4A8bNO(GK~ZqA;1Sr?DsEW4Bl35hbiRR2pjY;oaSx}m7&IoCjPAY*0MF`ZpWY{ zWW=yH9RU0IYw{m&;!{>4EwM2)i*xghpjD;Znp~a3%{lOob{bx4^Ywz5|+F6@;x%tiiLOAY8EZ8b*$ykdHBG8y7LaP?z{u3`GQ+KO`l?wxer#E5BnCsPs6yJg5TdP z2SuiJFxP5f`{5K?6E&@M09oD_VqNreHX2vLTYdJldTY^x!dqhfYCDRo@K)89R-3M> zbiB2hJQVhZ0kE_F>w4=>%+XNemZ1ny_x3iVu)<2cTN2Sbt7FA3`xu4&Qg_BTjo9IT zI}1d}!2`AZ;RP%yomQGjEBNUxEiG9U-`fjF2k`+tqj+@)4d=VSXanmgyQzmhn3Ph)J-g3 zB-W1-dz^(q{MFqhG!!>sPw75B{Q3CXAN;+sCH!Z0(p`OM6Z{g?{)`O;shjrI8oY#$i) zai61~##$aog0*o)R@}P?*T>aU?;ZNWYBZ~RzKv@$b8}_f>$t#_tO{T3)8#PyCBu@< zP!;NxFMK*y>_@lH3D(C~Vc%!!@s^ehOtZnt!3l)yfKP1zKy^jE3 z!^u!VZ{&XA^DM#eu*-0!!Y~6u+;r?-Fo=S;6{@n@&p^Vrdmh3KJ}5qu9bEx$UhSKZ zOPjD5VM4JCmv)v?l)Af$r3uA6q}VEM1Kw^Pz`O)yE_6dd7-s15Ge~$A<=3*WACw7G}fX~`On{UhvrBZqR_orkQ8g?&2 z;?7)QFFeH)<@=OMq=BE15anAO+CvOlmkAX?xwm#)1+*V|Pi19VO8eQ!evwD^0RsSC|l6VcFNnKqEz zh1Za<_dIH*zy5?lKOkU`w~anGJE@qurs8n4>=djm6bkt~=((qDU`6 zuxbBzzjcgVh%Dqdlgs5wxw_#l$AlqrO4vIN0Ao}Pg5C^F?TkPQYevK5#=c64o#aji zMqH*Li`D?&2gB^!AnfpG2Ij zKcGZ4T>i6&e~l}&RuTH5+{vUz@NWme3I+dq3~$IjrEb*uO4QZv^sFA)WqGcUYX5mI z|C3~su=i%-j}!dYOa7j2RMq|&E`Jg6AGpWKr2iXk z)JaOzJ?^xtJ@GGhg;f3DTIAU08`2r;9{^aE;D1c=PjsWI`j@->&mk&Hb%mZ#l(^32 zUo7~?5dZfdbN_lJ{~;{-h@w4BiJIi{k0$<|t`N05&Wg#gg^vFH1piL}ScTyK3PX5a z{@tj#O4M51mE|AVWhJhVs{f-d|0c2t%0KZB5c~@y|8O^|s{a(1e+ls?xI)z4I{IJY z@=q1~=MewakGOsMO8!socI*>Uq8cv$S;W7_6*{5_eX+pFq(|^?2fzvi|9YC%Q2BSG zzO6)Ejl04=^~f&EbA?(Ip?NO5TFZfMp5($0Ywm zH|mbJTBDY`{LdjOONEfzj=!PE<-18N6iH-L5_>=7S-6-dk-`pEDzR@@V)u4a$)HrW zt8&v6xr6hZj3kOwz5&4Ii&Q?g$WSTl9yRt^O6=DmAbhw}gSc`IEJ51?7e`LU$gOQ05xQ}^GMD-S=9IAfq)H54YJi48aK?j?TOgZCGtfc2y zhjXk?*ig!B^DNSc8Xpe*C?%*f}m;FIPtGm}5YzfA3d>xv`f9^VTKV|Iq>>W-v z-rdQw@c;mJKC(g96lny#3u&Yjm_L36z}N z$lGoWkh)Z@{hKbd2+|2^z=qi4d-_khLdS{KQqlp1B4p8wV@XK(pAI*~UzMY58C(xe z$4gJ%?s%zzd=U072f)rHH9>}wya;n2BaqH6ib~f_p}WeRzm*XkD17Tp#{iku5jGV? z6^`z^flvbS0EoS-+YrMS{Ml2wi<=7-dl>G)L{z6>)e4+=H`pt|XxX}LeQUP1-{xfN z0A(fYeG>qiK=njqt12Y2H6<$DK!t7|q=o;Vl$P1h5z0Wt74V`^e=Y8CRhznozA!`2 zz#lw{Km4}S-i#32cHQYz4{vM4aVXlBx#OLdmUQpG0$4-{VTi?Ru65M%J_sb^a6>|% z*Wt|>fnhigRk5q|CbsHjOy>ADUc-{}$vzaNVaY|Umm}q6Qv>kqR{TNdGP2QsgNabS)ZL-^0?{KZLpx1OCdv&$PXnOcJ_4$d#`zVt+R!f`OR|RZHEmmQ2NfUh2 zvo>^|Du@1zp+92{X7C;sizqqarRT!{ew>`7b;f)W_LXnM8V}~v6+`hw=VH*P(h{F2 ztHQ`JDEBDgm4k)IxfopNB45LjB=Q|Ze8G*h{_8Q9+=Cj4R0mS3;k-mzILptq{@^8z zeOZr^XVp{HIR-iQC&FS4V;9^@D}Cp(ix!iyu&pQ0tX4uHF`9i`>0z&eyF@`Lqh=xO z^#NdCLM2`oL^}}lo`V@89NiPT;pFN}5=EnZ8rxMa5|LB(WH*hAl{9{FCsRErBFeM9 z16HC&VEtttzO%joiy0e{6NB}Gi2VwJY@nN|zhFd>sT*Z_PNx2YW2w9U|Xm;YC#5A5q^XcR?6hBEQo5e~#r zsZx$m-FgzNvv>=YKc{z~s&n~PN{tQ}C%YN57Pa-(Zd7KGu{BD<&&+W$ww*E)_TCSG z4c;b{@HyU^kI5LkbrZ6Q%xxr5p1EY1i1ZeC5;SsYF8u;W}glsA06Zf^vQ{A zmoxZQ$LbTv z(qV630IY_Jr)ZnIVDgA$jvz0v_Z1|>?fv=!K`-sy-eu2K*mt^9t-}q1(S8|3ZrvA; z!=bXY?1Z8DSra@4mh2Dn8*%)G05WL7IG`eV34Ux{4DgxNOHW7b!cm)3ec`p3@nI>s zxn)xd{MBE89Vc%l{ z4mC`HU+EHO9_}O;bL}!xb!9SA2r+YlBVnsD2nyAaQjR~|C%I*O8p_xNBX9whm%q3P z4+Ew1Hn;RlQfADCSZLic9u%eBaa?6gOlfBh5y|*Gp`NvmKWvf&NeoSjlS^o0=r%vL zcu+wD$$iFI$H!Uw?U6{Ey|an3LY43dI8qdc*!gXX!$h2WlT(!v00%}@kD%T9YtTik}uAub0a58!2l z&lfl<*}9b%%S}=Ei~WVF$e$8e&MvE!PYH~(j$~r7y$?$TLe(dYwTA(mJXVXW;xN0G2@YUewcRm=(e?S0fuVJ9rLKU^9tAhB&391Ai|gYuyoXlOX-9r2cJ%8a^!o$=E7?r? zdADKkKpR)(f?+qts3M19h2a?piQalUawQ&AOJw5^g&tH(bkE=pd!aSe5;xU{$P!`i zWJ>iY75||3DxT^f*6D8)zdqkeG007JJSaBOBRy=SP0s+~`JgTBTaA!w9T&^cdk;}k z7_v~At+*0pC+t0+TFvh8ZrGbbmmT=!skHX|=SD)i)8((YWb?|n89zb25JtH6xmInS zxWzHTRx%y-{{XPFMTQn*MhF)dqxWFL1&f|RvCUq9{ zpnk-x_zlmRidOw=;J3pun*Ii5f_U-<@VZ=pyvK=vke zGnZf(IcK4-eBlJZr^6z=6*EJxLr%72N2tcO? zy*98ANZ<}%_=d#bF%Pz8arzEZMV~b~@o#j2i+$D&iA}leeA0v-bnr)3{76H~vp4&# zoo~{vH6NDxkNk==^MQ*phFcIK?yn?96Oz+#T}=}ZbZ{fgYI<}v(X4L3 zSF-i_COg5beg`p9=sFP>=dm+NQ5kV|;u`!JL4W-8XTmjMa9xEKIQMF5OY$>O&?kw? zH+W5J!PvkCXbD z)}-V#1eTHhnb2+oTO5$uWkR13zN<2Es=)6X|G#K>&nvAOmX|sjK8IqAmOs^Z3Ab(F zG{^3u5ae;EHu2Kz;iWlnExbF{6S#_(qS90b06SMH&;d8ccmln+x@IUJcH^p;72|LzbP0Zo+IuO2PS+%cRlO)` zOsOf2fQFMQOdf7dG&t|rn80AS0q=(--65zLSYkvWn*K~m;H0B`PB6hJJ&9Wg{kmjq zS_eDy&Tk#SJ>T?|ql(MFX+JpL`vVSADlat5@sy^t$tv1Xv@?NAcs5Iii7c+hk*%f_ zhF>2?|A2P2sSnSbcvzJrV*-oBVC6PQ=HOY@9R4h87A6v-!o^SI62k~9fV@(gBGI4FWuZa!$7Yv_f63&l)eCh+(Gn^HTsC#AG2yP|8I{{ zi05J!^XFpN7W6*;IBHqBiu?&y#|-R|TM4GODRg|Pwdii0<`mtgw~$WQ`#J!2#X3sU z`!7uOXjfsf8})r<+ zyD{{`0%P)YU*+S}afOSMi+$G1G*MvtOHh+78|R?0@<|#mQj$H{)i_;f977sE#;xQ% zgkIi2*AwPMXbm_wdt4B@$8FA6`05a)2B{j%My^}C(R3&%T>tDI6r6Jq+`z?dgc#b; z&|~l{JM;!J#K-jSlFh8GY$(#gN+O127A-(={acj5(f9%{!I+1=_6Wic-bPX`nMNqh`fQO!U~J z7&r4Gf)R5PWy)R*SQPu6qA+ie3@^ilvbJHYKyxOH6-s+JP|J9ydmc;OGv-pVz3493 z?jkny&bPOqV1$y(krsw01}?Eyil1lam(+6i5SO&Bmm)5g<7))|y+wae_LHdCceHB5 zt3u^Kf~HZ7r^Y-uE$mqFtaRYV5m|*e|;i zu8-ju+CL#b&-jAs7P}tZncssn(G1vrtO&5LUnknXAHMhxEfn9#Z{o2%{@{Q(pkW=d z@SJn&DUG{HFdc%{ZfwVdy?0XhY#9;zEy@CaaNl$v?R6Yd78kKRIHZ=<%yeq_MkOoX zPH_y;k;+)uy9)rDSLnlO;`+-580R-YB6Z_1;4~F=TDEhY!m#R6V8e+Pa9Nv2A1kbe$YQKc+oL#5Wt_N)ewox3pl6h%?-X54T2aeB5E{??xPfh?E)oYy3sTH(i+fCk_A?SdMa;s) zkSgL|H!IcHtHd7QP9?`8HZK?VeId%lrO<#Eftx7XR0J}&idGFp49kOPT!M8m=>F-M z)W~bxgcFdU^)j&Pg{2eOhNBX!4${) z=Z(nS{#Vcp-wgvOJ6qi8qY=eRv!I3N>9@|MTjoH+SacE%72^F?Tot%%deV!1HA6S? z1gPPd*tp&?@mbVlguVMQIbe5#5GLMxvoP@^pl}`3ONqT10-}~PIE9<|{N2LDUm$I6 z;#evm(!^%9F#`5&#(Wa?rD|TAXT`OUt~m8A1Iy;FGZNU z?gb$V3w%XVnOthIBNad;y>w7%7qp-SsIzc-(+DUcyb$rOLlidaWsKIkG{l{S0OFxm z`O;C*y_`ctqLeD`Ki1L`8IMtNh9}5|jkwHQ`X!_>t@RgVR-ln*Eqg@%oy6xO@sWc{ zW37*x7E_|`&Lv(Lgw{K53KGQj`ilBE27`E-D(mBDO6Ebez7y5DJ}zE{ih1Z5+G4tQ z142BZN2w@DLvvd^he;|Df-OFWtUVlp4RLQ#G@dOxS;SmW=){ulDHZX^J!I}J6uB8= z81COI8o7UQj=FR-4iM7xJYU&F$e@XFaLaUM2LU>+L+o;IHYFWMDEE%!7ai*3hSNo6 zc=%L&XH^_7y%Pm+0IsnP6X=5qvk{Fu`wP}V9)0o@4L&4IpiCxZejt$06|ci>fYDt}NdzU(c$-zbIidWCicPVq-Zd&-ZV z?>93K&~arvcHR=&Rr)h+r=B(k1)>gGEISPoEw;FbMw@a7@zPVR)#~4vSc!`90cjuh zt^~j?K;1^Q3S+sLTCP!Q?Ac1}TOc4}uR!b#lu3*uDv$~4)Rw@QtmXwW3M~jPg9u-D zJkuW@#HfL6DkLA!3wrSKpd9B(_rXd6om}-&g?fKr0>mC5D~Yv7hE5uT$p?`X29xLV zB(P2A5BFU{DOgSlv|H!43_G4|H9!>zWdF}IjfG)9$krtxFYr|!Sm-c<2idS6rbZYO zs{}tE!mx|F(KcJaYzX2!JRkGfk7^=IyL({xEt*s*q}b2Xz{ZEpAvDZRJC}$I3Vkjm zeZt-jl;fvf;tn~Ao*B8NcL=bT-RN0L^v@w7qPM_E_7PGiJa;m^rJ^UDni)9AV+7wK zb@ANT5V{NS3_LYy1-ApVPbbaByVToPw`#WV8i)EBvVYh+699`BnoS|iFvv=Dqu#4T z9p!2@gQ7;_x{oEZ2KnZ1B<>jz8pxGxN2^dhX%P-){+d~sZBbzwK#Hv^a7=ck!v6i$ zj$)mKVjlxwvtQ&UYoLJ#6kFs*?WIJ09(RSgb|N1ei>bsuge#WkipM?NYJz!gN>5U@ zasG``iW4baFH`F2M&15UYt#%kr6P(NiMzt@nt->kC~P&5NB0^_J=^LkJ`llTN!-?S zE-B0t6ki!IPTU9#9LPq6n7omVJTWj&B(AhozUh6oD6XqicvTl19wzrglYg!KT1YkvQ&<;dWzd&8!EwOVWc0B23;=B1 zzwzXc^*elEqH8q*LuD$gc8bEYLmAmH^tGY{cLNW_g?>Mt2c^AT(0Cf=y=Bk?{2L+2 zx4QOYw#&YG4z0uYxbnS?&b7tfjZ3S}UUmduAK6RASGoAQLwwOY%y91(@ih%!>|L<5 znYLj>cMQ`upY>^u^#O|FIhjZ_y&;e=4(}xlh2S#2`_+{HCZ;1)*85^AhT&`LOAAuo z6n^qPh!vdHJLuypYC#x_RgGM{-KFVebd7s3muh+z?^MHs7j+3a=|2Ys;8k^*=@*nX zjYG$bG3LB@P_SXo!vbhkKEX%|t6Jw};aEYW>u0dyIx1?SPDH$4UFVo`JVl7a?Erv& zMtyF&O>PtEk*j#$Mi0tzZe&1T)Z3f}?5@F{{~=x@7I{4Tc&_J1>gCL}S)M$s_l$#V z+%zOk?ZFC~HvR5$>6WGCLZ+2UW^LTtB9@C6$@nV3oN zLHr`DRbZuutak1H9MN3T0~sy~F7OzERc}FeLmZgkJ9nxHB0YnvJTkL;k=dqY$gX^C zf$s*K;Ba4fL8S;~<2}?x`4U#H0%^V7e`J^6`cN^$ZN1Ivb|bKJM9XR;JaewaDsV1e z#cG=9SO;X~MCSPbHX7@JiH<#rrdGV*mHU9zK1FMF?a&JML8%(y2_%B6D^`zR2h^Ji z8s2R*d=|F4Kz6|iG~@ORGJ0r?%-O>qnHR&}Lu=4lg6hn*<1xzpl^=QoO|B1B{%zi- zY)1L{3iVZ%AF)TwJ#R7jlB?LWWp?V}>?A5tSK(!ZV`Gq;Y~-eGY5Mg^{{9P;95?=m zv#EGVcbiKm!E1_ph${;pUTr+DAa(f&Dng0)Wo)$dT-&}enDu(q1mPMZdC02hQ@q2c*>{0x= zhSN0)DMCdy z6e`xB_Z3$B9K$ew!~aRe6&R?twc@_Vq>3+6GZgX8=j5AUCs734QtCEa=F&!pIx1q3 zj~rbw1S_d zqi5mJg41iW45->v%^D~0BRadTL{9nIz|RnJ zCT)LaI|J1I)INs2O#s*uUWL5>MYW`pP7eu&c2SnqK{rlSvO7_^?VHhZ`K@Lwe^s7>1+X4|>!=@VFaF?WEPi#X>=b&Z zbpLPoVSovb)u1cRvZ}$sV8ymDxS(vwNM4@F}rIL-whBY~PzGg9~G^Y(qzZ zK+b9ChYUL5UuTNmg~YqX@Y~(H6JIQj{5^-clSP!L5@qjL1p3rxC-p=C3qaw~v71;& zEBnm)?18-irEti~KBqo=@F@tNFBKia6&+rKw`v%{g*}YG@cQiZQz39MMd)K?kEqYi zI1OPRg?m`pqw2G>`ylM6aH5qxre-Xna;rwuoH7bakeblEA-gBSMolmL!Rt!1G5)H- zL`Yo6lLfeUTnh|V3iHj^;zP8KL?>T<~be z+s-L!z|t}@-l3gQ*Gfx?H2u>JgR1F2c)eU!B-S74aUcHoMS3hRvQDWh3Lak+xQOfD z*%i0iYIXYGwJPmrZEqTgR68iCY6>6ic)JO$Z|@Wth7@n*sc~g7S;pg7J&P|y>p;#- zzc|o~o{tJ{P6#%iREpVhTnFr8)>+Lsk+EqHRPLDyS?uAD0$(#1j%>Ep&GcWDJU*ReSt2imKlP|s2vtJ)ZbNOAH**KpyVr~*H ziq8$-mOzC!HaX~h3XKY_5F=mXD!vPJ@Rg59HcFGnRU9@$SYp5_yGxDIo(-|dRR7Sv zpM_}xW-n*+uUk44HH<<5t{>GQ6<6^nw4*x3;M7qE<1H%F#F&9T=-4>VeqViuQ`8;} zb1!v(g|`-GdvYR+Fqim&U4)Je?*Pg6L?*Rs2UP--TS*ezk{pRba+n=Nr>2m67wB!S zt;_YWdIJqYhKJg(VaXYlCGGIqKIaZ*2cC@ z4EU_PepKfke(Y7NTD4Kh8ik{R^Sb%z{c)3_lDYP}F2TAEWF(LM1t0O&=6JRi`5Lm2 zvadeN7!uCP^;Kpe@%W}?xslm0GPM$@)i82489BQX3LpG(7iZz7=D*O@>ZV25QG=WA z!k0Y(iO3nMb%?l5+;$+PF7?@6sKxC{?M$%4mL!(up*?-Z}V4gANd@m9cKju!Use0 zRop(D^Pr#KLB~xm`W9V*XyDNz!0}yTPWv$jWY~q5} zH;%K+F?>u~jYTjqCe0~p9g|M?IwQ(u50Evy*8pG#i8tsS1C5Qq<19un1YCv=3d30t z5@XU_FxWFO#$$7qQ*pi`#vjR!Eeie-bK7XX$m)9&Q@@s`0cLeV`a$X>^E_GBHs9Gh zGd-KkwRnkkW79Xb*^$R~rKT`VXZ*@Ec@fYKN=heD62aZ?V4upwwtoMA@ z$0(F&1uzx(bqsRRg>qE!a{x6t3r?U>#as`KH?hS2@^56hzUh$jV-Fr*^ue8$p3h>2 zKzpzFz@ZJ5&Lb$&P}J){;!eIPTroB1H#asEZH zE)`YF^sqm(X%m`G4F+i`j!$ozl&#pluEL{)!d*z=f1xNq;TCL{8G-ZJT9@Qfm*hEx zq!DsL=i`*qNN#dBj8^i|I&2sWgNcp!shFPgr?;_ib_7osx-UZh7Uj#LUoWZr5yH9Q zY022R_|6P{ju6_>Og!;qu4P3yA=I_VhljJe!-f~qAcB5@qdV&j*-P9hC)kN^Oyy_d4dAGPkmvwA5wCnYDa`q7e8yf?}3a`sX<_qvm@cQGw9StKt{B^g|MZ4`@N%y1Y(6Tn z^v*Z%&T(4H=Y?>GqUOppC$AeQe_`)Z0IWY%JK>$%MpJ%AV3d=<&T$xpZ(ej6?^GB^ zBZ9DAvEyUzn;Y(i8R)?=_HCwP|C?Qgua&*RSXZZWgigO*1f7;eNGIIRSfNr}-}|ktqEaHqnOE z29M)J_oWy;1&_BkLubOs8&84zt>?spsC9#I%?@CAAl^Hkk6i)Rl>zve zpHiv30P%D2s3hMfJWJH5EOO-ma%C4(wgu!%OWe7>J9}l6aHf3Kn+LSQnJr3w-=zr+ zDZ_pxW8rrP0kDbpkl}b$`#0v;M!<`zHZy@uawtVzc=;KJ@`% zI)TY9QMF5SokCO&S&{GOK*WEpHz#s^N8YGX8M>T2j6SayP9e1`KZ^Z0&=}=MW5m-t z8_pzsAa~Z^BE94$8Sbqr^;vnnp3TeP4+x4vz2Yb4!DlDD?CxkZI) zLqB=zr8c+d4G9}gm$&X~bBmrb+AvVwx?SGtg|`KC_CpuHRdG86{MLhuNRIZ8NvA7L zAh%S=-Ai(3Nx6YiF3BG*Y3C1@b@p3tT*KwoPsSGx>786zfxQM}!vGoM5LV*I5NZr6 zLw<`F8W96a|Hg*1WsEu*<7AveKpmyy=G=(b62ee(Kl{GId8PIkY5>A{_(jf)SY!-! zh%!dAVw?YpShyq>I(C7M;?{b)g$S6JWMJ{F;tdgDJw@n^;s_e60-Mie$V>7oeR$E|NP(dDzZq0uD^_CmY%fDM zz{V^=Qzd@RGs*f5!{WdFrVDSTX2-~83az5Co|>t*+T#zqd8AW&oI%NpxyY zc&Jn8Uqus%+H)rW))RFdwPz+N6Sx%pPnY1a*cu`}1mvfxU`KZEq5u%BTbUksR z>+6Cx6U!bwGn>#{_9MME(cqGfs$hK50W5BVISSpQ==*+$Tx;L=143vl3lfUxyra|e zalBB7(^4t=wkRSkkbhAc$;WSTZ4*h9ypG`8{PRxFv~C+jxHz^s-AS z$&a|t>|AQ`s=ZEU`7@ej;j`CJ{+(QWmg=PW7ik{$)&pQuQBdHsH#uF1-RhE5xg;|c zlKUVh6t4jZ|8aAS_PWFd`ZUB{MI59ms{JE(IX&kfl;wuFi{-thy;A@42rxq??our= z>`GKQc0cmpX6*_mN&d2a)4N#g6AVQ8<%Pe7O0AjI?Dntx!O{(+3cK%DWRCx;$2wFu zx3Kg=De{TZ!XTC#9wt`3zCZvy-e~Gxo1N};A$6}8L&Yp}?X2!B4-ceJiJ_yJf1t!n z1+lR86N;H`Io~nU3bJq5`wsx@P>5{JOH2+XgQz1&?S=&Q=OqrOC^KDM&an#TDei>Q z%Y8;=1|4kbVw@hPGNVqn@jE0jyv(S_NZ12NM-0GUKnEtYUXH%jDIsrQvrstq3$%pL z`Wf0i9LcrzK0Janevn<^k`%clV-=DT$O)2%Kw?jYgR#JZ1-^wHc{eVJK|jgyJU->04Irg2Cy!wh%McCX`g^4498Q1o~eu(MHV%gbW%d{jqi zBeQyJV5njd zr{p92E~?W(G?Kz$c`&!*us-KG4!ew;6!vxoz@9_Nmkv9Mc^8JOln{G4&EXWa=?Rze zFBrw4e8-)-?MG8ZG*B+VaCJ=NnZSEb@cVfi=tw53pnPMzakIHLza8@<75Cuy&G)qu zmz?Y9`VbN0{RaTpQJ5di>{VRP6xK1mt~67JoR$(WHyeblFDUnD$|h){MVf~YS+y_(X8Qhlty^U`9&YL1$%eG$8VvX z6vd`?d8;eFKie@tXDTXT@5cbxY*azGq5)Gynw~FmNp5pV<|!nrAtxG(dgO^u&&BZZ z5qQBLj!h2dpJMg>2TeSOxm4%6RErhoPIUE|N9GB8GfAJX7jk_r;`$5^_ce*i9`0mq z4?lN755iukP<3*(O662&S_aN@Mz5Pu;a$zP(9G@;mu`hiw{}IVrQS+$GWiWz2k-v` zz_Ns9kAVSAOStbHM0JHrHQJ>rR;WrKB<%GNsCfI&-iB&zy7iPC>o|_X=Fq|-@3)j6 zn#VF3pC^lcoJRW_Fep`@|UFws(As!UyZFOZ5t^f9I;PD8PJpp&0^GDp~ZF93GV0$hu(d?6-|eDH^v z@=%9S%#^oKm%&$3?ot>pcc-2KZeVBdS9FIc^mwz1yKZj&Kib{{JgQ=S{NI#?0D+Cv zNRa@G1_TXKE(og$1X(mF7(f(I6i`%D)D5D51~(Db!=fVCuU)Tx#jbQ|Nr+t(6%-qa zGD|?L6jAtp-kEdGo)}gtnp?Mn#YC4q)!+)3cHonMR>J}GBI~T~!9w7fBpHbvHFq^oDRwpchE%dP~`ZC$!kn2QASh%3&*J-N13qa54%FhhEZY(3H=F&lZK^Y#}bFdy4IQin{EPm=Ai`AdP)J z?t<*-0r|ZfoHf@PH};jM>Rsp9S6`2PeaoOCsKRE3FVEr@I!1I0hGvq8Go#bkU3 zp*;#L!A)XT^NTgv|R*frh5%G^?(D^NbO7)=58wqbGaMj zec=e7kStGaD#7@x#P~)5#?-n@7s3cOdODtBIz$+%9uNptXtGe{(^Q4V2t|RiSpq22 z1yn&pW7x~^0P60NQVbxy?P@58qBTY)lY)&no1ef<{#E-Wrjvgn-v#&hk#;_|rsQ8_S7cvC$Ub=8-fS?O+@A?iN<`jQv#xjl*A#- zK;%d4GGd+tG=&HB?XM2fV?nxwJfJyF6?>3&RfAomOUaN#>MG(bg0`B~8qy>p7+*<= zROD`^J~P#mbbzvLSr0s9tMeIsBql4r2$(XR(~a;qP$ugK7-^Qt)LgY}o4iD@6kfZk zD?Yz9I<)33J zHOsuT1RZb>*8;cD1(#L}T+JH=`!?o8x^PTJrca;H)7Tej!&?(yq&Yv_^|@#R`&6Q@ z=a+p@7#REmY4sUE{v84+SsEx?hFPyI3cp#MQx zu=+JI72^fHq~X}V(dWzYMVhXaA9ebtS`LYyu}u9b-F!;t;BNcVR_9Zy`IO4WHv7|S z&L{Tl2tYP3AN<4seAM~GLKOLw=H9#2iS)0>#|RMMqlsbO~dKFkdG zn96RR^e~y~VSoFS6sY+o%E-XGJgXZS9yiU}DB2*SlN#^63>D<~U+T`qSnO#^KW14%RR(y$!h5{I8&22 zBf(>(S_V~_2jytfH#*xzdBCH&2i)**!_|U9P)fCY1y5cW`zG-uqwk2}UvsdjU$9A@ zpOI%}o}ZBC&&>0K^1R7B-y_eJ=J`%}Uha8b;(319JP(mPi_CL>d7fvU>&vtBtianv zo~Q9#IL=roxnz2*20q6h|!WMWSZrRfN_W;6iu`# zN^GYN+HGu56@TbyV~OPPtj6sy=AT6Fkk?Jr9tq>Zuh!&5%W4{th6JZeR}2t?g&}JpN|(GaULe?dQH5 z=YE+Aq>XbQGCvo{PxajP(%aKdo$M!l+-@^D04`AWE19Z?w3RP4N13FrO_G!@v@Q=5 zBj8wgldBgQvHXPUx$y&dCML0uhJ6X2M#L7P$>`j$F0k0BV>PeKIoZ}D-4F6 zhvCk{80TSvJcuPt5!g$oqzx|H6DT(u!S|}!mjVep;gt0$4c0U#tAx6pbsMyCO&j~Y zg?_JE)7F0X>-Wkv?dkm$x#%4Oh9m1wx9~fqZi0eEIo*li>x#B0FXM^kw)ko7FUcM#ggls zf>ClPWM6#Xg}5Q(1MkM99Us`JrQ-v8h*~*xk#=cc3w+=mm&mhNC@f_0z{wRJ@57`{&!FRtK+O4M&8GIO}difG8O%LbWQ!q0; zk+nV{YEGrpGEZ<_JxCPlOUOy&DitEkjLh{e-orDKe0G8*6H*QpyqsApJU+{I4|w!z z9&b^vb#Q?6a)GSy0QsV$10)?FiL>c_WABWB3%QDlSzu-I#^B>Gg67gVjr+lRd z*9yf+P`R22Jw!1J1kUJto(t2+^AQ*1O&*YExFlVz^<{=Avem?kA@D`KFsF1@4aT1> zc>8zza+;N?NG-7R$H3Cx0!wH0P0OD*t5<&fnw0#&(tUxYk-W6{4QcU_Ez|PL_5}*J zMAxSj_FG@_o>Yt`04<773g!cJPeFVzcZ3$DvsRIT$D)+4#1gUR^k8gIk#RD2zl)Qh z$7(y7VK`x&%vRi;*U7y9v34@$>t;;Wjz%iNSmatFeq>*uT*f|ZKO&tz?MGxVQ-vrf ztwH}mVcg&$fJ5VDlcBLcJ)W|YXl#aZ&1+HrNkO_97ox4id*o6dw|CS$OnY7#68_-& zq?-0Ob=1^ORAYs{BSFoX!gDnANe(LFC~7gfxC;s6g^D9rrW6S=_H2#Mg=y^NY8T{L9*}KZ zk_rT*fT07Dt$I#^z&V(R(cH^#qd8fjvKAq7-97#a%^d?sujX2zP(A%Sqlc@x<2)_E zV#?Z@yOl>#&s!70bNxYtH~bh!3)MF|%olkU?OuJ9BfPss@>Xa93F;6g2;;@=wH7vs zI_tZDZgc^qc>v{-*J$BP03{xvjM2ioGytv^WYNMYZuzzzEvhW1d=o>+782Aonh>pp zjmIRlaDxleXrYGgt5NJ59+DPQ!jQDeQKr_yvll_&q2FL?Gjj5DF`~O?uxP$k|6)py zbw$>xoI*hlqI_(MZz%JOdcY}cUBIGacDDPkBsm-y%%&FUfLK?_Xgx4$yW1gqm&taagvY&_ZJjH$<6P`GvEtjiS<`-I5Wm`jvD3@WKB|f0JnvFx2mS7Ye zZ&qI9aR_ixun@sk;jxSq+IR$eTfmXRt45g8_S`_CYC$DN%U84$!CuY4RS=xBD)R?3 z&XPTh-bj`{XG4hQhL>)^0+z@W6@W5Ku+v=qO3QDACk@i5Vuy@VTrw7Ul#u}zql^=5 zGGyIt5tV#)Djp=hI;h)OL9OPa7P_eglKK!|W#A`AO*g5>OX`<)YOb0HG1ejJ?UoZH*o~eUF1&t-xwX-0r|F@ z`IbP*nV7lPfoQ^<8hnd^m?%j?eASJtw&7PCFVk1ObAdB(KE>0#=Lk>H${C)vT_lz* z5|fTbp!{7LGSyx%>kdUdjM3@9oDQuc0|9e1<=(lpw}^ZL-!RPKnuxh z=w1$Dy|h65<1^(E#(>%G~II>1`l&}R)b@}?rp zc#DJLz?ro1nWfGjU}$EkL2IaS?P~OUj$O$*5qGIUf|tKCtJ58`T9i$0J-MLGY9qDD zaJa+6;R}r%4nG$0SfPhVP$$z4+h(=k=%iTSr$w_yh;_9Tz6@oQbn;1T} zkf5%ape^jpY~f*!y4eM?!Ub}p2gqaOGeF)z0EuDfRn)zDoI4yxiuxQ*+_fcR>U#XJ zjwrtcsyMzZyz&nFnfS;5o&B8SNf+<_m7Ok;=4bYE-)QLB3~eqgQE4&^Uvna&F&6VM z;xZQVI#tHCm>`B^rlfuo`8tbJWWA9zs%ApUnI9hM*vq7rj=h-4s1iULgLxT>oT_|N z14l+rh;XgYl_aPJ7`|;V<69*S<|qfGF_=y+#0xzTyHmhOy;!TuHki}KI&LWJaYJw7 zYCtr`xS5Z+G=in6Xks?R&z%S$7-I!@fl{~S0$Nw3Hch-TZI8D^au%R!1=;V zXc18tj)E!KBP#3yy1)fg=mB)88=>6>phO{M3fQCwNi0|0Sx*3!Kg$cDV=LKT-xHxr z2BEjWV%du|t};t~O>k@uzdjgo$lc|^lID`z#gO|`eaMYtkC6KB%XP zc7de>Q{ux9vzl#sD^Bpw5#G?0FVS9S&2L<@-C;M8Nz#DylE#uCd*q}Ywr}1u2-#{B6=^6 zvtG8ft`Y1MIlGyu5%xZwLVy|@di2Vq4PA+LjSVdlMOdNnB&Y*euU#REh##HhvOWi* zX}*5C#(_B81F@|efezHFl?uTDW2)B}OiK@9uV4__RH+wS2(L~}()6j{p~+`xdV>Tt zNz)Ws-3)pHW#xZh*V9};m$-nYdH~%)Uc=lnoM6J-LpHO49)(dCiNLXnjuPK-R8~{Y zeCmWEn~Kgvku@3eBzB-zP@DsV09YNx*Cf;(u;etDf3x&;r+SM4Zcjt@PwgM20eH(J zw4Lb=rH!E!HGE8hy81j<4ON6?)DJbgHng)`XtO=g?sbE@wYDO)$$_(AJuQ^>!1Q!{ z>X?m3j^xjqk~QZW*`yg8!*clVu)l-TJodM^+HuF*L==uYewUZOYQRr)9P(!Bo`>%z zfN9LHlSe~)(;U8A2&Go&3lh}zRAk%y-XS7$`h{~Gh{kw|U5M{ro;03+x-ngaBR%mA zOGg=hT)P2ZBpf*nxat6WW7!9AG=|uAmnA_DbcvlJpsi33A@*&I00$P-gg&4?;~)S7 z{%}LRP5?;|?&(&#CG@R=8ed{GDe52mbz5oH&D0+EW!;R7X=X@%=BVrI z5l$JJ)DL>dO1^6c&M(u+dJ;4%hhKAvBsFNGB+ivUf&UnP{@rKB&aJtfNTBu4iLTwq zlRr?ljvs4oGn3pLh}o%9gor~tAiu#z<~P@uqu({g=&DwfXux6YQF&m&gU`}WG5_u0 zimv*H)U8M_E83q25H`1>r<$j1D>}$LwY8#WnWt`6^bC3O7wf{u0TM1cLWyiYpsUhi z>?({K0(@(=aTin|THB-6r*^m=wW8ATsPzCMYhE(+nwJ9FO`hpEgOc=)`u)+o+$$ z_;W5vA|H&@jW4}ikXxZ%B&hc=Ta5oWVJ1*^otW;nOb5_H7tofAlYoAAqp6PKQU_6n zAvEKZWiREM8vu!4ZW*1CmTIo9V)PpE$Nb|DcQ6~MB;TWUzQ)m20c{KAefS}64HCAN zv&bA@CGF1Y5i-9d7i)FIT1!{G2kX4Ng9seM`F1&m^C-z{1(3?XNjiq}F0REjoLfO@ zn&O6(P8!bd7~$U;&UkX#hO?epA|BB;oNEDXXU{ZDD6B@Qd~NY14?2z1!QW4E)P?1L^nS5 z&N`A%eo4P6X@S!;RY4W=!u!=Ds~7)pWVJ=)!}-4?sQ!kkI|&6jN=ghuaq>=*ua`X# zu5ts&#TtS`Otuh{ZxeF^;^FR}+`wu4K~A}9e*+J^$2{=byA%yJ6g3fwo~D8#rxcf> zT=l9K!i63PpSz)8S}lsSOM7mu5zo~|Vcpb~UNGl*z})q_BbwDB4l7hng32+3%m9ZG zO*hre3nAMBVWdmQ4M1SHijx>_d0K{V@u`dtutJoUCUG|@YA3$NG|CU6Ze!HnT$HTy z87?&^7;5$(f|`ZYOvJK_5DfD<`4E5}bODWW0o~^TwAPK^zJ--UJ<5nL*h;WVLs*Yh z0Y$-jGzB|fp+*mLYjmI24B67T{v96O8ohjt2nRPI2NU?!?Z7FWDN~+ugt+S~ZV_i< zH!>>(@}mM-7A~mIRwp4p>VGQb&@>m&s~$j~5*Hd5=mSuq17*bA zem=t}8T}e1r@rhcS*v0M&A;QeTLIWr@@91O|D=j62(=xysA9upM{pA^OtRhWpreYj zMf_H%4GC)LnZmYq*bQOFQNz z@B|mawHG95y61pH(;5*!`~OK$9W_m%8+1Tl_U3*7o!|occ9O$fR}Y}GTvAE^l(>sB zqKYX{B3j@y;T8*DL<=L_s`l0j`DEhQJAmnGVVI~#_|Q|Z`pA+YLPA^NDkM(>QLx+T z&8|B%#R#M)aPA}sQ>6x;CkS=elIlhG#Y&XU(0E|p=j3w3mQB3aVT%(r>*4GkRB^o! zT)MP&*dnK2JU7u1%T$k8`tEn+d%noj3LQs++CWvYb>5PQ)d^e7pbyKWjmE~g5Lv+S*ri30;A=?VQN`e}W z#o%0?A`bIK;%b5mWIxr($l)drkU4Gu_b>5k=Sp|=9Z1yoed&|e;-S|hrrRnw%C|+v zdd|wZd5Ol`nD5e9G+$99Ed zU&zM53k;SSg7F(9qr2x6t$%U9A@w8A2&)5FBW!l-3~F$3^gu9@%il8t<(VK|n@$L= z?q5j3hVoiZUgyiJETT4FXUpquUI(-`>!y)egA=(mm85X|+6MXt!nO6KWJI#ACtckl zLeUd5ub!W@>lc4^jADz(mHr(F zIwoB>34UgSj)C&NB(3!eDb;t7qYtmeIETDiv!uB)X3mCiSvkP{IU;twRy!t6P$rqe z@p-?94Xk``fAKd@+|PnMIk$dSc>Qnn!|M;88s>haE^meB9FjZhB9jDGhUzm|jvmM= zwlYRo;dK7b7*S}2GYjJb(PHD)nD@$3(3x@VcxZ^r%6O54Ec%Q4Pa;uB$t5c6r4REc zY00O&u^cBR=RZN8uADXVRW;~5r=<)J*V(DVyug}NdWz~faYz->0~|OdQP&zs_Qk4= z6q^F1^zDq;27Qfa>`RmSJHyE<$?97}?c`=1llmR0D`Wrzo4$;;pUd=;*C%ummsb?@ zj+~DW0Y)o_I}>vcn%19&_h7!2b5(VvYm$<=ONd5$dd<#E&A$&*!Q$(OQix* zHO}OBO)5a-B?V^Pmqk!aMbTQOz+#;d06agMAhLAV$I)Rfd%4O>uK;+gDs+) zOC9h8mdP84mUvp1d6YJCe@^%WWa3^RsZEgUs(sGx+hM16vs)INM_P|{2rE-`xRU>k z(XEwNbp&vxl|7C3B5r%)q`yK=l;WQ}OxF*xCU6DA0)_@oiIdfKS*$0hX8h4Ud!UF_ zw4QVRoA;1<&}_x!l#;V1pG3s9vcyMlW1MJiHA0eR{(C`5pCq$DAJnRsAnEEso-qSa zq&8LC{4OEk&!#$p)IXbQe^Qm7Fo)K07sP_Kx+aDNE_ zpf7T~sw7nL0vP21koesJ5CDJ}|0`PVZoOsRK)9PzDH?LC0E~Spw6!h(Q>+T3`+*wB z?NruypBf21ZF8f%Sl4=5!)w2Hu-;2CZ0;fw)E}6H7zn*Jc?cCj8-{lYZ*te;f+Pkx zx%1B$#(OXv#t>q->{X}wIjO$v9P>^x0Lt`I>FWn$Mb!eB$VVCZZr@|P=bIF=tGr1{CR(Q&; z>7q~Z5MPPMrgq(f5Mou)16P)eFNmifQ`qlNAi4{5F|I1%pLAohR5KVTqUB6u8Hy|l z$r0RXc$M&_>jasA&gCMFH}2~{{UJHsS!W7&_q}-^r*!hICWhUXEd-!?@}MnCJOsP_ zl`*pOVA^LUi2Hv@P-mfk+e17iy^Y8aeN%wrY?7b?9#A)s(|Ci2ZDrRm zN8|85>r~<098!_=z$F3D*z=ZVU+W_BfrrMW_I_ac;>I_8BW8JNub`HNoOlslxvkgI zEjNGBW{}$eHq{Mamp8-lbPgxNOv>_Z6VKNG#_usk% zlm!)Er9EC9d{`8P(;ZDO{)F>Vd@J%vFtuBr)S?jNu~Uyp>$2brq-Z%-iJ!+b2UDi6kf+ksnF70E z$uk?v^pmgV5jq+43})!Q-RC0pi6euYcU1UcPC@iYR-}Gmj7>`%QVR*s3D*qfWYIDG z5jc9Qtr9XUBRmw4t`!g{zncadpCbZyd=3}fsAmQMLfnnvHhLsA@~oP!B~vM$=hJM>4Vd=4^n?D^M(V}xylVk@+R1a*@(Djha&CZxjgOgds5Pm3gi>+2vG z>mtbUAXwzaXe;xDAlkfaV4=}HxL!0Gq?l^LHyUiM(Vw&!XqiM6!6MA!VM)>cK7-7f zbQ&pBcP0r`-M#yB?oZFDo4b$BU4+9ae zF=8p`K!PQMI-#Xhg^7M%@`;2{Tl)(wvjjRFvmVm4SDkD_)5ji5vYc(Do|ELLDf(@q zz9emvB;9k6NG`4=03y~%8`i{LyDf_Dk?4AZ}yBKgr`GhnHfa9rChV66I# zTJLqsm^8_ze?A;0MuKAnojcnP$dj7i54f@Ire`DbS(wI2|Vm1e8m^- zLNqn>VWOPC9atd#B3a*An@6@HhChZUoCpQf{R7#R;aBIyi5!dWzm6X zB=km3=VHsVHG(P^e%l=N+)p-qdMU}PEuS8h6v#VnjQ6wn9f2uh28<^dCW^&LN#ojY z1E(CH?BkT<)SQ|<^bN)19+5KC&n(*m?=|lwlewF$f7@UnUSEn&tSE{HtI!;EZlvx4 zY|!nz5!fnt(E*R_gj(s70(3m9RaA7#6|##M&EVmK8?A7Bk+zeflmq5&XxfDd8Y~Y} zoQG-7!wmCK7A%zq{)M@1UHaSdPo8f#kNWu@&-25c=chc+FL<6c?Ys+|b)NLsJOg{8En`W;R&)J=?t>ceytz*#2f_+JrIeW&FMI*O7Aiw+z$8XG#12ef! zE!^ecob`{B#?1|}H}EgUAyLpC=UjxFrr)vPxD;`8l|%o>_&5IQ^MG2J#UJSh?CYfm z(*uy;^oOOH`8g(?WV@NU2;TI#dF&K$s+TFEJw^1$1%a{_P^L-|DnTwTL+$vUaWoy^ z7AzMirrE6T(Qn$5%`qhNf47EM%Ni-NmeT~QmEUD2r%X$ zhO#b(vRgH*3iab2;eM;(w5*OlArKmrLrSisOpuf*q!42i_mz}sk}`vod{V;FZ!1NqVQZ209i$H@eT<}w|B&>1 zqqHAlnQYV2)^2hP)EHTL!%{wr~wbddazVMe01kw&jh4WziV<)enh5(UeJ zPXp=xGm{iu;8IjhDJVKqD3aR~?K&C~ssd#{&=l%%s2er#O7-n77x;EB@KZd%Q(ZE1 z1-R7Gr+}kQ7m0BZKnE^aDO(q59ZssWQ9u*Ac0*5{65`sf-FI`SFfyzeB#Jxcz5 zmneCDf%N2hYM52(K7l!^7R)gon5!vbl>N1>mIMRd-p;TV3sq^K9N87`eHcfnjwP>_ zf%Jxs#VR3I%pK#UJ=o6yQ(6o|`&E{-mX6acuqt;d=(!3xVBJXI`4dC}8<9Xc1vtY( z?N%DMoZ;vm;Qyn?HXFmSA-AWXutL9YLj-q=%1!^UhPclcu?nnHR1Xbvqq;|6vc1#` z^Pe7=E8N)eOIwtB^wstxb9QfqtJ-Vk%7U+xCV{~7(hQca)vJIa%wIk)PtwQ8AF7YL zUv~x}LMdrxs6d_!`IEX!|K)M*22IwN|D4qg(fW{E4}*(2#o$(k3|is*)KtFcib>!c zC==|epKuYYDzMrH)dM?4GSpHXJWGq9t?Ij?WF=kssUy;_M2uGGX%bYS*eoJ7)2qyX z5Q+IBXXskGRr1*g`u{b8au0%Y-Kg#+TTdFOj0w%xf^_>tf%1g}5%_fxx$Oaq;?L}b zD+y)uaW#ZpBY<5;rp^e5YTOOdh_BXB#NMMhYJ2xH;* zctCCa)&X@`RKPfz&S$QIwRzN6O6i-F<7c}Tq<;O42C(=KA~wA$kR?k;VGa(vVg**T z6F7$_IqwDHzvKL>E^yw!rdj$pF6W6MNzS`$amYPO$hAV*B&e6gvW<_c4?n{F+Hd}j z`};iLK6T?N-@oTxUmh#Lr(J*4{)2Eo>UU_~2jQ+i%+*3ZK+meVj%32IMZs*+dT`my zvfJQFPtGvz(E}=E8m^*rn=i0t8#CbDbfY0t5~c_>-@pizcf^}f^98tF^QFpX6~*U0 z4@gYv2j+xU`1ovGsXV#WzGbfHm7~sA>LC{@bnpNvG){L0mNJ|^gPrRQ5^ylUq9uQTlU=UnUHx{P6q6y2O?=P;T%A;} zuSvul?C~u<6(FnfJA2G<#7B-^uM>z?D4ztijoM)A^~7&Fpw~h4(WYz2l|(Dsw%dTs z@TUv-ZI6ZzxS`N7np7Fo*sGc%6A6BNiTTuPHAs^!V@F5Zz{hjS(BD8m#RL7m4;`}C z;V~gQMuH03WNQr6$ZMeiXVw8!())OotoAt4(SL*EQ}pRL%sws$;{*q0ZYckNswcdh z2Jwk1Uer|0#sSCDScyr~5L(#WSlWitPvwN4W0};BF6)4O%sWmR6sy$ys>ZK z48--Gt#iE~pKG?VaAVGHW8K~r@@2-l*Ewt5%gZ%VEU74N*~Ynb(4UOc_~u!ig@bPk ztutiAFkDEsEQ%ZRk|)=O&z78GpE{~$P)J?3j}+n_sa$S|P}0Y(B`{!!cn>&0Fa9$T z>h~a$n<;JBNypae*3H>%BCpz<3ei2*S##e6C8yOy{t*?spF!O;ev%ej9y~4fu14@M zr&55KdaJv3*4&L60@t9l(+~cVHRw#%=&Lq+#h;qx`cGST4k0aer%eCTX}_0KLE;5R zqQ9l+KrK+XA!c>r6Gw;$li_>JRg15r6;oSdVlOsM{8x+TX=y7VBzMRTO{B zNav};uxM6zodk>(@@ndoJQ1cDIUnx8FrB1Oo%DQFM?&z8;d?d~`l;}#v5oQ5nBLk{ zI;(~6#1hWkGF{Dy`@$*TYcWZg)|3gyUlx0nalY}pUF!{$l>)l91#|4Vq-mp#&$)?; z$MUs$D1vjQJXRgPC$Mx;+GcI~VnSZC=HMN%bxnYJ01p1nw45*3z2&F}@Pd z{urbsfC{Zuf`T)7Bs&_HYGG+3Vcs}X&Dds?&I}vJ@K7%b8I#~BP-(wD!U)$05>$Q1 zyN>Rci8f?a5c(%kwZh5J23$pO#>qSX317y)!8h51uY()w4RP=x|8wY)NuBSRgJZjd zo~^Nh2B-eX3f$UqpuMN3e+INlzx8%}nmPd>IsnyO{b%4)9pXEY=wRyvb-|~$U^tX5 zxzOhSz~qSJ#&;Z%JT4-zLRXNWGN_$)br`ozL^4Ri(ML64*4Xq>^&G%>p `oDm+t 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1d3d79575e30ab81691680da91d24a26c859acbc GIT binary patch literal 18064 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 dZ!oima|WEOR|-<|^64*`XG&a +#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 0000000000000000000000000000000000000000..a0589464c88be44f524537760a654e0cc4d0af06 GIT binary patch literal 23128 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 literal 0 HcmV?d00001 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