diff --git a/package/wwan/luci-app-hypermodem/Makefile b/package/wwan/luci-app-hypermodem/Makefile index fbb799164..98dc04411 100644 --- a/package/wwan/luci-app-hypermodem/Makefile +++ b/package/wwan/luci-app-hypermodem/Makefile @@ -8,7 +8,18 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=Hyper Modem Server -LUCI_DEPENDS:=+luci-compat +LUCI_DEPENDS:=+luci-compat +kmod-usb-net +kmod-usb-net-cdc-ether +kmod-usb-acm \ + +kmod-usb-net-qmi-wwan +kmod-usb-net-rndis +kmod-usb-serial-qualcomm \ + +kmod-usb-net-sierrawireless +kmod-usb-ohci +kmod-usb-serial \ + +kmod-usb-serial-option +kmod-usb-wdm \ + +kmod-usb2 +kmod-usb3 \ + +kmod-usb-net-cdc-mbim \ + +usbutils \ + +luci-proto-qmi \ + +pciutils \ + +kmod-pcie_mhi \ + +quectel-CM-5G \ + +grep \ include $(TOPDIR)/feeds/luci/luci.mk diff --git a/package/wwan/luci-app-hypermodem/README.md b/package/wwan/luci-app-hypermodem/README.md index d77455362..e6fe7bbd7 100644 --- a/package/wwan/luci-app-hypermodem/README.md +++ b/package/wwan/luci-app-hypermodem/README.md @@ -1 +1,20 @@ -# luci-app-hypermodem \ No newline at end of file +# luci-app-hypermodem + +# 目录 + +[一、说明](#一说明) + +# 一、说明 + +原项目地址:https://github.com/momokind/luci-app-hypermodem + +插件功能 + +- 支持USB和PCIe两种通信方式的通信模组 + +- 支持IPv6 + +- 支持高通和紫光展锐两个平台的通信模组 + +- 支持常见厂商的通信模组(例如:移远,广和通等) + diff --git a/package/wwan/luci-app-hypermodem/luasrc/model/cbi/hypermodem.lua b/package/wwan/luci-app-hypermodem/luasrc/model/cbi/hypermodem.lua index 9f94f781a..51e5459e7 100644 --- a/package/wwan/luci-app-hypermodem/luasrc/model/cbi/hypermodem.lua +++ b/package/wwan/luci-app-hypermodem/luasrc/model/cbi/hypermodem.lua @@ -5,7 +5,7 @@ mp = Map("hypermodem") mp.title = translate("Hyper Modem Server") mp.description = translate("Modem Server For OpenWrt") -s = mp:section(TypedSection, "service", "Base Setting") +s = mp:section(TypedSection, "service", translate("Base Setting")) s.anonymous = true enabled = s:option(Flag, "enabled", translate("Enable")) @@ -16,6 +16,11 @@ ipv6 = s:option(Flag, "ipv6", translate("Enable IPv6")) ipv6.default = 1 ipv6.rmempty = false +network_bridge = s:option(Flag, "network_bridge", translate("Enable Network bridge")) +network_bridge.description = translate("After checking, enable network interface bridge.") +network_bridge.default = 0 +network_bridge.rmempty = false + device = s:option(Value, "device", translate("Modem device")) device.rmempty = false @@ -29,20 +34,34 @@ if device_suggestions then end apn = s:option(Value, "apn", translate("APN")) +apn.default = "" apn.rmempty = true +apn:value("", translate("Auto Choose")) +apn:value("cmnet", translate("China Mobile")) +apn:value("3gnet", translate("China Unicom")) +apn:value("ctnet", translate("China Telecom")) +apn:value("cbnet", translate("China Broadcast")) +apn:value("5gscuiot", translate("Skytone")) -username = s:option(Value, "username", translate("PAP/CHAP username")) -username.rmempty = true - -password = s:option(Value, "password", translate("PAP/CHAP password")) -password.rmempty = true - -auth = s:option(Value, "auth", translate("Authentication Type")) -auth.rmempty = true -auth:value("", translate("-- Please choose --")) -auth:value("both", "PAP/CHAP (both)") +auth = s:option(ListValue, "auth", translate("Authentication Type")) +auth.default = "none" +auth.rmempty = false +auth:value("none", translate("NONE")) +auth:value("both", translate("PAP/CHAP (both)")) auth:value("pap", "PAP") auth:value("chap", "CHAP") -auth:value("none", "NONE") + +username = s:option(Value, "username", translate("PAP/CHAP Username")) +username.rmempty = true +username:depends("auth", "both") +username:depends("auth", "pap") +username:depends("auth", "chap") + +password = s:option(Value, "password", translate("PAP/CHAP Password")) +password.rmempty = true +password.password = true +password:depends("auth", "both") +password:depends("auth", "pap") +password:depends("auth", "chap") return mp diff --git a/package/wwan/luci-app-hypermodem/po/zh-cn/hypermodem.po b/package/wwan/luci-app-hypermodem/po/zh-cn/hypermodem.po index 2434a4187..383e59532 100644 --- a/package/wwan/luci-app-hypermodem/po/zh-cn/hypermodem.po +++ b/package/wwan/luci-app-hypermodem/po/zh-cn/hypermodem.po @@ -22,3 +22,42 @@ msgstr "OpenWrt移动网络拨号服务" msgid "Enable IPv6" msgstr "启用IPv6协商" + +msgid "Enable Network bridge" +msgstr "启用网络桥架" + +msgid "After checking, enable network interface bridge." +msgstr "勾选后,启用网络接口桥接。" + +msgid "APN" +msgstr "接入点" + +msgid "China Mobile" +msgstr "中国移动" + +msgid "China Unicom" +msgstr "中国联通" + +msgid "China Telecom" +msgstr "中国电信" + +msgid "China Broadcast" +msgstr "中国广电" + +msgid "Skytone" +msgstr "天际通" + +msgid "Authentication Type" +msgstr "认证类型" + +msgid "PAP/CHAP (both)" +msgstr "PAP/CHAP (均使用)" + +msgid "NONE" +msgstr "无" + +msgid "PAP/CHAP Username" +msgstr "PAP/CHAP 用户名" + +msgid "PAP/CHAP Password" +msgstr "PAP/CHAP 密码" \ No newline at end of file diff --git a/package/wwan/luci-app-hypermodem/root/etc/init.d/hypermodem b/package/wwan/luci-app-hypermodem/root/etc/init.d/hypermodem index 4c4e2080a..b44da1838 100755 --- a/package/wwan/luci-app-hypermodem/root/etc/init.d/hypermodem +++ b/package/wwan/luci-app-hypermodem/root/etc/init.d/hypermodem @@ -5,43 +5,98 @@ START=94 STOP=13 USE_PROCD=1 -pre_set() +#设置防火墙 +# $1:网络接口名称 +set_firewall() { - [ "$(uci get network.wan.ifname)" != "$1" ] && { - uci set network.wwan='interface' - uci set network.wwan.ifname="$1" - uci set network.wwan.proto='dhcp' - if [ "$ipv6" = 1 ]; then - uci set network.wwan6='interface' - uci set network.wwan6.ifname="$1" - uci set network.wwan6.proto='dhcpv6' - uci set network.wwan6.extendprefix='1' - fi - uci commit network + local interface_name="$1" - num=`uci show firewall |grep "name='wan'" |wc -l` - wwan_num=`uci get firewall.@zone[$num].network |grep -w wwan |wc -l` - wwan6_num=`uci get firewall.@zone[$num].network |grep -w wwan6 |wc -l` - if [ "$wwan_num" = "0" ]; then - uci add_list firewall.@zone[$num].network='wwan' - fi - if [ "$ipv6" = 1 ]; then - if [ "$wwan6_num" = "0" ]; then - uci add_list firewall.@zone[$num].network='wwan6' - fi - fi - uci commit firewall + local num=$(uci show firewall | grep "name='wan'" | wc -l) + local wwan_num=$(uci -q get firewall.@zone[$num].network | grep -w "${interface_name}" | wc -l) + if [ "$wwan_num" = "0" ]; then + uci add_list firewall.@zone[$num].network="${interface_name}" + fi + uci commit firewall +} - ifup wwan - if [ "$ipv6" = 1 ]; then - ifup wwan6 - fi +#设置IPv4网络接口 +# $1:网络接口名称 +# $2:网络接口 +set_ipv4_interface() +{ + local interface_name="$1" + local network_interface="$2" + + #添加或修改网络配置 + uci set network.${interface_name}='interface' + uci set network.${interface_name}.proto='dhcp' + uci set network.${interface_name}.device="${network_interface}" + uci set network.${interface_name}.ifname="${network_interface}" + uci commit network + + #加入WAN防火墙 + set_firewall "${interface_name}" + + #启动网络接口 + ifup "${interface_name}" +} + +#设置IPv6网络接口 +# $1:网络接口名称 +# $2:网络接口 +set_ipv6_interface() +{ + local interface_name="$1" + local network_interface="$2" + + #添加或修改网络配置 + uci set network.${interface_name}='interface' + uci set network.${interface_name}.proto='dhcpv6' + uci set network.${interface_name}.extendprefix='1' + uci set network.${interface_name}.device="${network_interface}" + uci set network.${interface_name}.ifname="${network_interface}" + uci commit network + + #加入WAN防火墙 + set_firewall "${interface_name}" + + #启动网络接口 + ifup "${interface_name}" +} + +#设置IPV4和IPv6网络接口 +# $1:IPV4网络接口名称 +# $2:IPv6网络接口名称 +# $3:网络接口 +set_ipv4v6_interface() +{ + local ipv4_interface_name="$1" + local ipv6_interface_name="$2" + local network_interface="$3" + + #设置IPV4网络接口 + set_ipv4_interface "${ipv4_interface_name}" "${network_interface}" + #设置IPV6网络接口(别名) + set_ipv6_interface "${ipv6_interface_name}" "@${ipv4_interface_name}" +} + +#设置网络接口 +# $2:网络接口 +set_interface() +{ + local network_interface="$1" + + local pdp_type + [ "$ipv6" = "1" ] && { + pdp_type="ipv4v6" } - [ "$(uci get network.wan6.ifname)" == "$1" ] && { - uci set network.wan6.extendprefix='1' - uci commit network - } + case $pdp_type in + "ipv4") set_ipv4_interface "wwan_5g" "${network_interface}" ;; + "ipv6") set_ipv6_interface "wwan6_5g" "${network_interface}" ;; + "ipv4v6") set_ipv4v6_interface "wwan_5g" "wwan6_5g" "${network_interface}" ;; + *) set_ipv4v6_interface "wwan_5g" "wwan6_5g" "${network_interface}" ;; + esac } run_dial() @@ -55,6 +110,7 @@ run_dial() local password local auth local ipv6 + local network_bridge local device config_get apn $1 apn @@ -62,48 +118,56 @@ run_dial() config_get password $1 password config_get auth $1 auth config_get ipv6 $1 ipv6 + config_get network_bridge $1 network_bridge config_get device $1 device - devname="$(basename "$device")" - devicepath="$(find /sys/class/ -name $devname)" - devpath="$(readlink -f $devicepath/device/)" - ifname="$( ls "$devpath"/net )" + devname="$(basename "${device}")" + devicepath="$(find /sys/class/ -name ${devname})" + devpath="$(readlink -f ${devicepath}/device/)" + network="$( ls "${devpath}"/net )" + #拨号配置 procd_open_instance procd_set_param command quectel-CM if [ "$ipv6" = 1 ]; then - procd_append_param command -4 -6 + procd_append_param command "-4" "-6" + fi + if [ "$network_bridge" = 1 ]; then + procd_append_param command "-b" fi if [ "$apn" != "" ];then - procd_append_param command -s $apn + procd_append_param command "-s" "$apn" fi - if [ "$user" != "" ]; then - procd_append_param command $user + if [ "$username" != "" ]; then + procd_append_param command "$username" fi if [ "$password" != "" ]; then - procd_append_param command $password + procd_append_param command "$password" fi if [ "$auth" != "" ]; then - procd_append_param command $auth + procd_append_param command "$auth" fi if [ "$device" != "" ]; then - procd_append_param command -i $ifname + procd_append_param command -i "$network" fi procd_set_param respawn procd_close_instance + #设置网络接口 + local network_interface if [ -d /sys/class/net/rmnet_mhi0 ]; then - pre_set rmnet_mhi0.1 + network_interface="rmnet_mhi0.1" elif [ -d /sys/class/net/wwan0_1 ]; then - pre_set wwan0_1 + network_interface="wwan0_1" elif [ -d /sys/class/net/wwan0.1 ]; then - pre_set wwan0.1 + network_interface="wwan0.1" elif [ -d /sys/class/net/wwan0 ]; then - pre_set wwan0 + network_interface="wwan0" fi + set_interface "${network_interface}" fi - sleep 3 + sleep 15 } service_triggers() diff --git a/package/wwan/luci-app-hypermodem/root/etc/uci-defaults/luci-hypermodem b/package/wwan/luci-app-hypermodem/root/etc/uci-defaults/luci-hypermodem old mode 100644 new mode 100755 diff --git a/package/wwan/luci-app-modem/Makefile b/package/wwan/luci-app-modem/Makefile index 16a8cc292..3b026664c 100644 --- a/package/wwan/luci-app-modem/Makefile +++ b/package/wwan/luci-app-modem/Makefile @@ -24,8 +24,10 @@ LUCI_DEPENDS:=+luci-compat \ +kmod-pcie_mhi \ +pciutils \ +quectel-CM-5G \ + +modemmanager \ + +luci-proto-modemmanager \ +sms-tool \ - +jq + +bc +jq define Package/luci-app-modem/conffiles /etc/config/modem diff --git a/package/wwan/luci-app-modem/README.md b/package/wwan/luci-app-modem/README.md index 52ba5004e..e75c283c4 100644 --- a/package/wwan/luci-app-modem/README.md +++ b/package/wwan/luci-app-modem/README.md @@ -26,6 +26,7 @@ | 厂家名称 | 模组名称 | 平台 | 数据传输模式 | 端口模式 | | -------- | -------------------------------------------------- | -------- | ------------ | ---------------------------- | +| 华为 | MH5000-31 | 华为 | USB | ECM,NCM | | 移远通信 | RG200U-CN(DONGLE版) | 紫光展锐 | USB | ECM,MBIM,RNDIS,NCM | | 移远通信 | RM500U-CN | 紫光展锐 | USB | ECM,MBIM,RNDIS,NCM | | 移远通信 | RM500U-EA | 紫光展锐 | USB | ECM,MBIM,RNDIS,NCM | diff --git a/package/wwan/luci-app-modem/luasrc/controller/modem.lua b/package/wwan/luci-app-modem/luasrc/controller/modem.lua index c7dcb06c2..bd712688d 100644 --- a/package/wwan/luci-app-modem/luasrc/controller/modem.lua +++ b/package/wwan/luci-app-modem/luasrc/controller/modem.lua @@ -34,6 +34,7 @@ function index() entry({"admin", "network", "modem", "set_mode"}, call("setMode"), nil).leaf = true entry({"admin", "network", "modem", "get_network_prefer_info"}, call("getNetworkPreferInfo"), nil).leaf = true entry({"admin", "network", "modem", "set_network_prefer"}, call("setNetworkPrefer"), nil).leaf = true + entry({"admin", "network", "modem", "set_band_prefer"}, call("setBandPrefer"), nil).leaf = true entry({"admin", "network", "modem", "get_self_test_info"}, call("getSelfTestInfo"), nil).leaf = true entry({"admin", "network", "modem", "get_quick_commands"}, call("getQuickCommands"), nil).leaf = true entry({"admin", "network", "modem", "send_at_command"}, call("sendATCommand"), nil).leaf = true @@ -587,14 +588,24 @@ end function getNetworkPreferInfo() local at_port = http.formvalue("port") - --获取制造商 - local manufacturer=getManufacturer(at_port) + --获取制造商,数据接口,模组名称 + local manufacturer + local data_interface + local name + uci:foreach("modem", "modem-device", function (modem_device) + if at_port == modem_device["at_port"] then + manufacturer=modem_device["manufacturer"] + data_interface=modem_device["data_interface"] + name=modem_device["name"] + return true --跳出循环 + end + end) --获取值 local network_prefer_info if manufacturer~="unknown" then --获取模组网络偏好 - local command="source "..script_path..manufacturer..".sh && "..manufacturer.."_get_network_prefer "..at_port + local command="source "..script_path..manufacturer..".sh && "..manufacturer.."_get_network_prefer "..at_port.." "..data_interface.." "..name local result=shell(command) network_prefer_info=json.parse(result) end @@ -615,8 +626,18 @@ function setNetworkPrefer() local at_port = http.formvalue("port") local network_prefer_config = json.stringify(http.formvalue("prefer_config")) - --获取制造商 - local manufacturer=getManufacturer(at_port) + --获取制造商,数据接口,模组名称 + local manufacturer + local data_interface + local name + uci:foreach("modem", "modem-device", function (modem_device) + if at_port == modem_device["at_port"] then + manufacturer=modem_device["manufacturer"] + data_interface=modem_device["data_interface"] + name=modem_device["name"] + return true --跳出循环 + end + end) --设置模组网络偏好 local command="source "..script_path..manufacturer..".sh && "..manufacturer.."_set_network_prefer "..at_port.." "..network_prefer_config @@ -624,11 +645,48 @@ function setNetworkPrefer() --获取设置好后的模组网络偏好 local network_prefer={} - if at_port and manufacturer and manufacturer~="unknown" then - local command="source "..script_path..manufacturer..".sh && "..manufacturer.."_get_network_prefer "..at_port - local result=shell(command) - network_prefer=json.parse(result) - end + -- if at_port and manufacturer and manufacturer~="unknown" then + -- local command="source "..script_path..manufacturer..".sh && "..manufacturer.."_get_network_prefer "..at_port.." "..data_interface.." "..name + -- local result=shell(command) + -- network_prefer=json.parse(result) + -- end + + -- 写入Web界面 + luci.http.prepare_content("application/json") + luci.http.write_json(network_prefer) +end + +--[[ +@Description 设置频段偏好 +]] +function setBandPrefer() + local at_port = http.formvalue("port") + local network_prefer_config = json.stringify(http.formvalue("prefer_config")) + + --获取制造商,数据接口,模组名称 + local manufacturer + local data_interface + local name + uci:foreach("modem", "modem-device", function (modem_device) + if at_port == modem_device["at_port"] then + manufacturer=modem_device["manufacturer"] + data_interface=modem_device["data_interface"] + name=modem_device["name"] + return true --跳出循环 + end + end) + + --设置模组网络偏好 + local command="source "..script_path..manufacturer..".sh && "..manufacturer.."_set_band_prefer "..at_port.." "..network_prefer_config + shell(command) + + --获取设置好后的模组网络偏好 + local network_prefer={} + -- if at_port and manufacturer and manufacturer~="unknown" then + -- local command="source "..script_path..manufacturer..".sh && "..manufacturer.."_get_network_prefer "..at_port.." "..data_interface.." "..name + -- local result=shell(command) + -- network_prefer=json.parse(result) + -- end -- 写入Web界面 luci.http.prepare_content("application/json") @@ -894,6 +952,7 @@ function getPluginInfo() --制造商私有驱动 usb_driver_info["qmi_wwan_q.ko"]="Not loaded" usb_driver_info["qmi_wwan_f.ko"]="Not loaded" + usb_driver_info["qmi_wwan_m.ko"]="Not loaded" usb_driver_info["meig_cdc_driver.ko"]="Not loaded" setModelStatus(usb_driver_info) diff --git a/package/wwan/luci-app-modem/luasrc/view/modem/modem_debug.htm b/package/wwan/luci-app-modem/luasrc/view/modem/modem_debug.htm index ebb911965..e92b87ba5 100644 --- a/package/wwan/luci-app-modem/luasrc/view/modem/modem_debug.htm +++ b/package/wwan/luci-app-modem/luasrc/view/modem/modem_debug.htm @@ -46,6 +46,39 @@ copy_to_input(); }); + // 设置网络偏好事件 + set_network_prefer_event(); + } + + // 设置频段偏好事件 + function set_band_prefer_event(network_type) + { + //获取网络偏好选项元素 + var prefer_option_auto = document.getElementById(network_type+'_prefer_option_auto'); + var prefer_option_custom = document.getElementById(network_type+'_prefer_option_custom'); + //网络偏好选项为自动时触发 + prefer_option_auto.addEventListener('change', function() { + if (prefer_option_auto.checked) + { + //禁用偏好复选框 + disabled_prefer_custom_config(network_type+"_",true); + //全选偏好复选框 + all_choose_prefer_custom_config(network_type+"_",true); + } + }); + //网络偏好选项为自定义时触发 + prefer_option_custom.addEventListener('change', function() { + if (prefer_option_custom.checked) + { + //禁用偏好复选框 + disabled_prefer_custom_config(network_type+"_",false); + } + }); + } + + // 设置网络偏好事件 + function set_network_prefer_event() + { //获取网络偏好选项元素 var prefer_option_auto = document.getElementById('prefer_option_auto'); var prefer_option_custom = document.getElementById('prefer_option_custom'); @@ -54,9 +87,9 @@ if (prefer_option_auto.checked) { //禁用偏好复选框 - disabled_prefer_custom_config(true); + disabled_prefer_custom_config("",true); //全选偏好复选框 - all_choose_prefer_custom_config(true); + all_choose_prefer_custom_config("",true); } }); //网络偏好选项为自定义时触发 @@ -64,9 +97,15 @@ if (prefer_option_custom.checked) { //禁用偏好复选框 - disabled_prefer_custom_config(false); + disabled_prefer_custom_config("",false); } }); + + //设置频段偏好事件 + set_band_prefer_event("5g"); + set_band_prefer_event("4g"); + set_band_prefer_event("3g"); + set_band_prefer_event("2g"); } //设置标签菜单事件 @@ -412,9 +451,9 @@ } // 全选网络偏好复选框 - function all_choose_prefer_custom_config(status) + function all_choose_prefer_custom_config(id_prefix,status) { - var checkboxes=document.getElementById('prefer_custom_config').querySelectorAll('input[type="checkbox"]'); + var checkboxes=document.getElementById(id_prefix+'prefer_custom_config').querySelectorAll('input[type="checkbox"]'); for(checkbox of checkboxes) { //设置网络偏好复选框状态 @@ -423,9 +462,9 @@ } // 禁用网络偏好复选框 - function disabled_prefer_custom_config(status) + function disabled_prefer_custom_config(id_prefix,status) { - var checkboxes=document.getElementById('prefer_custom_config').querySelectorAll('input[type="checkbox"]'); + var checkboxes=document.getElementById(id_prefix+'prefer_custom_config').querySelectorAll('input[type="checkbox"]'); for(checkbox of checkboxes) { //禁用 @@ -433,6 +472,25 @@ } } + // 禁用网络偏好功能 + function disabled_network_prefer_function(id_prefix,status) + { + //偏好选项 + var prefer_option_auto = document.getElementById('prefer_option_auto'); + prefer_option_auto.disabled=status; + document.getElementById('prefer_option_custom').disabled=status; + + //网络偏好为自动则不启用 + if (!prefer_option_auto.checked) + { + //偏好复选框 + disabled_prefer_custom_config("",status); + } + + //偏好按钮 + document.getElementById(id_prefix+'_prefer_button').disabled=status; + } + // 禁用功能 function disabled_function(function_name,status) { @@ -454,20 +512,10 @@ //网络偏好 else if (function_name=="network_prefer") { - //偏好选项 - document.getElementById('prefer_option_auto').disabled=status; - document.getElementById('prefer_option_custom').disabled=status; - - //网络偏好为自动则不启用 - var prefer_option_auto = document.getElementById('prefer_option_auto'); - if (!prefer_option_auto.checked) - { - //偏好复选框 - disabled_prefer_custom_config(status); - } - - //偏好按钮 - document.getElementById('network_prefer_button').disabled=status; + // 网络类型 + disabled_network_prefer_function("network",status); + // 频段 + disabled_network_prefer_function("band",status); } } @@ -549,29 +597,141 @@ ); } - // 获取当前网络视图 - function get_current_prefer_view(network_prefer) + // 设置网络偏好视图 + function set_network_prefer_view(network_prefer) { - var current_prefer_view=""; + var current_prefer_view=""; //当前网络偏好 + var prefer_check_view=""; //网络偏好复选框 + var network_type_count=0; //统计已启用的网络类型 - //自动状态判断(全部选中为自动) - if (network_prefer["3G"]&&network_prefer["4G"]&&network_prefer["5G"]) + for(var keys of network_prefer) { - //更新当前偏好 - current_prefer_view="AUTO"; - } - else - { - //更新当前偏好 - for(key in network_prefer) + for(var key in keys) { - if (network_prefer[key]) { + var band_info_display="none"; //频段偏好显示 + var is_check=""; //是否选中(启用) + var network_type=key.toLowerCase(); //转小写 + + //已启动的网络类型 + if (keys[key]["enable"]) + { + //设置偏好视图 current_prefer_view+=key+" "; + if (keys[key]["band"].length!=0) { + band_info_display="block"; + } + is_check="checked"; + network_type_count++; + } + //设置偏好复选框视图 + prefer_check_view+='<span class="cbi-checkbox"><input id="prefer_config_'+network_type+'" type="checkbox" class="cbi-input-checkbox" value="'+network_type+'" '+is_check+'><span>'+key+'</span></span> '; + + //频段偏好 + var current_prefer_band_view=""; //当前频段偏好 + var band_prefer_check_view=""; //频段偏好复选框 + var band_count=0; //统计已启用的频段 + + for(var bands of keys[key]["band"]) + { + for(var band in bands) + { + var band_is_check=""; //是否选中(启用) + var band_name=band.toLowerCase(); //转小写 + + //已启动的频段 + if (bands[band]) + { + //设置偏好视图 + current_prefer_band_view+=band+" "; + band_is_check="checked"; + band_count++; + } + //设置频段偏好复选框视图 + band_prefer_check_view+='<span class="cbi-checkbox"><input id="band_prefer_config_'+band_name+'" type="checkbox" class="cbi-input-checkbox" value="'+band_name+'" '+band_is_check+'><span>'+band+'</span></span> '; + } + } + + //设置当前频段偏好(全未启用则为空) + if (band_count==0) + { + current_prefer_band_view="NONE"; + } + document.getElementById(network_type+'_current_prefer').innerHTML=current_prefer_band_view; + + var first_cache=document.getElementById("first_cache"); + if (first_cache.checked) + { + //设置频段偏好复选框 + document.getElementById(network_type+'_band_check').innerHTML=band_prefer_check_view; + + //自动状态判断(全部启用为自动) + if (band_count==Object.keys(keys[key]["band"]).length) + { + //设置偏好选项 + document.getElementById(network_type+'_prefer_option_auto').checked=true; + //更新偏好配置 + all_choose_prefer_custom_config(network_type+"_",true); + //禁用用偏好复选框 + disabled_prefer_custom_config(network_type+"_",true); + } + else + { + //设置偏好选项 + document.getElementById(network_type+'_prefer_option_custom').checked=true; + //启用偏好复选框 + disabled_prefer_custom_config(network_type+"_",false); + } + } + + //设置频段偏好显示(beta功能) + band_info_display="none"; //不显示频段偏好 + + //获取当前启用的tab元素 + var tab_menu = document.getElementById("tab_menu"); + var tab_enable_element=get_tab_enable_element(tab_menu); + //获取当前选中的tab元素 + var data_tab=tab_enable_element.getAttribute("data-tab").replace("_tab",""); + if (data_tab=="network_prefer") + { + document.getElementById("cbi-"+network_type+"_band_info").style.display=band_info_display; } } } + + //设置当前网络偏好(全部启用为自动) + if (network_type_count==Object.keys(network_prefer).length) { + current_prefer_view="AUTO"; + } + document.getElementById('current_prefer').innerHTML=current_prefer_view; + + var first_cache=document.getElementById("first_cache"); + if (first_cache.checked) + { + //设置网络偏好复选框 + document.getElementById('network_type_check').innerHTML=prefer_check_view; + + //自动状态判断(全部启用为自动) + if (network_type_count==Object.keys(network_prefer).length) + { + //设置偏好选项 + document.getElementById('prefer_option_auto').checked=true; + //更新偏好配置 + all_choose_prefer_custom_config("",true); + //禁用用偏好复选框 + disabled_prefer_custom_config("",true); + } + else + { + //设置偏好选项 + document.getElementById('prefer_option_custom').checked=true; + //启用偏好复选框 + disabled_prefer_custom_config("",false); + } + + //设置第一次获取数据标志 + first_cache.checked=false; + } - return current_prefer_view; } // 设置网络偏好信息 @@ -580,45 +740,57 @@ //获取模组网络偏好 var network_prefer=network_prefer_info["network_prefer"]; - //获取偏好视图 - var current_prefer_view=get_current_prefer_view(network_prefer); + //设置网络偏好视图 + set_network_prefer_view(network_prefer); - //设置当前网络偏好 - document.getElementById('current_prefer').innerHTML=current_prefer_view; + // //设置当前网络偏好 + // document.getElementById('current_prefer').innerHTML=current_prefer_view; - //设置偏好选项和复选框 - var first_cache=document.getElementById("first_cache"); - if (first_cache.checked) - { - if (network_prefer["3G"]&&network_prefer["4G"]&&network_prefer["5G"]) - { - //设置偏好选项 - document.getElementById('prefer_option_auto').checked=true; - //更新偏好配置 - all_choose_prefer_custom_config(true); - //禁用用偏好复选框 - disabled_prefer_custom_config(true); - } - else - { - //设置偏好选项 - document.getElementById('prefer_option_custom').checked=true; - //更新偏好配置 - for (key in network_prefer) - { - //设置偏好配置 - var prefer_config_element=document.getElementById('prefer_config_'+key.toLowerCase()); - if (prefer_config_element!=null) { - prefer_config_element.checked=network_prefer[key]; - } - } - //启用偏好复选框 - disabled_prefer_custom_config(false); - } + // //设置偏好选项和复选框 + // var first_cache=document.getElementById("first_cache"); + // if (first_cache.checked) + // { + // var count=0; //统计启用 + // var prefer_check_view=""; + // for(var keys of network_prefer) + // { + // for(var key in keys) + // { + // var network_type=key.toLowerCase(); //转小写 + // var is_check=""; //是否选中(启用) + // if (keys[key]["enable"]) + // { + // is_check="checked"; + // count++; + // } + // //设置偏好复选框视图 + // prefer_check_view+='<span class="cbi-checkbox"><input id="prefer_config_'+network_type+'" type="checkbox" class="cbi-input-checkbox" value="'+network_type+'" '+is_check+'><span>'+key+'</span></span> '; + // } + // } - //设置第一次获取数据标志 - first_cache.checked=false; - } + // //设置网络偏好复现框 + // document.getElementById('network_type_check').innerHTML=prefer_check_view; + + // //自动状态判断(全部启用为自动) + // if (count==Object.keys(network_prefer).length) { + // //设置偏好选项 + // document.getElementById('prefer_option_auto').checked=true; + // //更新偏好配置 + // all_choose_prefer_custom_config("",true); + // //禁用用偏好复选框 + // disabled_prefer_custom_config("",true); + // } + // else + // { + // //设置偏好选项 + // document.getElementById('prefer_option_custom').checked=true; + // //启用偏好复选框 + // disabled_prefer_custom_config("",false); + // } + + // //设置第一次获取数据标志 + // first_cache.checked=false; + // } } // 设置模组自检信息 @@ -708,33 +880,30 @@ //获取偏好配置 var network_prefer_config={}; - if (prefer_option=="auto") + + var checkboxes=document.getElementById('prefer_custom_config').querySelectorAll('input[type="checkbox"]'); + for(checkbox of checkboxes) { - network_prefer_config["3G"]=1; - network_prefer_config["4G"]=1; - network_prefer_config["5G"]=1; - } - else - { - var checkboxes=document.getElementById('prefer_custom_config').querySelectorAll('input[type="checkbox"]'); - for(checkbox of checkboxes) - { - network_prefer_config[checkbox.value.toUpperCase()]=Number(checkbox.checked); - } + //获取网络类型配置 + var network_prefer={}; + network_prefer["enable"]=Number(checkbox.checked); + + var network_type=checkbox.value; + network_prefer_config[network_type.toUpperCase()]=network_prefer; } //设置偏好 XHR.get('<%=luci.dispatcher.build_url("admin", "network", "modem", "set_network_prefer")%>', {"port":at_port,"prefer_config":network_prefer_config}, function(x, data) { - //获取模组网络偏好 - var network_prefer=data["network_prefer"]; + // //获取模组网络偏好 + // var network_prefer=data["network_prefer"]; - //获取当前偏好视图 - var current_prefer_view=get_current_prefer_view(network_prefer); + // //获取当前偏好视图 + // var current_prefer_view=get_current_prefer_view(network_prefer); - //设置模组当前网络偏好 - document.getElementById('current_prefer').innerHTML=current_prefer_view; + // //设置模组当前网络偏好 + // document.getElementById('current_prefer').innerHTML=current_prefer_view; //启用功能 disabled_function("network_prefer",false); @@ -742,6 +911,130 @@ ); } + // 设置频段偏好 + function set_band_prefer() + { + //禁用功能 + disabled_function("network_prefer",true); + + //获取偏好选项 + var prefer_option = document.querySelector('input[name="prefer_option"]:checked').value; + //获取选中的模组 + var at_port = document.getElementById("modem_select").value; + + //获取偏好配置 + var network_prefer_config={}; + + var checkboxes=document.getElementById('prefer_custom_config').querySelectorAll('input[type="checkbox"]'); + for(checkbox of checkboxes) + { + //获取网络类型 + var network_type=checkbox.value; + //获取网络类型配置 + var network_prefer={}; + network_prefer["enable"]=Number(checkbox.checked); + + //获取频段配置 + network_prefer["band"]=[]; + if (checkbox.checked) + { + var band_check_element=document.getElementById(network_type+"_band_check"); + var bands=band_check_element.querySelectorAll('input[type="checkbox"]'); + for(band of bands) + { + var band_config={}; + band_config[band.value.toUpperCase().replace("-","_")]=Number(band.checked); + network_prefer["band"].push(band_config); + } + } + + network_prefer_config[network_type.toUpperCase()]=network_prefer; + } + + //设置偏好 + XHR.get('<%=luci.dispatcher.build_url("admin", "network", "modem", "set_band_prefer")%>', {"port":at_port,"prefer_config":network_prefer_config}, + function(x, data) + { + // //获取模组网络偏好 + // var network_prefer=data["network_prefer"]; + + // //获取当前偏好视图 + // var current_prefer_view=get_current_prefer_view(network_prefer); + + // //设置模组当前网络偏好 + // document.getElementById('current_prefer').innerHTML=current_prefer_view; + + //启用功能 + disabled_function("network_prefer",false); + } + ); + } + + // 设置网络偏好 + // function set_band_prefer() + // { + // //禁用功能 + // disabled_function("network_prefer",true); + + // //获取偏好选项 + // var prefer_option = document.querySelector('input[name="prefer_option"]:checked').value; + // //获取选中的模组 + // var at_port = document.getElementById("modem_select").value; + + // //获取偏好配置 + // var network_prefer_config={}; + // //获取网络类型配置 + // var checkboxes=document.getElementById('prefer_custom_config').querySelectorAll('input[type="checkbox"]'); + // for(checkbox of checkboxes) + // { + // network_prefer_config[checkbox.value.toUpperCase()]["enable"]=Number(checkbox.checked); + + // if (checkbox.checked) + // { + // //获取频段配置 + // var band_checkboxes=document.getElementById(checkbox.value+'prefer_custom_config').querySelectorAll('input[type="checkbox"]'); + // for(band_checkbox of band_checkboxes) + // { + // network_prefer_config[checkbox.value.toUpperCase()]["band"]=Number(band_checkbox.checked); + // } + // } + // else + // { + // network_prefer_config[checkbox.value.toUpperCase()]["band"]="[]"; + // } + + // } + + // //设置偏好 + // XHR.get('<%=luci.dispatcher.build_url("admin", "network", "modem", "set_network_prefer")%>', {"port":at_port,"prefer_config":network_prefer_config}, + // function(x, data) + // { + // //获取模组网络偏好 + // var network_prefer=data["network_prefer"]; + + // //获取当前偏好视图 + // var current_prefer_view=get_current_prefer_view(network_prefer); + + // //设置模组当前网络偏好 + // document.getElementById('current_prefer').innerHTML=current_prefer_view; + + // //启用功能 + // disabled_function("network_prefer",false); + // } + // ); + // } + + // 显示判断偏好 + function display_band_prefer(status) + { + //不显示频段偏好 + for (let i = 2; i <=5; i++) + { + network_type=i+"g"; + document.getElementById("cbi-"+network_type+"_band_info").style.display=status; + } + } + // 获取模组调试信息 function get_modem_debug_info() { @@ -755,7 +1048,11 @@ //获取当前选中的tab元素 var data_tab=tab_enable_element.getAttribute("data-tab").replace("_tab",""); - if (data_tab=="mode") { + if (data_tab=="mode") + { + //不显示频段偏好 + display_band_prefer("none"); + //获取拨号模式信息 XHR.get('<%=luci.dispatcher.build_url("admin", "network", "modem", "get_mode_info")%>', {"port":at_port}, function(x, data) @@ -771,7 +1068,8 @@ } ); } - else if (data_tab=="network_prefer") { + else if (data_tab=="network_prefer") + { //获取网络偏好信息 XHR.get('<%=luci.dispatcher.build_url("admin", "network", "modem", "get_network_prefer_info")%>', {"port":at_port}, function(x, data) @@ -787,7 +1085,11 @@ } ); } - else if (data_tab=="self_test") { + else if (data_tab=="self_test") + { + //不显示频段偏好 + display_band_prefer("none"); + //获取自检信息 XHR.get('<%=luci.dispatcher.build_url("admin", "network", "modem", "get_self_test_info")%>', {"port":at_port}, function(x, data) @@ -803,6 +1105,11 @@ } ); } + else if (data_tab=="at_command") + { + //不显示频段偏好 + display_band_prefer("none"); + } } // 定时触发更新AT串口 @@ -1066,6 +1373,7 @@ <th class="th cbi-section-table-cell"><%:Option%></th> <th class="th cbi-section-table-cell"><%:Config%></th> <th class="th cbi-section-table-cell cbi-section-actions"></th> + <th class="th cbi-section-table-cell cbi-section-actions"></th> </tr> <tr class="tr cbi-section-table-row cbi-rowstyle-1"> <td class="td cbi-value-field" data-title="<%:Current%>" id="current_prefer"></td> @@ -1082,8 +1390,8 @@ </div> </td> <td class="td cbi-value-field" data-title="<%:Config%>" id="prefer_custom_config"> - <div> - <span class="cbi-checkbox"> + <div id="network_type_check"></div> + <!-- <span class="cbi-checkbox"> <input id="prefer_config_3g" type="checkbox" class="cbi-input-checkbox" value="3g"> <span><%:3G%></span> </span> @@ -1095,13 +1403,19 @@ <input id="prefer_config_5g" type="checkbox" class="cbi-input-checkbox" value="5g"> <span><%:5G%></span> </span> - </div> + </div> --> </td> <td class="td"> <div> + <!-- <button class="btn cbi-button cbi-button-apply" id="network_prefer_button" onclick="set_network_prefer()" alt="<%:Apply Network Prefer%>" title="<%:Apply Network Prefer%>"><%:Apply Network Prefer%></button> --> <button class="btn cbi-button cbi-button-apply" id="network_prefer_button" onclick="set_network_prefer()" alt="<%:Apply%>" title="<%:Apply%>"><%:Apply%></button> </div> </td> + <td class="td" style="display: none;"> + <div> + <button class="btn cbi-button cbi-button-apply" id="band_prefer_button" onclick="set_band_prefer()" alt="<%:Apply Band Prefer%>" title="<%:Apply Band Prefer%>"><%:Apply Band Prefer%></button> + </div> + </td> </tr> </tbody> </table> @@ -1187,6 +1501,146 @@ </div> </div> + <fieldset class="cbi-section" id="cbi-5g_band_info" style="display: none;"> + <div class="cbi-section fade-in"> + <!-- <legend><%:5G Band Preferences%></legend> --> + <h3><%:5G Band Preferences%></h3> + <table class="table cbi-section-table"> + <tbody> + <tr class="tr cbi-section-table-titles anonymous"> + <th class="th cbi-section-table-cell"><%:Current%></th> + <th class="th cbi-section-table-cell"><%:Option%></th> + <th class="th cbi-section-table-cell"><%:Config%></th> + <th class="th cbi-section-table-cell cbi-section-actions"></th> + </tr> + <tr class="tr cbi-section-table-row cbi-rowstyle-1"> + <td class="td cbi-value-field" data-title="<%:Current%>" id="5g_current_prefer"></td> + <td class="td cbi-value-field" data-title="<%:Option%>" id="5g_prefer_option"> + <div> + <span class="cbi-radio"> + <input type="radio" name="5g_prefer_option" id="5g_prefer_option_auto" value="auto" checked="true"> + <span><%:Auto%></span> + </span> + <span class="cbi-radio"> + <input type="radio" name="5g_prefer_option" id="5g_prefer_option_custom" value="custom"> + <span><%:Custom%></span> + </span> + </div> + </td> + <td class="td cbi-value-field" data-title="<%:Config%>" id="5g_prefer_custom_config"> + <div id="5g_band_check"></div> + </td> + </tr> + </tbody> + </table> + </div> + </fieldset> + + <fieldset class="cbi-section" id="cbi-4g_band_info" style="display: none;"> + <div class="cbi-section fade-in"> + <!-- <legend><%:4G Band Preferences%></legend> --> + <h3><%:4G Band Preferences%></h3> + <table class="table cbi-section-table"> + <tbody> + <tr class="tr cbi-section-table-titles anonymous"> + <th class="th cbi-section-table-cell"><%:Current%></th> + <th class="th cbi-section-table-cell"><%:Option%></th> + <th class="th cbi-section-table-cell"><%:Config%></th> + <th class="th cbi-section-table-cell cbi-section-actions"></th> + </tr> + <tr class="tr cbi-section-table-row cbi-rowstyle-1"> + <td class="td cbi-value-field" data-title="<%:Current%>" id="4g_current_prefer"></td> + <td class="td cbi-value-field" data-title="<%:Option%>" id="4g_prefer_option"> + <div> + <span class="cbi-radio"> + <input type="radio" name="4g_prefer_option" id="4g_prefer_option_auto" value="auto" checked="true"> + <span><%:Auto%></span> + </span> + <span class="cbi-radio"> + <input type="radio" name="4g_prefer_option" id="4g_prefer_option_custom" value="custom"> + <span><%:Custom%></span> + </span> + </div> + </td> + <td class="td cbi-value-field" data-title="<%:Config%>" id="4g_prefer_custom_config"> + <div id="4g_band_check"></div> + </td> + </tr> + </tbody> + </table> + </div> + </fieldset> + + <fieldset class="cbi-section" id="cbi-3g_band_info" style="display: none;"> + <div class="cbi-section fade-in"> + <!-- <legend><%:3G Band Preferences%></legend> --> + <h3><%:3G Band Preferences%></h3> + <table class="table cbi-section-table"> + <tbody> + <tr class="tr cbi-section-table-titles anonymous"> + <th class="th cbi-section-table-cell"><%:Current%></th> + <th class="th cbi-section-table-cell"><%:Option%></th> + <th class="th cbi-section-table-cell"><%:Config%></th> + <th class="th cbi-section-table-cell cbi-section-actions"></th> + </tr> + <tr class="tr cbi-section-table-row cbi-rowstyle-1"> + <td class="td cbi-value-field" data-title="<%:Current%>" id="3g_current_prefer"></td> + <td class="td cbi-value-field" data-title="<%:Option%>" id="3g_prefer_option"> + <div> + <span class="cbi-radio"> + <input type="radio" name="3g_prefer_option" id="3g_prefer_option_auto" value="auto" checked="true"> + <span><%:Auto%></span> + </span> + <span class="cbi-radio"> + <input type="radio" name="3g_prefer_option" id="3g_prefer_option_custom" value="custom"> + <span><%:Custom%></span> + </span> + </div> + </td> + <td class="td cbi-value-field" data-title="<%:Config%>" id="3g_prefer_custom_config"> + <div id="3g_band_check"></div> + </td> + </tr> + </tbody> + </table> + </div> + </fieldset> + + <fieldset class="cbi-section" id="cbi-2g_band_info" style="display: none;"> + <div class="cbi-section fade-in"> + <!-- <legend><%:2G Band Preferences%></legend> --> + <h3><%:2G Band Preferences%></h3> + <table class="table cbi-section-table"> + <tbody> + <tr class="tr cbi-section-table-titles anonymous"> + <th class="th cbi-section-table-cell"><%:Current%></th> + <th class="th cbi-section-table-cell"><%:Option%></th> + <th class="th cbi-section-table-cell"><%:Config%></th> + <th class="th cbi-section-table-cell cbi-section-actions"></th> + </tr> + <tr class="tr cbi-section-table-row cbi-rowstyle-1"> + <td class="td cbi-value-field" data-title="<%:Current%>" id="2g_current_prefer"></td> + <td class="td cbi-value-field" data-title="<%:Option%>" id="2g_prefer_option"> + <div> + <span class="cbi-radio"> + <input type="radio" name="2g_prefer_option" id="2g_prefer_option_auto" value="auto" checked="true"> + <span><%:Auto%></span> + </span> + <span class="cbi-radio"> + <input type="radio" name="2g_prefer_option" id="2g_prefer_option_custom" value="custom"> + <span><%:Custom%></span> + </span> + </div> + </td> + <td class="td cbi-value-field" data-title="<%:Config%>" id="2g_prefer_custom_config"> + <div id="2g_band_check"></div> + </td> + </tr> + </tbody> + </table> + </div> + </fieldset> + </div> <%+footer%> diff --git a/package/wwan/luci-app-modem/luasrc/view/modem/plugin_info.htm b/package/wwan/luci-app-modem/luasrc/view/modem/plugin_info.htm index aa966862d..3268d68f6 100644 --- a/package/wwan/luci-app-modem/luasrc/view/modem/plugin_info.htm +++ b/package/wwan/luci-app-modem/luasrc/view/modem/plugin_info.htm @@ -257,6 +257,12 @@ <td class="td cbi-value-field" data-title="<%:Status%>" id="qmi_wwan_f_status">-</td> </tr> <tr class="tr cbi-section-table-row cbi-rowstyle-1"> + <td class="td cbi-value-field" data-title="<%:Driver Type%>"><%:Private (Meig)%></td> + <td class="td cbi-value-field" data-title="<%:Mode%>"><%:QMI%></td> + <td class="td cbi-value-field" data-title="<%:Kernel Model%>" id="qmi_wwan_m_kernel_model_name">qmi_wwan_m.ko</td> + <td class="td cbi-value-field" data-title="<%:Status%>" id="qmi_wwan_m_status">-</td> + </tr> + <tr class="tr cbi-section-table-row cbi-rowstyle-2"> <td class="td cbi-value-field" data-title="<%:Driver Type%>"><%:Private (Meig)%></td> <td class="td cbi-value-field" data-title="<%:Mode%>"><%:NCM%></td> <td class="td cbi-value-field" data-title="<%:Kernel Model%>" id="meig_cdc_driver_kernel_model_name">meig_cdc_driver.ko</td> diff --git a/package/wwan/luci-app-modem/po/zh-cn/modem.po b/package/wwan/luci-app-modem/po/zh-cn/modem.po index f8e70878f..55145ca03 100644 --- a/package/wwan/luci-app-modem/po/zh-cn/modem.po +++ b/package/wwan/luci-app-modem/po/zh-cn/modem.po @@ -103,6 +103,24 @@ msgstr "选择一个模组进行调试" msgid "Network Preferences" msgstr "网络偏好" +msgid "Apply Network Prefer" +msgstr "应用网络偏好" + +msgid "Apply Band Prefer" +msgstr "应用频段偏好" + +msgid "2G Band Preferences" +msgstr "2G 频段偏好" + +msgid "3G Band Preferences" +msgstr "3G 频段偏好" + +msgid "4G Band Preferences" +msgstr "4G 频段偏好" + +msgid "5G Band Preferences" +msgstr "5G 频段偏好" + msgid "Self Test" msgstr "自检" diff --git a/package/wwan/luci-app-modem/po/zh_Hans/modem.po b/package/wwan/luci-app-modem/po/zh_Hans/modem.po index f8e70878f..55145ca03 100644 --- a/package/wwan/luci-app-modem/po/zh_Hans/modem.po +++ b/package/wwan/luci-app-modem/po/zh_Hans/modem.po @@ -103,6 +103,24 @@ msgstr "选择一个模组进行调试" msgid "Network Preferences" msgstr "网络偏好" +msgid "Apply Network Prefer" +msgstr "应用网络偏好" + +msgid "Apply Band Prefer" +msgstr "应用频段偏好" + +msgid "2G Band Preferences" +msgstr "2G 频段偏好" + +msgid "3G Band Preferences" +msgstr "3G 频段偏好" + +msgid "4G Band Preferences" +msgstr "4G 频段偏好" + +msgid "5G Band Preferences" +msgstr "5G 频段偏好" + msgid "Self Test" msgstr "自检" diff --git a/package/wwan/luci-app-modem/root/etc/config/custom_at_commands b/package/wwan/luci-app-modem/root/etc/config/custom_at_commands index 86121064d..9b0194382 100644 --- a/package/wwan/luci-app-modem/root/etc/config/custom_at_commands +++ b/package/wwan/luci-app-modem/root/etc/config/custom_at_commands @@ -276,8 +276,8 @@ config custom-commands option command 'AT^SYSINFOEX' config custom-commands - option description '查询载波聚合小区信息 > AT^CELLINFO=3' - option command 'AT^CELLINFO=3' + option description '查询载波聚合小区信息 > AT^CELLINFO=1' + option command 'AT^CELLINFO=1' config custom-commands option description '查询当前拨号模式 > AT+SER?' @@ -329,4 +329,64 @@ config custom-commands config custom-commands option description '重启模组 > AT+RESET' - option command 'AT+RESET' \ No newline at end of file + option command 'AT+RESET' + +config custom-commands + option description '****************华为****************' + option command 'ATI' + +config custom-commands + option description '获取SIM卡卡槽状态 > AT^SIMSLOT?' + option command 'AT^SIMSLOT?' + +config custom-commands + option description '设置当前使用的为卡1 > AT^SIMSWITCH=1' + option command 'AT^SIMSWITCH=1' + +config custom-commands + option description '设置当前使用的为卡2 > AT^SIMSWITCH=0' + option command 'AT^SIMSWITCH=0' + +config custom-commands + option description '查询网络信息 > AT^SYSINFOEX' + option command 'AT^SYSINFOEX' + +config custom-commands + option description '查询当前拨号模式 > AT^SETMODE?' + option command 'AT^SETMODE?' + +config custom-commands + option description 'ECM拨号模式(Linux) > AT^SETMODE=0' + option command 'AT^SETMODE=0' + +config custom-commands + option description 'NCM拨号模式(Windows) > AT^SETMODE=1' + option command 'AT^SETMODE=1' + +config custom-commands + option description 'ECM拨号模式(Linux,Debug) > AT^SETMODE=2' + option command 'AT^SETMODE=2' + +config custom-commands + option description 'NCM拨号模式(Windows,Debug) > AT^SETMODE=3' + option command 'AT^SETMODE=3' + +config custom-commands + option description '锁4G > AT^SYSCFGEX="03",40000000,1,2,7FFFFFFFFFFFFFFF,,' + option command 'AT^SYSCFGEX="03",40000000,1,2,7FFFFFFFFFFFFFFF,,' + +config custom-commands + option description '锁5G > AT^SYSCFGEX="08",40000000,1,2,7FFFFFFFFFFFFFFF,,' + option command 'AT^SYSCFGEX="08",40000000,1,2,7FFFFFFFFFFFFFFF,,' + +config custom-commands + option description '恢复自动搜索网络 > AT^SYSCFGEX="00",3FFFFFFF,1,2,7FFFFFFFFFFFFFFF,,' + option command 'AT^SYSCFGEX="00",3FFFFFFF,1,2,7FFFFFFFFFFFFFFF,,' + +config custom-commands + option description '获取模组温度 > AT^CHIPTEMP?' + option command 'AT^CHIPTEMP?' + +config custom-commands + option description '重启模组 > AT^RESET' + option command 'AT^RESET' \ No newline at end of file diff --git a/package/wwan/luci-app-modem/root/etc/init.d/modem b/package/wwan/luci-app-modem/root/etc/init.d/modem index edf82a966..3edd09397 100755 --- a/package/wwan/luci-app-modem/root/etc/init.d/modem +++ b/package/wwan/luci-app-modem/root/etc/init.d/modem @@ -445,6 +445,8 @@ stop_ecm() fi elif [ "$manufacturer" = "meig" ]; then at_command="AT^NDISDUP=${define_connect},0" + elif [ "$manufacturer" = "huawei" ]; then + at_command="AT^NDISDUP=${define_connect},0" else at_command='ATI' fi @@ -633,13 +635,12 @@ dial() [ -z "${modem_no}" ] && return 0 #获取模组的拨号模式 - local time=0 + local time=20 local mode - while [ $time -lt 10 ]; do + for i in $(seq 1 ${time}); do mode=$(get_mode ${modem_no}) [ -n "$mode" ] && [ "$mode" != "unknown" ] && break sleep 5s - time=$((time+1)) done #获取不到拨号模式 diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/at_commands.json b/package/wwan/luci-app-modem/root/usr/share/modem/at_commands.json index 03ba7e7f0..20b709ad5 100644 --- a/package/wwan/luci-app-modem/root/usr/share/modem/at_commands.json +++ b/package/wwan/luci-app-modem/root/usr/share/modem/at_commands.json @@ -144,7 +144,7 @@ {"设置当前使用的为卡1 > AT^SIMSLOT=1":"AT^SIMSLOT=1"}, {"设置当前使用的为卡2 > AT^SIMSLOT=2":"AT^SIMSLOT=2"}, {"查询网络信息 > AT^SYSINFOEX":"AT^SYSINFOEX"}, - {"查询载波聚合小区信息 > AT^CELLINFO=3":"AT^CELLINFO=3"}, + {"查询载波聚合小区信息 > AT^CELLINFO=1":"AT^CELLINFO=1"}, {"查询当前拨号模式 > AT+SER?":"AT+SER?"}, {"QMI/GobiNet拨号模式 > AT+SER=1,1":"AT+SER=1,1"}, {"ECM拨号模式 > AT+SER=2,1":"AT+SER=2,1"}, @@ -159,6 +159,24 @@ {"获取模组温度 > AT+TEMP":"AT+TEMP"}, {"重启模组 > AT+RESET":"AT+RESET"} ] + }, + "huawei":{ + "hisilicon":[ + {"获取SIM卡卡槽状态 > AT^SIMSLOT?":"AT^SIMSLOT?"}, + {"设置当前使用的为卡1 > AT^SIMSWITCH=1":"AT^SIMSWITCH=1"}, + {"设置当前使用的为卡2 > AT^SIMSWITCH=0":"AT^SIMSWITCH=0"}, + {"查询网络信息 > AT^SYSINFOEX":"AT^SYSINFOEX"}, + {"查询当前拨号模式 > AT^SETMODE?":"AT^SETMODE?"}, + {"ECM拨号模式(Linux) > AT^SETMODE=0":"AT^SETMODE=0"}, + {"NCM拨号模式(Windows) > AT^SETMODE=1":"AT^SETMODE=1"}, + {"ECM拨号模式(Linux,Debug) > AT^SETMODE=2":"AT^SETMODE=2"}, + {"NCM拨号模式(Windows,Debug) > AT^SETMODE=3":"AT^SETMODE=3"}, + {"锁4G > AT^SYSCFGEX=\"03\",40000000,1,2,7FFFFFFFFFFFFFFF,,":"AT^SYSCFGEX=\"03\",40000000,1,2,7FFFFFFFFFFFFFFF,,"}, + {"锁5G > AT^SYSCFGEX=\"08\",40000000,1,2,7FFFFFFFFFFFFFFF,,":"AT^SYSCFGEX=\"08\",40000000,1,2,7FFFFFFFFFFFFFFF,,"}, + {"恢复自动搜索网络 > AT^SYSCFGEX=\"00\",3FFFFFFF,1,2,7FFFFFFFFFFFFFFF,,":"AT^SYSCFGEX=\"00\",3FFFFFFF,1,2,7FFFFFFFFFFFFFFF,,"}, + {"获取模组温度 > AT^CHIPTEMP?":"AT^CHIPTEMP?"}, + {"重启模组 > AT^RESET":"AT^RESET"} + ] } } } \ No newline at end of file diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/fibocom.sh b/package/wwan/luci-app-modem/root/usr/share/modem/fibocom.sh index 57e678bd8..4008eb02d 100755 --- a/package/wwan/luci-app-modem/root/usr/share/modem/fibocom.sh +++ b/package/wwan/luci-app-modem/root/usr/share/modem/fibocom.sh @@ -43,7 +43,7 @@ fibocom_get_dns() #获取DNS地址 at_command="AT+GTDNS=${define_connect}" - local response=$(at ${at_port} ${at_command} | grep "+GTDNS: " | grep -E '[0-9]+.[0-9]+.[0-9]+.[0-9]+' | sed -n '1p') + local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+GTDNS: " | grep -E '[0-9]+.[0-9]+.[0-9]+.[0-9]+' | sed -n '1p') local ipv4_dns1=$(echo "${response}" | awk -F'"' '{print $2}' | awk -F',' '{print $1}') [ -z "$ipv4_dns1" ] && { @@ -203,29 +203,32 @@ fibocom_set_mode() #获取网络偏好 # $1:AT串口 +# $2:数据接口 +# $3:模组名称 fibocom_get_network_prefer() { local at_port="$1" - at_command="AT+GTACT?" - local network_prefer_num=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "+GTACT:" | awk -F',' '{print $1}' | sed 's/+GTACT: //g') + local data_interface="$2" + local modem_name="$3" + at_command="AT+GTACT?" + local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+GTACT:" | sed 's/+GTACT: //g') + local network_type_num=$(echo "$response" | awk -F',' '{print $1}') + + #获取网络类型 + # local network_prefer_2g="0"; local network_prefer_3g="0"; local network_prefer_4g="0"; local network_prefer_5g="0"; #匹配不同的网络类型 - case "$network_prefer_num" in + case "$network_type_num" in "1") network_prefer_3g="1" ;; "2") network_prefer_4g="1" ;; "4") network_prefer_3g="1" network_prefer_4g="1" ;; - "10") - network_prefer_3g="1" - network_prefer_4g="1" - network_prefer_5g="1" - ;; "14") network_prefer_5g="1" ;; "16") network_prefer_3g="1" @@ -235,26 +238,37 @@ fibocom_get_network_prefer() network_prefer_4g="1" network_prefer_5g="1" ;; - "20") - network_prefer_3g="1" - network_prefer_4g="1" - network_prefer_5g="1" - ;; - *) + "10"|"20"|*) network_prefer_3g="1" network_prefer_4g="1" network_prefer_5g="1" ;; esac + #获取频段信息 + # local band_2g_info="[]" + local band_3g_info="[]" + local band_4g_info="[]" + local band_5g_info="[]" + + #生成网络偏好 local network_prefer="{ - \"network_prefer\":{ - \"3G\":$network_prefer_3g, - \"4G\":$network_prefer_4g, - \"5G\":$network_prefer_5g - } + \"network_prefer\":[ + {\"3G\":{ + \"enable\":$network_prefer_3g, + \"band\":$band_3g_info + }}, + {\"4G\":{ + \"enable\":$network_prefer_4g, + \"band\":$band_4g_info + }}, + {\"5G\":{ + \"enable\":$network_prefer_5g, + \"band\":$band_5g_info + }} + ] }" - echo "$network_prefer" + echo "${network_prefer}" } #设置网络偏好 @@ -265,41 +279,41 @@ fibocom_set_network_prefer() local at_port="$1" local network_prefer="$2" - #获取网络偏好数字 - local network_prefer_num + #获取网络偏好配置 + local network_prefer_config #获取选中的数量 local count=$(echo "$network_prefer" | grep -o "1" | wc -l) - #获取每个偏好的值 - local network_prefer_3g=$(echo "$network_prefer" | jq -r '.["3G"]') - local network_prefer_4g=$(echo "$network_prefer" | jq -r '.["4G"]') - local network_prefer_5g=$(echo "$network_prefer" | jq -r '.["5G"]') + #获取启用的网络偏好 + local enable_5g=$(echo "$network_prefer" | jq -r '.["5G"].enable') + local enable_4g=$(echo "$network_prefer" | jq -r '.["4G"].enable') + local enable_3g=$(echo "$network_prefer" | jq -r '.["3G"].enable') case "$count" in "1") - if [ "$network_prefer_3g" = "1" ]; then - network_prefer_num="1" - elif [ "$network_prefer_4g" = "1" ]; then - network_prefer_num="2" - elif [ "$network_prefer_5g" = "1" ]; then - network_prefer_num="14" + if [ "$enable_3g" = "1" ]; then + network_prefer_config="1" + elif [ "$enable_4g" = "1" ]; then + network_prefer_config="2" + elif [ "$enable_5g" = "1" ]; then + network_prefer_config="14" fi ;; "2") - if [ "$network_prefer_3g" = "1" ] && [ "$network_prefer_4g" = "1" ]; then - network_prefer_num="4" - elif [ "$network_prefer_3g" = "1" ] && [ "$network_prefer_5g" = "1" ]; then - network_prefer_num="16" - elif [ "$network_prefer_4g" = "1" ] && [ "$network_prefer_5g" = "1" ]; then - network_prefer_num="17" + if [ "$enable_3g" = "1" ] && [ "$enable_4g" = "1" ]; then + network_prefer_config="4" + elif [ "$enable_3g" = "1" ] && [ "$enable_5g" = "1" ]; then + network_prefer_config="16" + elif [ "$enable_4g" = "1" ] && [ "$enable_5g" = "1" ]; then + network_prefer_config="17" fi ;; - "3") network_prefer_num="20" ;; - *) network_prefer_num="10" ;; + "3") network_prefer_config="20" ;; + *) network_prefer_config="10" ;; esac - +echo "$network_prefer_config" >> /root/a #设置模组 - at_command="AT+GTACT=$network_prefer_num" + at_command="AT+GTACT=${network_prefer_config}" sh ${SCRIPT_DIR}/modem_at.sh $at_port "$at_command" } diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/huawei.sh b/package/wwan/luci-app-modem/root/usr/share/modem/huawei.sh new file mode 100755 index 000000000..e3d95ba1e --- /dev/null +++ b/package/wwan/luci-app-modem/root/usr/share/modem/huawei.sh @@ -0,0 +1,928 @@ +#!/bin/sh +# Copyright (C) 2023 Siriling <siriling@qq.com> + +#脚本目录 +SCRIPT_DIR="/usr/share/modem" + +#预设 +huawei_presets() +{ + #关闭模组主动上报 + at_command='AT^CURC=0' + sh "${SCRIPT_DIR}/modem_at.sh" "$at_port" "$at_command" + + #开启5G NA NSA接入 + at_command='AT^C5GOPTION=1,3,3' + sh "${SCRIPT_DIR}/modem_at.sh" "$at_port" "$at_command" +} + +#获取DNS +# $1:AT串口 +# $2:连接定义 +huawei_get_dns() +{ + local at_port="$1" + local define_connect="$2" + + [ -z "$define_connect" ] && { + define_connect="1" + } + + local public_dns1_ipv4="223.5.5.5" + local public_dns2_ipv4="119.29.29.29" + local public_dns1_ipv6="2400:3200::1" #下一代互联网北京研究中心:240C::6666,阿里:2400:3200::1,腾讯:2402:4e00:: + local public_dns2_ipv6="2402:4e00::" + + #获取DNS地址(IPv4) + at_command="AT^DHCP=${define_connect}" + local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "\^DHCP:" | sed -n '1p') + + local ipv4_dns1=$(echo "${response}" | awk -F',' '{print $5}') + if [ -z "$ipv4_dns1" ]; then + ipv4_dns1="${public_dns1_ipv4}" + else + #按字节(byte)将十六进制拆分并转换为对应的十进制表示 + ipv4_dns1=$(echo "$ipv4_dns1" | awk '{ + for (i = length; i >= 1; i -= 2) { + printf "%d.", "0x" substr($0, i-1, 2) + } + }') + ipv4_dns1="${ipv4_dns1%?}" + fi + + local ipv4_dns2=$(echo "${response}" | awk -F',' '{print $6}') + if [ -z "$ipv4_dns2" ]; then + ipv4_dns2="${public_dns1_ipv4}" + else + #按字节(byte)将十六进制拆分并转换为对应的十进制表示 + ipv4_dns2=$(echo "$ipv4_dns2" | awk '{ + for (i = length; i >= 1; i -= 2) { + printf "%d.", "0x" substr($0, i-1, 2) + } + }') + ipv4_dns2="${ipv4_dns2%?}" + fi + + #获取DNS地址(IPv6) + at_command="AT^DHCPV6=${define_connect}" + local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "\^DHCPV6:" | sed -n '1p') + + local ipv6_dns1=$(echo "${response}" | awk -F',' '{print $5}') + [ -z "$ipv6_dns1" ] && { + ipv6_dns1="${public_dns1_ipv6}" + } + + local ipv6_dns2=$(echo "${response}" | awk -F',' '{print $6}') + [ -z "$ipv6_dns2" ] && { + ipv6_dns2="${public_dns2_ipv6}" + } + + dns="{ + \"dns\":{ + \"ipv4_dns1\":\"$ipv4_dns1\", + \"ipv4_dns2\":\"$ipv4_dns2\", + \"ipv6_dns1\":\"$ipv6_dns1\", + \"ipv6_dns2\":\"$ipv6_dns2\" + } + }" + + echo "$dns" +} + +#获取拨号模式 +# $1:AT串口 +# $2:平台 +huawei_get_mode() +{ + local at_port="$1" + local platform="$2" + + at_command="AT^SETMODE?" + local mode_num=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "\^SETMODE:" | awk -F': ' '{print $2}' | sed 's/\r//g') + + if [ -z "$mode_num" ]; then + echo "unknown" + return + fi + + #获取芯片平台 + if [ -z "$platform" ]; then + local modem_number=$(uci -q get modem.@global[0].modem_number) + for i in $(seq 0 $((modem_number-1))); do + local at_port_tmp=$(uci -q get modem.modem$i.at_port) + if [ "$at_port" = "$at_port_tmp" ]; then + platform=$(uci -q get modem.modem$i.platform) + break + fi + done + fi + + local mode + case "$platform" in + "hisilicon") + case "$mode_num" in + "0"|"2") mode="ecm" ;; + "1"|"3"|"4"|"5") mode="ncm" ;; + "6") mode="rndis" ;; + "7") mode="mbim" ;; + "8") mode="ppp" ;; + *) mode="$mode_num" ;; + esac + ;; + *) + mode="$mode_num" + ;; + esac + echo "${mode}" +} + +#设置拨号模式 +# $1:AT串口 +# $2:拨号模式配置 +huawei_set_mode() +{ + local at_port="$1" + local mode_config="$2" + + #获取芯片平台 + local platform + local modem_number=$(uci -q get modem.@global[0].modem_number) + for i in $(seq 0 $((modem_number-1))); do + local at_port_tmp=$(uci -q get modem.modem$i.at_port) + if [ "$at_port" = "$at_port_tmp" ]; then + platform=$(uci -q get modem.modem$i.platform) + break + fi + done + + #获取拨号模式配置 + local mode_num + case "$platform" in + "hisilicon") + case "$mode_config" in + "ecm") mode_num="0" ;; + "ncm") mode_num="4" ;; + *) mode_num="0" ;; + esac + ;; + *) + mode_num="0" + ;; + esac + + #设置模组 + at_command="AT^SETMODE=${mode_num}" + sh ${SCRIPT_DIR}/modem_at.sh ${at_port} "${at_command}" +} + +#获取位 +# $1:频段名称 +huawei_get_bit() +{ + local band_name="$1" + + local bit + case "$band_name" in + "DCS_1800") bit="8" ;; + "E-GSM_900"|"E_GSM_900") bit="9" ;; + "P-GSM_900"|"P_GSM_900") bit="10" ;; + "GSM_450") bit="17" ;; + "GSM_480") bit="18" ;; + "GSM_750") bit="19" ;; + "GSM_850") bit="20" ;; + "R-GSM_900"|"R_GSM_900") bit="21" ;; + "PCS_1900") bit="22" ;; + esac + + echo "${bit}" +} + +#获取频段信息 +# $1:频段二进制数 +# $2:支持的频段 +# $3:频段类型(2G,3G,4G,5G) +huawei_get_band_info() +{ + local band_bin="$1" + local support_band="$2" + local band_type="$3" + + local band_info="" + local support_band=$(echo "$support_band" | sed 's/,/ /g') + if [ "$band_type" = "2G" ]; then + + for band in $support_band; do + #获取bit位 + local bit=$(huawei_get_bit ${band}) + #获取值 + local enable="${band_bin: $((-bit)):1}" + [ -z "$enable" ] && enable="0" + #设置频段信息 + # band_info=$(echo ${band_info} | jq '. += [{"'$band'":'$enable'}]') + band_info="${band_info},{\"$band\":$enable}" + done + else + #频段频段起始,前缀位置 + local start_bit + local band_prefix + case "$band_type" in + "3G") + start_bit="23" + band_prefix="WCDMA_B" + ;; + "4G") + start_bit="1" + band_prefix="LTE_BC" + ;; + "5G") + start_bit="1" + band_prefix="NR5G_N" + ;; + esac + + for band in $support_band; do + #获取值(从start_bit位开始) + local enable="${band_bin: $((-band-start_bit+1)):1}" + [ -z "$enable" ] && enable="0" + #设置频段信息 + # band_info=$(echo ${band_info} | jq '. += [{'$band_prefix$band':'$enable'}]') + band_info="${band_info},{\"$band_prefix$band\":$enable}" + done + fi + #去掉第一个, + band_info="["${band_info/,/}"]" + # band_info="[${band_info%?}]" + + echo "${band_info}" +} + +#获取网络偏好 +# $1:AT串口 +# $2:数据接口 +# $3:模组名称 +huawei_get_network_prefer() +{ + local at_port="$1" + local data_interface="$2" + local modem_name="$3" + + at_command="AT^SYSCFGEX?" + local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "\^SYSCFGEX:" | sed 's/\^SYSCFGEX://g') + local network_type_num=$(echo "$response" | awk -F'"' '{print $2}') + + #获取网络类型 + local network_prefer_2g="0"; + local network_prefer_3g="0"; + local network_prefer_4g="0"; + local network_prefer_5g="0"; + + #匹配不同的网络类型 + local auto=$(echo "${network_type_num}" | grep "00") + if [ -n "$auto" ]; then + network_prefer_2g="1" + network_prefer_3g="1" + network_prefer_4g="1" + network_prefer_5g="1" + else + local gsm=$(echo "${network_type_num}" | grep "01") + local wcdma=$(echo "${network_type_num}" | grep "02") + local lte=$(echo "${network_type_num}" | grep "03") + local nr=$(echo "${network_type_num}" | grep "08") + if [ -n "$gsm" ]; then + network_prefer_2g="1" + fi + if [ -n "$wcdma" ]; then + network_prefer_3g="1" + fi + if [ -n "$lte" ]; then + network_prefer_4g="1" + fi + if [ -n "$nr" ]; then + network_prefer_5g="1" + fi + fi + + #获取模组信息 + local modem_info=$(jq '.modem_support.'$data_interface'."'$modem_name'"' ${SCRIPT_DIR}/modem_support.json) + + #获取模组支持的频段 + local support_2g_band=$(echo "$modem_info" | jq -r '.band_2g') + local support_3g_band=$(echo "$modem_info" | jq -r '.band_3g') + local support_4g_band=$(echo "$modem_info" | jq -r '.band_4g') + local support_5g_band=$(echo "$modem_info" | jq -r '.band_5g') + + #获取频段信息 + local band_hex_2g_3g=$(echo "$response" | awk -F',' '{print $2}') + #十六进制转二进制 + local bin_2g_3g=$(echo "obase=2; ibase=16; $band_hex_2g_3g" | bc) + local band_2g_info=$(huawei_get_band_info "${bin_2g_3g}" "${support_2g_band}" "2G") + local band_3g_info=$(huawei_get_band_info "${bin_2g_3g}" "${support_3g_band}" "3G") + + local band_hex_4g_5g=$(echo "$response" | awk -F',' '{print $5}' | sed 's/\r//g') + #十六进制转二进制 + local bin_4g_5g=$(echo "obase=2; ibase=16; $band_hex_4g_5g" | bc) + local band_4g_info=$(huawei_get_band_info "${bin_4g_5g}" "${support_4g_band}" "4G") + local band_5g_info=$(huawei_get_band_info "${bin_4g_5g}" "${support_5g_band}" "5G") + + #生成网络偏好 + local network_prefer="{ + \"network_prefer\":[ + {\"2G\":{ + \"enable\":$network_prefer_2g, + \"band\":$band_2g_info + }}, + {\"3G\":{ + \"enable\":$network_prefer_3g, + \"band\":$band_3g_info + }}, + {\"4G\":{ + \"enable\":$network_prefer_4g, + \"band\":$band_4g_info + }}, + {\"5G\":{ + \"enable\":$network_prefer_5g, + \"band\":$band_5g_info + }} + ] + }" + echo "${network_prefer}" +} + +#设置网络偏好 +# $1:AT串口 +# $2:网络偏好配置 +huawei_set_network_prefer() +{ + local at_port="$1" + local network_prefer="$2" + + #获取启用的网络偏好 + local enable_5g=$(echo "$network_prefer" | jq -r '.["5G"].enable') + local enable_4g=$(echo "$network_prefer" | jq -r '.["4G"].enable') + local enable_3g=$(echo "$network_prefer" | jq -r '.["3G"].enable') + local enable_2g=$(echo "$network_prefer" | jq -r '.["2G"].enable') + + #获取网络偏好配置 + local network_prefer_config + [ "$enable_5g" = "1" ] && network_prefer_config="${network_prefer_config}08" + [ "$enable_4g" = "1" ] && network_prefer_config="${network_prefer_config}03" + [ "$enable_3g" = "1" ] && network_prefer_config="${network_prefer_config}02" + [ "$enable_2g" = "1" ] && network_prefer_config="${network_prefer_config}01" + + [ -z "$network_prefer_config" ] && network_prefer_config="99" + + #设置模组 + at_command='AT^SYSCFGEX="'${network_prefer_config}'",40000000,1,2,40000000,,' + sh ${SCRIPT_DIR}/modem_at.sh "${at_port}" "${at_command}" +} + +#设置频段 +# $1:AT串口 +# $2:频段偏好配置 +huawei_set_band_prefer() +{ + local at_port="$1" + local network_prefer="$2" + + #获取选中的数量 + local count=$(echo "$network_prefer" | grep -o "1" | wc -l) + #获取每个偏好的值 + local network_prefer_5g=$(echo "$network_prefer" | jq -r '.["5G"]') + local network_prefer_4g=$(echo "$network_prefer" | jq -r '.["4G"]') + local network_prefer_3g=$(echo "$network_prefer" | jq -r '.["3G"]') + local network_prefer_2g=$(echo "$network_prefer" | jq -r '.["2G"]') + + #获取启用的网络偏好 + local enable_5g=$(echo "$network_prefer_5g" | jq -r '.enable') + local enable_4g=$(echo "$network_prefer_4g" | jq -r '.enable') + local enable_3g=$(echo "$network_prefer_3g" | jq -r '.enable') + local enable_2g=$(echo "$network_prefer_2g" | jq -r '.enable') + + #获取网络偏好配置和频段偏好配置 + local network_prefer_config + local band_hex_2g_3g=0 + local band_hex_4g_5g=0 + + [ "$enable_5g" = "1" ] && { + network_prefer_config="${network_prefer_config}08" + local band_tmp=$(echo "$network_prefer_5g" | jq -r '.band[]') + + local i=0 + local bands=$(echo "$band_tmp" | jq -r 'to_entries | .[] | .key') + #遍历band的值 + for band in $bands; do + local value=$(echo "$network_prefer_5g" | jq -r '.band'"[$i].$band") + [ "$value" = "1" ] && { + #获取bit位 + local bit=$(echo "$band" | sed 's/NR5G_N//g') + #获取值 + local result=$(echo "obase=16; ibase=10; 2^($bit-1)" | bc) + band_hex_4g_5g=$(echo "obase=16; ibase=16; $band_hex_4g_5g + $result" | bc) + } + i=$((i+1)) + done + } + + [ "$enable_4g" = "1" ] && { + network_prefer_config="${network_prefer_config}03" + local band_tmp=$(echo "$network_prefer_4g" | jq -r '.band[]') + + local i=0 + local bands=$(echo "$band_tmp" | jq -r 'to_entries | .[] | .key') + #遍历band的值 + for band in $bands; do + local value=$(echo "$network_prefer_4g" | jq -r '.band'"[$i].$band") + [ "$value" = "1" ] && { + #获取bit位 + local bit=$(echo "$band" | sed 's/LTE_BC//g') + #获取值 + local result=$(echo "obase=16; ibase=10; 2^($bit-1)" | bc) + band_hex_4g_5g=$(echo "obase=16; ibase=16; $band_hex_4g_5g + $result" | bc) + } + i=$((i+1)) + done + } + + [ "$enable_3g" = "1" ] && { + network_prefer_config="${network_prefer_config}02" + local band_tmp=$(echo "$network_prefer_3g" | jq -r '.band[]') + + local i=0 + local bands=$(echo "$band_tmp" | jq -r 'to_entries | .[] | .key') + #遍历band的值 + for band in $bands; do + local value=$(echo "$network_prefer_3g" | jq -r '.band'"[$i].$band") + [ "$value" = "1" ] && { + #获取bit位 + local bit=$(echo "$band" | sed 's/WCDMA_B//g') + #获取值 + local result=$(echo "obase=16; ibase=10; 2^($bit+22-1)" | bc) + band_hex_2g_3g=$(echo "obase=16; ibase=16; $band_hex_2g_3g + $result" | bc) + } + i=$((i+1)) + done + } + + [ "$enable_2g" = "1" ] && { + network_prefer_config="${network_prefer_config}01" + local band_tmp=$(echo "$network_prefer_2g" | jq -r '.band[]') + + local i=0 + local bands=$(echo "$band_tmp" | jq -r 'to_entries | .[] | .key') + #遍历band的值 + for band in $bands; do + # band_format=$(echo "$band" | sed 's/-/_/g') + local value=$(echo "$network_prefer_2g" | jq -r '.band'"[$i].$band") + [ "$value" = "1" ] && { + #获取bit位 + local bit=$(huawei_get_bit ${band}) + #获取值 + local result=$(echo "obase=16; ibase=10; 2^($bit-1)" | bc) + band_hex_2g_3g=$(echo "obase=16; ibase=16; $band_hex_2g_3g + $result" | bc) + } + i=$((i+1)) + done + } + + [ -z "$network_prefer_config" ] && network_prefer_config="99" + + #设置模组 + at_command='AT^SYSCFGEX="'${network_prefer_config}'",'"${band_hex_2g_3g},1,2,${band_hex_4g_5g},," + sh ${SCRIPT_DIR}/modem_at.sh "${at_port}" "${at_command}" +} + +#获取电压 +# $1:AT串口 +huawei_get_voltage() +{ + local at_port="$1" + + # #Voltage(电压) + # at_command="AT+ADCREAD=0" + # local voltage=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "+ADCREAD:" | awk -F' ' '{print $2}' | sed 's/\r//g') + # voltage=$(awk "BEGIN{ printf \"%.2f\", $voltage / 1000000 }" | sed 's/\.*0*$//') + # echo "${voltage}" +} + +#获取温度 +# $1:AT串口 +huawei_get_temperature() +{ + local at_port="$1" + + #Temperature(温度) + at_command="AT^CHIPTEMP?" + response=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "\^CHIPTEMP" | awk -F',' '{print $6}' | sed 's/\r//g') + + local temperature + if [ -n "$response" ]; then + response=$(awk "BEGIN{ printf \"%.2f\", $response / 10 }" | sed 's/\.*0*$//') + temperature="${response}$(printf "\xc2\xb0")C" + else + temperature="NaN $(printf "\xc2\xb0")C" + fi + + echo "${temperature}" +} + +#获取连接状态 +# $1:AT串口 +# $2:连接定义 +huawei_get_connect_status() +{ + local at_port="$1" + local define_connect="$2" + + #默认值为1 + [ -z "$define_connect" ] && { + define_connect="1" + } + + at_command="AT+CGPADDR=${define_connect}" + local ipv4=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+CGPADDR: " | awk -F'"' '{print $2}') + local not_ip="0.0.0.0" + + #设置连接状态 + local connect_status + if [ -z "$ipv4" ] || [[ "$ipv4" = *"$not_ip"* ]]; then + connect_status="disconnect" + else + connect_status="connect" + fi + + echo "${connect_status}" +} + +#基本信息 +huawei_base_info() +{ + debug "Huawei base info" + + #Name(名称) + at_command="AT+CGMM" + name=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "+CGMM: " | awk -F': ' '{print $2}' | sed 's/\r//g') + #Manufacturer(制造商) + at_command="AT+CGMI" + manufacturer=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | sed -n '2p' | sed 's/\r//g') + #Revision(固件版本) + at_command="AT+CGMR" + revision=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | sed -n '2p' | sed 's/\r//g') + + #Mode(拨号模式) + mode=$(huawei_get_mode ${at_port} ${platform} | tr 'a-z' 'A-Z') + + #Temperature(温度) + temperature=$(huawei_get_temperature ${at_port}) +} + +#获取SIM卡状态 +# $1:SIM卡状态标志 +huawei_get_sim_status() +{ + local sim_status + case $1 in + "") sim_status="miss" ;; + *"ERROR"*) sim_status="miss" ;; + *"READY"*) sim_status="ready" ;; + *"SIM PIN"*) sim_status="MT is waiting SIM PIN to be given" ;; + *"SIM PUK"*) sim_status="MT is waiting SIM PUK to be given" ;; + *"PH-FSIM PIN"*) sim_status="MT is waiting phone-to-SIM card password to be given" ;; + *"PH-FSIM PIN"*) sim_status="MT is waiting phone-to-very first SIM card password to be given" ;; + *"PH-FSIM PUK"*) sim_status="MT is waiting phone-to-very first SIM card unblocking password to be given" ;; + *"SIM PIN2"*) sim_status="MT is waiting SIM PIN2 to be given" ;; + *"SIM PUK2"*) sim_status="MT is waiting SIM PUK2 to be given" ;; + *"PH-NET PIN"*) sim_status="MT is waiting network personalization password to be given" ;; + *"PH-NET PUK"*) sim_status="MT is waiting network personalization unblocking password to be given" ;; + *"PH-NETSUB PIN"*) sim_status="MT is waiting network subset personalization password to be given" ;; + *"PH-NETSUB PUK"*) sim_status="MT is waiting network subset personalization unblocking password to be given" ;; + *"PH-SP PIN"*) sim_status="MT is waiting service provider personalization password to be given" ;; + *"PH-SP PUK"*) sim_status="MT is waiting service provider personalization unblocking password to be given" ;; + *"PH-CORP PIN"*) sim_status="MT is waiting corporate personalization password to be given" ;; + *"PH-CORP PUK"*) sim_status="MT is waiting corporate personalization unblocking password to be given" ;; + *) sim_status="unknown" ;; + esac + echo "${sim_status}" +} + +#SIM卡信息 +huawei_sim_info() +{ + debug "Huawei sim info" + + #SIM Slot(SIM卡卡槽) + # at_command="AT^SIMSLOT?" + # response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "\^SIMSLOT:" | awk -F': ' '{print $2}' | awk -F',' '{print $2}') + + # if [ "$response" != "0" ]; then + # sim_slot="1" + # else + # sim_slot="2" + # fi + sim_slot="1" + + #IMEI(国际移动设备识别码) + at_command="AT+CGSN" + imei=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | sed -n '2p' | sed 's/\r//g') + + #SIM Status(SIM状态) + at_command="AT+CPIN?" + sim_status_flag=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+CPIN: ") + sim_status=$(huawei_get_sim_status "$sim_status_flag") + + if [ "$sim_status" != "ready" ]; then + return + fi + + #ISP(互联网服务提供商) + at_command="AT+COPS?" + isp=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+COPS" | awk -F'"' '{print $2}') + # if [ "$isp" = "CHN-CMCC" ] || [ "$isp" = "CMCC" ]|| [ "$isp" = "46000" ]; then + # isp="中国移动" + # elif [ "$isp" = "CHN-UNICOM" ] || [ "$isp" = "UNICOM" ] || [ "$isp" = "46001" ]; then + # isp="中国联通" + # elif [ "$isp" = "CHN-CT" ] || [ "$isp" = "CT" ] || [ "$isp" = "46011" ]; then + # isp="中国电信" + # fi + + #SIM Number(SIM卡号码,手机号) + at_command="AT+CNUM" + sim_number=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+CNUM: " | awk -F'"' '{print $2}') + [ -z "$sim_number" ] && { + sim_number=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+CNUM: " | awk -F'"' '{print $4}') + } + + #IMSI(国际移动用户识别码) + at_command="AT+CIMI" + imsi=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | sed -n '2p' | sed 's/\r//g') + + #ICCID(集成电路卡识别码) + at_command="AT+ICCID" + iccid=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep -o "+ICCID:[ ]*[-0-9]\+" | grep -o "[-0-9]\{1,4\}") +} + +#获取网络类型 +# $1:网络类型数字 +huawei_get_rat() +{ + local rat + case $1 in + "0"|"1"|"3"|"8") rat="GSM" ;; + "2"|"4"|"5"|"6"|"9"|"10") rat="WCDMA" ;; + "7") rat="LTE" ;; + "11"|"12") rat="NR" ;; + esac + echo "${rat}" +} + +#获取信号强度指示(4G) +# $1:信号强度指示数字 +huawei_get_rssi() +{ + local rssi + case $1 in + "99") rssi="unknown" ;; + * ) rssi=$((2 * $1 - 113)) ;; + esac + echo "$rssi" +} + +#网络信息 +huawei_network_info() +{ + debug "Huawei network info" + + #Connect Status(连接状态) + connect_status=$(huawei_get_connect_status ${at_port} ${define_connect}) + if [ "$connect_status" != "connect" ]; then + return + fi + + #Network Type(网络类型) + at_command="AT^SYSINFOEX" + network_type=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "\^SYSINFOEX:" | awk -F'"' '{print $4}') + + [ -z "$network_type" ] && { + at_command='AT+COPS?' + local rat_num=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+COPS:" | awk -F',' '{print $4}' | sed 's/\r//g') + network_type=$(huawei_get_rat ${rat_num}) + } + + #设置网络类型为5G时,信号强度指示用RSRP代替 + # at_command="AT+GTCSQNREN=1" + # sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command + + #CSQ(信号强度) + at_command="AT+CSQ" + response=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "+CSQ:" | sed 's/+CSQ: //g' | sed 's/\r//g') + + #RSSI(4G信号强度指示) + # rssi_num=$(echo $response | awk -F',' '{print $1}') + # rssi=$(huawei_get_rssi $rssi_num) + #BER(4G信道误码率) + # ber=$(echo $response | awk -F',' '{print $2}') + + # #PER(信号强度) + # if [ -n "$csq" ]; then + # per=$(($csq * 100/31))"%" + # fi + + #AMBR(最大比特率) + at_command="AT^DHCP?" + response=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "\^DHCP:" | sed 's/\^DHCP: //g' | sed 's/\r//g') + ambr_ul_tmp=$(echo "$response" | awk -F',' '{print $8}') + ambr_dl_tmp=$(echo "$response" | awk -F',' '{print $7}') + + [ -z "$ambr_ul_tmp" ] && { + at_command="AT^DHCPV6?" + response=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "\^DHCPV6:" | sed 's/\^DHCPV6: //g' | sed 's/\r//g') + ambr_ul_tmp=$(echo "$response" | awk -F',' '{print $8}') + ambr_dl_tmp=$(echo "$response" | awk -F',' '{print $7}') + } + + #AMBR UL(上行签约速率,单位,Mbps) + ambr_ul=$(awk "BEGIN{ printf \"%.2f\", $ambr_ul_tmp / 1000000 }" | sed 's/\.*0*$//') + #AMBR DL(下行签约速率,单位,Mbps) + ambr_dl=$(awk "BEGIN{ printf \"%.2f\", $ambr_dl_tmp / 1000000 }" | sed 's/\.*0*$//') + + # #速率统计 + # at_command='AT^DSFLOWQRY' + # response=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "\^DSFLOWRPT:" | sed 's/\^DSFLOWRPT: //g' | sed 's/\r//g') + + # #当前上传速率(单位,Byte/s) + # tx_rate=$(echo $response | awk -F',' '{print $1}') + + # #当前下载速率(单位,Byte/s) + # rx_rate=$(echo $response | awk -F',' '{print $2}') +} + +#获取NR子载波间隔 +# $1:NR子载波间隔数字 +huawei_get_scs() +{ + local scs + case $1 in + "0") scs="15" ;; + "1") scs="30" ;; + "2") scs="60" ;; + "3") scs="120" ;; + "4") scs="240" ;; + *) scs=$(awk "BEGIN{ print 2^$1 * 15 }") ;; + esac + echo "$scs" +} + +#获取频段 +# $1:网络类型 +# $2:频段数字 +huawei_get_band() +{ + local band + case $1 in + "GSM") + case $2 in + "0") band="850" ;; + "1") band="900" ;; + "2") band="1800" ;; + "3") band="1900" ;; + esac + ;; + "WCDMA") band="$2" ;; + "LTE") band="$(($2-100))" ;; + "NR") band="$2" band="${band#*50}" ;; + esac + echo "$band" +} + +#小区信息 +huawei_cell_info() +{ + debug "Huawei cell info" + + at_command="AT^MONSC" + response=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "\^MONSC:" | sed 's/\^MONSC: //') + + local rat=$(echo "$response" | awk -F',' '{print $1}') + + case $rat in + "NR") + network_mode="NR5G-SA Mode" + nr_mcc=$(echo "$response" | awk -F',' '{print $2}') + nr_mnc=$(echo "$response" | awk -F',' '{print $3}') + nr_arfcn=$(echo "$response" | awk -F',' '{print $4}') + nr_scs_num=$(echo "$response" | awk -F',' '{print $5}') + nr_scs=$(tdtech_get_scs ${nr_scs_num}) + nr_cell_id_hex=$(echo "$response" | awk -F',' '{print $6}') + nr_cell_id=$(echo "ibase=16; $nr_cell_id_hex" | bc) + nr_physical_cell_id_hex=$(echo "$response" | awk -F',' '{print $7}') + nr_physical_cell_id=$(echo "ibase=16; $nr_physical_cell_id_hex" | bc) + nr_tac=$(echo "$response" | awk -F',' '{print $8}') + nr_rsrp=$(echo "$response" | awk -F',' '{print $9}') + nr_rsrq=$(echo "$response" | awk -F',' '{print $10}') + nr_sinr=$(echo "$response" | awk -F',' '{print $11}' | sed 's/\r//g') + ;; + "LTE-NR") + network_mode="EN-DC Mode" + #LTE + endc_lte_mcc=$(echo "$response" | awk -F',' '{print $2}') + endc_lte_mnc=$(echo "$response" | awk -F',' '{print $3}') + endc_lte_earfcn=$(echo "$response" | awk -F',' '{print $4}') + endc_lte_cell_id_hex=$(echo "$response" | awk -F',' '{print $5}') + endc_lte_cell_id=$(echo "ibase=16; $endc_lte_cell_id_hex" | bc) + endc_lte_physical_cell_id_hex=$(echo "$response" | awk -F',' '{print $6}') + endc_lte_physical_cell_id=$(echo "ibase=16; $endc_lte_physical_cell_id_hex" | bc) + endc_lte_tac=$(echo "$response" | awk -F',' '{print $7}') + endc_lte_rsrp=$(echo "$response" | awk -F',' '{print $8}') + endc_lte_rsrq=$(echo "$response" | awk -F',' '{print $9}') + endc_lte_rxlev=$(echo "$response" | awk -F',' '{print $10}' | sed 's/\r//g') + #NR5G-NSA + endc_nr_mcc=$(echo "$response" | awk -F',' '{print $2}') + endc_nr_mnc=$(echo "$response" | awk -F',' '{print $3}') + endc_nr_arfcn=$(echo "$response" | awk -F',' '{print $4}') + endc_nr_scs_num=$(echo "$response" | awk -F',' '{print $5}') + endc_nr_scs=$(tdtech_get_scs ${nr_scs_num}) + endc_nr_cell_id_hex=$(echo "$response" | awk -F',' '{print $6}') + endc_nr_cell_id=$(echo "ibase=16; $endc_nr_cell_id_hex" | bc) + endc_nr_physical_cell_id_hex=$(echo "$response" | awk -F',' '{print $7}') + endc_nr_physical_cell_id=$(echo "ibase=16; $endc_nr_physical_cell_id_hex" | bc) + endc_nr_tac=$(echo "$response" | awk -F',' '{print $8}') + endc_nr_rsrp=$(echo "$response" | awk -F',' '{print $9}') + endc_nr_rsrq=$(echo "$response" | awk -F',' '{print $10}') + endc_nr_sinr=$(echo "$response" | awk -F',' '{print $11}' | sed 's/\r//g') + ;; + "LTE"|"eMTC"|"NB-IoT") + network_mode="LTE Mode" + lte_mcc=$(echo "$response" | awk -F',' '{print $2}') + lte_mnc=$(echo "$response" | awk -F',' '{print $3}') + lte_earfcn=$(echo "$response" | awk -F',' '{print $4}') + lte_cell_id_hex=$(echo "$response" | awk -F',' '{print $5}') + lte_cell_id=$(echo "ibase=16; $lte_cell_id_hex" | bc) + lte_physical_cell_id_hex=$(echo "$response" | awk -F',' '{print $6}') + lte_physical_cell_id=$(echo "ibase=16; $lte_physical_cell_id_hex" | bc) + lte_tac=$(echo "$response" | awk -F',' '{print $7}') + lte_rsrp=$(echo "$response" | awk -F',' '{print $8}') + lte_rsrq=$(echo "$response" | awk -F',' '{print $9}') + lte_rxlev=$(echo "$response" | awk -F',' '{print $10}' | sed 's/\r//g') + ;; + "WCDMA"|"TD-SCDMA"|"UMTS") + network_mode="WCDMA Mode" + wcdma_mcc=$(echo "$response" | awk -F',' '{print $2}') + wcdma_mnc=$(echo "$response" | awk -F',' '{print $3}') + wcdma_arfcn=$(echo "$response" | awk -F',' '{print $4}') + wcdma_psc=$(echo "$response" | awk -F',' '{print $5}') + wcdma_cell_id_hex=$(echo "$response" | awk -F',' '{print $6}') + wcdma_cell_id=$(echo "ibase=16; $wcdma_cell_id_hex" | bc) + wcdma_lac=$(echo "$response" | awk -F',' '{print $7}') + wcdma_rscp=$(echo "$response" | awk -F',' '{print $8}') + wcdma_rxlev=$(echo "$response" | awk -F',' '{print $9}') + wcdma_ecn0=$(echo "$response" | awk -F',' '{print $10}') + wcdma_drx=$(echo "$response" | awk -F',' '{print $11}') + wcdma_ura=$(echo "$response" | awk -F',' '{print $12}' | sed 's/\r//g') + ;; + "GSM") + network_mode="GSM Mode" + gsm_mcc=$(echo "$response" | awk -F',' '{print $2}') + gsm_mnc=$(echo "$response" | awk -F',' '{print $3}') + gsm_band_num=$(echo "$response" | awk -F',' '{print $4}') + gsm_band=$(tdtech_get_band "GSM" ${gsm_band_num}) + gsm_arfcn=$(echo "$response" | awk -F',' '{print $5}') + gsm_bsic=$(echo "$response" | awk -F',' '{print $6}') + gsm_cell_id_hex=$(echo "$response" | awk -F',' '{print $7}') + gsm_cell_id=$(echo "ibase=16; $gsm_cell_id_hex" | bc) + gsm_lac=$(echo "$response" | awk -F',' '{print $8}') + gsm_rxlev=$(echo "$response" | awk -F',' '{print $9}') + gsm_rx_quality=$(echo "$response" | awk -F',' '{print $10}') + gsm_ta=$(echo "$response" | awk -F',' '{print $11}' | sed 's/\r//g') + ;; + esac +} + +#获取华为模组信息 +# $1:AT串口 +# $2:平台 +# $3:连接定义 +get_huawei_info() +{ + debug "get huawei info" + #设置AT串口 + at_port="$1" + platform="$2" + define_connect="$3" + + #基本信息 + huawei_base_info + + #SIM卡信息 + huawei_sim_info + if [ "$sim_status" != "ready" ]; then + return + fi + + #网络信息 + huawei_network_info + if [ "$connect_status" != "connect" ]; then + return + fi + + #小区信息 + huawei_cell_info +} \ No newline at end of file diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/meig.sh b/package/wwan/luci-app-modem/root/usr/share/modem/meig.sh index f832eb546..adb73cbd2 100755 --- a/package/wwan/luci-app-modem/root/usr/share/modem/meig.sh +++ b/package/wwan/luci-app-modem/root/usr/share/modem/meig.sh @@ -35,7 +35,7 @@ meig_get_dns() #获取DNS地址 at_command="AT+CGCONTRDP=${define_connect}" - local response=$(at ${at_port} ${at_command} | grep "+CGCONTRDP: " | grep -E '[0-9]+.[0-9]+.[0-9]+.[0-9]+' | sed -n '1p') + local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+CGCONTRDP: " | grep -E '[0-9]+.[0-9]+.[0-9]+.[0-9]+' | sed -n '1p') local ipv4_dns1=$(echo "${response}" | awk -F',' '{print $7}' | awk -F' ' '{print $1}') [ -z "$ipv4_dns1" ] && { @@ -161,28 +161,122 @@ meig_set_mode() sh ${SCRIPT_DIR}/modem_at.sh ${at_port} "${at_command}" } +#获取位 +# $1:频段名称 +meig_get_bit() +{ + local band_name="$1" + + local bit + case "$band_name" in + "DCS_1800") bit="8" ;; + "E-GSM_900") bit="9" ;; + "P-GSM_900") bit="10" ;; + "GSM_450") bit="17" ;; + "GSM_480") bit="18" ;; + "GSM_750") bit="19" ;; + "GSM_850") bit="20" ;; + "R-GSM_900") bit="21" ;; + "PCS_1900") bit="22" ;; + esac + + echo "${bit}" +} + +#获取频段信息 +# $1:频段二进制数 +# $2:支持的频段 +# $3:频段类型(2G,3G,4G,5G) +meig_get_band_info() +{ + local band_bin="$1" + local support_band="$2" + local band_type="$3" + + local band_info="" + local support_band=$(echo "$support_band" | sed 's/,/ /g') + if [ "$band_type" = "2G" ]; then + + for band in $support_band; do + #获取bit位 + local bit=$(meig_get_bit ${band}) + #获取值 + local enable="${band_bin: $((-bit)):1}" + [ -z "$enable" ] && enable="0" + #设置频段信息 + # band_info=$(echo ${band_info} | jq '. += [{"'$band'":'$enable'}]') + band_info="${band_info},{\"$band\":$enable}" + done + else + #频段频段起始,前缀位置 + local start_bit + local band_prefix + case "$band_type" in + "3G") + start_bit="23" + band_prefix="WCDMA_B" + ;; + "4G") + start_bit="1" + band_prefix="LTE_BC" + ;; + "5G") + start_bit="1" + band_prefix="NR5G_N" + ;; + esac + + for band in $support_band; do + #获取值(从start_bit位开始) + local enable="${band_bin: $((-band-start_bit+1)):1}" + [ -z "$enable" ] && enable="0" + #设置频段信息 + # band_info=$(echo ${band_info} | jq '. += [{'$band_prefix$band':'$enable'}]') + band_info="${band_info},{\"$band_prefix$band\":$enable}" + done + fi + #去掉第一个, + band_info="["${band_info/,/}"]" + # band_info="[${band_info%?}]" + + echo "${band_info}" +} + #获取网络偏好 # $1:AT串口 +# $2:数据接口 +# $3:模组名称 meig_get_network_prefer() { local at_port="$1" + local data_interface="$2" + local modem_name="$3" + at_command="AT^SYSCFGEX?" - local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "\^SYSCFGEX:" | awk -F'"' '{print $2}') - + local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "\^SYSCFGEX:" | sed 's/\^SYSCFGEX://g') + local network_type_num=$(echo "$response" | awk -F'"' '{print $2}') + + #获取网络类型 + local network_prefer_2g="0"; local network_prefer_3g="0"; local network_prefer_4g="0"; local network_prefer_5g="0"; #匹配不同的网络类型 - local auto=$(echo "${response}" | grep "00") + local auto=$(echo "${network_type_num}" | grep "00") if [ -n "$auto" ]; then + network_prefer_2g="1" network_prefer_3g="1" network_prefer_4g="1" network_prefer_5g="1" else - local wcdma=$(echo "${response}" | grep "02") - local lte=$(echo "${response}" | grep "03") - local nr=$(echo "${response}" | grep "04") + local gsm=$(echo "${network_type_num}" | grep "01") + local wcdma=$(echo "${network_type_num}" | grep "02") + local lte=$(echo "${network_type_num}" | grep "03") + local nr=$(echo "${network_type_num}" | grep "04") + if [ -n "$gsm" ]; then + network_prefer_2g="1" + fi if [ -n "$wcdma" ]; then network_prefer_3g="1" fi @@ -194,14 +288,58 @@ meig_get_network_prefer() fi fi + #获取模组信息 + local modem_info=$(jq '.modem_support.'$data_interface'."'$modem_name'"' ${SCRIPT_DIR}/modem_support.json) + + #获取模组支持的频段 + local support_2g_band=$(echo "$modem_info" | jq -r '.band_2g') + local support_3g_band=$(echo "$modem_info" | jq -r '.band_3g') + local support_4g_band=$(echo "$modem_info" | jq -r '.band_4g') + local support_5g_band=$(echo "$modem_info" | jq -r '.band_5g') + + #获取2G,3G频段信息 + local band_hex_2g_3g=$(echo "$response" | awk -F',' '{print $2}') + #十六进制转二进制 + local bin_2g_3g=$(echo "obase=2; ibase=16; $band_hex_2g_3g" | bc) + local band_2g_info=$(meig_get_band_info "${bin_2g_3g}" "${support_2g_band}" "2G") + local band_3g_info=$(meig_get_band_info "${bin_2g_3g}" "${support_3g_band}" "3G") + + #获取4G频段信息 + local band_hex_4g_1=$(echo "$response" | awk -F',' '{print $5}' | sed 's/\r//g') + local band_hex_4g_2=$(echo "$response" | awk -F',' '{print $7}' | sed 's/\r//g') + #十六进制转二进制 + local bin_4g=$(echo "obase=2; ibase=16; $band_hex_4g_1 + $band_hex_4g_2" | bc) + local band_4g_info=$(meig_get_band_info "${bin_4g}" "${support_4g_band}" "4G") + + #获取5G频段信息 + local band_hex_5g_1=$(echo "$response" | awk -F',' '{print $8}' | sed 's/\r//g') + local band_hex_5g_2=$(echo "$response" | awk -F',' '{print $9}' | sed 's/\r//g') + #十六进制转二进制 + local bin_5g=$(echo "obase=2; ibase=16; $band_hex_5g_1 + $band_hex_5g_2" | bc) + local band_5g_info=$(meig_get_band_info "${bin_5g}" "${support_5g_band}" "5G") + + #生成网络偏好 local network_prefer="{ - \"network_prefer\":{ - \"3G\":$network_prefer_3g, - \"4G\":$network_prefer_4g, - \"5G\":$network_prefer_5g - } + \"network_prefer\":[ + {\"2G\":{ + \"enable\":$network_prefer_2g, + \"band\":$band_2g_info + }}, + {\"3G\":{ + \"enable\":$network_prefer_3g, + \"band\":$band_3g_info + }}, + {\"4G\":{ + \"enable\":$network_prefer_4g, + \"band\":$band_4g_info + }}, + {\"5G\":{ + \"enable\":$network_prefer_5g, + \"band\":$band_5g_info + }} + ] }" - echo "$network_prefer" + echo "${network_prefer}" } #设置网络偏好 @@ -215,35 +353,18 @@ meig_set_network_prefer() #获取网络偏好配置 local network_prefer_config - #获取选中的数量 - local count=$(echo "$network_prefer" | grep -o "1" | wc -l) - #获取每个偏好的值 - local network_prefer_3g=$(echo "$network_prefer" | jq -r '.["3G"]') - local network_prefer_4g=$(echo "$network_prefer" | jq -r '.["4G"]') - local network_prefer_5g=$(echo "$network_prefer" | jq -r '.["5G"]') + #获取启用的网络偏好 + local enable_5g=$(echo "$network_prefer" | jq -r '.["5G"].enable') + local enable_4g=$(echo "$network_prefer" | jq -r '.["4G"].enable') + local enable_3g=$(echo "$network_prefer" | jq -r '.["3G"].enable') - case "$count" in - "1") - if [ "$network_prefer_3g" = "1" ]; then - network_prefer_config="02" - elif [ "$network_prefer_4g" = "1" ]; then - network_prefer_config="03" - elif [ "$network_prefer_5g" = "1" ]; then - network_prefer_config="04" - fi - ;; - "2") - if [ "$network_prefer_3g" = "1" ] && [ "$network_prefer_4g" = "1" ]; then - network_prefer_config="0302" - elif [ "$network_prefer_3g" = "1" ] && [ "$network_prefer_5g" = "1" ]; then - network_prefer_config="0402" - elif [ "$network_prefer_4g" = "1" ] && [ "$network_prefer_5g" = "1" ]; then - network_prefer_config="0403" - fi - ;; - "3") network_prefer_config="00" ;; - *) network_prefer_config="00" ;; - esac + #获取网络偏好配置 + local network_prefer_config + [ "$enable_5g" = "1" ] && network_prefer_config="${network_prefer_config}04" + [ "$enable_4g" = "1" ] && network_prefer_config="${network_prefer_config}03" + [ "$enable_3g" = "1" ] && network_prefer_config="${network_prefer_config}02" + + [ -z "$network_prefer_config" ] && network_prefer_config="00" #设置模组 at_command='AT^SYSCFGEX="'${network_prefer_config}'",all,0,2,all,all,all,all,1' @@ -314,7 +435,7 @@ meig_get_connect_status() #基本信息 meig_base_info() { - debug "meig base info" + debug "Meig base info" #Name(名称) at_command="AT+CGMM" @@ -365,7 +486,7 @@ meig_get_sim_status() #SIM卡信息 meig_sim_info() { - debug "meig sim info" + debug "Meig sim info" #SIM Slot(SIM卡卡槽) at_command="AT^SIMSLOT?" @@ -517,7 +638,7 @@ meig_get_lte_ambr() #网络信息 meig_network_info() { - debug "meig network info" + debug "Meig network info" #Connect Status(连接状态) connect_status=$(meig_get_connect_status ${at_port} ${define_connect}) diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/modem_band.sh b/package/wwan/luci-app-modem/root/usr/share/modem/modem_band.sh new file mode 100755 index 000000000..7972b7fcd --- /dev/null +++ b/package/wwan/luci-app-modem/root/usr/share/modem/modem_band.sh @@ -0,0 +1,94 @@ +#!/bin/sh +# Copyright (C) 2023 Siriling <siriling@qq.com> + +#脚本目录 +SCRIPT_DIR="/usr/share/modem" +source "${SCRIPT_DIR}/modem_debug.sh" + +#初始化模组频段 +init_modem_band() +{ + #2G + DCS_1800="-" + E-GSM_900="-" + P-GSM_900="-" + GSM_450="-" + GSM_480="-" + GSM_750="-" + GSM_850="-" + R-GSM_900="-" + PCS_1900="-" + + #3G + + #4G + B1="-" + B2="-" + B3="-" + B4="-" + B5="-" + B6="-" + B7="-" + B8="-" + B9="-" + B10="-" + B11="-" + B12="-" + B13="-" + B14="-" + B17="-" + B18="-" + B19="-" + B20="-" + B21="-" + B25="-" + B26="-" + B28="-" + B29="-" + B30="-" + B32="-" + B34="-" + B38="-" + B39="-" + B40="-" + B41="-" + B42="-" + B66="-" + B71="-" + + #5G + N1="-" + N2="-" + N3="-" + N5="-" + N7="-" + N8="-" + N12="-" + N20="-" + N25="-" + N28="-" + N38="-" + N40="-" + N41="-" + N48="-" + N66="-" + N71="-" + N77="-" + N78="-" + N79="-" +} + +#获取模组数据信息 +# $1:AT串口 +# $2:制造商 +# $3:平台 +# $4:连接定义 +modem_band() +{ + #初始化模组频段 + debug "初始化模组频段" + init_modem_band + debug "初始化模组频段完成" +} + +modem_band "$1" "$2" "$3" "$4" \ No newline at end of file diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/modem_debug.sh b/package/wwan/luci-app-modem/root/usr/share/modem/modem_debug.sh index 44b6b4664..eec9cf42d 100755 --- a/package/wwan/luci-app-modem/root/usr/share/modem/modem_debug.sh +++ b/package/wwan/luci-app-modem/root/usr/share/modem/modem_debug.sh @@ -7,6 +7,7 @@ SCRIPT_DIR="/usr/share/modem" source "${SCRIPT_DIR}/quectel.sh" source "${SCRIPT_DIR}/fibocom.sh" source "${SCRIPT_DIR}/meig.sh" +source "${SCRIPT_DIR}/huawei.sh" # source "${SCRIPT_DIR}/simcom.sh" #调试开关 diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/modem_info.sh b/package/wwan/luci-app-modem/root/usr/share/modem/modem_info.sh index 0028d1173..d27fc4f39 100755 --- a/package/wwan/luci-app-modem/root/usr/share/modem/modem_info.sh +++ b/package/wwan/luci-app-modem/root/usr/share/modem/modem_info.sh @@ -371,6 +371,7 @@ get_modem_info() "fibocom") get_fibocom_info "${at_port}" "${platform}" "${define_connect}" ;; "meig") get_meig_info "${at_port}" "${platform}" "${define_connect}" ;; "simcom") get_simcom_info "${at_port}" "${platform}" "${define_connect}" ;; + "huawei") get_huawei_info "${at_port}" "${platform}" "${define_connect}" ;; *) debug "未适配该模组" ;; esac diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/modem_init.sh b/package/wwan/luci-app-modem/root/usr/share/modem/modem_init.sh old mode 100644 new mode 100755 diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/modem_network_task.sh b/package/wwan/luci-app-modem/root/usr/share/modem/modem_network_task.sh index e9ebafe04..17b401eda 100755 --- a/package/wwan/luci-app-modem/root/usr/share/modem/modem_network_task.sh +++ b/package/wwan/luci-app-modem/root/usr/share/modem/modem_network_task.sh @@ -24,13 +24,13 @@ reset_network_interface() local interface_name_ipv6="wwan6_5g_${modem_no}" #获取IPv4地址 - at_command="AT+CGPADDR=${define_connect}" - local ipv4=$(at ${at_port} ${at_command} | grep "+CGPADDR: " | awk -F',' '{print $2}' | sed 's/"//g') + local at_command="AT+CGPADDR=${define_connect}" + local ipv4=$(at ${at_port} ${at_command} | grep "+CGPADDR: " | sed -n '1p' | awk -F',' '{print $2}' | sed 's/"//g') #输出日志 # echo "[$(date +"%Y-%m-%d %H:%M:%S")] Get Modem new IPv4 address : ${ipv4}" >> "${MODEM_RUNDIR}/modem${modem_no}_dial.cache" #获取DNS地址 - dns=$(fibocom_get_dns ${at_port} ${define_connect}) + local dns=$(fibocom_get_dns ${at_port} ${define_connect}) local ipv4_dns1=$(echo "${dns}" | jq -r '.dns.ipv4_dns1') local ipv4_dns2=$(echo "${dns}" | jq -r '.dns.ipv4_dns2') #输出日志 @@ -117,13 +117,17 @@ ecm_dial() # at "${at_port}" "${at_command}" #拨号 - at_command + local at_command if [ "$manufacturer" = "quectel" ]; then at_command="AT+QNETDEVCTL=${define_connect},3,1" elif [ "$manufacturer" = "fibocom" ]; then at_command="AT+GTRNDIS=1,${define_connect}" elif [ "$manufacturer" = "meig" ]; then at_command="AT^NDISDUP=${define_connect},1" + elif [ "$manufacturer" = "huawei" ]; then + at_command="AT^NDISDUP=${define_connect},1" + elif [ "$manufacturer" = "tdtech" ]; then + at_command="AT^NDISDUP=${define_connect},1" else at_command='ATI' fi @@ -150,10 +154,10 @@ rndis_dial() local define_connect="$4" local modem_no="$5" - #手动设置IP(广和通FM350-GL) + #手动拨号(广和通FM350-GL) if [ "$manufacturer" = "fibocom" ] && [ "$platform" = "mediatek" ]; then - at_command="AT+CGACT=1,${define_connect}" + local at_command="AT+CGACT=1,${define_connect}" #打印日志 dial_log "${at_command}" "${MODEM_RUNDIR}/modem${modem_no}_dial.cache" #激活并拨号 @@ -202,6 +206,15 @@ modem_network_task() local interface_name="wwan_5g_${modem_no}" local interface_name_ipv6="wwan6_5g_${modem_no}" + #AT串口未获取到重新获取(解决模组还在识别中,就已经开始拨号的问题) + while [ -z "$manufacturer" ] || [ "$manufacturer" = "unknown" ]; do + at_port=$(uci -q get modem.modem${modem_no}.at_port) + manufacturer=$(uci -q get modem.modem${modem_no}.manufacturer) + platform=$(uci -q get modem.modem${modem_no}.platform) + define_connect=$(uci -q get modem.modem${modem_no}.define_connect) + sleep 1s + done + #重载配置(解决AT命令发不出去的问题) # service modem reload @@ -228,7 +241,7 @@ modem_network_task() #网络连接检查 local at_command="AT+CGPADDR=${define_connect}" - local ipv4=$(at ${at_port} ${at_command} | grep "+CGPADDR: " | awk -F',' '{print $2}' | sed 's/"//g') + local ipv4=$(at ${at_port} ${at_command} | grep "+CGPADDR: " | sed -n '1p' | awk -F',' '{print $2}' | sed 's/"//g') if [ -z "$ipv4" ]; then diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/modem_support.json b/package/wwan/luci-app-modem/root/usr/share/modem/modem_support.json index 0df94657e..aaa85f3d0 100644 --- a/package/wwan/luci-app-modem/root/usr/share/modem/modem_support.json +++ b/package/wwan/luci-app-modem/root/usr/share/modem/modem_support.json @@ -55,7 +55,8 @@ "platform":"qualcomm", "data_interface":"usb", "define_connect":"1", - "modes":["qmi","gobinet","ecm","mbim","rndis","ncm"] + "modes":["qmi","gobinet","ecm","mbim","rndis","ncm"], + "band_5g":"1,2,3,5,7,8,12,20,25,28,38,40,41,48,66,71,77,78,79,257,258,260,261" }, "rm502q-ae":{ "manufacturer_id":"2c7c", @@ -152,6 +153,18 @@ "data_interface":"usb", "define_connect":"1", "modes":["qmi","gobinet","ecm","rndis","ncm"] + }, + "mh5000-31":{ + "manufacturer_id":"12d1", + "manufacturer":"huawei", + "platform":"hisilicon", + "data_interface":"usb", + "define_connect":"1", + "modes":["ecm","ncm"], + "band_2g":"DCS_1800,E-GSM_900,P-GSM_900,GSM_850,R-GSM_900,PCS_1900", + "band_3g":"1,2,5,6,8,9,19", + "band_4g":"1,2,3,4,5,6,7,8,9,10,11,12,13,14,17,18,19,20,21,25,26,28,33,34,35,36,37,38,39,40,41,42,43", + "band_5g":"1,2,3,5,7,8,12,20,25,28,38,40,41,78,79" } }, "pcie":{ @@ -258,6 +271,12 @@ "vendor_id":["2dee"], "product_id":["4d57","4d58","4d59"] } + }, + "huawei":{ + "hisilicon":{ + "vendor_id":["12d1"], + "product_id":["15c3"] + } } } } diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/modem_util.sh b/package/wwan/luci-app-modem/root/usr/share/modem/modem_util.sh index 9e5455203..4b08eacf2 100755 --- a/package/wwan/luci-app-modem/root/usr/share/modem/modem_util.sh +++ b/package/wwan/luci-app-modem/root/usr/share/modem/modem_util.sh @@ -83,6 +83,7 @@ m_modem_presets() "fibocom") fibocom_presets ;; "meig") meig_presets ;; "simcom") simcom_presets ;; + "huawei") huawei_presets ;; esac } diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/quectel.sh b/package/wwan/luci-app-modem/root/usr/share/modem/quectel.sh index fa54fdd86..d937dae5e 100755 --- a/package/wwan/luci-app-modem/root/usr/share/modem/quectel.sh +++ b/package/wwan/luci-app-modem/root/usr/share/modem/quectel.sh @@ -30,7 +30,7 @@ quectel_get_dns() #获取DNS地址 at_command="AT+GTDNS=${define_connect}" - local response=$(at ${at_port} ${at_command} | grep "+GTDNS: ") + local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+GTDNS: ") local ipv4_dns1=$(echo "${response}" | awk -F'"' '{print $2}' | awk -F',' '{print $1}') [ -z "$ipv4_dns1" ] && { @@ -174,12 +174,20 @@ quectel_set_mode() #获取网络偏好 # $1:AT串口 +# $2:数据接口 +# $3:模组名称 quectel_get_network_prefer() { local at_port="$1" + local data_interface="$2" + local modem_name="$3" + at_command='AT+QNWPREFCFG="mode_pref"' - local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+QNWPREFCFG:" | awk -F',' '{print $2}' | sed 's/\r//g') - + local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+QNWPREFCFG:" | sed 's/\r//g') + local network_type_num=$(echo "$response" | awk -F',' '{print $2}') + + #获取网络类型 + # local network_prefer_2g="0"; local network_prefer_3g="0"; local network_prefer_4g="0"; local network_prefer_5g="0"; @@ -205,14 +213,30 @@ quectel_get_network_prefer() fi fi + #获取频段信息 + # local band_2g_info="[]" + local band_3g_info="[]" + local band_4g_info="[]" + local band_5g_info="[]" + + #生成网络偏好 local network_prefer="{ - \"network_prefer\":{ - \"3G\":$network_prefer_3g, - \"4G\":$network_prefer_4g, - \"5G\":$network_prefer_5g - } + \"network_prefer\":[ + {\"3G\":{ + \"enable\":$network_prefer_3g, + \"band\":$band_3g_info + }}, + {\"4G\":{ + \"enable\":$network_prefer_4g, + \"band\":$band_4g_info + }}, + {\"5G\":{ + \"enable\":$network_prefer_5g, + \"band\":$band_5g_info + }} + ] }" - echo "$network_prefer" + echo "${network_prefer}" } #设置网络偏好 @@ -223,32 +247,32 @@ quectel_set_network_prefer() local at_port="$1" local network_prefer="$2" - #获取网络偏好配置 + #获取网络偏好配置 local network_prefer_config #获取选中的数量 local count=$(echo "$network_prefer" | grep -o "1" | wc -l) - #获取每个偏好的值 - local network_prefer_3g=$(echo "$network_prefer" | jq -r '.["3G"]') - local network_prefer_4g=$(echo "$network_prefer" | jq -r '.["4G"]') - local network_prefer_5g=$(echo "$network_prefer" | jq -r '.["5G"]') + #获取启用的网络偏好 + local enable_5g=$(echo "$network_prefer" | jq -r '.["5G"].enable') + local enable_4g=$(echo "$network_prefer" | jq -r '.["4G"].enable') + local enable_3g=$(echo "$network_prefer" | jq -r '.["3G"].enable') case "$count" in "1") - if [ "$network_prefer_3g" = "1" ]; then + if [ "$enable_3g" = "1" ]; then network_prefer_config="WCDMA" - elif [ "$network_prefer_4g" = "1" ]; then + elif [ "$enable_4g" = "1" ]; then network_prefer_config="LTE" - elif [ "$network_prefer_5g" = "1" ]; then + elif [ "$enable_5g" = "1" ]; then network_prefer_config="NR5G" fi ;; "2") - if [ "$network_prefer_3g" = "1" ] && [ "$network_prefer_4g" = "1" ]; then + if [ "$enable_3g" = "1" ] && [ "$enable_4g" = "1" ]; then network_prefer_config="WCDMA:LTE" - elif [ "$network_prefer_3g" = "1" ] && [ "$network_prefer_5g" = "1" ]; then + elif [ "$enable_3g" = "1" ] && [ "$enable_5g" = "1" ]; then network_prefer_config="WCDMA:NR5G" - elif [ "$network_prefer_4g" = "1" ] && [ "$network_prefer_5g" = "1" ]; then + elif [ "$enable_4g" = "1" ] && [ "$enable_5g" = "1" ]; then network_prefer_config="LTE:NR5G" fi ;; @@ -934,7 +958,7 @@ quectel_cell_info() esac } -# SIMCOM获取基站信息 +# Quectel获取基站信息 Quectel_Cellinfo() { # return diff --git a/package/wwan/luci-app-sms-tool/LICENSE b/package/wwan/luci-app-sms-tool/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/package/wwan/luci-app-sms-tool/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<https://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<https://www.gnu.org/licenses/why-not-lgpl.html>. diff --git a/package/wwan/luci-app-sms-tool/Makefile b/package/wwan/luci-app-sms-tool/Makefile new file mode 100644 index 000000000..db9d2a20c --- /dev/null +++ b/package/wwan/luci-app-sms-tool/Makefile @@ -0,0 +1,32 @@ +# Copyright (C) 2023 Siriling <siriling@qq.com> +# This is free software, licensed under the GNU General Public License v3. + +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-sms-tool +LUCI_TITLE:=LuCI Support for sms_tool +LUCI_PKGARCH:=all +PKG_VERSION:=1.0.0 +PKG_LICENSE:=GPLv3 +PKG_LINCESE_FILES:=LICENSE +PKF_MAINTAINER:=siriling <siriling@qq.com> +LUCI_DEPENDS:=+sms-tool +luci-compat +kmod-usb-serial +kmod-usb-serial-option + +define Package/luci-app-sms-tool/postinst +#!/bin/sh +rm -rf /tmp/luci-indexcache +rm -rf /tmp/luci-modulecache/ +/sbin/set_sms_ports.sh +exit 0 +endef + +define Package/$(PKG_NAME)/config +# shown in make menuconfig <Help> +help + $(LUCI_TITLE) + Version: $(PKG_VERSION) +endef + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/package/wwan/luci-app-sms-tool/htdocs/luci-static/resources/icons/delsms.png b/package/wwan/luci-app-sms-tool/htdocs/luci-static/resources/icons/delsms.png new file mode 100644 index 000000000..b036e813f Binary files /dev/null and b/package/wwan/luci-app-sms-tool/htdocs/luci-static/resources/icons/delsms.png differ diff --git a/package/wwan/luci-app-sms-tool/htdocs/luci-static/resources/icons/delsms2.png b/package/wwan/luci-app-sms-tool/htdocs/luci-static/resources/icons/delsms2.png new file mode 100644 index 000000000..eeb13c5e6 Binary files /dev/null and b/package/wwan/luci-app-sms-tool/htdocs/luci-static/resources/icons/delsms2.png differ diff --git a/package/wwan/luci-app-sms-tool/luasrc/controller/modem/sms.lua b/package/wwan/luci-app-sms-tool/luasrc/controller/modem/sms.lua new file mode 100644 index 000000000..f807e9e72 --- /dev/null +++ b/package/wwan/luci-app-sms-tool/luasrc/controller/modem/sms.lua @@ -0,0 +1,242 @@ + local util = require "luci.util" + local fs = require "nixio.fs" + local sys = require "luci.sys" + local http = require "luci.http" + local dispatcher = require "luci.dispatcher" + local http = require "luci.http" + local sys = require "luci.sys" + local uci = require "luci.model.uci".cursor() + +module("luci.controller.modem.sms", package.seeall) + +function index() + entry({"admin", "modem"}, firstchild(), "Modem", 30).dependent=false + entry({"admin", "modem", "sms"}, alias("admin", "modem", "sms", "readsms"), translate("短信"), 20) + entry({"admin", "modem", "sms", "readsms"},template("modem/readsms"),translate("收到的信息"), 10) + entry({"admin", "modem", "sms", "sendsms"},template("modem/sendsms"),translate("发送消息"), 20) + entry({"admin", "modem", "sms", "ussd"},template("modem/ussd"),translate("USSD 代码"), 30) + entry({"admin", "modem", "sms", "atcommands"},template("modem/atcommands"),translate("AT 命令"), 40) + entry({"admin", "modem", "sms", "smsconfig"},cbi("modem/smsconfig"),translate("配置"), 50) + entry({"admin", "modem", "sms", "delete_one"}, call("delete_sms", smsindex), nil).leaf = true + entry({"admin", "modem", "sms", "delete_all"}, call("delete_all_sms"), nil).leaf = true + entry({"admin", "modem", "sms", "run_ussd"}, call("ussd"), nil).leaf = true + entry({"admin", "modem", "sms", "run_at"}, call("at"), nil).leaf = true + entry({"admin", "modem", "sms", "run_sms"}, call("sms"), nil).leaf = true + entry({"admin", "modem", "sms", "readsim"}, call("slots"), nil).leaf = true + entry({"admin", "modem", "sms", "countsms"}, call("count_sms"), nil).leaf = true + entry({"admin", "modem", "sms", "user_ussd"}, call("userussd"), nil).leaf = true + entry({"admin", "modem", "sms", "user_atc"}, call("useratc"), nil).leaf = true + entry({"admin", "modem", "sms", "user_phonebook"}, call("userphb"), nil).leaf = true +end + + +function delete_sms(smsindex) +local devv = tostring(uci:get("sms_tool", "general", "readport")) +local s = smsindex +for d in s:gmatch("%d+") do + os.execute("sms_tool -d " .. devv .. " delete " .. d .. "") +end +end + +function delete_all_sms() + local devv = tostring(uci:get("sms_tool", "general", "readport")) + os.execute("sms_tool -d " .. devv .. " delete all") +end + +function get_ussd() + local cursor = luci.model.uci.cursor() + if cursor:get("sms_tool", "general", "ussd") == "1" then + return " -R" + else + return "" + end +end + + +function get_pdu() + local cursor = luci.model.uci.cursor() + if cursor:get("sms_tool", "general", "pdu") == "1" then + return " -r" + else + return "" + end +end + + +function ussd() + local devv = tostring(uci:get("sms_tool", "general", "ussdport")) + + local ussd = get_ussd() + local pdu = get_pdu() + + local ussd_code = http.formvalue("code") + if ussd_code then + local odpall = io.popen("sms_tool -d " .. devv .. ussd .. pdu .. " ussd " .. ussd_code .." 2>&1") + local odp = odpall:read("*a") + odpall:close() + http.write(tostring(odp)) + else + http.write_json(http.formvalue()) + end +end + + +function at() + local devv = tostring(uci:get("sms_tool", "general", "atport")) + + local at_code = http.formvalue("code") + if at_code then + local odpall = io.popen("sms_tool -d " .. devv .. " at " ..at_code:gsub("[$]", "\\\$"):gsub("\"", "\\\"").." 2>&1") + local odp = odpall:read("*a") + odpall:close() + http.write(tostring(odp)) + else + http.write_json(http.formvalue()) + end +end + + +function sms() + local devv = tostring(uci:get("sms_tool", "general", "sendport")) + local sms_code = http.formvalue("scode") + + nr = (string.sub(sms_code, 1, 20)) + msgall = string.sub(sms_code, 21) + msg = string.gsub(msgall, "\n", " ") + + if sms_code then + local odpall = io.popen("sms_tool -d " .. devv .. " send " .. nr .." '".. msg .."'") + local odp = odpall:read("*a") + odpall:close() + http.write(tostring(odp)) + else + http.write_json(http.formvalue()) + end + +end + +function slots() + local sim = { } + local devv = tostring(uci:get("sms_tool", "general", "readport")) + local led = tostring(uci:get("sms_tool", "general", "smsled")) + local dsled = tostring(uci:get("sms_tool", "general", "ledtype")) + local ln = tostring(uci:get("sms_tool", "general", "lednotify")) + + local smsmem = tostring(uci:get("sms_tool", "general", "storage")) + + local statusb = luci.util.exec("sms_tool -s" .. smsmem .. " -d ".. devv .. " status") + local usex = string.sub (statusb, 23, 27) + local max = statusb:match('[^: ]+$') + sim["use"] = string.match(usex, '%d+') + local smscount = string.match(usex, '%d+') + if ln == "1" then + luci.sys.call("echo " .. smscount .. " > /etc/config/sms_count") + if dsled == "S" then + luci.util.exec("/etc/init.d/led restart") + end + if dsled == "D" then + luci.sys.call("echo 0 > '/sys/class/leds/" .. led .. "/brightness'") + end + end + sim["all"] = string.match(max, '%d+') + luci.http.prepare_content("application/json") + luci.http.write_json(sim) +end + + +function count_sms() + os.execute("sleep 3") + local cursor = luci.model.uci.cursor() + if cursor:get("sms_tool", "general", "lednotify") == "1" then + local devv = tostring(uci:get("sms_tool", "general", "readport")) + + local smsmem = tostring(uci:get("sms_tool", "general", "storage")) + + local statusb = luci.util.exec("sms_tool -s" .. smsmem .. " -d ".. devv .. " status") + local smsnum = string.sub (statusb, 23, 27) + local smscount = string.match(smsnum, '%d+') + os.execute("echo " .. smscount .. " > /etc/config/sms_count") + end +end + + +function uussd(rv) + local c = nixio.fs.access("/etc/config/ussd.user") and + io.popen("cat /etc/config/ussd.user") + + if c then + for l in c:lines() do + local i = l + if i then + rv[#rv + 1] = { + usd = i + } + end + end + c:close() + end +end + + + +function userussd() + local usd = { } + uussd(usd) + luci.http.prepare_content("application/json") + luci.http.write_json(usd) +end + + +function uat(rv) + local c = nixio.fs.access("/etc/config/atcmds.user") and + io.popen("cat /etc/config/atcmds.user") + + if c then + for l in c:lines() do + local i = l + if i then + rv[#rv + 1] = { + atu = i + } + end + end + c:close() + end +end + + + +function useratc() + local atu = { } + uat(atu) + luci.http.prepare_content("application/json") + luci.http.write_json(atu) +end + + + +function uphb(rv) + local c = nixio.fs.access("/etc/config/phonebook.user") and + io.popen("cat /etc/config/phonebook.user") + + if c then + for l in c:lines() do + local i = l + if i then + rv[#rv + 1] = { + phb = i + } + end + end + c:close() + end +end + + + +function userphb() + local phb = { } + uphb(phb) + luci.http.prepare_content("application/json") + luci.http.write_json(phb) +end diff --git a/package/wwan/luci-app-sms-tool/luasrc/model/cbi/modem/smsconfig.lua b/package/wwan/luci-app-sms-tool/luasrc/model/cbi/modem/smsconfig.lua new file mode 100644 index 000000000..dc756e5c4 --- /dev/null +++ b/package/wwan/luci-app-sms-tool/luasrc/model/cbi/modem/smsconfig.lua @@ -0,0 +1,226 @@ +local util = require "luci.util" +local fs = require "nixio.fs" +local sys = require "luci.sys" +local http = require "luci.http" +local dispatcher = require "luci.dispatcher" +local http = require "luci.http" +local sys = require "luci.sys" +local uci = require "luci.model.uci".cursor() + +local USSD_FILE_PATH = "/etc/config/ussd.user" +local PHB_FILE_PATH = "/etc/config/phonebook.user" +local SMSC_FILE_PATH = "/etc/config/smscommands.user" +local AT_FILE_PATH = "/etc/config/atcmds.user" + +local led = tostring(uci:get("sms_tool", "general", "smsled")) +local dsled = tostring(uci:get("sms_tool", "general", "ledtype")) +local ledtime = tostring(uci:get("sms_tool", "general", "checktime")) + +local m +local s +local dev1, dev2, dev3, dev4, leds +local try_devices1 = nixio.fs.glob("/dev/tty[A-Z][A-Z]*") +local try_devices2 = nixio.fs.glob("/dev/tty[A-Z][A-Z]*") +local try_devices3 = nixio.fs.glob("/dev/tty[A-Z][A-Z]*") +local try_devices4 = nixio.fs.glob("/dev/tty[A-Z][A-Z]*") +local try_leds = nixio.fs.glob("/sys/class/leds/*") + + +local devv = tostring(uci:get("sms_tool", "general", "readport")) + +local smsmem = tostring(uci:get("sms_tool", "general", "storage")) + +local statusb = luci.util.exec("sms_tool -s".. smsmem .. " -d ".. devv .. " status") + +local smsnum = string.sub (statusb, 23, 27) + +local smscount = string.match(smsnum, '%d+') + +m = Map("sms_tool", translate("配置短信工具"), + translate("sms_tool和gui应用程序的配置面板。")) + +s = m:section(NamedSection, 'general' , "sms_tool" , "" .. translate("")) +s.anonymous = true +s:tab("sms", translate("SMS 设置")) +s:tab("ussd", translate("USSD 代码设置")) +s:tab("at", translate("AT 命令设置")) +s:tab("info", translate("通知设置")) + +this_tab = "sms" + +dev1 = s:taboption(this_tab, Value, "readport", translate("短信读取端口")) +if try_devices1 then +local node +for node in try_devices1 do +dev1:value(node, node) +end +end + +mem = s:taboption(this_tab, ListValue, "storage", translate("信息存储区"), translate("信息存储在一个特定的位置(例如,在SIM卡或调制解调器内存),但根据设备的类型,其他区域也可能是可用的。")) +mem.default = "SM" +mem:value("SM", translate("SIM 卡")) +mem:value("ME", translate("调制解调器内存")) +mem.rmempty = true + +local msm = s:taboption(this_tab, Flag, "mergesms", translate("合并分割的信息"), translate("勾选这个选项会使阅读信息更容易,但会导致显示和接收的信息数量不一致。")) +msm.rmempty = false + +dev2 = s:taboption(this_tab, Value, "sendport", translate("短信发送端口")) +if try_devices2 then +local node +for node in try_devices2 do +dev2:value(node, node) +end +end + +local t = s:taboption(this_tab, Value, "pnumber", translate("前缀号码"), translate("电话号码的前面应该有国家的前缀(波兰是48,没有'+')。如果号码是5个、4个或3个字符,它将被视为 '短',不应该在前面加上国家前缀。")) +t.rmempty = true +t.default = 48 + +local f = s:taboption(this_tab, Flag, "prefix", translate("为电话号码添加前缀"), translate("自动添加电话号码字段的前缀。")) +f.rmempty = false + + +local i = s:taboption(this_tab, Flag, "information", translate("号码和前缀的解释"), translate("在发送短信的标签中,显示前缀的解释和正确的电话号码。")) +i.rmempty = false + +local ta = s:taboption(this_tab, TextValue, "user_phonebook", translate("用户电话簿"), translate("每一行必须有以下格式。'联系人姓名;电话号码'。保存到文件'/etc/config/phonebook.user'。")) +ta.rows = 7 +ta.rmempty = false + +function ta.cfgvalue(self, section) + return fs.readfile(PHB_FILE_PATH) +end + +function ta.write(self, section, value) + value = value:gsub("\r\n", "\n") + fs.writefile(PHB_FILE_PATH, value) +end + +this_taba = "ussd" + +dev3 = s:taboption(this_taba, Value, "ussdport", translate("USSD发送端口")) +if try_devices3 then +local node +for node in try_devices3 do +dev3:value(node, node) +end +end + +local u = s:taboption(this_taba, Flag, "ussd", translate("以纯文本发送USSD代码"), translate("以纯文本发送USSD代码。命令没有被编码到PDU中。")) +u.rmempty = false + +local p = s:taboption(this_taba, Flag, "pdu", translate("接收没有PDU解码的信息"), translate("接收并显示消息,而不将其解码为PDU。")) +p.rmempty = false + +local tb = s:taboption(this_taba, TextValue, "user_ussd", translate("用户USSD代码"), translate("每一行必须有以下格式。'代码名称;代码'。保存到文件'/etc/config/ussd.user'。")) +tb.rows = 7 +tb.rmempty = true + +function tb.cfgvalue(self, section) + return fs.readfile(USSD_FILE_PATH) +end + +function tb.write(self, section, value) + value = value:gsub("\r\n", "\n") + fs.writefile(USSD_FILE_PATH, value) +end + +this_tabc = "at" + +dev4 = s:taboption(this_tabc, Value, "atport", translate("AT命令的发送端口")) +if try_devices4 then +local node +for node in try_devices4 do +dev4:value(node, node) +end +end + +local tat = s:taboption(this_tabc, TextValue, "user_at", translate("用户AT命令"), translate("每一行必须有以下格式。'AT命令名称;AT命令'。保存到文件'/etc/config/atcmds.user'。")) +tat.rows = 20 +tat.rmempty = true + +function tat.cfgvalue(self, section) + return fs.readfile(AT_FILE_PATH) +end + +function tat.write(self, section, value) + value = value:gsub("\r\n", "\n") + fs.writefile(AT_FILE_PATH, value) +end + +this_tabb = "info" + +local uw = s:taboption(this_tabb, Flag, "lednotify", translate("通知新消息"), translate("LED通知有新的信息。在激活这个功能之前,请配置并保存短信阅读端口,检查短信收件箱的时间,并选择通知LED。")) +uw.rmempty = false + +function uw.write(self, section, value) +if devv ~= nil or devv ~= '' then +if ( smscount ~= nil and led ~= nil ) then + if value == '1' then + + luci.sys.call("echo " .. smscount .. " > /etc/config/sms_count") + luci.sys.call("uci set sms_tool.general.lednotify=" .. 1 .. ";/etc/init.d/smsled enable;/etc/init.d/smsled start") + luci.sys.call("/sbin/cronsync.sh") + + elseif value == '0' then + luci.sys.call("uci set sms_tool.general.lednotify=" .. 0 .. ";/etc/init.d/smsled stop;/etc/init.d/smsled disable") + if dsled == 'D' then + luci.sys.call("echo 0 > '/sys/class/leds/" .. led .. "/brightness'") + end + luci.sys.call("/sbin/cronsync.sh") + + end +return Flag.write(self, section ,value) + end +end +end + +local time = s:taboption(this_tabb, Value, "checktime", translate("每(几)分钟检查一次收件箱"), translate("指定你想在多少分钟内检查你的收件箱。")) +time.rmempty = false +time.maxlength = 2 +time.default = 5 + +function time.validate(self, value) + if ( tonumber(value) < 60 and tonumber(value) > 0 ) then + return value + end +end + +sync = s:taboption(this_tabb, ListValue, "prestart", translate("每隔一段时间重新启动收件箱检查程序"), translate("该过程将在选定的时间间隔内重新启动。这将消除检查收件箱的延迟。")) +sync.default = "6" +sync:value("4", translate("4h")) +sync:value("6", translate("6h")) +sync:value("8", translate("8h")) +sync:value("12", translate("12h")) +sync.rmempty = true + + +leds = s:taboption(this_tabb, Value, "smsled", translate("通知LED"), translate("选择通知LED。")) +if try_leds then +local node +local status +for node in try_leds do +local status = node +local all = string.sub (status, 17) +leds:value(all, all) +end +end + +oled = s:taboption(this_tabb, ListValue, "ledtype", translate("该二极管只专门用于这些通知"), translate("如果路由器只有一个LED,或者LED是多任务的,就选'No'。")) +oled.default = "D" +oled:value("S", translate("No")) +oled:value("D", translate("Yes")) +oled.rmempty = true + +local timeon = s:taboption(this_tabb, Value, "ledtimeon", translate("每(几)秒打开LED灯"), translate("指定LED应该亮多长时间。")) +timeon.rmempty = false +timeon.maxlength = 3 +timeon.default = 1 + +local timeoff = s:taboption(this_tabb, Value, "ledtimeoff", translate("每(几)秒关闭LED灯"), translate("指定LED应该关闭多长时间。")) +timeoff.rmempty = false +timeoff.maxlength = 3 +timeoff.default = 5 + +return m diff --git a/package/wwan/luci-app-sms-tool/luasrc/view/modem/atcommands.htm b/package/wwan/luci-app-sms-tool/luasrc/view/modem/atcommands.htm new file mode 100644 index 000000000..3510d8b87 --- /dev/null +++ b/package/wwan/luci-app-sms-tool/luasrc/view/modem/atcommands.htm @@ -0,0 +1,136 @@ +<%+header%> + +<!-- +-- Copyright 2020-2022 Rafał Wabik (IceG) - From eko.one.pl forum +-- Licensed to the GNU General Public License v3.0. +--> + + <h2><%:AT 命令%></h2> + <div class="cbi-map-descr"><%:通过sms_tool处理AT命令的Web用户界面。关于sms-tool的更多信息,请见%> <a href="https://eko.one.pl/?p=openwrt-sms_tool" target="_blank"><%:eko.one.pl 论坛%></a>.</div> + <p></p> + <h4><%:向调制解调器发送命令%></h4> + <div class="table" width="100%"> + + <div class="tr"> + <div class="td left"><%:用户AT命令%>:</div> + + <div class="td left";"> + <select name="ussd" id="pl" onclick="copyFunction()"> + + </select> + </div> + <div class="td left""></div> + </div> + + <div class="tr"> + <div class="td left" ><%:要发送的命令%>:</div> + <div class="td left" ><input type="text" id="code"></div> + </div> + + </div> + + <div class="table" width="100%"> + <div class="td left";"><%:回复%>: + <p> + <pre id="odp" style="visibility: hidden;"></pre></div> + + <div class="tr cbi-rowstyle-2"> + <div class="td right"><input type="button" style="margin-right: 5%"; id="sendcmd" class="btn cbi-button cbi-button-neutral" value="<%:发送命令%>" /></div> + </div> + + </div> + +<script type="text/javascript"> + +window.onload = function readUSER() { + + + XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "user_atc")%>', null, + function(x, json) + { + select = document.getElementById('pl'); + + var count = Object.keys(json).length; + + + for(var d=0;d<=count;d++) + { + var opt = document.createElement('option'); + var s = json[d].atu; + var fields = s.split(/;/); + var name = fields[0]; + var code = fields[1]; + opt.text = name; + opt.value = code.trim(); + opt.innerHTML = name; + select.appendChild(opt); + } + + } + ); + + + +} + +function copyFunction() { + + var node = document.getElementById('odp'); + node.style.visibility = 'hidden'; + + var x = document.getElementById("pl").value; + document.getElementById("code").value = x; + document.getElementById("odp").innerHTML = ""; +} + + +function postcmd(cmd) { + (new XHR()).post("<%=luci.dispatcher.build_url("admin", "modem", "sms", "run_at")%>", {"code":cmd}, function(x) { + console.log(x.response) + console.log(x) + + var aStr = x.response; + var myre = /^[\s\t]*(\r\n|\n|\r)/gm; + var bStr = aStr.replace(myre,""); + document.getElementById("odp").innerHTML = bStr; + var el = document.getElementsByName("odp")[0]; + el.value.replace(/(\r\n|\n|\r)/gm, ""); + + + }); + return false; +} + + +document.addEventListener('DOMContentLoaded', function (ev) {var button = document.getElementById("sendcmd"); + button.addEventListener("click", function () { + + + var s = document.getElementById("code").value; + if ( s.length == 0 ) + { + document.getElementById("odp").innerHTML = ""; + alert("<%:Please enter a AT Command%>"); + return false; + } + + var cmd = document.getElementById("code"); + postcmd(cmd.value); + cmd.value = ""; + + var node = document.getElementById('odp'); + if (node.style.visibility=='visible') { + node.style.visibility = 'hidden'; + } + else + node.style.visibility = 'visible' + + return true; + }); + }, true); + + +</script> + +<%+footer%> + diff --git a/package/wwan/luci-app-sms-tool/luasrc/view/modem/readsms.htm b/package/wwan/luci-app-sms-tool/luasrc/view/modem/readsms.htm new file mode 100644 index 000000000..dc8245e2d --- /dev/null +++ b/package/wwan/luci-app-sms-tool/luasrc/view/modem/readsms.htm @@ -0,0 +1,285 @@ +<% + local util = require "luci.util" + local fs = require "nixio.fs" + local sys = require "luci.sys" + local http = require "luci.http" + local dispatcher = require "luci.dispatcher" + local uci = require "luci.model.uci".cursor() + + local devv = tostring(uci:get("sms_tool", "general", "readport")) + local smsmem = tostring(uci:get("sms_tool", "general", "storage")) + local sms = tostring(luci.sys.exec("sms_tool -s" .. smsmem .. " -d " .. devv .. " -f '%Y-%m-%d %H:%M' -j recv 2>/dev/null")) + local smsmer = tostring(uci:get("sms_tool", "general", "mergesms")) + local smscuta = string.sub (sms, 8) + local smscut = smscuta:sub(1, #smscuta - 2) + local statusb = luci.util.exec("sms_tool -s" .. smsmem .. " -d ".. devv .. " status") + local all = statusb:match('[^: ]+$') + local location = "" + local l = string.sub (statusb, 15, 16) + if l == "SM" then + location = translate("SIM card") + end + if l == "ME" then + location = translate("Modem memory") + end +-%> + +<%+header%> + +<!-- +-- Copyright 2020-2022 Rafał Wabik (IceG) - From eko.one.pl forum +-- Licensed to the GNU General Public License v3.0. +--> + +<style> + +table { + border-collapse: collapse; + width: 100%; +} + +th, td { + border-bottom: 1px solid var(--border-color-medium); + font-size: 12px; + padding: 10px; + text-align: justify; + display: table-cell; + vertical-align: top; +} + +td input[type="checkbox"] { + float: left; + margin: 0 auto; + width: 40%; +} + +tr:nth-child(odd) {background-color: var(--background-color-medium)} + +</style> + +<script type="text/javascript">//<![CDATA[ + + window.onload = function readSMS() { + + XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "readsim")%>', null, + function(x, sim) + { + var vn = parseInt(sim.use) || 0; + var mn = parseInt(sim.all) || 100; + var pc = Math.floor((100 / mn) * vn); + document.getElementById('usse').innerHTML=sim.use + " / " + sim.all + " " + '('+ pc + '%)'; + } + ); + + var msgm = <%=smsmer%>; + if ( msgm == "0" ) { + + var array = <%=smscut%>; + var sortedData = array .sort((function (a, b) { return new Date(b.timestamp) - new Date(a.timestamp) })); + var table = document.getElementById("smsTable"); + var data = JSON.stringify(sortedData); + var json = JSON.parse(data); + + var x = <%=all%>; + for (var d = 0; d < json.length; d++) { + + row = table.insertRow(-1); + var cell1 = row.insertCell(0); + var cell2 = row.insertCell(0); + var cell3 = row.insertCell(0); + var cell4 = row.insertCell(0); + cell4.innerHTML = "<input type='checkbox' name='smsn' id="+"btn"+json[d].index+" /><img src='<%=resource%>/icons/delsms.png'>" + cell3.innerHTML = json[d].sender; + cell2.innerHTML = json[d].timestamp; + cell1.innerHTML = json[d].content; + } + } + + if ( msgm == "1" ) { + + var array = <%=smscut%>; + var sortedData = array .sort((function (a, b) { return new Date(b.timestamp) - new Date(a.timestamp) })); + var table = document.getElementById("smsTable"); + + var MergeMySMS = sortedData; + result = []; + + MergeMySMS.forEach(function (o) { + if (!this[o.sender]) { + if(o.part > 0){ + this[o.sender] = { index: o.index, sender: o.sender, timestamp: o.timestamp, part: o.part, total: o.total, content: o.content, contentparts: [] }; + this[o.sender].contentparts[o.part] = o.content; + }else{ + this[o.sender] = { index: o.index, sender: o.sender, timestamp: o.timestamp, part: o.part, total: o.total, content: o.content}; + } + result.push(this[o.sender]); + return; + } + if (this[o.sender].total == o.total && this[o.sender].timestamp == o.timestamp && this[o.sender].sender == o.sender && this[o.sender].part > 0) { + this[o.sender].index += '-' + o.index; + this[o.sender].contentparts[o.part] = o.content;} + else { + this[o.sender] = { index: o.index, sender: o.sender, timestamp: o.timestamp, part: o.part, total: o.total, content: o.content }; + result.push(this[o.sender]); + return; + } + }, Object.create(null)); + result.forEach(function(o) { + if(o.contentparts){ + o.contentparts.shift(); + o.content = o.contentparts.join(''); + } + }); + + var data = JSON.stringify(result); + var json = JSON.parse(data); + + var x = <%=all%>; + for (var d = 0; d < json.length; d++) { + + row = table.insertRow(-1); + var cell1 = row.insertCell(0); + var cell2 = row.insertCell(0); + var cell3 = row.insertCell(0); + var cell4 = row.insertCell(0); + cell4.innerHTML = "<input type='checkbox' name='smsn' id="+"btn"+json[d].index+" /><img src='<%=resource%>/icons/delsms.png'>" + cell3.innerHTML = json[d].sender; + cell2.innerHTML = json[d].timestamp; + cell1.innerHTML = json[d].content; + } + } + } + + +function refreshSMS() { +window.location.reload(); +} + +function toggle(source) { + var checkboxes = document.querySelectorAll('input[type="checkbox"]'); + for (var i = 0; i < checkboxes.length; i++) { + if (checkboxes[i] != source) + checkboxes[i].checked = source.checked; + } +} + + +function delete_new() +{ +if (document.querySelectorAll('input[name="smsn"]:checked').length === document.querySelectorAll('input[name="smsn"]').length) { + + if (confirm("<%:Delete all the messages?%>")) { + var rowCount = smsTable.rows.length; + document.getElementById('usse').innerHTML = "0" + " / " + <%=all%>; + for (var i = rowCount - 1; i > 0; i--) { + smsTable.deleteRow(i);} + + XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "delete_all")%>/' , null, + + function() + { + document.getElementById('usse').innerHTML = "0" + " / " + <%=all%> + " " + '(0%)'; + } + ); + + XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "countsms")%>/' , null, + + function() + { + } + ); + return false; + } + + + } else { + + if (document.querySelectorAll('input[name="smsn"]:checked').length == 0){ + alert('<%:Please select the message(s) to be deleted%>'); + } else { + + var items = document.getElementsByName("smsn"); + var selectedItems = ""; + for (var i = 0; i < items.length; i++) { + if (items[i].type == "checkbox" && items[i].checked == true) selectedItems += items[i].id + "\n"; + } + smsindex = selectedItems.replace(/\s/g, ''); + + if (confirm("<%:Delete selected message(s)?%>")) { + + XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "delete_one")%>/' + smsindex, null, + + function() + { + var table = document.getElementById("smsTable"); + var index = 1; + while (index < table.rows.length) { + + var input = table.rows[index].cells[0].children[0]; + if (input && input.checked) { + table.deleteRow(index); + } + else { + index++; + } + } + + XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "readsim")%>', null, + function(x, sim) + { + var vn = parseInt(sim.use) || 0; + var mn = parseInt(sim.all) || 100; + var pc = Math.floor((100 / mn) * vn); + document.getElementById('usse').innerHTML=sim.use + " / " + sim.all + " " + '('+ pc + '%)'; + + } + ); + } + + ); + + XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "countsms")%>/' , null, + + function() + { + } + ); + return false; + } + } + } +} + +//]]></script> + +<h2><%:短信%></h2> + <div class="cbi-map-descr"><%:通过sms_tool接收信息的Web用户界面。关于短信工具的更多信息,请见%> <a href="https://eko.one.pl/?p=openwrt-sms_tool" target="_blank"><%:eko.one.pl 论坛%></a>。</div> + <p></p> + <h4><%:接收的信息%></h4> + <div class="table" width="100%"> + <div class="tr"><div class="td left" width="33%"><%:信息存储在%>:</div><div class="td left"><%=location%></div></div> + <div class="tr"><div class="td left" width="33%"><%:信息 (收件箱 / 最大值)%>:</div><div class="td left" id="usse"></div></div> + </div> + + <table id="smsTable" width="100%" class="table"> + <tr class="tr cbi-rowstyle-2"> + + <td class="td left" width="7%"><input type='checkbox' id='allcheck' onclick='toggle(this);'/></td> + <td class="td left" width="11%"><strong><%:来自%></strong></td> + <td class="td left" width="15%"><strong><%:收到%></strong></td> + <td class="td center" width="66%" ><strong><%:信息%></strong></td> + + </tr> + </table> + + <div class="table" width="100%" > + <div class="tr cbi-rowstyle-2"> + + <div class="td right" style="width:100%;"> + <div style="float: left;"></div> + <input type="button" id="delbtn" class="btn cbi-button cbi-button-neutral" value="<%:刷新消息%>" onclick="refreshSMS()" /> <input type="button" id="delabtn" class="btn cbi-button cbi-button-neutral" value="<%:删除消息(s)%>" onclick="delete_new()" /> + + </div> + </div> + +<%+footer%> diff --git a/package/wwan/luci-app-sms-tool/luasrc/view/modem/sendsms.htm b/package/wwan/luci-app-sms-tool/luasrc/view/modem/sendsms.htm new file mode 100644 index 000000000..f9747c6a5 --- /dev/null +++ b/package/wwan/luci-app-sms-tool/luasrc/view/modem/sendsms.htm @@ -0,0 +1,237 @@ +<% + local util = require "luci.util" + local sys = require "luci.sys" + local uci = require "luci.model.uci".cursor() + + local pon = tostring(uci:get("sms_tool", "general", "prefix")) + local pnumber = tostring(uci:get("sms_tool", "general", "pnumber")) + local info = tostring(uci:get("sms_tool", "general", "information")) + +-%> + +<%+header%> + +<!-- +-- Copyright 2020-2022 Rafał Wabik (IceG) - From eko.one.pl forum +-- Licensed to the GNU General Public License v3.0. +--> + +<style> + +textarea { + height:120px; + max-height:120px; + min-height:120px; + min-width:100%; + resize:vertical; + } + +} + +</style> + +<script type="text/javascript">//<![CDATA[ + + +window.onload = function prefiksSMS() { + + var phk = document.getElementById("numer").value; + var on = <%=pon%>; + + var msg = <%=info%>; + + var pfixnum = document.getElementById("numer"); + if ( on == "1" ) { + var phn2 = <%=pnumber%>; + pfixnum.value = phn2; + } + + if ( msg == "1" ) { + alert("<%:电话号码的前面应该有国家的前缀(波兰是48,没有+)。如果号码是5个、4个或3个字符,它将被视为短号码,而不应在前面加上国家前缀。(The phone number should be preceded by the country prefix (for Poland it is 48, without +). If the number is 5, 4 or 3 characters, it is treated as.. short and should not be preceded by a country prefix.)%>"); + } + + + +XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "user_phonebook")%>', null, + function(x, json) + { + select = document.getElementById('pb'); + var opt = document.createElement('option'); + opt.text = phn2; + opt.value = phn2; + opt.innerHTML = ">"; + select.appendChild(opt); + + var countpb = Object.keys(json).length; + + + for(var d=0;d<=countpb;d++) + { + var opt = document.createElement('option'); + var s = json[d].phb; + var fields = s.split(/;/); + var name = fields[0]; + var number = fields[1]; + opt.text = name; + opt.value = number.trim(); + opt.innerHTML = name; + select.appendChild(opt); + } + + } + ); + + + + +} + + +function copyFunction2() { + + var x = document.getElementById("pb").value; + document.getElementById("numer").value = x; + document.getElementById("odp").innerHTML = ""; + + +} + + +function count_replace(obj) { + + document.getElementById('counter').innerHTML = (160 - document.getElementById('smstxt').value.length); + + obj.value = obj.value.replace(/ą/g, 'a').replace(/Ą/g, 'A'); + obj.value = obj.value.replace(/ć/g, 'c').replace(/Ć/g, 'C') + obj.value = obj.value.replace(/ę/g, 'e').replace(/Ę/g, 'E') + obj.value = obj.value.replace(/ł/g, 'l').replace(/Ł/g, 'L') + obj.value = obj.value.replace(/ń/g, 'n').replace(/Ń/g, 'N') + obj.value = obj.value.replace(/ó/g, 'o').replace(/Ó/g, 'O') + obj.value = obj.value.replace(/ś/g, 's').replace(/Ś/g, 'S') + obj.value = obj.value.replace(/ż/g, 'z').replace(/Ż/g, 'Z') + obj.value = obj.value.replace(/ź/g, 'z').replace(/Ź/g, 'Z'); + document.getElementById("odp").innerHTML = ""; + +} + +function codsp() { + + document.getElementById("odp").innerHTML = ""; + +} + + + +function postcmd(cmd) { + (new XHR()).post("<%=luci.dispatcher.build_url("admin", "modem", "sms", "run_sms")%>", {"scode":cmd}, function(x) { + console.log(x.response) + console.log(x) + var cut = x.response; + cut = cut.substr(0, 20); + if ( cut == "sms sent sucessfully" ) { + document.getElementById("odp").innerHTML = "<%:SMS sent sucessfully%>"}; + + }); + return false; +} + + +document.addEventListener('DOMContentLoaded', function (ev) {var button = document.getElementById("sendsms"); + button.addEventListener("click", function () { + + + var ph = document.getElementById("numer").value; + if ( ph.length < 2 || ph.length == 0 ) + { + document.getElementById("odp").innerHTML = ""; + alert("<%:Please enter phone number%>"); + return false; + } + + ph = ph.trim(); + + if ( isNaN(ph) ) + { + alert("<%:Please correct phone number%>"); + return false; + } + + var num = ph.concat(" "); + num = num.substr(0, 20); + + var tm = document.getElementById("smstxt").value; + if ( tm.length == 0 ) + { + alert("<%:Please enter a message text%>"); + return false; + } + num = num.concat(tm); + postcmd(num); + var delsms = document.getElementById("smstxt"); + var delnum = document.getElementById("numer"); + document.getElementById('counter').innerHTML = "160"; + delsms.value = ""; + delnum.value = ""; + + var on = <%=pon%>; + var pfixnum = document.getElementById("numer"); + if ( on == "1" ) { + var phn2 = <%=pnumber%>; + pfixnum.value = phn2; + } + + return true; + }); + }, true); + + +//]]></script> + + + <h2><%:短信%></h2> + <div class="cbi-map-descr"><%:通过sms_tool发送信息的Web用户界面。关于短信工具的更多信息,请见%> <a href="https://eko.one.pl/?p=openwrt-sms_tool" target="_blank"><%:eko.one.pl 论坛%></a>.</div> + <p></p> + + <h4><%:发送短信%></h4> + + <div class="table"> + + <div class="tr"> + <div class="td left" ><%:用户电话簿%>:</div> + <div class="td left" > + + <select name="ussd" id="pb" onchange="copyFunction2()"> + + </select> +</div> + <div class="td left" ></div> + </div> + + <div class="tr"> + <div class="td left" ><%:发送至%>:</div> + <div class="td left" ><input type="text" id="numer" onKeyUp="codsp()" placeholder="<%:PHONE_NUMBER%>" required minlength="3" maxlength="24" size="11"></div> + <div class="td left" ></div> + </div> + <div class="tr"> + <div class="td left" ><%:消息正文%>:</div> + <div class="td left" ><textarea id="smstxt" onKeyUp="count_replace(this)" required></textarea><div id="counter">160</div></div> + <div class="td left" ></div> + </div> + + <div class="tr"> + <div class="td left" ><%:状态%>:</div><div class="td left" id="odp"></div> + + </div> + + </div> + <div class="table"> + + <div class="tr cbi-rowstyle-2"> + <div class="td right"><input type="button" style="margin-right: 5%"; id="sendsms" class="btn cbi-button cbi-button-neutral" value="<%:发送短信%>" /></div> + </div> + + </div> + + + +<%+footer%> diff --git a/package/wwan/luci-app-sms-tool/luasrc/view/modem/ussd.htm b/package/wwan/luci-app-sms-tool/luasrc/view/modem/ussd.htm new file mode 100644 index 000000000..38be1c3c4 --- /dev/null +++ b/package/wwan/luci-app-sms-tool/luasrc/view/modem/ussd.htm @@ -0,0 +1,265 @@ +<% + local util = require "luci.util" + local fs = require "nixio.fs" + local sys = require "luci.sys" + local http = require "luci.http" + local dispatcher = require "luci.dispatcher" + local uci = require "luci.model.uci".cursor() + +-%> + + +<%+header%> + +<!-- +-- Copyright 2020-2022 Rafał Wabik (IceG) - From eko.one.pl forum +-- Licensed to the GNU General Public License v3.0. +--> + + <h2><%:USSD 代码%></h2> + <div class="cbi-map-descr"><%:通过sms_tool处理USSD代码的Web用户界面。关于短信工具的更多信息,请见%> <a href="https://eko.one.pl/?p=openwrt-sms_tool" target="_blank"><%:eko.one.pl forum%></a>.</div> + <p></p> + <h4><%:发送 USSD 代码%></h4> + <div class="table" width="100%"> + + <div class="tr"> + <div class="td left" width="25%"><%:用户 USSD 代码%>:</div> + + <div class="td left" > + <select name="ussd" id="pl" onclick="copyFunction()"> + + </select> + </div> + <div class="td left" ></div> + </div> + + <div class="tr"> + <div class="td left" ><%:要发送的代码%>:</div> + <div class="td left" ><input type="text" id="code" required size="20"></div> + + </div> + <div class="tr"> + <div class="td left" ><%:回复%>:</div><div class="td left" ><pre id="odp" style="visibility: hidden;"></pre></div> + + </div> + </div> + + <div class="table" width="100%"> + <div class="tr cbi-rowstyle-2"> + <div class="td right"><input type="button" style="margin-right: 5%"; id="sendussd" class="btn cbi-button cbi-button-neutral" value="<%:发送代码%>" /></div> + </div> + + </div> + +<script type="text/javascript">//<![CDATA[ + + +window.onload = function readUSER() { + + + XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "user_ussd")%>', null, + function(x, json) + { + select = document.getElementById('pl'); + + var count = Object.keys(json).length; + + + for(var d=0;d<=count;d++) + { + var opt = document.createElement('option'); + var s = json[d].usd; + var fields = s.split(/;/); + var name = fields[0]; + var code = fields[1]; + opt.text = name; + opt.value = code.trim(); + opt.innerHTML = name; + select.appendChild(opt); + } + + } + ); + + + +} + +function copyFunction() { + var node = document.getElementById('odp'); + node.style.visibility = 'hidden'; + + var x = document.getElementById("pl").value; + document.getElementById("code").value = x; + document.getElementById("odp").innerHTML = ""; +} + +function postcmd(cmd) { + (new XHR()).post("<%=luci.dispatcher.build_url("admin", "modem", "sms", "run_ussd")%>", {"code":cmd}, function(x) { + console.log(x.response) + console.log(x) + var cut = x.response; + if ( true == cut.includes("error: 0") ) { + document.getElementById("odp").innerHTML = "<%:Phone/Modem failure.%>"}; + if ( true == cut.includes("error: 1") ) { + document.getElementById("odp").innerHTML = "<%:No connection to phone.%>"}; + if ( true == cut.includes("error: 2") ) { + document.getElementById("odp").innerHTML = "<%:Phone/Modem adapter link reserved.%>"}; + if ( true == cut.includes("error: 3") ) { + document.getElementById("odp").innerHTML = "<%:Operation not allowed.%>"}; + if ( true == cut.includes("error: 4") ) { + document.getElementById("odp").innerHTML = "<%:Operation not supported.%>"}; + if ( true == cut.includes("error: 5") ) { + document.getElementById("odp").innerHTML = "<%:PH_SIM PIN required.%>"}; + if ( true == cut.includes("error: 6") ) { + document.getElementById("odp").innerHTML = "<%:PH_FSIM PIN required.%>"}; + if ( true == cut.includes("error: 7") ) { + document.getElementById("odp").innerHTML = "<%:PH_FSIM PUK required.%>"}; + if ( true == cut.includes("error: 10") ) { + document.getElementById("odp").innerHTML = "<%:SIM not inserted.%>"}; + if ( true == cut.includes("error: 11") ) { + document.getElementById("odp").innerHTML = "<%:SIM PIN required.%>"}; + if ( true == cut.includes("error: 12") ) { + document.getElementById("odp").innerHTML = "<%:SIM PUK required.%>"}; + if ( true == cut.includes("error: 13") ) { + document.getElementById("odp").innerHTML = "<%:SIM failure.%>"}; + if ( true == cut.includes("error: 14") ) { + document.getElementById("odp").innerHTML = "<%:SIM busy.%>"}; + if ( true == cut.includes("error: 15") ) { + document.getElementById("odp").innerHTML = "<%:SIM wrong.%>"}; + if ( true == cut.includes("error: 16") ) { + document.getElementById("odp").innerHTML = "<%:Incorrect password.%>"}; + if ( true == cut.includes("error: 17") ) { + document.getElementById("odp").innerHTML = "<%:SIM PIN2 required.%>"}; + if ( true == cut.includes("error: 18") ) { + document.getElementById("odp").innerHTML = "<%:SIM PUK2 required.%>"}; + if ( true == cut.includes("error: 20") ) { + document.getElementById("odp").innerHTML = "<%:Memory full.%>"}; + if ( true == cut.includes("error: 21") ) { + document.getElementById("odp").innerHTML = "<%:Invalid index.%>"}; + if ( true == cut.includes("error: 22") ) { + document.getElementById("odp").innerHTML = "<%:Not found.%>"}; + if ( true == cut.includes("error: 23") ) { + document.getElementById("odp").innerHTML = "<%:Memory failure.%>"}; + if ( true == cut.includes("error: 24") ) { + document.getElementById("odp").innerHTML = "<%:Text string too long.%>"}; + if ( true == cut.includes("error: 25") ) { + document.getElementById("odp").innerHTML = "<%:Invalid characters in text string.%>"}; + if ( true == cut.includes("error: 26") ) { + document.getElementById("odp").innerHTML = "<%:Dial string too long.%>"}; + if ( true == cut.includes("error: 27") ) { + document.getElementById("odp").innerHTML = "<%:Invalid characters in dial string.%>"}; + if ( true == cut.includes("error: 30") ) { + document.getElementById("odp").innerHTML = "<%:No network service.%>"}; + if ( true == cut.includes("error: 31") ) { + document.getElementById("odp").innerHTML = "<%:Network timeout.%>"}; + if ( true == cut.includes("error: 32") ) { + document.getElementById("odp").innerHTML = "<%:Network not allowed, emergency calls only.%>"}; + if ( true == cut.includes("error: 40") ) { + document.getElementById("odp").innerHTML = "<%:Network personalization PIN required.%>"}; + if ( true == cut.includes("error: 41") ) { + document.getElementById("odp").innerHTML = "<%:Network personalization PUK required.%>"}; + if ( true == cut.includes("error: 42") ) { + document.getElementById("odp").innerHTML = "<%:Network subset personalization PIN required.%>"}; + if ( true == cut.includes("error: 43") ) { + document.getElementById("odp").innerHTML = "<%:Network subset personalization PUK required.%>"}; + if ( true == cut.includes("error: 44") ) { + document.getElementById("odp").innerHTML = "<%:Service provider personalization PIN required.%>"}; + if ( true == cut.includes("error: 45") ) { + document.getElementById("odp").innerHTML = "<%:Service provider personalization PUK required.%>"}; + if ( true == cut.includes("error: 46") ) { + document.getElementById("odp").innerHTML = "<%:Corporate personalization PIN required.%>"}; + if ( true == cut.includes("error: 47") ) { + document.getElementById("odp").innerHTML = "<%:Corporate personalization PUK required.%>"}; + if ( true == cut.includes("error: 48") ) { + document.getElementById("odp").innerHTML = "<%:PH-SIM PUK required.%>"}; + if ( true == cut.includes("error: 100") ) { + document.getElementById("odp").innerHTML = "<%:Unknown error.%>"}; + if ( true == cut.includes("error: 103") ) { + document.getElementById("odp").innerHTML = "<%:Illegal MS.%>"}; + if ( true == cut.includes("error: 106") ) { + document.getElementById("odp").innerHTML = "<%:Illegal ME.%>"}; + if ( true == cut.includes("error: 107") ) { + document.getElementById("odp").innerHTML = "<%:GPRS services not allowed.%>"}; + if ( true == cut.includes("error: 111") ) { + document.getElementById("odp").innerHTML = "<%:PLMN not allowed.%>"}; + if ( true == cut.includes("error: 112") ) { + document.getElementById("odp").innerHTML = "<%:Location area not allowed.%>"}; + if ( true == cut.includes("error: 113") ) { + document.getElementById("odp").innerHTML = "<%:Roaming not allowed in this location area.%>"}; + if ( true == cut.includes("error: 126") ) { + document.getElementById("odp").innerHTML = "<%:Operation temporary not allowed.%>"}; + if ( true == cut.includes("error: 132") ) { + document.getElementById("odp").innerHTML = "<%:Service operation not supported.%>"}; + if ( true == cut.includes("error: 133") ) { + document.getElementById("odp").innerHTML = "<%:Requested service option not subscribed.%>"}; + if ( true == cut.includes("error: 134") ) { + document.getElementById("odp").innerHTML = "<%:Service option temporary out of order.%>"}; + if ( true == cut.includes("error: 148") ) { + document.getElementById("odp").innerHTML = "<%:Unspecified GPRS error.%>"}; + if ( true == cut.includes("error: 149") ) { + document.getElementById("odp").innerHTML = "<%:PDP authentication failure.%>"}; + if ( true == cut.includes("error: 150") ) { + document.getElementById("odp").innerHTML = "<%:Invalid mobile class.%>"}; + if ( true == cut.includes("error: 256") ) { + document.getElementById("odp").innerHTML = "<%:Operation temporarily not allowed.%>"}; + if ( true == cut.includes("error: 257") ) { + document.getElementById("odp").innerHTML = "<%:Call barred.%>"}; + if ( true == cut.includes("error: 258") ) { + document.getElementById("odp").innerHTML = "<%:Phone/Modem is busy.%>"}; + if ( true == cut.includes("error: 259") ) { + document.getElementById("odp").innerHTML = "<%:User abort.%>"}; + if ( true == cut.includes("error: 260") ) { + document.getElementById("odp").innerHTML = "<%:Invalid dial string.%>"}; + if ( true == cut.includes("error: 261") ) { + document.getElementById("odp").innerHTML = "<%:SS not executed.%>"}; + if ( true == cut.includes("error: 262") ) { + document.getElementById("odp").innerHTML = "<%:SIM Blocked.%>"}; + if ( true == cut.includes("error: 263") ) { + document.getElementById("odp").innerHTML = "<%:Invalid block.%>"}; + if ( true == cut.includes("error: 527") ) { + document.getElementById("odp").innerHTML = "<%:Please wait, and retry your selection later (Specific Modem Sierra).%>"}; + if ( true == cut.includes("error: 528") ) { + document.getElementById("odp").innerHTML = "<%:Location update failure – emergency calls only (Specific Modem Sierra).%>"}; + if ( true == cut.includes("error: 529") ) { + document.getElementById("odp").innerHTML = "<%:Selection failure – emergency calls only (Specific Modem Sierra).%>"}; + if ( true == cut.includes("error: 772") ) { + document.getElementById("odp").innerHTML = "<%:SIM powered down.%>"}; + if ( true != cut.includes("error: ") ) { + document.getElementById("odp").innerHTML = x.response}; + + }); + return false; +} + +document.addEventListener('DOMContentLoaded', function (ev) {var button = document.getElementById("sendussd"); + button.addEventListener("click", function () { + + + var s = document.getElementById("code").value; + if ( s.length == 0 ) + { + document.getElementById("odp").innerHTML = ""; + alert("<%:Please enter a USSD code%>"); + return false; + } + + var cmd = document.getElementById("code"); + postcmd(cmd.value); + cmd.value = ""; + + var node = document.getElementById('odp'); + if (node.style.visibility=='visible') { + node.style.visibility = 'hidden'; + } + else + node.style.visibility = 'visible' + + return true; + }); + }, true); + + +//]]></script> + +<%+footer%> diff --git a/package/wwan/luci-app-sms-tool/po/pl/sms-tool.po b/package/wwan/luci-app-sms-tool/po/pl/sms-tool.po new file mode 100644 index 000000000..2ef0778b5 --- /dev/null +++ b/package/wwan/luci-app-sms-tool/po/pl/sms-tool.po @@ -0,0 +1,288 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +"Language: pl\n" +"Last-Translator: Rafał Wabik (IceG) - From eko.one.pl forum\n" + +msgid "SMS Messages" +msgstr "Wiadomości SMS" + +msgid "Received Messages" +msgstr "Odebrane wiadomości" + +msgid "Send Messages" +msgstr "Wysyłanie wiadomości" + +msgid "USSD" +msgstr "USSD" + +msgid "From" +msgstr "Nadawca" + +msgid "Received" +msgstr "Odebrano" + +msgid "Message" +msgstr "Wiadomość" + +msgid "Configuration" +msgstr "Ustawienia" + +msgid "Sample codes" +msgstr "Przykładowe kody" + +msgid "Sending USSD codes" +msgstr "Wysyłanie kodów USSD" + +msgid "Please enter phone number" +msgstr "Proszę podać numer telefonu" + +msgid "Please correct phone number" +msgstr "Proszę poprawić numer telefonu" + +msgid "Please enter a message text" +msgstr "Proszę podać treść wiadomości" + +msgid "Please select the message(s) to be deleted" +msgstr "Proszę wybrać wiadomość(-ci) do usunięcia" + +msgid "Reply" +msgstr "Odpowiedź" + +msgid "SMS sent sucessfully" +msgstr "SMS został wysłany" + +msgid "Please enter a USSD code" +msgstr "Proszę podać kod do wysłania" + +msgid "Delete selected message(s)?" +msgstr "Usunąć wybraną(-e) wiadomość(-ci)?" + +msgid "Delete all the messages?" +msgstr "Usunąć wszystkie wiadomości?" + +msgid "Configuration sms-tool" +msgstr "Konfiguracja sms-tool" + +msgid "Configuration panel for sms_tool and gui application." +msgstr "Panel ustawień dla aplikacji sms_tool oraz dla interfejsu użytkownika." + +msgid "Device" +msgstr "Urządzenie" + +msgid "Web UI for handling messages via sms_tool." +msgstr "Interfejs użytkownika dla sms_tool do obsługi wiadomości SMS." + +msgid "Web UI for handling USSD codes via sms_tool." +msgstr "Interfejs użytkownika dla sms_tool do obsługi kodów USSD." + +msgid "SIM card" +msgstr "Karta SIM" + +msgid "Modem memory" +msgstr "Pamięć modemu" + +msgid "Messages store in" +msgstr "Miejsce przechowywania wiadomości" + +msgid "Messages (Inbox / Maximum)" +msgstr "Wiadomości (Odebrane / Maksymalnie)" + +msgid "Delete Selected" +msgstr "Usuń wybraną" + +msgid "Delete message(s)" +msgstr "Usuń wiadomość(-ci)" + +msgid "Delete All" +msgstr "Usuń wszystkie" + +msgid "USSD Codes" +msgstr "Kody USSD" + +msgid "Send to" +msgstr "Wyślij do" + +msgid "Message text" +msgstr "Treść wiadomości" + +msgid "Send Message" +msgstr "Wyślij SMS" + +msgid "Code to send" +msgstr "Kod do wysłania" + +msgid "Send Code" +msgstr "Wyślij kod" + +msgid "SMS Settings" +msgstr "Ustawienia dla wiadomości SMS" + +msgid "USSD Codes Settings" +msgstr "Ustawienia dla kodów USSD" + +msgid "SMS Reading Port" +msgstr "Port do odczytu SMS" + +msgid "SMS Sending Port" +msgstr "Port do wysyłania SMS" + +msgid "USSD Sending Port" +msgstr "Port do wysyłania kodów USSD" + +msgid "Add Prefix to Phone Number" +msgstr "Dodaj prefiks do numeru telefonu" + +msgid "Automatically add prefix to the phone number field." +msgstr "Automatycznie uzupełnij pole numeru telefonu o prefiks." + +msgid "Prefix Number" +msgstr "Numer prefiks" + +msgid "PHONE_NUMBER" +msgstr "48NumerTelefonu" + +msgid "Refresh SMS" +msgstr "Odśwież" + +msgid "Sending USSD Code in plain text" +msgstr "Wysyłaj kod USSD jawnym tekstem" + +msgid "Receive message without PDU decoding" +msgstr "Odbierz wiadomość bez dekodowania jej jako PDU" + +msgid "Send the USSD code in plain text. Command is not being coded to the PDU." +msgstr "Wysyła kod USSD jawnym tekstem. Polecenie nie jest dekodowane na PDU." + +msgid "Receive and display the message without decoding it as a PDU." +msgstr "Odebrana wiadomość wyświetlana jest bez dekodowania jej jako PDU." + +msgid "Explanation of number and prefix" +msgstr "Wyjaśnienie dot. numeru i prefiksu" + +msgid "In the tab for sending SMSes, show an explanation of the prefix and the correct phone number." +msgstr "W zakładce umożliwiającej wysyłanie SMS-ów pokaż wyjaśnienie dotyczące prefiksu i poprawnego numeru telefonu." + +msgid "The phone number should be preceded by the country prefix (for Poland it is 48, without '+'). If the number is 5, 4 or 3 characters, it is treated as 'short' and should not be preceded by a country prefix." +msgstr "Numer telefonu należy poprzedzić prefiksem kraju (dla Polski jest to 48, bez znaku '+'). Jeżeli numer jest 5, 4 lub 3 znakowy to jest on traktowany jako 'krótki' i nie należy go poprzedzać prefiksem kraju." + +msgid "The phone number should be preceded by the country prefix (for Poland it is 48, without +). If the number is 5, 4 or 3 characters, it is treated as.. short and should not be preceded by a country prefix." +msgstr "Numer telefonu należy poprzedzić prefiksem kraju (dla Polski jest to 48, bez znaku +). Jeżeli numer jest 5, 4 lub 3 znakowy to jest on traktowany jako krótki i nie należy go poprzedzać prefiksem kraju." + +msgid "Each line must have the following format: 'Contact name;Phone number'. Save to file '/etc/config/phonebook.user'." +msgstr "Każda linijka powinna mieć następujący format: 'Nazwa kontaktu;Numer telefonu'. Dla wygody kontakty użytkownika zapisywane są w pliku '/etc/config/phonebook.user'." + +msgid "User Phonebook" +msgstr "Kontakty użytkownika" + +msgid "User USSD Codes" +msgstr "Kody USSD użytkownika" + +msgid "Each line must have the following format: 'Code name;Code'. Save to file '/etc/config/ussd.user'." +msgstr "Każda linijka powinna mieć następujący format: 'Etykieta kodu;Kod'. Dla wygody kody użytkownika zapisywane są w pliku '/etc/config/ussd.user'." + +msgid "Notification Settings" +msgstr "Ustawienia powiadomień" + +msgid "The LED informs about a new message. Before activating this function, please config and save the SMS reading port, time to check SMS inbox and select the notification LED." +msgstr "Powiadomienie diodą o nowej wiadomości. Przed uruchomieniem tej funkcji proszę ustawić port odczytu wiadomości, czas sprawdzania skrzynki odbiorczej oraz wybrać diodę powiadomień." + +msgid "Check inbox every minute(s)" +msgstr "Sprawdzaj skrzynkę odbiorczą co minut(-y)" + +msgid "Specify how many minutes you want your inbox to be checked." +msgstr "Podaj co ile minut ma być sprawdzana skrzynka odbiorcza w poszukiwaniu nowych wiadomości." + +msgid "Notification LED" +msgstr "Dioda powiadomień" + +msgid "Select the notification LED." +msgstr "Wybierz diodę powiadomień." + +msgid "Notify new messages" +msgstr "Powiadomienie o nowych wiadomościach" + +msgid "Turn on the LED for seconds(s)" +msgstr "Włącz diodę na sekund(-y)" + +msgid "Turn off the LED for seconds(s)" +msgstr "Wyłącz diodę na sekund(-y)" + +msgid "Specify for how long the LED should be on." +msgstr "Podaj przez jaki czas dioda ma być włączona." + +msgid "Specify for how long the LED should be off." +msgstr "Podaj przez jaki czas dioda ma być wyłączona." + +msgid "Merge split messages" +msgstr "Połącz podzielone wiadomości" + +msgid "Checking this option will make it easier to read the messages, but it will cause a discrepancy in the number of messages shown and received." +msgstr "Podzielone wiadomości zostaną złączone. Zaznaczenie tej opcji ułatwi czytanie SMS-ów, ale spowoduje niezgodność w ilości pokazanych, odebranych wiadomości." + +msgid "Message storage area" +msgstr "Miejsce przechowywania wiadomości" + +msgid "Messages are stored in a specific location (for example, on the SIM card or modem memory), but other areas may also be available depending on the type of device." +msgstr "Wiadomości przechowywane są w określonym miejscu (np. na karcie SIM lub pamięci modemu), ale w zależności od typu urządzenia mogą być dostępne także inne obszary." + +msgid "AT Commands" +msgstr "Polecenia AT" + +msgid "Web UI for handling AT commands via sms_tool." +msgstr "Interfejs użytkownika dla sms_tool do obsługi poleceń AT." + +msgid "Sending commands to modem" +msgstr "Wysyłanie poleceń do modemu" + +msgid "User AT Commands" +msgstr "Polecenia AT użytkownika" + +msgid "Command to send" +msgstr "Polecenie do wysłania" + +msgid "Please enter a AT Command" +msgstr "Proszę podać polecenie AT do wysłania" + +msgid "AT Commands Sending Port" +msgstr "Port do wysyłania poleceń AT" + +msgid "Send Command" +msgstr "Wyślij polecenie" + +msgid "AT Commands Settings" +msgstr "Ustawienia dla poleceń AT" + +msgid "Each line must have the following format: 'AT Command name;AT Command'. Save to file '/etc/config/atcmds.user'." +msgstr "Każda linijka powinna mieć następujący format: 'Etykieta polecenia;polecenie AT'. Dla wygody polecenia użytkownika zapisywane są w pliku '/etc/config/atcmds.user'." + +msgid "Restart the inbox checking process every" +msgstr "Uruchom proces ponownie po" + +msgid "The process will restart at the selected time interval. This will eliminate the delay in checking your inbox." +msgstr "Proces zostanie uruchomiony ponownie po wybranym przez użytkownika czasie. Pozwoli to wyeliminować opóźnienie w sprawdzaniu skrzynki odbiorczej." + +msgid "4h" +msgstr "4 godz." + +msgid "6h" +msgstr "6 godz." + +msgid "8h" +msgstr "8 godz." + +msgid "12h" +msgstr "12 godz." + +msgid "The diode is dedicated only to these notifications" +msgstr "Dioda jest dedykowana tylko tym powiadomieniom" + +msgid "Select 'No' in case the router has only one LED or if the LED is multi-tasking." +msgstr "Wybierz 'Nie' w przypadku, kiedy router ma tylko jedną diodę lub gdy dioda obsługuje wiele zadań." + +msgid "No" +msgstr "Nie" + +msgid "Yes" +msgstr "Tak" + diff --git a/package/wwan/luci-app-sms-tool/po/zh-cn/sms-tool.po b/package/wwan/luci-app-sms-tool/po/zh-cn/sms-tool.po new file mode 100644 index 000000000..4417c9cbe --- /dev/null +++ b/package/wwan/luci-app-sms-tool/po/zh-cn/sms-tool.po @@ -0,0 +1,246 @@ +msgid "Modem" +msgstr "调制解调器" + +msgid "SMS Messages" +msgstr "SMS 信息" + +msgid "Received Messages" +msgstr "接收信息" + +msgid "Send Messages" +msgstr "发送信息" + +msgid "From" +msgstr "发件人" + +msgid "Received" +msgstr "接收时间" + +msgid "Message" +msgstr "信息" + +msgid "Configuration" +msgstr "参数配置" + +msgid "Sample codes" +msgstr "代码样本" + +msgid "Please enter phone number" +msgstr "请输入手机号" + +msgid "Please correct phone number" +msgstr "请更正手机号" + +msgid "Please enter a message text" +msgstr "请输入信息内容" + +msgid "Please select the message(s) to be deleted" +msgstr "请选择要删除的信息" + +msgid "Reply" +msgstr "回复" + +msgid "SMS sent sucessfully" +msgstr "信息发送成功" + +msgid "Delete selected message(s)?" +msgstr "删除选择的信息?" + +msgid "Delete all the messages?" +msgstr "删除所有信息?" + +msgid "Configuration sms-tool" +msgstr "配置 sms-tool" + +msgid "Configuration panel for sms_tool and gui application." +msgstr "sms_tool 和 gui 应用程序的配置面板." + +msgid "Device" +msgstr "设备" + +msgid "Web UI for handling messages via sms_tool." +msgstr "用于通过 sms_tool 处理信息的 Web UI." + +msgid "Web UI for sending messages via sms_tool." +msgstr "用于通过 sms_tool 发送信息的 Web UI." + +msgid "Web UI for receiveling messages via sms_tool." +msgstr "用于通过 sms_tool 接收信息的 Web UI." + +msgid "SIM card" +msgstr "SIM卡" + +msgid "Modem memory" +msgstr "Modem内存" + +msgid "Messages store in" +msgstr "信息存储在" + +msgid "Messages (Inbox / Maximum)" +msgstr "信息(收件箱/最大)" + +msgid "Delete Selected" +msgstr "删除所选" + +msgid "Delete message(s)" +msgstr "删除信息" + +msgid "Delete All" +msgstr "删除所有" + +msgid "Send to" +msgstr "发送到" + +msgid "Message text" +msgstr "信息文本" + +msgid "Send Message" +msgstr "发送信息" + +msgid "Code to send" +msgstr "代码发送" + +msgid "Send Code" +msgstr "发送代码" + +msgid "SMS Settings" +msgstr "SMS 设置" + +msgid "SMS Reading Port" +msgstr "SMS 读取端口" + +msgid "SMS Sending Port" +msgstr "SMS 发送端口" + +msgid "Add Prefix to Phone Number" +msgstr "为手机号码添加前缀" + +msgid "Automatically add prefix to the phone number field." +msgstr "自动为手机号码字段添加前缀." + +msgid "Prefix Number" +msgstr "前缀编号" + +msgid "PHONE_NUMBER" +msgstr "手机号码" + +msgid "Refresh SMS" +msgstr "刷新信息" + +msgid "Explanation of number and prefix" +msgstr "数字和前缀的解释" + +msgid "In the tab for sending SMSes, show an explanation of the prefix and the correct phone number." +msgstr "在发送短信的选项卡中,显示前缀说明和正确的电话号码." + +msgid "The phone number should be preceded by the country prefix (for Poland it is 48, without '+'). If the number is 5, 4 or 3 characters, it is treated as 'short' and should not be preceded by a country prefix." +msgstr "电话号码前面应有国家/地区前缀(中国为 86,不带“+”), 号码是 5、4 或 3 个字符的短号除外." + +msgid "The phone number should be preceded by the country prefix (for Poland it is 48, without +). If the number is 5, 4 or 3 characters, it is treated as.. short and should not be preceded by a country prefix." +msgstr "电话号码前面应有国家/地区前缀(中国为 86,不带“+”), 号码是 5、4 或 3 个字符的短号除外." + +msgid "Each line must have the following format: 'Contact name;Phone number'. Save to file '/etc/config/phonebook.user'." +msgstr "每行必须具有以下格式:“联系人姓名;电话号码”。 保存到文件“/etc/config/phonebook.user”." + +msgid "User Phonebook" +msgstr "电话簿" + +msgid "Notification Settings" +msgstr "通知设置" + +msgid "The LED informs about a new message. Before activating this function, please config and save the SMS reading port, time to check SMS inbox and select the notification LED." +msgstr "LED 新消息通知。 激活此功能前,请配置并保存短信阅读端口,及时查看短信收件箱并选择通知LED." + +msgid "Check inbox every minute(s)" +msgstr "每分钟检查收件箱" + +msgid "Specify how many minutes you want your inbox to be checked." +msgstr "指定要检查收件箱的分钟数." + +msgid "Notification LED" +msgstr "通知LED" + +msgid "Select the notification LED." +msgstr "选择通知LED." + +msgid "Notify new messages" +msgstr "通知新消息" + +msgid "Turn on the LED for seconds(s)" +msgstr "打开LED秒数" + +msgid "Turn off the LED for seconds(s)" +msgstr "关闭LED秒数" + +msgid "Specify for how long the LED should be on." +msgstr "指定 LED 应亮多长时间." + +msgid "Specify for how long the LED should be off." +msgstr "指定 LED 应关闭多长时间." + +msgid "Merge split messages" +msgstr "合并分割的消息" + +msgid "Checking this option will make it easier to read the messages, but it will cause a discrepancy in the number of messages shown and received." +msgstr "选中此选项将使阅读消息更容易,但会导致显示和接收的消息数量出现差异." + +msgid "Message storage area" +msgstr "消息存储区" + +msgid "Messages are stored in a specific location (for example, on the SIM card or modem memory), but other areas may also be available depending on the type of device." +msgstr "消息存储在特定位置(例如,SIM 卡或调制解调器内存中),但其他区域也可能可用,具体取决于设备类型." + +msgid "AT Commands" +msgstr "AT 命令" + +msgid "Web UI for handling AT commands via sms_tool." +msgstr "用于通过 sms_tool 处理 AT 命令的 Web UI." + +msgid "Sending commands to modem" +msgstr "向调制解调器发送命令" + +msgid "User AT Commands" +msgstr "用户 AT 命令" + +msgid "Command to send" +msgstr "命令发送" + +msgid "Please enter a AT Command" +msgstr "请输入 AT 命令" + +msgid "AT Commands Sending Port" +msgstr "AT命令发送端口" + +msgid "Send Command" +msgstr "发送命令" + +msgid "AT Commands Settings" +msgstr "AT命令设置" + +msgid "Each line must have the following format: 'AT Command name;AT Command'. Save to file '/etc/config/atcmds.user'." +msgstr "每行必须具有以下格式:'AT Command name;AT Command'。 保存到文件“/etc/config/atcmds.user”." + +msgid "Restart the inbox checking process every" +msgstr "每次重新启动收件箱检查过程" + +msgid "The process will restart at the selected time interval. This will eliminate the delay in checking your inbox." +msgstr "该过程将在选定的时间间隔重新开始。 这将消除检查收件箱的延迟." + +msgid "4h" +msgstr "4小时." + +msgid "6h" +msgstr "6小时." + +msgid "8h" +msgstr "8小时." + +msgid "12h" +msgstr "12小时." + +msgid "The diode is dedicated only to these notifications" +msgstr "二极管仅用于这些通知" + +msgid "Select 'No' in case the router has only one LED or if the LED is multi-tasking." +msgstr "如果路由器只有一个 LED 或者 LED 是多任务处理,请选择“否”." + diff --git a/package/wwan/luci-app-sms-tool/root/etc/config/atcmds.user b/package/wwan/luci-app-sms-tool/root/etc/config/atcmds.user new file mode 100644 index 000000000..44bafb90e --- /dev/null +++ b/package/wwan/luci-app-sms-tool/root/etc/config/atcmds.user @@ -0,0 +1,28 @@ +------查询------;AT +查看IMEI;AT+CGSN +固件版本信息;AT+GMR +信号强度;AT+CSQ +正在使用的网络信息;AT+QNWINFO +查询限速;AT+C5GQOSRDP=1 +模块温度;AT+QTEMP +查询运营商名称;AT+QSPN +查询小区信息;AT+QENG="servingcell" +-----3/4/5G网络配置-----;AT +查询当前网络搜索模式;AT+QNWPREFCFG="mode_pref" +!切换仅3G;AT+QNWPREFCFG="mode_pref",WCDMA +切换仅4G;AT+QNWPREFCFG="mode_pref",LTE +切换仅5G;AT+QNWPREFCFG="mode_pref",NR5G +切换5,4G;AT+QNWPREFCFG="mode_pref",NR5G:LTE +切换AUTO;AT+QNWPREFCFG="mode_pref",AUTO +-----锁频段-----;AT +查询支持的所有频段;AT+QNWPREFCFG=? +查询当前配置的5G频段;AT+QNWPREFCFG="nr5g_band" +查询当前配置的4G频段;AT+QNWPREFCFG="lte_band" +!5G锁频段(78);AT+QNWPREFCFG="nr5g_band",78 +5G默认频段;AT+QNWPREFCFG="nr5g_band",1:28:41:77:78:79 +!4G锁频段(1);AT+QNWPREFCFG="lte_band",1 +4G默认频段;AT+QNWPREFCFG="lte_band",1:2:3:5:7:8:20:28:34:38:39:40:41 +-----MORE-----;AT +查看产品型号;ATI +挂断现有语音;ATH +!更改IMEI,需重启;AT+EGMR=1,7,"868227050701486" \ No newline at end of file diff --git a/package/wwan/luci-app-sms-tool/root/etc/config/phonebook.user b/package/wwan/luci-app-sms-tool/root/etc/config/phonebook.user new file mode 100644 index 000000000..74c98ab4d --- /dev/null +++ b/package/wwan/luci-app-sms-tool/root/etc/config/phonebook.user @@ -0,0 +1 @@ +other user;8613188888888 diff --git a/package/wwan/luci-app-sms-tool/root/etc/config/sms_tool b/package/wwan/luci-app-sms-tool/root/etc/config/sms_tool new file mode 100644 index 000000000..7bff8f6d2 --- /dev/null +++ b/package/wwan/luci-app-sms-tool/root/etc/config/sms_tool @@ -0,0 +1,14 @@ + +config sms_tool 'general' + option pnumber '86' + option prefix '1' + option ledtimeon '1' + option ledtimeoff '5' + option lednotify '0' + option checktime '10' + option mergesms '0' + option information '0' + option pdu '0' + option storage 'SM' + option prestart '6' + option ledtype 'D' diff --git a/package/wwan/luci-app-sms-tool/root/etc/init.d/smsled b/package/wwan/luci-app-sms-tool/root/etc/init.d/smsled new file mode 100755 index 000000000..2442b145e --- /dev/null +++ b/package/wwan/luci-app-sms-tool/root/etc/init.d/smsled @@ -0,0 +1,26 @@ +#!/bin/sh /etc/rc.common +# Copyright 2020 Rafał Wabik (IceG) - From eko.one.pl forum +# Licensed to the GNU General Public License v3.0. + +USE_PROCD=1 +START=99 +STOP=01 + +start_service() { + procd_open_instance + procd_set_param command /bin/sh "/sbin/smsled-init.sh" + procd_close_instance +} + +stop_service() { + for KILLPID in `ps | grep 'smsled' | awk ' { print $1;}'`; do + (kill -9 $KILLPID >/dev/null 2>&1 )& + done + sleep 1 + return 0 +} + +restart_service() { + stop_service + start_service +} diff --git a/package/wwan/luci-app-sms-tool/root/etc/uci-defaults/set_sms_ports.sh b/package/wwan/luci-app-sms-tool/root/etc/uci-defaults/set_sms_ports.sh new file mode 100755 index 000000000..fbc77d705 --- /dev/null +++ b/package/wwan/luci-app-sms-tool/root/etc/uci-defaults/set_sms_ports.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# Copyright 2020-2021 Rafał Wabik (IceG) - From eko.one.pl forum +# Licensed to the GNU General Public License v3.0. + +work=false +for port in /dev/ttyUSB* +do + [[ -e $port ]] || continue + gcom -d $port info &> /tmp/testusb + testUSB=`cat /tmp/testusb | grep "Error\|Can't"` + if [ -z "$testUSB" ]; then + work=$port + break + fi +done +rm -rf /tmp/testusb + +if [ $work != false ]; then +uci set sms_tool.@sms_tool[0].readport=$work +uci set sms_tool.@sms_tool[0].sendport=$work +uci set sms_tool.@sms_tool[0].atport=$work +uci commit sms_tool +fi diff --git a/package/wwan/luci-app-sms-tool/root/etc/uci-defaults/start-smsled b/package/wwan/luci-app-sms-tool/root/etc/uci-defaults/start-smsled new file mode 100755 index 000000000..95ca388b7 --- /dev/null +++ b/package/wwan/luci-app-sms-tool/root/etc/uci-defaults/start-smsled @@ -0,0 +1,7 @@ +#!/bin/sh +# Copyright 2020 Rafał Wabik (IceG) - From eko.one.pl forum +# Licensed to the GNU General Public License v3.0. + +/etc/init.d/smsled disable + +exit 0 diff --git a/package/wwan/luci-app-sms-tool/root/sbin/cronsync.sh b/package/wwan/luci-app-sms-tool/root/sbin/cronsync.sh new file mode 100755 index 000000000..39f7899c3 --- /dev/null +++ b/package/wwan/luci-app-sms-tool/root/sbin/cronsync.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +[ -e /etc/crontabs/root ] || touch /etc/crontabs/root + +SLED=$(uci -q get sms_tool.general.lednotify) +if [ "x$SLED" != "x1" ]; then + if grep -q "smsled" /etc/crontabs/root; then + grep -v "/init.d/smsled" /etc/crontabs/root > /tmp/new_cron + mv /tmp/new_cron /etc/crontabs/root + /etc/init.d/cron restart + fi + exit 0 +fi + +if ! grep -q "smsled" /etc/crontabs/root; then +PTR=$(uci -q get sms_tool.general.prestart) + echo "1 */$PTR * * * /etc/init.d/smsled enable" >> /etc/crontabs/root + /etc/init.d/cron restart +fi + +exit 0 diff --git a/package/wwan/luci-app-sms-tool/root/sbin/set_sms_ports.sh b/package/wwan/luci-app-sms-tool/root/sbin/set_sms_ports.sh new file mode 100755 index 000000000..e89eb1737 --- /dev/null +++ b/package/wwan/luci-app-sms-tool/root/sbin/set_sms_ports.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# Copyright 2020-2021 Rafał Wabik (IceG) - From eko.one.pl forum +# Licensed to the GNU General Public License v3.0. + +work=false +for port in /dev/ttyUSB* +do + [[ -e $port ]] || continue + gcom -d $port info &> /tmp/testusb + testUSB=`cat /tmp/testusb | grep "Error\|Can't"` + if [ -z "$testUSB" ]; then + work=$port + break + fi +done +rm -rf /tmp/testusb + +if [ $work != false ]; then +uci set sms_tool.@sms_tool[0].readport=$work +uci set sms_tool.@sms_tool[0].sendport=$work +uci set sms_tool.@sms_tool[0].ussdport=$work +uci set sms_tool.@sms_tool[0].atport=$work +uci commit sms_tool +fi diff --git a/package/wwan/luci-app-sms-tool/root/sbin/smsled-init.sh b/package/wwan/luci-app-sms-tool/root/sbin/smsled-init.sh new file mode 100755 index 000000000..fa7d86304 --- /dev/null +++ b/package/wwan/luci-app-sms-tool/root/sbin/smsled-init.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# Copyright 2020 Rafał Wabik (IceG) - From eko.one.pl forum +# Licensed to the GNU General Public License v3.0. + +sleep 10 +CT=$(uci -q get sms_tool.general.checktime) +TX=$(echo $CT | tr -dc '0-9') +TM=$(($TX * 60)) + +while [ 1 ]; do + LED=$(uci -q get sms_tool.general.lednotify) + if [ $LED == "1" ]; then + sleep $TM + (/sbin/smsled.sh >/dev/null 2>&1 )& + continue + fi + sleep 1 +done + +exit 0 + diff --git a/package/wwan/luci-app-sms-tool/root/sbin/smsled.sh b/package/wwan/luci-app-sms-tool/root/sbin/smsled.sh new file mode 100755 index 000000000..fffd8566c --- /dev/null +++ b/package/wwan/luci-app-sms-tool/root/sbin/smsled.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# Copyright 2020-2021 Rafał Wabik (IceG) - From eko.one.pl forum +# Licensed to the GNU General Public License v3.0. + + DEV=$(uci -q get sms_tool.general.readport) + LEDX=$(uci -q get sms_tool.general.smsled) + MEM=$(uci -q get sms_tool.general.storage) + STX=$(sms_tool -s $MEM -d $DEV status | cut -c23-27) + SMS=$(echo $STX | tr -dc '0-9') + SMSC=$(cat /etc/config/sms_count) + LEDT="/sys/class/leds/$LEDX/trigger" + LEDON="/sys/class/leds/$LEDX/delay_on" + LEDOFF="/sys/class/leds/$LEDX/delay_off" + LED="/sys/class/leds/$LEDX/brightness" + + LON=$(uci -q get sms_tool.general.ledtimeon) + TXON=$(echo $LON | tr -dc '0-9') + TMON=$(($TXON * 1000)) + + LOFF=$(uci -q get sms_tool.general.ledtimeoff) + TXOFF=$(echo $LOFF | tr -dc '0-9') + TMOFF=$(($TXOFF * 1000)) + +if [ $SMS == $SMSC ]; then + + exit 0 +fi + +if [ $SMS > $SMSC ]; then + +echo timer > $LEDT +echo $TMOFF > $LEDOFF +echo $TMON > $LEDON +exit 0 + +fi + + +exit 0 diff --git a/package/wwan/luci-app-sms-tool/root/usr/share/rpcd/acl.d/luci-app-sms-tool.json b/package/wwan/luci-app-sms-tool/root/usr/share/rpcd/acl.d/luci-app-sms-tool.json new file mode 100644 index 000000000..3f4e02f78 --- /dev/null +++ b/package/wwan/luci-app-sms-tool/root/usr/share/rpcd/acl.d/luci-app-sms-tool.json @@ -0,0 +1,24 @@ +{ + "luci-app-sms-tool": { + "description": "Grant UCI and file access for luci-app-sms-tool", + "read": { + "file": { + "/usr/bin/sms_tool *": [ "exec" ], + }, + "uci": [ "sms_tool" ] + }, + "write": { + "file": { + "/usr/bin/sms_tool *": [ "exec" ], + + }, + "uci": [ "sms_tool" ] + } + } +} + + + + + + diff --git a/package/wwan/meig-cm/Makefile b/package/wwan/meig-cm/Makefile new file mode 100644 index 000000000..72efee8cc --- /dev/null +++ b/package/wwan/meig-cm/Makefile @@ -0,0 +1,34 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:= meig-cm +PKG_VERSION:=1.2.1 +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/meig-cm + SECTION:=utils + CATEGORY:=Utilities + TITLE:=meig-cm app +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + $(CP) ./src/* $(PKG_BUILD_DIR)/ +endef + +define Build/Compile + $(MAKE) -C "$(PKG_BUILD_DIR)" \ + EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \ + CROSS_COMPILE="$(TARGET_CROSS)" \ + ARCH="$(LINUX_KARCH)" \ + M="$(PKG_BUILD_DIR)" \ + CC="$(TARGET_CC)" +endef + +define Package/meig-cm/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/meig-cm $(1)/usr/bin +endef + +$(eval $(call BuildPackage,meig-cm)) diff --git a/package/wwan/meig-cm/src/GobiNetCM.c b/package/wwan/meig-cm/src/GobiNetCM.c new file mode 100644 index 000000000..0dbbcbbda --- /dev/null +++ b/package/wwan/meig-cm/src/GobiNetCM.c @@ -0,0 +1,229 @@ +#include <stdio.h> +#include <string.h> +#include <termios.h> +#include <stdio.h> +#include <ctype.h> +#include "QMIThread.h" + +#ifdef CONFIG_GOBINET + +// IOCTL to generate a client ID for this service type +#define IOCTL_QMI_GET_SERVICE_FILE 0x8BE0 + 1 + +// IOCTL to get the VIDPID of the device +#define IOCTL_QMI_GET_DEVICE_VIDPID 0x8BE0 + 2 + +// IOCTL to get the MEID of the device +#define IOCTL_QMI_GET_DEVICE_MEID 0x8BE0 + 3 + +static int GobiNetSendQMI(PQCQMIMSG pRequest) { + int ret, fd; + + fd = qmiclientId[pRequest->QMIHdr.QMIType]; + + if (fd <= 0) { + dbg_time("%s QMIType: %d has no clientID", __func__, pRequest->QMIHdr.QMIType); + return -ENODEV; + } + + // Always ready to write + 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 = open(qcqmi, O_RDWR | O_NONBLOCK | O_NOCTTY); + if (ClientId == -1) { + dbg_time("failed to open %s, errno: %d (%s)", qcqmi, errno, strerror(errno)); + return -1; + } + + if (ioctl(ClientId, IOCTL_QMI_GET_SERVICE_FILE, QMIType) != 0) { + dbg_time("failed to get ClientID for 0x%02x errno: %d (%s)", QMIType, errno, strerror(errno)); + close(ClientId); + ClientId = 0; + } + + switch (QMIType) { + case QMUX_TYPE_WDS: dbg_time("Get clientWDS = %d", ClientId); break; + case QMUX_TYPE_DMS: dbg_time("Get clientDMS = %d", ClientId); break; + case QMUX_TYPE_NAS: dbg_time("Get clientNAS = %d", ClientId); break; + case QMUX_TYPE_QOS: dbg_time("Get clientQOS = %d", ClientId); break; + case QMUX_TYPE_WMS: dbg_time("Get clientWMS = %d", ClientId); break; + case QMUX_TYPE_PDS: dbg_time("Get clientPDS = %d", ClientId); break; + case QMUX_TYPE_UIM: dbg_time("Get clientUIM = %d", ClientId); break; + case QMUX_TYPE_WDS_ADMIN: dbg_time("Get clientWDA = %d", ClientId); + break; + default: break; + } + + return ClientId; +} + +static int GobiNetDeInit(void) { + unsigned int i; + for (i = 0; i < sizeof(qmiclientId)/sizeof(qmiclientId[0]); i++) + { + if (qmiclientId[i] != 0) + { + close(qmiclientId[i]); + qmiclientId[i] = 0; + } + } + + return 0; +} + +static void * GobiNetThread(void *pData) { + PROFILE_T *profile = (PROFILE_T *)pData; + const char *qcqmi = (const char *)profile->qmichannel; + int wait_for_request_quit = 0; + + qmiclientId[QMUX_TYPE_WDS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS); + if (profile->enable_ipv6 || profile->IsDualIPSupported) + 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); + if (profile->qmap_mode == 0) //when QMAP enabled, set data format in GobiNet Driver + qmiclientId[QMUX_TYPE_WDS_ADMIN] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS_ADMIN); + + //donot check clientWDA, there is only one client for WDA, if meig-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 SIGTERM: + case SIGHUP: + case SIGINT: + wait_for_request_quit = 1; + break; + default: + break; + } + } + continue; + } + + { + ssize_t nreads; + UCHAR QMIBuf[512]; + PQCQMIMSG pResponse = (PQCQMIMSG)QMIBuf; + + nreads = read(fd, &pResponse->MUXMsg, sizeof(QMIBuf) - sizeof(QCQMI_HDR)); + if (nreads <= 0) + { + dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads, errno, strerror(errno)); + break; + } + + for (i = 0; i < sizeof(qmiclientId)/sizeof(qmiclientId[0]); i++) + { + if (qmiclientId[i] == fd) + { + pResponse->QMIHdr.QMIType = i; + } + } + + pResponse->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI; + pResponse->QMIHdr.Length = cpu_to_le16(nreads + sizeof(QCQMI_HDR) - 1); + pResponse->QMIHdr.CtlFlags = 0x00; + pResponse->QMIHdr.ClientId = fd & 0xFF; + + QmiThreadRecvQMI(pResponse); + } + } + } + +__GobiNetThread_quit: + GobiNetDeInit(); + qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED); + QmiThreadRecvQMI(NULL); //main thread may pending on QmiThreadSendQMI() + dbg_time("%s exit", __func__); + pthread_exit(NULL); + return NULL; +} + +#else +static int GobiNetSendQMI(PQCQMIMSG pRequest) {return -1;} +static void * GobiNetThread(void *pData) {dbg_time("please set CONFIG_GOBINET"); return NULL;} +#endif + +const struct qmi_device_ops gobi_qmidev_ops = { + .deinit = GobiNetDeInit, + .send = GobiNetSendQMI, + .read = GobiNetThread, +}; \ No newline at end of file diff --git a/package/wwan/meig-cm/src/MPQCTL.h b/package/wwan/meig-cm/src/MPQCTL.h new file mode 100644 index 000000000..046906557 --- /dev/null +++ b/package/wwan/meig-cm/src/MPQCTL.h @@ -0,0 +1,377 @@ +/*=========================================================================== + + M P Q C T L. H +DESCRIPTION: + + This module contains QMI QCTL module. + +INITIALIZATION AND SEQUENCING REQUIREMENTS: + +Copyright (C) 2011 by Qualcomm Technologies, Incorporated. All Rights Reserved. +===========================================================================*/ + +#ifndef MPQCTL_H +#define MPQCTL_H + +#include "MPQMI.h" + +#pragma pack(push, 1) + +// ================= QMICTL ================== + +// QMICTL Control Flags +#define QMICTL_CTL_FLAG_CMD 0x00 +#define QMICTL_CTL_FLAG_RSP 0x01 +#define QMICTL_CTL_FLAG_IND 0x02 + +#if 0 +typedef struct _QMICTL_TRANSACTION_ITEM +{ + LIST_ENTRY List; + UCHAR TransactionId; // QMICTL transaction id + PVOID Context; // Adapter or IocDev + PIRP Irp; +} QMICTL_TRANSACTION_ITEM, *PQMICTL_TRANSACTION_ITEM; +#endif + +typedef struct _QCQMICTL_MSG_HDR +{ + UCHAR CtlFlags; // 00-cmd, 01-rsp, 10-ind + UCHAR TransactionId; + USHORT QMICTLType; + USHORT Length; +} __attribute__ ((packed)) QCQMICTL_MSG_HDR, *PQCQMICTL_MSG_HDR; + +#define QCQMICTL_MSG_HDR_SIZE sizeof(QCQMICTL_MSG_HDR) + +typedef struct _QCQMICTL_MSG_HDR_RESP +{ + UCHAR CtlFlags; // 00-cmd, 01-rsp, 10-ind + UCHAR TransactionId; + USHORT QMICTLType; + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} __attribute__ ((packed)) QCQMICTL_MSG_HDR_RESP, *PQCQMICTL_MSG_HDR_RESP; + +typedef struct _QCQMICTL_MSG +{ + UCHAR CtlFlags; // 00-cmd, 01-rsp, 10-ind + UCHAR TransactionId; + USHORT QMICTLType; + USHORT Length; + UCHAR Payload; +} __attribute__ ((packed)) QCQMICTL_MSG, *PQCQMICTL_MSG; + +// TLV Header +typedef struct _QCQMICTL_TLV_HDR +{ + UCHAR TLVType; + USHORT TLVLength; +} __attribute__ ((packed)) QCQMICTL_TLV_HDR, *PQCQMICTL_TLV_HDR; + +#define QCQMICTL_TLV_HDR_SIZE sizeof(QCQMICTL_TLV_HDR) + +// QMICTL Type +#define QMICTL_SET_INSTANCE_ID_REQ 0x0020 +#define QMICTL_SET_INSTANCE_ID_RESP 0x0020 +#define QMICTL_GET_VERSION_REQ 0x0021 +#define QMICTL_GET_VERSION_RESP 0x0021 +#define QMICTL_GET_CLIENT_ID_REQ 0x0022 +#define QMICTL_GET_CLIENT_ID_RESP 0x0022 +#define QMICTL_RELEASE_CLIENT_ID_REQ 0x0023 +#define QMICTL_RELEASE_CLIENT_ID_RESP 0x0023 +#define QMICTL_REVOKE_CLIENT_ID_IND 0x0024 +#define QMICTL_INVALID_CLIENT_ID_IND 0x0025 +#define QMICTL_SET_DATA_FORMAT_REQ 0x0026 +#define QMICTL_SET_DATA_FORMAT_RESP 0x0026 +#define QMICTL_SYNC_REQ 0x0027 +#define QMICTL_SYNC_RESP 0x0027 +#define QMICTL_SYNC_IND 0x0027 + +#define QMICTL_FLAG_REQUEST 0x00 +#define QMICTL_FLAG_RESPONSE 0x01 +#define QMICTL_FLAG_INDICATION 0x02 + +// QMICTL Message Definitions + +typedef struct _QMICTL_SET_INSTANCE_ID_REQ_MSG +{ + UCHAR CtlFlags; // QMICTL_FLAG_REQUEST + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_SET_INSTANCE_ID_REQ + USHORT Length; // 4 + UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLVLength; // 1 + UCHAR Value; // Host-unique QMI instance for this device driver +} __attribute__ ((packed)) QMICTL_SET_INSTANCE_ID_REQ_MSG, *PQMICTL_SET_INSTANCE_ID_REQ_MSG; + +typedef struct _QMICTL_SET_INSTANCE_ID_RESP_MSG +{ + UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_SET_INSTANCE_ID_RESP + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE + USHORT TLVLength; // 0x0004 + USHORT QMIResult; + USHORT QMIError; + UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLV2Length; // 0x0002 + USHORT QMI_ID; // Upper byte is assigned by MSM, + // lower assigned by host +} __attribute__ ((packed)) QMICTL_SET_INSTANCE_ID_RESP_MSG, *PQMICTL_SET_INSTANCE_ID_RESP_MSG; + +typedef struct _QMICTL_GET_VERSION_REQ_MSG +{ + UCHAR CtlFlags; // QMICTL_FLAG_REQUEST + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_GET_VERSION_REQ + USHORT Length; // 0 + UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLVLength; // var + UCHAR QMUXTypes; // List of one byte QMUX_TYPE values + // 0xFF returns a list of versions for all + // QMUX_TYPEs implemented on the device +} __attribute__ ((packed)) QMICTL_GET_VERSION_REQ_MSG, *PQMICTL_GET_VERSION_REQ_MSG; + +typedef struct _QMUX_TYPE_VERSION_STRUCT +{ + UCHAR QMUXType; + USHORT MajorVersion; + USHORT MinorVersion; +} __attribute__ ((packed)) QMUX_TYPE_VERSION_STRUCT, *PQMUX_TYPE_VERSION_STRUCT; + +typedef struct _ADDENDUM_VERSION_PREAMBLE +{ + UCHAR LabelLength; + UCHAR Label; +} __attribute__ ((packed)) ADDENDUM_VERSION_PREAMBLE, *PADDENDUM_VERSION_PREAMBLE; + +#define QMICTL_GET_VERSION_RSP_TLV_TYPE_VERSION 0x01 +#define QMICTL_GET_VERSION_RSP_TLV_TYPE_ADD_VERSION 0x10 + +typedef struct _QMICTL_GET_VERSION_RESP_MSG +{ + UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_GET_VERSION_RESP + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE + USHORT TLVLength; // 0x0004 + USHORT QMIResult; + USHORT QMIError; + UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLV2Length; // var + UCHAR NumElements; // Num of QMUX_TYPE_VERSION_STRUCT + QMUX_TYPE_VERSION_STRUCT TypeVersion[0]; +} __attribute__ ((packed)) QMICTL_GET_VERSION_RESP_MSG, *PQMICTL_GET_VERSION_RESP_MSG; + +typedef struct _QMICTL_GET_CLIENT_ID_REQ_MSG +{ + UCHAR CtlFlags; // QMICTL_FLAG_REQUEST + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_GET_CLIENT_ID_REQ + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLVLength; // 1 + UCHAR QMIType; // QMUX type +} __attribute__ ((packed)) QMICTL_GET_CLIENT_ID_REQ_MSG, *PQMICTL_GET_CLIENT_ID_REQ_MSG; + +typedef struct _QMICTL_GET_CLIENT_ID_RESP_MSG +{ + UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_GET_CLIENT_ID_RESP + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE + USHORT TLVLength; // 0x0004 + USHORT QMIResult; // result code + USHORT QMIError; // error code + UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLV2Length; // 2 + UCHAR QMIType; + UCHAR ClientId; +} __attribute__ ((packed)) QMICTL_GET_CLIENT_ID_RESP_MSG, *PQMICTL_GET_CLIENT_ID_RESP_MSG; + +typedef struct _QMICTL_RELEASE_CLIENT_ID_REQ_MSG +{ + UCHAR CtlFlags; // QMICTL_FLAG_REQUEST + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_RELEASE_CLIENT_ID_REQ + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLVLength; // 0x0002 + UCHAR QMIType; + UCHAR ClientId; +} __attribute__ ((packed)) QMICTL_RELEASE_CLIENT_ID_REQ_MSG, *PQMICTL_RELEASE_CLIENT_ID_REQ_MSG; + +typedef struct _QMICTL_RELEASE_CLIENT_ID_RESP_MSG +{ + UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_RELEASE_CLIENT_ID_RESP + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE + USHORT TLVLength; // 0x0004 + USHORT QMIResult; // result code + USHORT QMIError; // error code + UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLV2Length; // 2 + UCHAR QMIType; + UCHAR ClientId; +} __attribute__ ((packed)) QMICTL_RELEASE_CLIENT_ID_RESP_MSG, *PQMICTL_RELEASE_CLIENT_ID_RESP_MSG; + +typedef struct _QMICTL_REVOKE_CLIENT_ID_IND_MSG +{ + UCHAR CtlFlags; // QMICTL_FLAG_INDICATION + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLVLength; // 0x0002 + UCHAR QMIType; + UCHAR ClientId; +} __attribute__ ((packed)) QMICTL_REVOKE_CLIENT_ID_IND_MSG, *PQMICTL_REVOKE_CLIENT_ID_IND_MSG; + +typedef struct _QMICTL_INVALID_CLIENT_ID_IND_MSG +{ + UCHAR CtlFlags; // QMICTL_FLAG_INDICATION + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLVLength; // 0x0002 + UCHAR QMIType; + UCHAR ClientId; +} __attribute__ ((packed)) QMICTL_INVALID_CLIENT_ID_IND_MSG, *PQMICTL_INVALID_CLIENT_ID_IND_MSG; + +typedef struct _QMICTL_SET_DATA_FORMAT_REQ_MSG +{ + UCHAR CtlFlags; // QMICTL_FLAG_REQUEST + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_SET_DATA_FORMAT_REQ + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + USHORT TLVLength; // 1 + UCHAR DataFormat; // 0-default; 1-QoS hdr present +} __attribute__ ((packed)) QMICTL_SET_DATA_FORMAT_REQ_MSG, *PQMICTL_SET_DATA_FORMAT_REQ_MSG; + +#ifdef QC_IP_MODE +#define SET_DATA_FORMAT_TLV_TYPE_LINK_PROTO 0x10 +#define SET_DATA_FORMAT_LINK_PROTO_ETH 0x0001 +#define SET_DATA_FORMAT_LINK_PROTO_IP 0x0002 +typedef struct _QMICTL_SET_DATA_FORMAT_TLV_LINK_PROT +{ + UCHAR TLVType; // Link-Layer Protocol + USHORT TLVLength; // 2 + USHORT LinkProt; // 0x1: ETH; 0x2: IP +} QMICTL_SET_DATA_FORMAT_TLV_LINK_PROT, *PQMICTL_SET_DATA_FORMAT_TLV_LINK_PROT; + +#ifdef QCMP_UL_TLP +#define SET_DATA_FORMAT_TLV_TYPE_UL_TLP 0x11 +typedef struct _QMICTL_SET_DATA_FORMAT_TLV_UL_TLP +{ + UCHAR TLVType; // 0x11, Uplink TLP Setting + USHORT TLVLength; // 1 + UCHAR UlTlpSetting; // 0x0: Disable; 0x01: Enable +} QMICTL_SET_DATA_FORMAT_TLV_UL_TLP, *PQMICTL_SET_DATA_FORMAT_TLV_UL_TLP; +#endif // QCMP_UL_TLP + +#ifdef QCMP_DL_TLP +#define SET_DATA_FORMAT_TLV_TYPE_DL_TLP 0x13 +typedef struct _QMICTL_SET_DATA_FORMAT_TLV_DL_TLP +{ + UCHAR TLVType; // 0x11, Uplink TLP Setting + USHORT TLVLength; // 1 + UCHAR DlTlpSetting; // 0x0: Disable; 0x01: Enable +} QMICTL_SET_DATA_FORMAT_TLV_DL_TLP, *PQMICTL_SET_DATA_FORMAT_TLV_DL_TLP; +#endif // QCMP_DL_TLP + +#endif // QC_IP_MODE + +#ifdef MP_QCQOS_ENABLED +#define SET_DATA_FORMAT_TLV_TYPE_QOS_SETTING 0x12 +typedef struct _QMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING +{ + UCHAR TLVType; // 0x12, QoS setting + USHORT TLVLength; // 1 + UCHAR QosSetting; // 0x0: Disable; 0x01: Enable +} QMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING, *PQMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING; +#endif // MP_QCQOS_ENABLED + +typedef struct _QMICTL_SET_DATA_FORMAT_RESP_MSG +{ + UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_SET_DATA_FORMAT_RESP + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE + USHORT TLVLength; // 0x0004 + USHORT QMIResult; // result code + USHORT QMIError; // error code +} __attribute__ ((packed)) QMICTL_SET_DATA_FORMAT_RESP_MSG, *PQMICTL_SET_DATA_FORMAT_RESP_MSG; + +typedef struct _QMICTL_SYNC_REQ_MSG +{ + UCHAR CtlFlags; // QMICTL_FLAG_REQUEST + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_CTL_SYNC_REQ + USHORT Length; // 0 +} __attribute__ ((packed)) QMICTL_SYNC_REQ_MSG, *PQMICTL_SYNC_REQ_MSG; + +typedef struct _QMICTL_SYNC_RESP_MSG +{ + UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_CTL_SYNC_RESP + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE + USHORT TLVLength; // 0x0004 + USHORT QMIResult; + USHORT QMIError; +} __attribute__ ((packed)) QMICTL_SYNC_RESP_MSG, *PQMICTL_SYNC_RESP_MSG; + +typedef struct _QMICTL_SYNC_IND_MSG +{ + UCHAR CtlFlags; // QMICTL_FLAG_INDICATION + UCHAR TransactionId; + USHORT QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND + USHORT Length; +} __attribute__ ((packed)) QMICTL_SYNC_IND_MSG, *PQMICTL_SYNC_IND_MSG; + +typedef struct _QMICTL_MSG +{ + union + { + // Message Header + QCQMICTL_MSG_HDR QMICTLMsgHdr; + QCQMICTL_MSG_HDR_RESP QMICTLMsgHdrRsp; + + // QMICTL Message + QMICTL_SET_INSTANCE_ID_REQ_MSG SetInstanceIdReq; + QMICTL_SET_INSTANCE_ID_RESP_MSG SetInstanceIdRsp; + QMICTL_GET_VERSION_REQ_MSG GetVersionReq; + QMICTL_GET_VERSION_RESP_MSG GetVersionRsp; + QMICTL_GET_CLIENT_ID_REQ_MSG GetClientIdReq; + QMICTL_GET_CLIENT_ID_RESP_MSG GetClientIdRsp; + QMICTL_RELEASE_CLIENT_ID_REQ_MSG ReleaseClientIdReq; + QMICTL_RELEASE_CLIENT_ID_RESP_MSG ReleaseClientIdRsp; + QMICTL_REVOKE_CLIENT_ID_IND_MSG RevokeClientIdInd; + QMICTL_INVALID_CLIENT_ID_IND_MSG InvalidClientIdInd; + QMICTL_SET_DATA_FORMAT_REQ_MSG SetDataFormatReq; + QMICTL_SET_DATA_FORMAT_RESP_MSG SetDataFormatRsp; + QMICTL_SYNC_REQ_MSG SyncReq; + QMICTL_SYNC_RESP_MSG SyncRsp; + QMICTL_SYNC_IND_MSG SyncInd; + }; +} __attribute__ ((packed)) QMICTL_MSG, *PQMICTL_MSG; +#pragma pack(pop) + +#endif // MPQCTL_H diff --git a/package/wwan/meig-cm/src/MPQMI.h b/package/wwan/meig-cm/src/MPQMI.h new file mode 100644 index 000000000..64ae390e5 --- /dev/null +++ b/package/wwan/meig-cm/src/MPQMI.h @@ -0,0 +1,301 @@ +/*=========================================================================== + + M P Q M I. H +DESCRIPTION: + + This module contains forward references to the QMI module. + +INITIALIZATION AND SEQUENCING REQUIREMENTS: + +Copyright (C) 2011 by Qualcomm Technologies, Incorporated. All Rights Reserved. +===========================================================================*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + $Header: //depot/QMI/win/qcdrivers/ndis/MPQMI.h#3 $ + +when who what, where, why +-------- --- ---------------------------------------------------------- +11/20/04 hg Initial version. +===========================================================================*/ + +#ifndef USBQMI_H +#define USBQMI_H + +typedef char CHAR; +typedef unsigned char UCHAR; +typedef unsigned short USHORT; +typedef int INT; +typedef unsigned int UINT; +typedef long LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef char *PCHAR; +typedef unsigned char *PUCHAR; +typedef int *PINT; +typedef int BOOL; + +#define TRUE (1 == 1) +#define FALSE (1 != 1) + +#define QMICTL_SUPPORTED_MAJOR_VERSION 1 +#define QMICTL_SUPPORTED_MINOR_VERSION 0 + +#pragma pack(push, 1) + +// ========= USB Control Message ========== + +#define USB_CTL_MSG_TYPE_QMI 0x01 + +// USB Control Message +typedef struct _QCUSB_CTL_MSG_HDR +{ + UCHAR IFType; +} __attribute__ ((packed)) QCUSB_CTL_MSG_HDR, *PQCUSB_CTL_MSG_HDR; + +#define QCUSB_CTL_MSG_HDR_SIZE sizeof(QCUSB_CTL_MSG_HDR) + +typedef struct _QCUSB_CTL_MSG +{ + UCHAR IFType; + UCHAR Message; +} __attribute__ ((packed)) QCUSB_CTL_MSG, *PQCUSB_CTL_MSG; + +#define QCTLV_TYPE_REQUIRED_PARAMETER 0x01 +#define QCTLV_TYPE_RESULT_CODE 0x02 + +// ================= QMI ================== + +// Define QMI Type +typedef enum _QMI_SERVICE_TYPE +{ + QMUX_TYPE_CTL = 0x00, + QMUX_TYPE_WDS = 0x01, + QMUX_TYPE_DMS = 0x02, + QMUX_TYPE_NAS = 0x03, + QMUX_TYPE_QOS = 0x04, + QMUX_TYPE_WMS = 0x05, + QMUX_TYPE_PDS = 0x06, + QMUX_TYPE_UIM = 0x0B, + QMUX_TYPE_WDS_IPV6 = 0x11, + QMUX_TYPE_WDS_ADMIN = 0x1A, + QMUX_TYPE_MAX = 0xFF, + QMUX_TYPE_ALL = 0xFF +} QMI_SERVICE_TYPE; + +typedef enum _QMI_RESULT_CODE_TYPE +{ + QMI_RESULT_SUCCESS = 0x0000, + QMI_RESULT_FAILURE = 0x0001 +} QMI_RESULT_CODE_TYPE; + +typedef enum _QMI_ERROR_CODE_TYPE +{ + QMI_ERR_NONE = 0x0000 + ,QMI_ERR_MALFORMED_MSG = 0x0001 + ,QMI_ERR_NO_MEMORY = 0x0002 + ,QMI_ERR_INTERNAL = 0x0003 + ,QMI_ERR_ABORTED = 0x0004 + ,QMI_ERR_CLIENT_IDS_EXHAUSTED = 0x0005 + ,QMI_ERR_UNABORTABLE_TRANSACTION = 0x0006 + ,QMI_ERR_INVALID_CLIENT_ID = 0x0007 + ,QMI_ERR_NO_THRESHOLDS = 0x0008 + ,QMI_ERR_INVALID_HANDLE = 0x0009 + ,QMI_ERR_INVALID_PROFILE = 0x000A + ,QMI_ERR_INVALID_PINID = 0x000B + ,QMI_ERR_INCORRECT_PIN = 0x000C + ,QMI_ERR_NO_NETWORK_FOUND = 0x000D + ,QMI_ERR_CALL_FAILED = 0x000E + ,QMI_ERR_OUT_OF_CALL = 0x000F + ,QMI_ERR_NOT_PROVISIONED = 0x0010 + ,QMI_ERR_MISSING_ARG = 0x0011 + ,QMI_ERR_ARG_TOO_LONG = 0x0013 + ,QMI_ERR_INVALID_TX_ID = 0x0016 + ,QMI_ERR_DEVICE_IN_USE = 0x0017 + ,QMI_ERR_OP_NETWORK_UNSUPPORTED = 0x0018 + ,QMI_ERR_OP_DEVICE_UNSUPPORTED = 0x0019 + ,QMI_ERR_NO_EFFECT = 0x001A + ,QMI_ERR_NO_FREE_PROFILE = 0x001B + ,QMI_ERR_INVALID_PDP_TYPE = 0x001C + ,QMI_ERR_INVALID_TECH_PREF = 0x001D + ,QMI_ERR_INVALID_PROFILE_TYPE = 0x001E + ,QMI_ERR_INVALID_SERVICE_TYPE = 0x001F + ,QMI_ERR_INVALID_REGISTER_ACTION = 0x0020 + ,QMI_ERR_INVALID_PS_ATTACH_ACTION = 0x0021 + ,QMI_ERR_AUTHENTICATION_FAILED = 0x0022 + ,QMI_ERR_PIN_BLOCKED = 0x0023 + ,QMI_ERR_PIN_PERM_BLOCKED = 0x0024 + ,QMI_ERR_SIM_NOT_INITIALIZED = 0x0025 + ,QMI_ERR_MAX_QOS_REQUESTS_IN_USE = 0x0026 + ,QMI_ERR_INCORRECT_FLOW_FILTER = 0x0027 + ,QMI_ERR_NETWORK_QOS_UNAWARE = 0x0028 + ,QMI_ERR_INVALID_QOS_ID = 0x0029 + ,QMI_ERR_INVALID_ID = 0x0029 + ,QMI_ERR_REQUESTED_NUM_UNSUPPORTED = 0x002A + ,QMI_ERR_INTERFACE_NOT_FOUND = 0x002B + ,QMI_ERR_FLOW_SUSPENDED = 0x002C + ,QMI_ERR_INVALID_DATA_FORMAT = 0x002D + ,QMI_ERR_GENERAL = 0x002E + ,QMI_ERR_UNKNOWN = 0x002F + ,QMI_ERR_INVALID_ARG = 0x0030 + ,QMI_ERR_INVALID_INDEX = 0x0031 + ,QMI_ERR_NO_ENTRY = 0x0032 + ,QMI_ERR_DEVICE_STORAGE_FULL = 0x0033 + ,QMI_ERR_DEVICE_NOT_READY = 0x0034 + ,QMI_ERR_NETWORK_NOT_READY = 0x0035 + ,QMI_ERR_CAUSE_CODE = 0x0036 + ,QMI_ERR_MESSAGE_NOT_SENT = 0x0037 + ,QMI_ERR_MESSAGE_DELIVERY_FAILURE = 0x0038 + ,QMI_ERR_INVALID_MESSAGE_ID = 0x0039 + ,QMI_ERR_ENCODING = 0x003A + ,QMI_ERR_AUTHENTICATION_LOCK = 0x003B + ,QMI_ERR_INVALID_TRANSITION = 0x003C + ,QMI_ERR_NOT_A_MCAST_IFACE = 0x003D + ,QMI_ERR_MAX_MCAST_REQUESTS_IN_USE = 0x003E + ,QMI_ERR_INVALID_MCAST_HANDLE = 0x003F + ,QMI_ERR_INVALID_IP_FAMILY_PREF = 0x0040 + ,QMI_ERR_SESSION_INACTIVE = 0x0041 + ,QMI_ERR_SESSION_INVALID = 0x0042 + ,QMI_ERR_SESSION_OWNERSHIP = 0x0043 + ,QMI_ERR_INSUFFICIENT_RESOURCES = 0x0044 + ,QMI_ERR_DISABLED = 0x0045 + ,QMI_ERR_INVALID_OPERATION = 0x0046 + ,QMI_ERR_INVALID_QMI_CMD = 0x0047 + ,QMI_ERR_TPDU_TYPE = 0x0048 + ,QMI_ERR_SMSC_ADDR = 0x0049 + ,QMI_ERR_INFO_UNAVAILABLE = 0x004A + ,QMI_ERR_SEGMENT_TOO_LONG = 0x004B + ,QMI_ERR_SEGMENT_ORDER = 0x004C + ,QMI_ERR_BUNDLING_NOT_SUPPORTED = 0x004D + ,QMI_ERR_OP_PARTIAL_FAILURE = 0x004E + ,QMI_ERR_POLICY_MISMATCH = 0x004F + ,QMI_ERR_SIM_FILE_NOT_FOUND = 0x0050 + ,QMI_ERR_EXTENDED_INTERNAL = 0x0051 + ,QMI_ERR_ACCESS_DENIED = 0x0052 + ,QMI_ERR_HARDWARE_RESTRICTED = 0x0053 + ,QMI_ERR_ACK_NOT_SENT = 0x0054 + ,QMI_ERR_INJECT_TIMEOUT = 0x0055 + ,QMI_ERR_INCOMPATIBLE_STATE = 0x005A + ,QMI_ERR_FDN_RESTRICT = 0x005B + ,QMI_ERR_SUPS_FAILURE_CAUSE = 0x005C + ,QMI_ERR_NO_RADIO = 0x005D + ,QMI_ERR_NOT_SUPPORTED = 0x005E + ,QMI_ERR_NO_SUBSCRIPTION = 0x005F + ,QMI_ERR_CARD_CALL_CONTROL_FAILED = 0x0060 + ,QMI_ERR_NETWORK_ABORTED = 0x0061 + ,QMI_ERR_MSG_BLOCKED = 0x0062 + ,QMI_ERR_INVALID_SESSION_TYPE = 0x0064 + ,QMI_ERR_INVALID_PB_TYPE = 0x0065 + ,QMI_ERR_NO_SIM = 0x0066 + ,QMI_ERR_PB_NOT_READY = 0x0067 + ,QMI_ERR_PIN_RESTRICTION = 0x0068 + ,QMI_ERR_PIN2_RESTRICTION = 0x0069 + ,QMI_ERR_PUK_RESTRICTION = 0x006A + ,QMI_ERR_PUK2_RESTRICTION = 0x006B + ,QMI_ERR_PB_ACCESS_RESTRICTED = 0x006C + ,QMI_ERR_PB_DELETE_IN_PROG = 0x006D + ,QMI_ERR_PB_TEXT_TOO_LONG = 0x006E + ,QMI_ERR_PB_NUMBER_TOO_LONG = 0x006F + ,QMI_ERR_PB_HIDDEN_KEY_RESTRICTION = 0x0070 +} QMI_ERROR_CODE_TYPE; + +#define QCQMI_CTL_FLAG_SERVICE 0x80 +#define QCQMI_CTL_FLAG_CTL_POINT 0x00 + +typedef struct _QCQMI_HDR +{ + UCHAR IFType; + USHORT Length; + UCHAR CtlFlags; // reserved + UCHAR QMIType; + UCHAR ClientId; +} __attribute__ ((packed)) QCQMI_HDR, *PQCQMI_HDR; + +#define QCQMI_HDR_SIZE (sizeof(QCQMI_HDR)-1) + +typedef struct _QCQMI +{ + UCHAR IFType; + USHORT Length; + UCHAR CtlFlags; // reserved + UCHAR QMIType; + UCHAR ClientId; + UCHAR SDU; +} __attribute__ ((packed)) QCQMI, *PQCQMI; + +typedef struct _QMI_SERVICE_VERSION +{ + USHORT Major; + USHORT Minor; + USHORT AddendumMajor; + USHORT AddendumMinor; +} __attribute__ ((packed)) QMI_SERVICE_VERSION, *PQMI_SERVICE_VERSION; + +// ================= QMUX ================== + +#define QMUX_MSG_OVERHEAD_BYTES 4 // Type(USHORT) Length(USHORT) -- header + +#define QMUX_BROADCAST_CID 0xFF + +typedef struct _QCQMUX_HDR +{ + UCHAR CtlFlags; // 0: single QMUX Msg; 1: + USHORT TransactionId; +} __attribute__ ((packed)) QCQMUX_HDR, *PQCQMUX_HDR; + +typedef struct _QCQMUX +{ + UCHAR CtlFlags; // 0: single QMUX Msg; 1: + USHORT TransactionId; + UCHAR Message; // Type(2), Length(2), Value +} __attribute__ ((packed)) QCQMUX, *PQCQMUX; + +#define QCQMUX_HDR_SIZE sizeof(QCQMUX_HDR) + +typedef struct _QCQMUX_MSG_HDR +{ + USHORT Type; + USHORT Length; +} __attribute__ ((packed)) QCQMUX_MSG_HDR, *PQCQMUX_MSG_HDR; + +#define QCQMUX_MSG_HDR_SIZE sizeof(QCQMUX_MSG_HDR) + +typedef struct _QCQMUX_MSG_HDR_RESP +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} __attribute__ ((packed)) QCQMUX_MSG_HDR_RESP, *PQCQMUX_MSG_HDR_RESP; + +typedef struct _QCQMUX_TLV +{ + UCHAR Type; + USHORT Length; + UCHAR Value; +} __attribute__ ((packed)) QCQMUX_TLV, *PQCQMUX_TLV; + +typedef struct _QMI_TLV_HDR +{ + UCHAR TLVType; + USHORT TLVLength; +} __attribute__ ((packed)) QMI_TLV_HDR, *PQMI_TLV_HDR; + +// QMUX Message Definitions -- QMI SDU +#define QMUX_CTL_FLAG_SINGLE_MSG 0x00 +#define QMUX_CTL_FLAG_COMPOUND_MSG 0x01 +#define QMUX_CTL_FLAG_TYPE_CMD 0x00 +#define QMUX_CTL_FLAG_TYPE_RSP 0x02 +#define QMUX_CTL_FLAG_TYPE_IND 0x04 +#define QMUX_CTL_FLAG_MASK_COMPOUND 0x01 +#define QMUX_CTL_FLAG_MASK_TYPE 0x06 // 00-cmd, 01-rsp, 10-ind + +#pragma pack(pop) + +#endif // USBQMI_H diff --git a/package/wwan/meig-cm/src/MPQMUX.c b/package/wwan/meig-cm/src/MPQMUX.c new file mode 100644 index 000000000..6d0880d12 --- /dev/null +++ b/package/wwan/meig-cm/src/MPQMUX.c @@ -0,0 +1,426 @@ +#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), +}; + +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_nas_Type[] = { +// ======================= NAS ============================== +qmi_name_item(QMINAS_SET_EVENT_REPORT_REQ), // 0x0002 +qmi_name_item(QMINAS_SET_EVENT_REPORT_RESP), // 0x0002 +qmi_name_item(QMINAS_EVENT_REPORT_IND), // 0x0002 +qmi_name_item(QMINAS_GET_SIGNAL_STRENGTH_REQ), // 0x0020 +qmi_name_item(QMINAS_GET_SIGNAL_STRENGTH_RESP), // 0x0020 +qmi_name_item(QMINAS_PERFORM_NETWORK_SCAN_REQ), // 0x0021 +qmi_name_item(QMINAS_PERFORM_NETWORK_SCAN_RESP), // 0x0021 +qmi_name_item(QMINAS_INITIATE_NW_REGISTER_REQ), // 0x0022 +qmi_name_item(QMINAS_INITIATE_NW_REGISTER_RESP), // 0x0022 +qmi_name_item(QMINAS_INITIATE_ATTACH_REQ), // 0x0023 +qmi_name_item(QMINAS_INITIATE_ATTACH_RESP), // 0x0023 +qmi_name_item(QMINAS_GET_SERVING_SYSTEM_REQ), // 0x0024 +qmi_name_item(QMINAS_GET_SERVING_SYSTEM_RESP), // 0x0024 +qmi_name_item(QMINAS_SERVING_SYSTEM_IND), // 0x0024 +qmi_name_item(QMINAS_GET_HOME_NETWORK_REQ), // 0x0025 +qmi_name_item(QMINAS_GET_HOME_NETWORK_RESP), // 0x0025 +qmi_name_item(QMINAS_GET_PREFERRED_NETWORK_REQ), // 0x0026 +qmi_name_item(QMINAS_GET_PREFERRED_NETWORK_RESP), // 0x0026 +qmi_name_item(QMINAS_SET_PREFERRED_NETWORK_REQ), // 0x0027 +qmi_name_item(QMINAS_SET_PREFERRED_NETWORK_RESP), // 0x0027 +qmi_name_item(QMINAS_GET_FORBIDDEN_NETWORK_REQ), // 0x0028 +qmi_name_item(QMINAS_GET_FORBIDDEN_NETWORK_RESP), // 0x0028 +qmi_name_item(QMINAS_SET_FORBIDDEN_NETWORK_REQ), // 0x0029 +qmi_name_item(QMINAS_SET_FORBIDDEN_NETWORK_RESP), // 0x0029 +qmi_name_item(QMINAS_SET_TECHNOLOGY_PREF_REQ), // 0x002A +qmi_name_item(QMINAS_SET_TECHNOLOGY_PREF_RESP), // 0x002A +qmi_name_item(QMINAS_GET_RF_BAND_INFO_REQ), // 0x0031 +qmi_name_item(QMINAS_GET_RF_BAND_INFO_RESP), // 0x0031 +qmi_name_item(QMINAS_GET_PLMN_NAME_REQ), // 0x0044 +qmi_name_item(QMINAS_GET_PLMN_NAME_RESP), // 0x0044 +qmi_name_item(MEIG_PACKET_TRANSFER_START_IND), // 0X100 +qmi_name_item(MEIG_PACKET_TRANSFER_END_IND), // 0X101 +qmi_name_item(QMINAS_GET_SYS_INFO_REQ), // 0x004D +qmi_name_item(QMINAS_GET_SYS_INFO_RESP), // 0x004D +qmi_name_item(QMINAS_SYS_INFO_IND), // 0x004D +}; + +static const QMI_NAME_T qmux_wms_Type[] = { +// ======================= WMS ============================== +qmi_name_item(QMIWMS_SET_EVENT_REPORT_REQ), // 0x0001 +qmi_name_item(QMIWMS_SET_EVENT_REPORT_RESP), // 0x0001 +qmi_name_item(QMIWMS_EVENT_REPORT_IND), // 0x0001 +qmi_name_item(QMIWMS_RAW_SEND_REQ), // 0x0020 +qmi_name_item(QMIWMS_RAW_SEND_RESP), // 0x0020 +qmi_name_item(QMIWMS_RAW_WRITE_REQ), // 0x0021 +qmi_name_item(QMIWMS_RAW_WRITE_RESP), // 0x0021 +qmi_name_item(QMIWMS_RAW_READ_REQ), // 0x0022 +qmi_name_item(QMIWMS_RAW_READ_RESP), // 0x0022 +qmi_name_item(QMIWMS_MODIFY_TAG_REQ), // 0x0023 +qmi_name_item(QMIWMS_MODIFY_TAG_RESP), // 0x0023 +qmi_name_item(QMIWMS_DELETE_REQ), // 0x0024 +qmi_name_item(QMIWMS_DELETE_RESP), // 0x0024 +qmi_name_item(QMIWMS_GET_MESSAGE_PROTOCOL_REQ), // 0x0030 +qmi_name_item(QMIWMS_GET_MESSAGE_PROTOCOL_RESP), // 0x0030 +qmi_name_item(QMIWMS_LIST_MESSAGES_REQ), // 0x0031 +qmi_name_item(QMIWMS_LIST_MESSAGES_RESP), // 0x0031 +qmi_name_item(QMIWMS_GET_SMSC_ADDRESS_REQ), // 0x0034 +qmi_name_item(QMIWMS_GET_SMSC_ADDRESS_RESP), // 0x0034 +qmi_name_item(QMIWMS_SET_SMSC_ADDRESS_REQ), // 0x0035 +qmi_name_item(QMIWMS_SET_SMSC_ADDRESS_RESP), // 0x0035 +qmi_name_item(QMIWMS_GET_STORE_MAX_SIZE_REQ), // 0x0036 +qmi_name_item(QMIWMS_GET_STORE_MAX_SIZE_RESP), // 0x0036 +}; + +static const QMI_NAME_T qmux_wds_admin_Type[] = { +qmi_name_item(QMIWDS_ADMIN_SET_DATA_FORMAT_REQ), // 0x0020 +qmi_name_item(QMIWDS_ADMIN_SET_DATA_FORMAT_RESP), // 0x0020 +qmi_name_item(QMIWDS_ADMIN_GET_DATA_FORMAT_REQ), // 0x0021 +qmi_name_item(QMIWDS_ADMIN_GET_DATA_FORMAT_RESP), // 0x0021 +qmi_name_item(QMIWDS_ADMIN_SET_QMAP_SETTINGS_REQ), // 0x002B +qmi_name_item(QMIWDS_ADMIN_SET_QMAP_SETTINGS_RESP), // 0x002B +qmi_name_item(QMIWDS_ADMIN_GET_QMAP_SETTINGS_REQ), // 0x002C +qmi_name_item(QMIWDS_ADMIN_GET_QMAP_SETTINGS_RESP), // 0x002C +}; + +static const QMI_NAME_T qmux_uim_Type[] = { +qmi_name_item( QMIUIM_READ_TRANSPARENT_REQ), // 0x0020 +qmi_name_item( QMIUIM_READ_TRANSPARENT_RESP), // 0x0020 +qmi_name_item( QMIUIM_READ_TRANSPARENT_IND), // 0x0020 +qmi_name_item( QMIUIM_READ_RECORD_REQ), // 0x0021 +qmi_name_item( QMIUIM_READ_RECORD_RESP), // 0x0021 +qmi_name_item( QMIUIM_READ_RECORD_IND), // 0x0021 +qmi_name_item( QMIUIM_WRITE_TRANSPARENT_REQ), // 0x0022 +qmi_name_item( QMIUIM_WRITE_TRANSPARENT_RESP), // 0x0022 +qmi_name_item( QMIUIM_WRITE_TRANSPARENT_IND), // 0x0022 +qmi_name_item( QMIUIM_WRITE_RECORD_REQ), // 0x0023 +qmi_name_item( QMIUIM_WRITE_RECORD_RESP), // 0x0023 +qmi_name_item( QMIUIM_WRITE_RECORD_IND), // 0x0023 +qmi_name_item( QMIUIM_SET_PIN_PROTECTION_REQ), // 0x0025 +qmi_name_item( QMIUIM_SET_PIN_PROTECTION_RESP), // 0x0025 +qmi_name_item( QMIUIM_SET_PIN_PROTECTION_IND), // 0x0025 +qmi_name_item( QMIUIM_VERIFY_PIN_REQ), // 0x0026 +qmi_name_item( QMIUIM_VERIFY_PIN_RESP), // 0x0026 +qmi_name_item( QMIUIM_VERIFY_PIN_IND), // 0x0026 +qmi_name_item( QMIUIM_UNBLOCK_PIN_REQ), // 0x0027 +qmi_name_item( QMIUIM_UNBLOCK_PIN_RESP), // 0x0027 +qmi_name_item( QMIUIM_UNBLOCK_PIN_IND), // 0x0027 +qmi_name_item( QMIUIM_CHANGE_PIN_REQ), // 0x0028 +qmi_name_item( QMIUIM_CHANGE_PIN_RESP), // 0x0028 +qmi_name_item( QMIUIM_CHANGE_PIN_IND), // 0x0028 +qmi_name_item( QMIUIM_DEPERSONALIZATION_REQ), // 0x0029 +qmi_name_item( QMIUIM_DEPERSONALIZATION_RESP), // 0x0029 +qmi_name_item( QMIUIM_EVENT_REG_REQ), // 0x002E +qmi_name_item( QMIUIM_EVENT_REG_RESP), // 0x002E +qmi_name_item( QMIUIM_GET_CARD_STATUS_REQ), // 0x002F +qmi_name_item( QMIUIM_GET_CARD_STATUS_RESP), // 0x002F +qmi_name_item( QMIUIM_STATUS_CHANGE_IND), // 0x0032 +}; + +static const char * qmi_name_get(const QMI_NAME_T *table, size_t size, int type, const char *tag) { + static char unknow[40]; + size_t i; + + if (qmux_CtlFlags == table) { + if (!strcmp(tag, "_REQ")) + tag = "_CMD"; + else if (!strcmp(tag, "_RESP")) + tag = "_RSP"; + } + + for (i = 0; i < size; i++) { + if (table[i].type == (UINT)type) { + if (!tag || (strstr(table[i].name, tag))) + return table[i].name; + } + } + sprintf(unknow, "unknow_%x", type); + return unknow; +} + +#define QMI_NAME(table, type) qmi_name_get(table, sizeof(table) / sizeof(table[0]), type, 0) +#define QMUX_NAME(table, type, tag) qmi_name_get(table, sizeof(table) / sizeof(table[0]), type, tag) + +void dump_tlv(PQCQMUX_MSG_HDR pQMUXMsgHdr) { + int TLVFind = 0; + int i; + //dbg("QCQMUX_TLV-----------------------------------\n"); + //dbg("{Type,\tLength,\tValue}\n"); + + while (1) { + PQMI_TLV_HDR TLVHdr = GetTLV(pQMUXMsgHdr, 0x1000 + (++TLVFind)); + if (TLVHdr == NULL) + break; + + //if ((TLVHdr->TLVType == 0x02) && ((USHORT *)(TLVHdr+1))[0]) + { + dbg("{%02x,\t%04x,\t", TLVHdr->TLVType, le16_to_cpu(TLVHdr->TLVLength)); + for (i = 0; i < le16_to_cpu(TLVHdr->TLVLength); i++) { + dbg("%02x ", ((UCHAR *)(TLVHdr+1))[i]); + } + dbg("}\n"); + } + } // while +} + +void dump_ctl(PQCQMICTL_MSG_HDR CTLHdr) { + const char *tag; + + //dbg("QCQMICTL_MSG--------------------------------------------\n"); + //dbg("CtlFlags: %02x\t\t%s\n", CTLHdr->CtlFlags, QMI_NAME(qmi_ctl_CtlFlags, CTLHdr->CtlFlags)); + dbg("TransactionId: %02x\n", CTLHdr->TransactionId); + switch (CTLHdr->CtlFlags) { + case QMICTL_FLAG_REQUEST: tag = "_REQ"; break; + case QMICTL_FLAG_RESPONSE: tag = "_RESP"; break; + case QMICTL_FLAG_INDICATION: tag = "_IND"; break; + default: tag = 0; break; + } + dbg("QMICTLType: %04x\t%s\n", le16_to_cpu(CTLHdr->QMICTLType), + QMUX_NAME(qmux_ctl_QMICTLType, le16_to_cpu(CTLHdr->QMICTLType), tag)); + dbg("Length: %04x\n", le16_to_cpu(CTLHdr->Length)); + + dump_tlv((PQCQMUX_MSG_HDR)(&CTLHdr->QMICTLType)); +} + +int dump_qmux(QMI_SERVICE_TYPE serviceType, PQCQMUX_HDR QMUXHdr) { + PQCQMUX_MSG_HDR QMUXMsgHdr = (PQCQMUX_MSG_HDR) (QMUXHdr + 1); + CHAR *tag; + + //dbg("QCQMUX--------------------------------------------\n"); + switch (QMUXHdr->CtlFlags&QMUX_CTL_FLAG_MASK_TYPE) { + case QMUX_CTL_FLAG_TYPE_CMD: tag = "_REQ"; break; + case QMUX_CTL_FLAG_TYPE_RSP: tag = "_RESP"; break; + case QMUX_CTL_FLAG_TYPE_IND: tag = "_IND"; break; + default: tag = 0; break; + } + //dbg("CtlFlags: %02x\t\t%s\n", QMUXHdr->CtlFlags, QMUX_NAME(qmux_CtlFlags, QMUXHdr->CtlFlags, tag)); + dbg("TransactionId: %04x\n", le16_to_cpu(QMUXHdr->TransactionId)); + + //dbg("QCQMUX_MSG_HDR-----------------------------------\n"); + switch (serviceType) { + case QMUX_TYPE_DMS: + dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), + QMUX_NAME(qmux_dms_Type, le16_to_cpu(QMUXMsgHdr->Type), tag)); + break; + case QMUX_TYPE_NAS: + dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), + QMUX_NAME(qmux_nas_Type, le16_to_cpu(QMUXMsgHdr->Type), tag)); + break; + case QMUX_TYPE_WDS: + dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), + QMUX_NAME(qmux_wds_Type, le16_to_cpu(QMUXMsgHdr->Type), tag)); + break; + case QMUX_TYPE_WMS: + dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), + QMUX_NAME(qmux_wms_Type, le16_to_cpu(QMUXMsgHdr->Type), tag)); + break; + case QMUX_TYPE_WDS_ADMIN: + dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), + QMUX_NAME(qmux_wds_admin_Type, le16_to_cpu(QMUXMsgHdr->Type), tag)); + break; + case QMUX_TYPE_UIM: + dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), + QMUX_NAME(qmux_uim_Type, le16_to_cpu(QMUXMsgHdr->Type), tag)); + break; + case QMUX_TYPE_PDS: + case QMUX_TYPE_QOS: + case QMUX_TYPE_CTL: + default: + dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), "PDS/QOS/CTL/unknown!"); + break; + } + dbg("Length: %04x\n", le16_to_cpu(QMUXMsgHdr->Length)); + + dump_tlv(QMUXMsgHdr); + + return 0; +} + +void dump_qmi(void *dataBuffer, int dataLen) +{ + PQCQMI_HDR QMIHdr = (PQCQMI_HDR)dataBuffer; + PQCQMUX_HDR QMUXHdr = (PQCQMUX_HDR) (QMIHdr + 1); + PQCQMICTL_MSG_HDR CTLHdr = (PQCQMICTL_MSG_HDR) (QMIHdr + 1); + + int i; + + if (!debug_qmi) + return; + + pthread_mutex_lock(&dumpQMIMutex); + line[0] = 0; + for (i = 0; i < dataLen; i++) { + dbg("%02x ", ((unsigned char *)dataBuffer)[i]); + } + dbg_time("%s", line); + line[0] = 0; + + //dbg("QCQMI_HDR-----------------------------------------"); + //dbg("IFType: %02x\t\t%s", QMIHdr->IFType, QMI_NAME(qmi_IFType, QMIHdr->IFType)); + //dbg("Length: %04x", le16_to_cpu(QMIHdr->Length)); + //dbg("CtlFlags: %02x\t\t%s", QMIHdr->CtlFlags, QMI_NAME(qmi_CtlFlags, QMIHdr->CtlFlags)); + //dbg("QMIType: %02x\t\t%s", QMIHdr->QMIType, QMI_NAME(qmi_QMIType, QMIHdr->QMIType)); + //dbg("ClientId: %02x", QMIHdr->ClientId); + + if (QMIHdr->QMIType == QMUX_TYPE_CTL) { + dump_ctl(CTLHdr); + } else { + dump_qmux(QMIHdr->QMIType, QMUXHdr); + } + dbg_time("%s", line); + pthread_mutex_unlock(&dumpQMIMutex); +} diff --git a/package/wwan/meig-cm/src/MPQMUX.h b/package/wwan/meig-cm/src/MPQMUX.h new file mode 100644 index 000000000..12993907f --- /dev/null +++ b/package/wwan/meig-cm/src/MPQMUX.h @@ -0,0 +1,3322 @@ +/*=========================================================================== + + M P Q M U X. H +DESCRIPTION: + + This file provides support for QMUX. + +INITIALIZATION AND SEQUENCING REQUIREMENTS: + +Copyright (C) 2011 by Qualcomm Technologies, Incorporated. All Rights Reserved. +===========================================================================*/ + +#ifndef MPQMUX_H +#define MPQMUX_H + +#include "MPQMI.h" + +#pragma pack(push, 1) + +#define QMIWDS_SET_EVENT_REPORT_REQ 0x0001 +#define QMIWDS_SET_EVENT_REPORT_RESP 0x0001 +#define QMIWDS_EVENT_REPORT_IND 0x0001 +#define QMIWDS_START_NETWORK_INTERFACE_REQ 0x0020 +#define QMIWDS_START_NETWORK_INTERFACE_RESP 0x0020 +#define QMIWDS_STOP_NETWORK_INTERFACE_REQ 0x0021 +#define QMIWDS_STOP_NETWORK_INTERFACE_RESP 0x0021 +#define QMIWDS_GET_PKT_SRVC_STATUS_REQ 0x0022 +#define QMIWDS_GET_PKT_SRVC_STATUS_RESP 0x0022 +#define QMIWDS_GET_PKT_SRVC_STATUS_IND 0x0022 +#define QMIWDS_GET_CURRENT_CHANNEL_RATE_REQ 0x0023 +#define QMIWDS_GET_CURRENT_CHANNEL_RATE_RESP 0x0023 +#define QMIWDS_GET_PKT_STATISTICS_REQ 0x0024 +#define QMIWDS_GET_PKT_STATISTICS_RESP 0x0024 +#define QMIWDS_MODIFY_PROFILE_SETTINGS_REQ 0x0028 +#define QMIWDS_MODIFY_PROFILE_SETTINGS_RESP 0x0028 +#define QMIWDS_GET_PROFILE_SETTINGS_REQ 0x002B +#define QMIWDS_GET_PROFILE_SETTINGS_RESP 0x002B +#define QMIWDS_GET_DEFAULT_SETTINGS_REQ 0x002C +#define QMIWDS_GET_DEFAULT_SETTINGS_RESP 0x002C +#define QMIWDS_GET_RUNTIME_SETTINGS_REQ 0x002D +#define QMIWDS_GET_RUNTIME_SETTINGS_RESP 0x002D +#define QMIWDS_GET_MIP_MODE_REQ 0x002F +#define QMIWDS_GET_MIP_MODE_RESP 0x002F +#define QMIWDS_GET_DATA_BEARER_REQ 0x0037 +#define QMIWDS_GET_DATA_BEARER_RESP 0x0037 +#define QMIWDS_DUN_CALL_INFO_REQ 0x0038 +#define QMIWDS_DUN_CALL_INFO_RESP 0x0038 +#define QMIWDS_DUN_CALL_INFO_IND 0x0038 +#define QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ 0x004D +#define QMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP 0x004D +#define QMIWDS_SET_AUTO_CONNECT_REQ 0x0051 +#define QMIWDS_SET_AUTO_CONNECT_RESP 0x0051 +#define QMIWDS_BIND_MUX_DATA_PORT_REQ 0x00A2 +#define QMIWDS_BIND_MUX_DATA_PORT_RESP 0x00A2 + + +// Stats masks +#define QWDS_STAT_MASK_TX_PKT_OK 0x00000001 +#define QWDS_STAT_MASK_RX_PKT_OK 0x00000002 +#define QWDS_STAT_MASK_TX_PKT_ER 0x00000004 +#define QWDS_STAT_MASK_RX_PKT_ER 0x00000008 +#define QWDS_STAT_MASK_TX_PKT_OF 0x00000010 +#define QWDS_STAT_MASK_RX_PKT_OF 0x00000020 + +// TLV Types for xfer statistics +#define TLV_WDS_TX_GOOD_PKTS 0x10 +#define TLV_WDS_RX_GOOD_PKTS 0x11 +#define TLV_WDS_TX_ERROR 0x12 +#define TLV_WDS_RX_ERROR 0x13 +#define TLV_WDS_TX_OVERFLOW 0x14 +#define TLV_WDS_RX_OVERFLOW 0x15 +#define TLV_WDS_CHANNEL_RATE 0x16 +#define TLV_WDS_DATA_BEARER 0x17 +#define TLV_WDS_DORMANCY_STATUS 0x18 + +#define QWDS_PKT_DATA_DISCONNECTED 0x01 +#define QWDS_PKT_DATA_CONNECTED 0x02 +#define QWDS_PKT_DATA_SUSPENDED 0x03 +#define QWDS_PKT_DATA_AUTHENTICATING 0x04 + +#define QMIWDS_ADMIN_SET_DATA_FORMAT_REQ 0x0020 +#define QMIWDS_ADMIN_SET_DATA_FORMAT_RESP 0x0020 +#define QMIWDS_ADMIN_GET_DATA_FORMAT_REQ 0x0021 +#define QMIWDS_ADMIN_GET_DATA_FORMAT_RESP 0x0021 +#define QMIWDS_ADMIN_SET_QMAP_SETTINGS_REQ 0x002B +#define QMIWDS_ADMIN_SET_QMAP_SETTINGS_RESP 0x002B +#define QMIWDS_ADMIN_GET_QMAP_SETTINGS_REQ 0x002C +#define QMIWDS_ADMIN_GET_QMAP_SETTINGS_RESP 0x002C + +#define NETWORK_DESC_ENCODING_OCTET 0x00 +#define NETWORK_DESC_ENCODING_EXTPROTOCOL 0x01 +#define NETWORK_DESC_ENCODING_7BITASCII 0x02 +#define NETWORK_DESC_ENCODING_IA5 0x03 +#define NETWORK_DESC_ENCODING_UNICODE 0x04 +#define NETWORK_DESC_ENCODING_SHIFTJIS 0x05 +#define NETWORK_DESC_ENCODING_KOREAN 0x06 +#define NETWORK_DESC_ENCODING_LATINH 0x07 +#define NETWORK_DESC_ENCODING_LATIN 0x08 +#define NETWORK_DESC_ENCODING_GSM7BIT 0x09 +#define NETWORK_DESC_ENCODING_GSMDATA 0x0A +#define NETWORK_DESC_ENCODING_UNKNOWN 0xFF + +typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT +{ + USHORT Type; // QMUX type 0x0000 + USHORT Length; +} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT, *PQMIWDS_ADMIN_SET_DATA_FORMAT; + +typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR QOSSetting; +} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS, *PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS; + +typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_TLV +{ + UCHAR TLVType; + USHORT TLVLength; + ULONG Value; +} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_TLV, *PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV; + +typedef struct _QMIWDS_ENDPOINT_TLV +{ + UCHAR TLVType; + USHORT TLVLength; + ULONG ep_type; + ULONG iface_id; +} __attribute__ ((packed)) QMIWDS_ENDPOINT_TLV, *PQMIWDS_ENDPOINT_TLV; + + +typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG +{ + USHORT Type; + USHORT Length; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS QosDataFormatTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UnderlyingLinkLayerProtocolTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationProtocolTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationProtocolTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationMaxDatagramsTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationMaxSizeTlv; +#if 0 + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationMaxDatagramsTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationMaxSizeTlv; +#else + QMIWDS_ENDPOINT_TLV epTlv; +#endif +} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG, *PQMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG; + +#if 0 +typedef enum _QMI_RETURN_CODES { + QMI_SUCCESS = 0, + QMI_SUCCESS_NOT_COMPLETE, + QMI_FAILURE +}QMI_RETURN_CODES; + +typedef struct _QMIWDS_GET_PKT_SRVC_STATUS_REQ_MSG +{ + USHORT Type; // 0x0022 + USHORT Length; // 0x0000 +} QMIWDS_GET_PKT_SRVC_STATUS_REQ_MSG, *PQMIWDS_GET_PKT_SRVC_STATUS_REQ_MSG; + +typedef struct _QMIWDS_GET_PKT_SRVC_STATUS_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLVType2; + USHORT TLVLength2; + UCHAR ConnectionStatus; // 0x01: QWDS_PKT_DATAC_DISCONNECTED + // 0x02: QWDS_PKT_DATA_CONNECTED + // 0x03: QWDS_PKT_DATA_SUSPENDED + // 0x04: QWDS_PKT_DATA_AUTHENTICATING +} QMIWDS_GET_PKT_SRVC_STATUS_RESP_MSG, *PQMIWDS_GET_PKT_SRVC_STATUS_RESP_MSG; + +typedef struct _QMIWDS_GET_PKT_SRVC_STATUS_IND_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR ConnectionStatus; // 0x01: QWDS_PKT_DATAC_DISCONNECTED + // 0x02: QWDS_PKT_DATA_CONNECTED + // 0x03: QWDS_PKT_DATA_SUSPENDED + UCHAR ReconfigRequired; // 0x00: No need to reconfigure + // 0x01: Reconfiguration required +} QMIWDS_GET_PKT_SRVC_STATUS_IND_MSG, *PQMIWDS_GET_PKT_SRVC_STATUS_IND_MSG; + +typedef struct _WDS_PKT_SRVC_IP_FAMILY_TLV +{ + UCHAR TLVType; // 0x12 + USHORT TLVLength; // 1 + UCHAR IpFamily; // IPV4-0x04, IPV6-0x06 +} WDS_PKT_SRVC_IP_FAMILY_TLV, *PWDS_PKT_SRVC_IP_FAMILY_TLV; + +typedef struct _QMIWDS_DUN_CALL_INFO_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + ULONG Mask; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR ReportConnectionStatus; +} QMIWDS_DUN_CALL_INFO_REQ_MSG, *PQMIWDS_DUN_CALL_INFO_REQ_MSG; + +typedef struct _QMIWDS_DUN_CALL_INFO_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} QMIWDS_DUN_CALL_INFO_RESP_MSG, *PQMIWDS_DUN_CALL_INFO_RESP_MSG; + +typedef struct _QMIWDS_DUN_CALL_INFO_IND_MSG +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR ConnectionStatus; +} QMIWDS_DUN_CALL_INFO_IND_MSG, *PQMIWDS_DUN_CALL_INFO_IND_MSG; + +typedef struct _QMIWDS_GET_CURRENT_CHANNEL_RATE_REQ_MSG +{ + USHORT Type; // QMUX type 0x0040 + USHORT Length; +} QMIWDS_GET_CURRENT_CHANNEL_RATE_REQ_MSG, *PQMIWDS_GET_CURRENT_CHANNEL_RATE_REQ_MSG; + +typedef struct _QMIWDS_GET_CURRENT_CHANNEL_RATE_RESP_MSG +{ + USHORT Type; // QMUX type 0x0040 + USHORT Length; + UCHAR TLVType; // 0x02 + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + + UCHAR TLV2Type; // 0x01 + USHORT TLV2Length; // 16 + //ULONG CallHandle; // Context corresponding to reported channel + ULONG CurrentTxRate; // bps + ULONG CurrentRxRate; // bps + ULONG ServingSystemTxRate; // bps + ULONG ServingSystemRxRate; // bps + +} QMIWDS_GET_CURRENT_CHANNEL_RATE_RESP_MSG, *PQMIWDS_GET_CURRENT_CHANNEL_RATE_RESP; + +#define QWDS_EVENT_REPORT_MASK_RATES 0x01 +#define QWDS_EVENT_REPORT_MASK_STATS 0x02 + +#ifdef QCUSB_MUX_PROTOCOL +#error code not present +#endif // QCUSB_MUX_PROTOCOL + +typedef struct _QMIWDS_SET_EVENT_REPORT_REQ_MSG +{ + USHORT Type; // QMUX type 0x0042 + USHORT Length; + + UCHAR TLVType; // 0x10 -- current channel rate indicator + USHORT TLVLength; // 1 + UCHAR Mode; // 0-do not report; 1-report when rate changes + + UCHAR TLV2Type; // 0x11 + USHORT TLV2Length; // 5 + UCHAR StatsPeriod; // seconds between reports; 0-do not report + ULONG StatsMask; // + + UCHAR TLV3Type; // 0x12 -- current data bearer indicator + USHORT TLV3Length; // 1 + UCHAR Mode3; // 0-do not report; 1-report when changes + + UCHAR TLV4Type; // 0x13 -- dormancy status indicator + USHORT TLV4Length; // 1 + UCHAR DormancyStatus; // 0-do not report; 1-report when changes +} QMIWDS_SET_EVENT_REPORT_REQ_MSG, *PQMIWDS_SET_EVENT_REPORT_REQ_MSG; + +typedef struct _QMIWDS_SET_EVENT_REPORT_RESP_MSG +{ + USHORT Type; // QMUX type 0x0042 + USHORT Length; + + UCHAR TLVType; // 0x02 result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_NO_BATTERY + // QMI_ERR_FAULT +} QMIWDS_SET_EVENT_REPORT_RESP_MSG, *PQMIWDS_SET_EVENT_REPORT_RESP_MSG; + +typedef struct _QMIWDS_EVENT_REPORT_IND_MSG +{ + USHORT Type; // QMUX type 0x0001 + USHORT Length; +} QMIWDS_EVENT_REPORT_IND_MSG, *PQMIWDS_EVENT_REPORT_IND_MSG; + +// PQCTLV_PKT_STATISTICS + +typedef struct _QMIWDS_EVENT_REPORT_IND_CHAN_RATE_TLV +{ + UCHAR Type; + USHORT Length; // 8 + ULONG TxRate; + ULONG RxRate; +} QMIWDS_EVENT_REPORT_IND_CHAN_RATE_TLV, *PQMIWDS_EVENT_REPORT_IND_CHAN_RATE_TLV; + +#ifdef QCUSB_MUX_PROTOCOL +#error code not present +#endif // QCUSB_MUX_PROTOCOL + +typedef struct _QMIWDS_GET_PKT_STATISTICS_REQ_MSG +{ + USHORT Type; // QMUX type 0x0041 + USHORT Length; + UCHAR TLVType; // 0x01 + USHORT TLVLength; // 4 + ULONG StateMask; // 0x00000001 tx success packets + // 0x00000002 rx success packets + // 0x00000004 rx packet errors (checksum) + // 0x00000008 rx packets dropped (memory) + +} QMIWDS_GET_PKT_STATISTICS_REQ_MSG, *PQMIWDS_GET_PKT_STATISTICS_REQ_MSG; + +typedef struct _QMIWDS_GET_PKT_STATISTICS_RESP_MSG +{ + USHORT Type; // QMUX type 0x0041 + USHORT Length; + UCHAR TLVType; // 0x02 + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMIWDS_GET_PKT_STATISTICS_RESP_MSG, *PQMIWDS_GET_PKT_STATISTICS_RESP_MSG; + +// optional TLV for stats +typedef struct _QCTLV_PKT_STATISTICS +{ + UCHAR TLVType; // see above definitions for TLV types + USHORT TLVLength; // 4 + ULONG Count; +} QCTLV_PKT_STATISTICS, *PQCTLV_PKT_STATISTICS; +#endif + +//#ifdef QC_IP_MODE + +#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4DNS_ADDR 0x0010 +#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4_ADDR 0x0100 +#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4GATEWAY_ADDR 0x0200 +#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_MTU 0x2000 + +typedef struct _QMIWDS_GET_RUNTIME_SETTINGS_REQ_MSG +{ + USHORT Type; // QMIWDS_GET_RUNTIME_SETTINGS_REQ + USHORT Length; + UCHAR TLVType; // 0x10 + USHORT TLVLength; // 0x0004 + ULONG Mask; // mask, bit 8: IP addr -- 0x0100 +} __attribute__ ((packed)) QMIWDS_GET_RUNTIME_SETTINGS_REQ_MSG, *PQMIWDS_GET_RUNTIME_SETTINGS_REQ_MSG; + +typedef struct _QMIWDS_BIND_MUX_DATA_PORT_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + ULONG ep_type; + ULONG iface_id; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR MuxId; + UCHAR TLV3Type; + USHORT TLV3Length; + ULONG client_type; +} __attribute__ ((packed)) QMIWDS_BIND_MUX_DATA_PORT_REQ_MSG, *PQMIWDS_BIND_MUX_DATA_PORT_REQ_MSG; + +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4PRIMARYDNS 0x15 +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4SECONDARYDNS 0x16 +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4 0x1E +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4GATEWAY 0x20 +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4SUBNET 0x21 + +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6 0x25 +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6GATEWAY 0x26 +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6PRIMARYDNS 0x27 +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6SECONDARYDNS 0x28 +#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_MTU 0x29 + +typedef struct _QMIWDS_GET_RUNTIME_SETTINGS_TLV_MTU +{ + UCHAR TLVType; // QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_MTU + USHORT TLVLength; // 4 + ULONG Mtu; // MTU +} __attribute__ ((packed)) QMIWDS_GET_RUNTIME_SETTINGS_TLV_MTU, *PQMIWDS_GET_RUNTIME_SETTINGS_TLV_MTU; + +typedef struct _QMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR +{ + UCHAR TLVType; // QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4 + USHORT TLVLength; // 4 + ULONG IPV4Address; // address +} __attribute__ ((packed)) QMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR, *PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR; + +typedef struct _QMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR +{ + UCHAR TLVType; // QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6 + USHORT TLVLength; // 16 + UCHAR IPV6Address[16]; // address + UCHAR PrefixLength; // prefix length +} __attribute__ ((packed)) QMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR, *PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR; + +typedef struct _QMIWDS_GET_RUNTIME_SETTINGS_RESP_MSG +{ + USHORT Type; // QMIWDS_GET_RUNTIME_SETTINGS_RESP + USHORT Length; + UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE + USHORT TLVLength; // 0x0004 + USHORT QMUXResult; // result code + USHORT QMUXError; // error code +} __attribute__ ((packed)) QMIWDS_GET_RUNTIME_SETTINGS_RESP_MSG, *PQMIWDS_GET_RUNTIME_SETTINGS_RESP_MSG; + +//#endif // QC_IP_MODE + +typedef struct _QMIWDS_IP_FAMILY_TLV +{ + UCHAR TLVType; // 0x12 + USHORT TLVLength; // 1 + UCHAR IpFamily; // IPV4-0x04, IPV6-0x06 +} __attribute__ ((packed)) QMIWDS_IP_FAMILY_TLV, *PQMIWDS_IP_FAMILY_TLV; + +typedef struct _QMIWDS_PKT_SRVC_TLV +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR ConnectionStatus; + UCHAR ReconfigReqd; +} __attribute__ ((packed)) QMIWDS_PKT_SRVC_TLV, *PQMIWDS_PKT_SRVC_TLV; + +typedef struct _QMIWDS_CALL_END_REASON_TLV +{ + UCHAR TLVType; + USHORT TLVLength; + USHORT CallEndReason; +} __attribute__ ((packed)) QMIWDS_CALL_END_REASON_TLV, *PQMIWDS_CALL_END_REASON_TLV; + +typedef struct _QMIWDS_CALL_END_REASON_V_TLV +{ + UCHAR TLVType; + USHORT TLVLength; + USHORT CallEndReasonType; + USHORT CallEndReason; +} __attribute__ ((packed)) QMIWDS_CALL_END_REASON_V_TLV, *PQMIWDS_CALL_END_REASON_V_TLV; + +typedef struct _QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ_MSG +{ + USHORT Type; // QMUX type 0x004D + USHORT Length; + UCHAR TLVType; // 0x01 + USHORT TLVLength; // 1 + UCHAR IpPreference; // IPV4-0x04, IPV6-0x06 +} __attribute__ ((packed)) QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ_MSG, *PQMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ_MSG; + +typedef struct _QMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP_MSG +{ + USHORT Type; // QMUX type 0x0037 + USHORT Length; + UCHAR TLVType; // 0x02 + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS, QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INTERNAL, QMI_ERR_MALFORMED_MSG, QMI_ERR_INVALID_ARG +} __attribute__ ((packed)) QMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP_MSG, *PQMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP_MSG; + +typedef struct _QMIWDS_SET_AUTO_CONNECT_REQ_MSG +{ + USHORT Type; // QMUX type 0x0051 + USHORT Length; + UCHAR TLVType; // 0x01 + USHORT TLVLength; // 1 + UCHAR autoconnect_setting; // 0x00 ?C Disabled, 0x01 ?C Enabled, 0x02 ?C Paused (resume on power cycle) +} __attribute__ ((packed)) QMIWDS_SET_AUTO_CONNECT_REQ_MSG, *PQMIWDS_SET_AUTO_CONNECT_REQ_MSG; + +#if 0 +typedef struct _QMIWDS_GET_MIP_MODE_REQ_MSG +{ + USHORT Type; // QMUX type 0x0040 + USHORT Length; +} QMIWDS_GET_MIP_MODE_REQ_MSG, *PQMIWDS_GET_MIP_MODE_REQ_MSG; + +typedef struct _QMIWDS_GET_MIP_MODE_RESP_MSG +{ + USHORT Type; // QMUX type 0x0040 + USHORT Length; + UCHAR TLVType; // 0x02 + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + + UCHAR TLV2Type; // 0x01 + USHORT TLV2Length; // 20 + UCHAR MipMode; // +} QMIWDS_GET_MIP_MODE_RESP_MSG, *PQMIWDS_GET_MIP_MODE_RESP_MSG; +#endif + +typedef struct _QMIWDS_TECHNOLOGY_PREFERECE +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR TechPreference; +} __attribute__ ((packed)) QMIWDS_TECHNOLOGY_PREFERECE, *PQMIWDS_TECHNOLOGY_PREFERECE; + +typedef struct _QMIWDS_PROFILE_IDENTIFIER +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR ProfileIndex; +} __attribute__ ((packed)) QMIWDS_PROFILE_IDENTIFIER, *PQMIWDS_PROFILE_IDENTIFIER; + +#if 0 +typedef struct _QMIWDS_IPADDRESS +{ + UCHAR TLVType; + USHORT TLVLength; + ULONG IPv4Address; +}QMIWDS_IPADDRESS, *PQMIWDS_IPADDRESS; + +/* +typedef struct _QMIWDS_UMTS_QOS +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR TrafficClass; + ULONG MaxUplinkBitRate; + ULONG MaxDownlinkBitRate; + ULONG GuarUplinkBitRate; + ULONG GuarDownlinkBitRate; + UCHAR QOSDevOrder; + ULONG MAXSDUSize; + UCHAR SDUErrorRatio; + UCHAR ResidualBerRatio; + UCHAR DeliveryErrorSDUs; + ULONG TransferDelay; + ULONG TrafficHndPri; +}QMIWDS_UMTS_QOS, *PQMIWDS_UMTS_QOS; + +typedef struct _QMIWDS_GPRS_QOS +{ + UCHAR TLVType; + USHORT TLVLength; + ULONG PrecedenceClass; + ULONG DelayClass; + ULONG ReliabilityClass; + ULONG PeekThroClass; + ULONG MeanThroClass; +}QMIWDS_GPRS_QOS, *PQMIWDS_GPRS_QOS; +*/ +#endif + +typedef struct _QMIWDS_PROFILENAME +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR ProfileName; +} __attribute__ ((packed)) QMIWDS_PROFILENAME, *PQMIWDS_PROFILENAME; + +typedef struct _QMIWDS_PDPTYPE +{ + UCHAR TLVType; + USHORT TLVLength; +// 0 ?C PDP-IP (IPv4) +// 1 ?C PDP-PPP +// 2 ?C PDP-IPv6 +// 3 ?C PDP-IPv4v6 + UCHAR PdpType; +} __attribute__ ((packed)) QMIWDS_PDPTYPE, *PQMIWDS_PDPTYPE; + +typedef struct _QMIWDS_USERNAME +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR UserName; +} __attribute__ ((packed)) QMIWDS_USERNAME, *PQMIWDS_USERNAME; + +typedef struct _QMIWDS_PASSWD +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR Passwd; +} __attribute__ ((packed)) QMIWDS_PASSWD, *PQMIWDS_PASSWD; + +typedef struct _QMIWDS_AUTH_PREFERENCE +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR AuthPreference; +} __attribute__ ((packed)) QMIWDS_AUTH_PREFERENCE, *PQMIWDS_AUTH_PREFERENCE; + +typedef struct _QMIWDS_APNNAME +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR ApnName; +} __attribute__ ((packed)) QMIWDS_APNNAME, *PQMIWDS_APNNAME; + +typedef struct _QMIWDS_AUTOCONNECT +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR AutoConnect; +} __attribute__ ((packed)) QMIWDS_AUTOCONNECT, *PQMIWDS_AUTOCONNECT; + +typedef struct _QMIWDS_START_NETWORK_INTERFACE_REQ_MSG +{ + USHORT Type; + USHORT Length; +} __attribute__ ((packed)) QMIWDS_START_NETWORK_INTERFACE_REQ_MSG, *PQMIWDS_START_NETWORK_INTERFACE_REQ_MSG; + +typedef struct _QMIWDS_CALLENDREASON +{ + UCHAR TLVType; + USHORT TLVLength; + USHORT Reason; +}__attribute__ ((packed)) QMIWDS_CALLENDREASON, *PQMIWDS_CALLENDREASON; + +typedef struct _QMIWDS_START_NETWORK_INTERFACE_RESP_MSG +{ + USHORT Type; // QMUX type 0x0040 + USHORT Length; + UCHAR TLVType; // 0x02 + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + + UCHAR TLV2Type; // 0x01 + USHORT TLV2Length; // 20 + ULONG Handle; // +} __attribute__ ((packed)) QMIWDS_START_NETWORK_INTERFACE_RESP_MSG, *PQMIWDS_START_NETWORK_INTERFACE_RESP_MSG; + +typedef struct _QMIWDS_STOP_NETWORK_INTERFACE_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + ULONG Handle; +} __attribute__ ((packed)) QMIWDS_STOP_NETWORK_INTERFACE_REQ_MSG, *PQMIWDS_STOP_NETWORK_INTERFACE_REQ_MSG; + +typedef struct _QMIWDS_STOP_NETWORK_INTERFACE_RESP_MSG +{ + USHORT Type; // QMUX type 0x0040 + USHORT Length; + UCHAR TLVType; // 0x02 + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + +} __attribute__ ((packed)) QMIWDS_STOP_NETWORK_INTERFACE_RESP_MSG, *PQMIWDS_STOP_NETWORK_INTERFACE_RESP_MSG; + +typedef struct _QMIWDS_GET_DEFAULT_SETTINGS_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR ProfileType; +} __attribute__ ((packed)) QMIWDS_GET_DEFAULT_SETTINGS_REQ_MSG, *PQMIWDS_GET_DEFAULT_SETTINGS_REQ_MSG; + +typedef struct _QMIWDS_GET_DEFAULT_SETTINGS_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} __attribute__ ((packed)) QMIWDS_GET_DEFAULT_SETTINGS_RESP_MSG, *PQMIWDS_GET_DEFAULT_SETTINGS_RESP_MSG; + +typedef struct _QMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR ProfileType; + UCHAR ProfileIndex; +} __attribute__ ((packed)) QMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG, *PQMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG; + +typedef struct _QMIWDS_MODIFY_PROFILE_SETTINGS_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} __attribute__ ((packed)) QMIWDS_MODIFY_PROFILE_SETTINGS_RESP_MSG, *PQMIWDS_MODIFY_PROFILE_SETTINGS_RESP_MSG; + +typedef struct _QMIWDS_GET_PROFILE_SETTINGS_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR ProfileType; + UCHAR ProfileIndex; +} __attribute__ ((packed)) QMIWDS_GET_PROFILE_SETTINGS_REQ_MSG, *PQMIWDS_GET_PROFILE_SETTINGS_REQ_MSG; + +#if 0 +typedef struct _QMIWDS_EVENT_REPORT_IND_DATA_BEARER_TLV +{ + UCHAR Type; + USHORT Length; + UCHAR DataBearer; +} QMIWDS_EVENT_REPORT_IND_DATA_BEARER_TLV, *PQMIWDS_EVENT_REPORT_IND_DATA_BEARER_TLV; + +typedef struct _QMIWDS_EVENT_REPORT_IND_DORMANCY_STATUS_TLV +{ + UCHAR Type; + USHORT Length; + UCHAR DormancyStatus; +} QMIWDS_EVENT_REPORT_IND_DORMANCY_STATUS_TLV, *PQMIWDS_EVENT_REPORT_IND_DORMANCY_STATUS_TLV; + + +typedef struct _QMIWDS_GET_DATA_BEARER_REQ_MSG +{ + USHORT Type; // QMUX type 0x0037 + USHORT Length; +} QMIWDS_GET_DATA_BEARER_REQ_MSG, *PQMIWDS_GET_DATA_BEARER_REQ_MSG; + +typedef struct _QMIWDS_GET_DATA_BEARER_RESP_MSG +{ + USHORT Type; // QMUX type 0x0037 + USHORT Length; + UCHAR TLVType; // 0x02 + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INTERNAL + // QMI_ERR_MALFORMED_MSG + // QMI_ERR_NO_MEMORY + // QMI_ERR_OUT_OF_CALL + // QMI_ERR_INFO_UNAVAILABLE + UCHAR TLV2Type; // 0x01 + USHORT TLV2Length; // + UCHAR Technology; // +} QMIWDS_GET_DATA_BEARER_RESP_MSG, *PQMIWDS_GET_DATA_BEARER_RESP_MSG; +#endif + +// ======================= DMS ============================== +#define QMIDMS_SET_EVENT_REPORT_REQ 0x0001 +#define QMIDMS_SET_EVENT_REPORT_RESP 0x0001 +#define QMIDMS_EVENT_REPORT_IND 0x0001 +#define QMIDMS_GET_DEVICE_CAP_REQ 0x0020 +#define QMIDMS_GET_DEVICE_CAP_RESP 0x0020 +#define QMIDMS_GET_DEVICE_MFR_REQ 0x0021 +#define QMIDMS_GET_DEVICE_MFR_RESP 0x0021 +#define QMIDMS_GET_DEVICE_MODEL_ID_REQ 0x0022 +#define QMIDMS_GET_DEVICE_MODEL_ID_RESP 0x0022 +#define QMIDMS_GET_DEVICE_REV_ID_REQ 0x0023 +#define QMIDMS_GET_DEVICE_REV_ID_RESP 0x0023 +#define QMIDMS_GET_MSISDN_REQ 0x0024 +#define QMIDMS_GET_MSISDN_RESP 0x0024 +#define QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ 0x0025 +#define QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP 0x0025 +#define QMIDMS_UIM_SET_PIN_PROTECTION_REQ 0x0027 +#define QMIDMS_UIM_SET_PIN_PROTECTION_RESP 0x0027 +#define QMIDMS_UIM_VERIFY_PIN_REQ 0x0028 +#define QMIDMS_UIM_VERIFY_PIN_RESP 0x0028 +#define QMIDMS_UIM_UNBLOCK_PIN_REQ 0x0029 +#define QMIDMS_UIM_UNBLOCK_PIN_RESP 0x0029 +#define QMIDMS_UIM_CHANGE_PIN_REQ 0x002A +#define QMIDMS_UIM_CHANGE_PIN_RESP 0x002A +#define QMIDMS_UIM_GET_PIN_STATUS_REQ 0x002B +#define QMIDMS_UIM_GET_PIN_STATUS_RESP 0x002B +#define QMIDMS_GET_DEVICE_HARDWARE_REV_REQ 0x002C +#define QMIDMS_GET_DEVICE_HARDWARE_REV_RESP 0x002C +#define QMIDMS_GET_OPERATING_MODE_REQ 0x002D +#define QMIDMS_GET_OPERATING_MODE_RESP 0x002D +#define QMIDMS_SET_OPERATING_MODE_REQ 0x002E +#define QMIDMS_SET_OPERATING_MODE_RESP 0x002E +#define QMIDMS_GET_ACTIVATED_STATUS_REQ 0x0031 +#define QMIDMS_GET_ACTIVATED_STATUS_RESP 0x0031 +#define QMIDMS_ACTIVATE_AUTOMATIC_REQ 0x0032 +#define QMIDMS_ACTIVATE_AUTOMATIC_RESP 0x0032 +#define QMIDMS_ACTIVATE_MANUAL_REQ 0x0033 +#define QMIDMS_ACTIVATE_MANUAL_RESP 0x0033 +#define QMIDMS_UIM_GET_ICCID_REQ 0x003C +#define QMIDMS_UIM_GET_ICCID_RESP 0x003C +#define QMIDMS_UIM_GET_CK_STATUS_REQ 0x0040 +#define QMIDMS_UIM_GET_CK_STATUS_RESP 0x0040 +#define QMIDMS_UIM_SET_CK_PROTECTION_REQ 0x0041 +#define QMIDMS_UIM_SET_CK_PROTECTION_RESP 0x0041 +#define QMIDMS_UIM_UNBLOCK_CK_REQ 0x0042 +#define QMIDMS_UIM_UNBLOCK_CK_RESP 0x0042 +#define QMIDMS_UIM_GET_IMSI_REQ 0x0043 +#define QMIDMS_UIM_GET_IMSI_RESP 0x0043 +#define QMIDMS_UIM_GET_STATE_REQ 0x0044 +#define QMIDMS_UIM_GET_STATE_RESP 0x0044 +#define QMIDMS_GET_BAND_CAP_REQ 0x0045 +#define QMIDMS_GET_BAND_CAP_RESP 0x0045 + +#if 0 +typedef struct _QMIDMS_GET_DEVICE_MFR_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; +} QMIDMS_GET_DEVICE_MFR_REQ_MSG, *PQMIDMS_GET_DEVICE_MFR_REQ_MSG; + +typedef struct _QMIDMS_GET_DEVICE_MFR_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; // 0x01 - required parameter + USHORT TLV2Length; // length of the mfr string + UCHAR DeviceManufacturer; // first byte of string +} QMIDMS_GET_DEVICE_MFR_RESP_MSG, *PQMIDMS_GET_DEVICE_MFR_RESP_MSG; + +typedef struct _QMIDMS_GET_DEVICE_MODEL_ID_REQ_MSG +{ + USHORT Type; // QMUX type 0x0004 + USHORT Length; +} QMIDMS_GET_DEVICE_MODEL_ID_REQ_MSG, *PQMIDMS_GET_DEVICE_MODEL_ID_REQ_MSG; + +typedef struct _QMIDMS_GET_DEVICE_MODEL_ID_RESP_MSG +{ + USHORT Type; // QMUX type 0x0004 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; // 0x01 - required parameter + USHORT TLV2Length; // length of the modem id string + UCHAR DeviceModelID; // device model id +} QMIDMS_GET_DEVICE_MODEL_ID_RESP_MSG, *PQMIDMS_GET_DEVICE_MODEL_ID_RESP_MSG; +#endif + +typedef struct _QMIDMS_GET_DEVICE_REV_ID_REQ_MSG +{ + USHORT Type; // QMUX type 0x0005 + USHORT Length; +} __attribute__ ((packed)) QMIDMS_GET_DEVICE_REV_ID_REQ_MSG, *PQMIDMS_GET_DEVICE_REV_ID_REQ_MSG; + +typedef struct _DEVICE_REV_ID +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR RevisionID; +} __attribute__ ((packed)) DEVICE_REV_ID, *PDEVICE_REV_ID; + +#if 0 +typedef struct _QMIDMS_GET_DEVICE_REV_ID_RESP_MSG +{ + USHORT Type; // QMUX type 0x0023 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMIDMS_GET_DEVICE_REV_ID_RESP_MSG, *PQMIDMS_GET_DEVICE_REV_ID_RESP_MSG; + +typedef struct _QMIDMS_GET_MSISDN_REQ_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; +} QMIDMS_GET_MSISDN_REQ_MSG, *PQMIDMS_GET_MSISDN_REQ_MSG; + +typedef struct _QCTLV_DEVICE_VOICE_NUMBERS +{ + UCHAR TLVType; // as defined above + USHORT TLVLength; // 4/7/7 + UCHAR VoideNumberString; // ESN, IMEI, or MEID + +} QCTLV_DEVICE_VOICE_NUMBERS, *PQCTLV_DEVICE_VOICE_NUMBERS; + + +typedef struct _QMIDMS_GET_MSISDN_RESP_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG +} QMIDMS_GET_MSISDN_RESP_MSG, *PQMIDMS_GET_MSISDN_RESP_MSG; +#endif + +typedef struct _QMIDMS_UIM_GET_IMSI_REQ_MSG +{ + USHORT Type; + USHORT Length; +} __attribute__ ((packed)) QMIDMS_UIM_GET_IMSI_REQ_MSG, *PQMIDMS_UIM_GET_IMSI_REQ_MSG; + +typedef struct _QMIDMS_UIM_GET_IMSI_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR IMSI; +} __attribute__ ((packed)) QMIDMS_UIM_GET_IMSI_RESP_MSG, *PQMIDMS_UIM_GET_IMSI_RESP_MSG; + +#if 0 +typedef struct _QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ_MSG +{ + USHORT Type; // QMUX type 0x0007 + USHORT Length; +} QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ_MSG, *PQMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ_MSG; + +#define QCTLV_TYPE_SER_NUM_ESN 0x10 +#define QCTLV_TYPE_SER_NUM_IMEI 0x11 +#define QCTLV_TYPE_SER_NUM_MEID 0x12 + +typedef struct _QCTLV_DEVICE_SERIAL_NUMBER +{ + UCHAR TLVType; // as defined above + USHORT TLVLength; // 4/7/7 + UCHAR SerialNumberString; // ESN, IMEI, or MEID + +} QCTLV_DEVICE_SERIAL_NUMBER, *PQCTLV_DEVICE_SERIAL_NUMBER; + +typedef struct _QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP_MSG +{ + USHORT Type; // QMUX type 0x0007 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + // followed by optional TLV +} QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP_MSG, *PQMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP; + +typedef struct _QMIDMS_GET_DMS_BAND_CAP +{ + USHORT Type; + USHORT Length; +} QMIDMS_GET_BAND_CAP_REQ_MSG, *PQMIDMS_GET_BAND_CAP_REQ_MSG; + +typedef struct _QMIDMS_GET_BAND_CAP_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_NONE + // QMI_ERR_INTERNAL + // QMI_ERR_MALFORMED_MSG + // QMI_ERR_NO_MEMORY + + UCHAR TLV2Type; // 0x01 + USHORT TLV2Length; // 2 + ULONG64 BandCap; +} QMIDMS_GET_BAND_CAP_RESP_MSG, *PQMIDMS_GET_BAND_CAP_RESP; + +typedef struct _QMIDMS_GET_DEVICE_CAP_REQ_MSG +{ + USHORT Type; // QMUX type 0x0002 + USHORT Length; +} QMIDMS_GET_DEVICE_CAP_REQ_MSG, *PQMIDMS_GET_DEVICE_CAP_REQ_MSG; + +typedef struct _QMIDMS_GET_DEVICE_CAP_RESP_MSG +{ + USHORT Type; // QMUX type 0x0002 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMUX_RESULT_SUCCESS + // QMUX_RESULT_FAILURE + USHORT QMUXError; // QMUX_ERR_INVALID_ARG + // QMUX_ERR_NO_MEMORY + // QMUX_ERR_INTERNAL + // QMUX_ERR_FAULT + UCHAR TLV2Type; // 0x01 + USHORT TLV2Length; // 2 + + ULONG MaxTxChannelRate; + ULONG MaxRxChannelRate; + UCHAR VoiceCap; + UCHAR SimCap; + + UCHAR RadioIfListCnt; // #elements in radio interface list + UCHAR RadioIfList; // N 1-byte elements +} QMIDMS_GET_DEVICE_CAP_RESP_MSG, *PQMIDMS_GET_DEVICE_CAP_RESP_MSG; + +typedef struct _QMIDMS_GET_ACTIVATED_STATUS_REQ_MSG +{ + USHORT Type; // QMUX type 0x0002 + USHORT Length; +} QMIDMS_GET_ACTIVATED_STATUS_REQ_MSG, *PQMIDMS_GET_ACTIVATES_STATUD_REQ_MSG; + +typedef struct _QMIDMS_GET_ACTIVATED_STATUS_RESP_MSG +{ + USHORT Type; // QMUX type 0x0002 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMUX_RESULT_SUCCESS + // QMUX_RESULT_FAILURE + USHORT QMUXError; // QMUX_ERR_INVALID_ARG + // QMUX_ERR_NO_MEMORY + // QMUX_ERR_INTERNAL + // QMUX_ERR_FAULT + UCHAR TLV2Type; // 0x01 + USHORT TLV2Length; // 2 + + USHORT ActivatedStatus; +} QMIDMS_GET_ACTIVATED_STATUS_RESP_MSG, *PQMIDMS_GET_ACTIVATED_STATUS_RESP_MSG; + +typedef struct _QMIDMS_GET_OPERATING_MODE_REQ_MSG +{ + USHORT Type; // QMUX type 0x0002 + USHORT Length; +} QMIDMS_GET_OPERATING_MODE_REQ_MSG, *PQMIDMS_GET_OPERATING_MODE_REQ_MSG; + +typedef struct _OFFLINE_REASON +{ + UCHAR TLVType; + USHORT TLVLength; + USHORT OfflineReason; +} OFFLINE_REASON, *POFFLINE_REASON; + +typedef struct _HARDWARE_RESTRICTED_MODE +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR HardwareControlledMode; +} HARDWARE_RESTRICTED_MODE, *PHARDWARE_RESTRICTED_MODE; + +typedef struct _QMIDMS_GET_OPERATING_MODE_RESP_MSG +{ + USHORT Type; // QMUX type 0x0002 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMUX_RESULT_SUCCESS + // QMUX_RESULT_FAILURE + USHORT QMUXError; // QMUX_ERR_INVALID_ARG + // QMUX_ERR_NO_MEMORY + // QMUX_ERR_INTERNAL + // QMUX_ERR_FAULT + UCHAR TLV2Type; // 0x01 + USHORT TLV2Length; // 2 + + UCHAR OperatingMode; +} QMIDMS_GET_OPERATING_MODE_RESP_MSG, *PQMIDMS_GET_OPERATING_MODE_RESP_MSG; + +typedef struct _QMIDMS_UIM_GET_ICCID_REQ_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; +} QMIDMS_UIM_GET_ICCID_REQ_MSG, *PQMIDMS_UIM_GET_ICCID_REQ_MSG; + +typedef struct _QMIDMS_UIM_GET_ICCID_RESP_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; // 0x01 - required parameter + USHORT TLV2Length; // var + UCHAR ICCID; // String of voice number +} QMIDMS_UIM_GET_ICCID_RESP_MSG, *PQMIDMS_UIM_GET_ICCID_RESP_MSG; +#endif + +typedef struct _QMIDMS_SET_OPERATING_MODE_REQ_MSG +{ + USHORT Type; // QMUX type 0x0002 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + UCHAR OperatingMode; +} __attribute__ ((packed)) QMIDMS_SET_OPERATING_MODE_REQ_MSG, *PQMIDMS_SET_OPERATING_MODE_REQ_MSG; + +typedef struct _QMIDMS_SET_OPERATING_MODE_RESP_MSG +{ + USHORT Type; // QMUX type 0x0002 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMUX_RESULT_SUCCESS + // QMUX_RESULT_FAILURE + USHORT QMUXError; // QMUX_ERR_INVALID_ARG + // QMUX_ERR_NO_MEMORY + // QMUX_ERR_INTERNAL + // QMUX_ERR_FAULT +} __attribute__ ((packed)) QMIDMS_SET_OPERATING_MODE_RESP_MSG, *PQMIDMS_SET_OPERATING_MODE_RESP_MSG; + +#if 0 +typedef struct _QMIDMS_ACTIVATE_AUTOMATIC_REQ_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // + UCHAR ActivateCodelen; + UCHAR ActivateCode; +} QMIDMS_ACTIVATE_AUTOMATIC_REQ_MSG, *PQMIDMS_ACTIVATE_AUTOMATIC_REQ_MSG; + +typedef struct _QMIDMS_ACTIVATE_AUTOMATIC_RESP_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMIDMS_ACTIVATE_AUTOMATIC_RESP_MSG, *PQMIDMS_ACTIVATE_AUTOMATIC_RESP_MSG; + + +typedef struct _SPC_MSG +{ + UCHAR SPC[6]; + USHORT SID; +} SPC_MSG, *PSPC_MSG; + +typedef struct _MDN_MSG +{ + UCHAR MDNLEN; + UCHAR MDN; +} MDN_MSG, *PMDN_MSG; + +typedef struct _MIN_MSG +{ + UCHAR MINLEN; + UCHAR MIN; +} MIN_MSG, *PMIN_MSG; + +typedef struct _PRL_MSG +{ + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // + USHORT PRLLEN; + UCHAR PRL; +} PRL_MSG, *PPRL_MSG; + +typedef struct _MN_HA_KEY_MSG +{ + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // + UCHAR MN_HA_KEY_LEN; + UCHAR MN_HA_KEY; +} MN_HA_KEY_MSG, *PMN_HA_KEY_MSG; + +typedef struct _MN_AAA_KEY_MSG +{ + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // + UCHAR MN_AAA_KEY_LEN; + UCHAR MN_AAA_KEY; +} MN_AAA_KEY_MSG, *PMN_AAA_KEY_MSG; + +typedef struct _QMIDMS_ACTIVATE_MANUAL_REQ_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // + UCHAR Value; +} QMIDMS_ACTIVATE_MANUAL_REQ_MSG, *PQMIDMS_ACTIVATE_MANUAL_REQ_MSG; + +typedef struct _QMIDMS_ACTIVATE_MANUAL_RESP_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMIDMS_ACTIVATE_MANUAL_RESP_MSG, *PQMIDMS_ACTIVATE_MANUAL_RESP_MSG; +#endif + +typedef struct _QMIDMS_UIM_GET_STATE_REQ_MSG +{ + USHORT Type; + USHORT Length; +} __attribute__ ((packed)) QMIDMS_UIM_GET_STATE_REQ_MSG, *PQMIDMS_UIM_GET_STATE_REQ_MSG; + +typedef struct _QMIDMS_UIM_GET_STATE_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR UIMState; +} __attribute__ ((packed)) QMIDMS_UIM_GET_STATE_RESP_MSG, *PQMIDMS_UIM_GET_STATE_RESP_MSG; + +typedef struct _QMIDMS_UIM_GET_PIN_STATUS_REQ_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; +} __attribute__ ((packed)) QMIDMS_UIM_GET_PIN_STATUS_REQ_MSG, *PQMIDMS_UIM_GET_PIN_STATUS_REQ_MSG; + +typedef struct _QMIDMS_UIM_PIN_STATUS +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR PINStatus; + UCHAR PINVerifyRetriesLeft; + UCHAR PINUnblockRetriesLeft; +} __attribute__ ((packed)) QMIDMS_UIM_PIN_STATUS, *PQMIDMS_UIM_PIN_STATUS; + +#define QMI_PIN_STATUS_NOT_INIT 0 +#define QMI_PIN_STATUS_NOT_VERIF 1 +#define QMI_PIN_STATUS_VERIFIED 2 +#define QMI_PIN_STATUS_DISABLED 3 +#define QMI_PIN_STATUS_BLOCKED 4 +#define QMI_PIN_STATUS_PERM_BLOCKED 5 +#define QMI_PIN_STATUS_UNBLOCKED 6 +#define QMI_PIN_STATUS_CHANGED 7 + + +typedef struct _QMIDMS_UIM_GET_PIN_STATUS_RESP_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR PinStatus; +} __attribute__ ((packed)) QMIDMS_UIM_GET_PIN_STATUS_RESP_MSG, *PQMIDMS_UIM_GET_PIN_STATUS_RESP_MSG; + +#if 0 +typedef struct _QMIDMS_UIM_GET_CK_STATUS_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR Facility; +} QMIDMS_UIM_GET_CK_STATUS_REQ_MSG, *PQMIDMS_UIM_GET_CK_STATUS_REQ_MSG; + + +typedef struct _QMIDMS_UIM_CK_STATUS +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR FacilityStatus; + UCHAR FacilityVerifyRetriesLeft; + UCHAR FacilityUnblockRetriesLeft; +} QMIDMS_UIM_CK_STATUS, *PQMIDMS_UIM_CK_STATUS; + +typedef struct _QMIDMS_UIM_CK_OPERATION_STATUS +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR OperationBlocking; +} QMIDMS_UIM_CK_OPERATION_STATUS, *PQMIDMS_UIM_CK_OPERATION_STATUS; + +typedef struct _QMIDMS_UIM_GET_CK_STATUS_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR CkStatus; +} QMIDMS_UIM_GET_CK_STATUS_RESP_MSG, *PQMIDMS_UIM_GET_CK_STATUS_RESP_MSG; +#endif + +typedef struct _QMIDMS_UIM_VERIFY_PIN_REQ_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + UCHAR PINID; + UCHAR PINLen; + UCHAR PINValue; +} __attribute__ ((packed)) QMIDMS_UIM_VERIFY_PIN_REQ_MSG, *PQMIDMS_UIM_VERIFY_PIN_REQ_MSG; + +typedef struct _QMIDMS_UIM_VERIFY_PIN_RESP_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR PINVerifyRetriesLeft; + UCHAR PINUnblockRetriesLeft; +} __attribute__ ((packed)) QMIDMS_UIM_VERIFY_PIN_RESP_MSG, *PQMIDMS_UIM_VERIFY_PIN_RESP_MSG; + +#if 0 +typedef struct _QMIDMS_UIM_SET_PIN_PROTECTION_REQ_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + UCHAR PINID; + UCHAR ProtectionSetting; + UCHAR PINLen; + UCHAR PINValue; +} QMIDMS_UIM_SET_PIN_PROTECTION_REQ_MSG, *PQMIDMS_UIM_SET_PIN_PROTECTION_REQ_MSG; + +typedef struct _QMIDMS_UIM_SET_PIN_PROTECTION_RESP_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR PINVerifyRetriesLeft; + UCHAR PINUnblockRetriesLeft; +} QMIDMS_UIM_SET_PIN_PROTECTION_RESP_MSG, *PQMIDMS_UIM_SET_PIN_PROTECTION_RESP_MSG; + +typedef struct _QMIDMS_UIM_SET_CK_PROTECTION_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR Facility; + UCHAR FacilityState; + UCHAR FacliltyLen; + UCHAR FacliltyValue; +} QMIDMS_UIM_SET_CK_PROTECTION_REQ_MSG, *PQMIDMS_UIM_SET_CK_PROTECTION_REQ_MSG; + +typedef struct _QMIDMS_UIM_SET_CK_PROTECTION_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR FacilityRetriesLeft; +} QMIDMS_UIM_SET_CK_PROTECTION_RESP_MSG, *PQMIDMS_UIM_SET_CK_PROTECTION_RESP_MSG; + + +typedef struct _UIM_PIN +{ + UCHAR PinLength; + UCHAR PinValue; +} UIM_PIN, *PUIM_PIN; + +typedef struct _QMIDMS_UIM_CHANGE_PIN_REQ_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + UCHAR PINID; + UCHAR PinDetails; +} QMIDMS_UIM_CHANGE_PIN_REQ_MSG, *PQMIDMS_UIM_CHANGE_PIN_REQ_MSG; + +typedef struct QMIDMS_UIM_CHANGE_PIN_RESP_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR PINVerifyRetriesLeft; + UCHAR PINUnblockRetriesLeft; +} QMIDMS_UIM_CHANGE_PIN_RESP_MSG, *PQMIDMS_UIM_CHANGE_PIN_RESP_MSG; + +typedef struct _UIM_PUK +{ + UCHAR PukLength; + UCHAR PukValue; +} UIM_PUK, *PUIM_PUK; + +typedef struct _QMIDMS_UIM_UNBLOCK_PIN_REQ_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + UCHAR PINID; + UCHAR PinDetails; +} QMIDMS_UIM_UNBLOCK_PIN_REQ_MSG, *PQMIDMS_UIM_BLOCK_PIN_REQ_MSG; + +typedef struct QMIDMS_UIM_UNBLOCK_PIN_RESP_MSG +{ + USHORT Type; // QMUX type 0x0024 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR PINVerifyRetriesLeft; + UCHAR PINUnblockRetriesLeft; +} QMIDMS_UIM_UNBLOCK_PIN_RESP_MSG, *PQMIDMS_UIM_UNBLOCK_PIN_RESP_MSG; + +typedef struct _QMIDMS_UIM_UNBLOCK_CK_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR Facility; + UCHAR FacliltyUnblockLen; + UCHAR FacliltyUnblockValue; +} QMIDMS_UIM_UNBLOCK_CK_REQ_MSG, *PQMIDMS_UIM_BLOCK_CK_REQ_MSG; + +typedef struct QMIDMS_UIM_UNBLOCK_CK_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR FacilityUnblockRetriesLeft; +} QMIDMS_UIM_UNBLOCK_CK_RESP_MSG, *PQMIDMS_UIM_UNBLOCK_CK_RESP_MSG; + +typedef struct _QMIDMS_SET_EVENT_REPORT_REQ_MSG +{ + USHORT Type; + USHORT Length; +} QMIDMS_SET_EVENT_REPORT_REQ_MSG, *PQMIDMS_SET_EVENT_REPORT_REQ_MSG; + +typedef struct _QMIDMS_SET_EVENT_REPORT_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG +} QMIDMS_SET_EVENT_REPORT_RESP_MSG, *PQMIDMS_SET_EVENT_REPORT_RESP_MSG; + +typedef struct _PIN_STATUS +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR ReportPinState; +} PIN_STATUS, *PPIN_STATUS; + +typedef struct _POWER_STATUS +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR PowerStatus; + UCHAR BatteryLvl; +} POWER_STATUS, *PPOWER_STATUS; + +typedef struct _ACTIVATION_STATE +{ + UCHAR TLVType; + USHORT TLVLength; + USHORT ActivationState; +} ACTIVATION_STATE, *PACTIVATION_STATE; + +typedef struct _ACTIVATION_STATE_REQ +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR ActivationState; +} ACTIVATION_STATE_REQ, *PACTIVATION_STATE_REQ; + +typedef struct _OPERATING_MODE +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR OperatingMode; +} OPERATING_MODE, *POPERATING_MODE; + +typedef struct _UIM_STATE +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR UIMState; +} UIM_STATE, *PUIM_STATE; + +typedef struct _WIRELESS_DISABLE_STATE +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR WirelessDisableState; +} WIRELESS_DISABLE_STATE, *PWIRELESS_DISABLE_STATE; + +typedef struct _QMIDMS_EVENT_REPORT_IND_MSG +{ + USHORT Type; + USHORT Length; +} QMIDMS_EVENT_REPORT_IND_MSG, *PQMIDMS_EVENT_REPORT_IND_MSG; +#endif + +// ============================ END OF DMS =============================== + +// ======================= QOS ============================== +typedef struct _MPIOC_DEV_INFO MPIOC_DEV_INFO, *PMPIOC_DEV_INFO; + +#define QMI_QOS_SET_EVENT_REPORT_REQ 0x0001 +#define QMI_QOS_SET_EVENT_REPORT_RESP 0x0001 +#define QMI_QOS_EVENT_REPORT_IND 0x0001 + +#if 0 +typedef struct _QMI_QOS_SET_EVENT_REPORT_REQ_MSG +{ + USHORT Type; // QMUX type 0x0001 + USHORT Length; + // UCHAR TLVType; // 0x01 - physical link state + // USHORT TLVLength; // 1 + // UCHAR PhyLinkStatusRpt; // 0-enable; 1-disable + UCHAR TLVType2; // 0x02 = global flow reporting + USHORT TLVLength2; // 1 + UCHAR GlobalFlowRpt; // 1-enable; 0-disable +} QMI_QOS_SET_EVENT_REPORT_REQ_MSG, *PQMI_QOS_SET_EVENT_REPORT_REQ_MSG; + +typedef struct _QMI_QOS_SET_EVENT_REPORT_RESP_MSG +{ + USHORT Type; // QMUX type 0x0010 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMUX_RESULT_SUCCESS + // QMUX_RESULT_FAILURE + USHORT QMUXError; // QMUX_ERR_INVALID_ARG + // QMUX_ERR_NO_MEMORY + // QMUX_ERR_INTERNAL + // QMUX_ERR_FAULT +} QMI_QOS_SET_EVENT_REPORT_RESP_MSG, *PQMI_QOS_SET_EVENT_REPORT_RESP_MSG; + +typedef struct _QMI_QOS_EVENT_REPORT_IND_MSG +{ + USHORT Type; // QMUX type 0x0001 + USHORT Length; + UCHAR TLVs; +} QMI_QOS_EVENT_REPORT_IND_MSG, *PQMI_QOS_EVENT_REPORT_IND_MSG; + +#define QOS_EVENT_RPT_IND_FLOW_ACTIVATED 0x01 +#define QOS_EVENT_RPT_IND_FLOW_MODIFIED 0x02 +#define QOS_EVENT_RPT_IND_FLOW_DELETED 0x03 +#define QOS_EVENT_RPT_IND_FLOW_SUSPENDED 0x04 +#define QOS_EVENT_RPT_IND_FLOW_ENABLED 0x05 +#define QOS_EVENT_RPT_IND_FLOW_DISABLED 0x06 + +#define QOS_EVENT_RPT_IND_TLV_PHY_LINK_STATE_TYPE 0x01 +#define QOS_EVENT_RPT_IND_TLV_GLOBAL_FL_RPT_STATE 0x10 +#define QOS_EVENT_RPT_IND_TLV_GLOBAL_FL_RPT_TYPE 0x10 +#define QOS_EVENT_RPT_IND_TLV_TX_FLOW_TYPE 0x11 +#define QOS_EVENT_RPT_IND_TLV_RX_FLOW_TYPE 0x12 +#define QOS_EVENT_RPT_IND_TLV_TX_FILTER_TYPE 0x13 +#define QOS_EVENT_RPT_IND_TLV_RX_FILTER_TYPE 0x14 +#define QOS_EVENT_RPT_IND_TLV_FLOW_SPEC 0x10 +#define QOS_EVENT_RPT_IND_TLV_FILTER_SPEC 0x10 + +typedef struct _QOS_EVENT_RPT_IND_TLV_PHY_LINK_STATE +{ + UCHAR TLVType; // 0x01 + USHORT TLVLength; // 1 + UCHAR PhyLinkState; // 0-dormant, 1-active +} QOS_EVENT_RPT_IND_TLV_PHY_LINK_STATE, *PQOS_EVENT_RPT_IND_TLV_PHY_LINK_STATE; + +typedef struct _QOS_EVENT_RPT_IND_TLV_GLOBAL_FL_RPT +{ + UCHAR TLVType; // 0x10 + USHORT TLVLength; // 6 + ULONG QosId; + UCHAR NewFlow; // 1: newly added flow; 0: existing flow + UCHAR StateChange; // 1: activated; 2: modified; 3: deleted; + // 4: suspended(delete); 5: enabled; 6: disabled +} QOS_EVENT_RPT_IND_TLV_GLOBAL_FL_RPT, *PQOS_EVENT_RPT_IND_TLV_GLOBAL_FL_RPT; + +// QOS Flow + +typedef struct _QOS_EVENT_RPT_IND_TLV_FLOW +{ + UCHAR TLVType; // 0x10-TX flow; 0x11-RX flow + USHORT TLVLength; // var + // embedded TLV's +} QOS_EVENT_RPT_IND_TLV_TX_FLOW, *PQOS_EVENT_RPT_IND_TLV_TX_FLOW; + +#define QOS_FLOW_TLV_IP_FLOW_IDX_TYPE 0x10 +#define QOS_FLOW_TLV_IP_FLOW_TRAFFIC_CLASS_TYPE 0x11 +#define QOS_FLOW_TLV_IP_FLOW_DATA_RATE_MIN_MAX_TYPE 0x12 +#define QOS_FLOW_TLV_IP_FLOW_DATA_RATE_TOKEN_BUCKET_TYPE 0x13 +#define QOS_FLOW_TLV_IP_FLOW_LATENCY_TYPE 0x14 +#define QOS_FLOW_TLV_IP_FLOW_JITTER_TYPE 0x15 +#define QOS_FLOW_TLV_IP_FLOW_PKT_ERR_RATE_TYPE 0x16 +#define QOS_FLOW_TLV_IP_FLOW_MIN_PKT_SIZE_TYPE 0x17 +#define QOS_FLOW_TLV_IP_FLOW_MAX_PKT_SIZE_TYPE 0x18 +#define QOS_FLOW_TLV_IP_FLOW_3GPP_BIT_ERR_RATE_TYPE 0x19 +#define QOS_FLOW_TLV_IP_FLOW_3GPP_TRAF_PRIORITY_TYPE 0x1A +#define QOS_FLOW_TLV_IP_FLOW_3GPP2_PROFILE_ID_TYPE 0x1B + +typedef struct _QOS_FLOW_TLV_IP_FLOW_IDX +{ + UCHAR TLVType; // 0x10 + USHORT TLVLength; // 1 + UCHAR IpFlowIndex; +} QOS_FLOW_TLV_IP_FLOW_IDX, *PQOS_FLOW_TLV_IP_FLOW_IDX; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_TRAFFIC_CLASS +{ + UCHAR TLVType; // 0x11 + USHORT TLVLength; // 1 + UCHAR TrafficClass; +} QOS_FLOW_TLV_IP_FLOW_TRAFFIC_CLASS, *PQOS_FLOW_TLV_IP_FLOW_TRAFFIC_CLASS; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_DATA_RATE_MIN_MAX +{ + UCHAR TLVType; // 0x12 + USHORT TLVLength; // 8 + ULONG DataRateMax; + ULONG GuaranteedRate; +} QOS_FLOW_TLV_IP_FLOW_DATA_RATE_MIN_MAX, *PQOS_FLOW_TLV_IP_FLOW_DATA_RATE_MIN_MAX; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_DATA_RATE_TOKEN_BUCKET +{ + UCHAR TLVType; // 0x13 + USHORT TLVLength; // 12 + ULONG PeakRate; + ULONG TokenRate; + ULONG BucketSize; +} QOS_FLOW_TLV_IP_FLOW_DATA_RATE_TOKEN_BUCKET, *PQOS_FLOW_TLV_IP_FLOW_DATA_RATE_TOKEN_BUCKET; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_LATENCY +{ + UCHAR TLVType; // 0x14 + USHORT TLVLength; // 4 + ULONG IpFlowLatency; +} QOS_FLOW_TLV_IP_FLOW_LATENCY, *PQOS_FLOW_TLV_IP_FLOW_LATENCY; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_JITTER +{ + UCHAR TLVType; // 0x15 + USHORT TLVLength; // 4 + ULONG IpFlowJitter; +} QOS_FLOW_TLV_IP_FLOW_JITTER, *PQOS_FLOW_TLV_IP_FLOW_JITTER; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_PKT_ERR_RATE +{ + UCHAR TLVType; // 0x16 + USHORT TLVLength; // 4 + USHORT ErrRateMultiplier; + USHORT ErrRateExponent; +} QOS_FLOW_TLV_IP_FLOW_PKT_ERR_RATE, *PQOS_FLOW_TLV_IP_FLOW_PKT_ERR_RATE; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_MIN_PKT_SIZE +{ + UCHAR TLVType; // 0x17 + USHORT TLVLength; // 4 + ULONG MinPolicedPktSize; +} QOS_FLOW_TLV_IP_FLOW_MIN_PKT_SIZE, *PQOS_FLOW_TLV_IP_FLOW_MIN_PKT_SIZE; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_MAX_PKT_SIZE +{ + UCHAR TLVType; // 0x18 + USHORT TLVLength; // 4 + ULONG MaxAllowedPktSize; +} QOS_FLOW_TLV_IP_FLOW_MAX_PKT_SIZE, *PQOS_FLOW_TLV_IP_FLOW_MAX_PKT_SIZE; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_3GPP_BIT_ERR_RATE +{ + UCHAR TLVType; // 0x19 + USHORT TLVLength; // 1 + UCHAR ResidualBitErrorRate; +} QOS_FLOW_TLV_IP_FLOW_3GPP_BIT_ERR_RATE, *PQOS_FLOW_TLV_IP_FLOW_3GPP_BIT_ERR_RATE; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_3GPP_TRAF_PRIORITY +{ + UCHAR TLVType; // 0x1A + USHORT TLVLength; // 1 + UCHAR TrafficHandlingPriority; +} QOS_FLOW_TLV_IP_FLOW_3GPP_TRAF_PRIORITY, *PQOS_FLOW_TLV_IP_FLOW_3GPP_TRAF_PRIORITY; + +typedef struct _QOS_FLOW_TLV_IP_FLOW_3GPP2_PROFILE_ID +{ + UCHAR TLVType; // 0x1B + USHORT TLVLength; // 2 + USHORT ProfileId; +} QOS_FLOW_TLV_IP_FLOW_3GPP2_PROFILE_ID, *PQOS_FLOW_TLV_IP_FLOW_3GPP2_PROFILE_ID; + +// QOS Filter + +#define QOS_FILTER_TLV_IP_FILTER_IDX_TYPE 0x10 +#define QOS_FILTER_TLV_IP_VERSION_TYPE 0x11 +#define QOS_FILTER_TLV_IPV4_SRC_ADDR_TYPE 0x12 +#define QOS_FILTER_TLV_IPV4_DEST_ADDR_TYPE 0x13 +#define QOS_FILTER_TLV_NEXT_HDR_PROTOCOL_TYPE 0x14 +#define QOS_FILTER_TLV_IPV4_TYPE_OF_SERVICE_TYPE 0x15 +#define QOS_FILTER_TLV_TCP_UDP_PORT_SRC_TCP_TYPE 0x1B +#define QOS_FILTER_TLV_TCP_UDP_PORT_DEST_TCP_TYPE 0x1C +#define QOS_FILTER_TLV_TCP_UDP_PORT_SRC_UDP_TYPE 0x1D +#define QOS_FILTER_TLV_TCP_UDP_PORT_DEST_UDP_TYPE 0x1E +#define QOS_FILTER_TLV_ICMP_FILTER_MSG_TYPE_TYPE 0x1F +#define QOS_FILTER_TLV_ICMP_FILTER_MSG_CODE_TYPE 0x20 +#define QOS_FILTER_TLV_TCP_UDP_PORT_SRC_TYPE 0x24 +#define QOS_FILTER_TLV_TCP_UDP_PORT_DEST_TYPE 0x25 + +typedef struct _QOS_EVENT_RPT_IND_TLV_FILTER +{ + UCHAR TLVType; // 0x12-TX filter; 0x13-RX filter + USHORT TLVLength; // var + // embedded TLV's +} QOS_EVENT_RPT_IND_TLV_RX_FILTER, *PQOS_EVENT_RPT_IND_TLV_RX_FILTER; + +typedef struct _QOS_FILTER_TLV_IP_FILTER_IDX +{ + UCHAR TLVType; // 0x10 + USHORT TLVLength; // 1 + UCHAR IpFilterIndex; +} QOS_FILTER_TLV_IP_FILTER_IDX, *PQOS_FILTER_TLV_IP_FILTER_IDX; + +typedef struct _QOS_FILTER_TLV_IP_VERSION +{ + UCHAR TLVType; // 0x11 + USHORT TLVLength; // 1 + UCHAR IpVersion; +} QOS_FILTER_TLV_IP_VERSION, *PQOS_FILTER_TLV_IP_VERSION; + +typedef struct _QOS_FILTER_TLV_IPV4_SRC_ADDR +{ + UCHAR TLVType; // 0x12 + USHORT TLVLength; // 8 + ULONG IpSrcAddr; + ULONG IpSrcSubnetMask; +} QOS_FILTER_TLV_IPV4_SRC_ADDR, *PQOS_FILTER_TLV_IPV4_SRC_ADDR; + +typedef struct _QOS_FILTER_TLV_IPV4_DEST_ADDR +{ + UCHAR TLVType; // 0x13 + USHORT TLVLength; // 8 + ULONG IpDestAddr; + ULONG IpDestSubnetMask; +} QOS_FILTER_TLV_IPV4_DEST_ADDR, *PQOS_FILTER_TLV_IPV4_DEST_ADDR; + +typedef struct _QOS_FILTER_TLV_NEXT_HDR_PROTOCOL +{ + UCHAR TLVType; // 0x14 + USHORT TLVLength; // 1 + UCHAR NextHdrProtocol; +} QOS_FILTER_TLV_NEXT_HDR_PROTOCOL, *PQOS_FILTER_TLV_NEXT_HDR_PROTOCOL; + +typedef struct _QOS_FILTER_TLV_IPV4_TYPE_OF_SERVICE +{ + UCHAR TLVType; // 0x15 + USHORT TLVLength; // 2 + UCHAR Ipv4TypeOfService; + UCHAR Ipv4TypeOfServiceMask; +} QOS_FILTER_TLV_IPV4_TYPE_OF_SERVICE, *PQOS_FILTER_TLV_IPV4_TYPE_OF_SERVICE; + +typedef struct _QOS_FILTER_TLV_TCP_UDP_PORT +{ + UCHAR TLVType; // source port: 0x1B-TCP; 0x1D-UDP + // dest port: 0x1C-TCP; 0x1E-UDP + USHORT TLVLength; // 4 + USHORT FilterPort; + USHORT FilterPortRange; +} QOS_FILTER_TLV_TCP_UDP_PORT, *PQOS_FILTER_TLV_TCP_UDP_PORT; + +typedef struct _QOS_FILTER_TLV_ICMP_FILTER_MSG_TYPE +{ + UCHAR TLVType; // 0x1F + USHORT TLVLength; // 1 + UCHAR IcmpFilterMsgType; +} QOS_FILTER_TLV_ICMP_FILTER_MSG_TYPE, *PQOS_FILTER_TLV_ICMP_FILTER_MSG_TYPE; + +typedef struct _QOS_FILTER_TLV_ICMP_FILTER_MSG_CODE +{ + UCHAR TLVType; // 0x20 + USHORT TLVLength; // 1 + UCHAR IcmpFilterMsgCode; +} QOS_FILTER_TLV_ICMP_FILTER_MSG_CODE, *PQOS_FILTER_TLV_ICMP_FILTER_MSG_CODE; + +#define QOS_FILTER_PRECEDENCE_INVALID 256 +#define QOS_FILTER_TLV_PRECEDENCE_TYPE 0x22 +#define QOS_FILTER_TLV_ID_TYPE 0x23 + +typedef struct _QOS_FILTER_TLV_PRECEDENCE +{ + UCHAR TLVType; // 0x22 + USHORT TLVLength; // 2 + USHORT Precedence; // precedence of the filter +} QOS_FILTER_TLV_PRECEDENCE, *PQOS_FILTER_TLV_PRECEDENCE; + +typedef struct _QOS_FILTER_TLV_ID +{ + UCHAR TLVType; // 0x23 + USHORT TLVLength; // 2 + USHORT FilterId; // filter ID +} QOS_FILTER_TLV_ID, *PQOS_FILTER_TLV_ID; + +#ifdef QCQOS_IPV6 + +#define QOS_FILTER_TLV_IPV6_SRC_ADDR_TYPE 0x16 +#define QOS_FILTER_TLV_IPV6_DEST_ADDR_TYPE 0x17 +#define QOS_FILTER_TLV_IPV6_NEXT_HDR_PROTOCOL_TYPE 0x14 // same as IPV4 +#define QOS_FILTER_TLV_IPV6_TRAFFIC_CLASS_TYPE 0x19 +#define QOS_FILTER_TLV_IPV6_FLOW_LABEL_TYPE 0x1A + +typedef struct _QOS_FILTER_TLV_IPV6_SRC_ADDR +{ + UCHAR TLVType; // 0x16 + USHORT TLVLength; // 17 + UCHAR IpSrcAddr[16]; + UCHAR IpSrcAddrPrefixLen; // [0..128] +} QOS_FILTER_TLV_IPV6_SRC_ADDR, *PQOS_FILTER_TLV_IPV6_SRC_ADDR; + +typedef struct _QOS_FILTER_TLV_IPV6_DEST_ADDR +{ + UCHAR TLVType; // 0x17 + USHORT TLVLength; // 17 + UCHAR IpDestAddr[16]; + UCHAR IpDestAddrPrefixLen; // [0..128] +} QOS_FILTER_TLV_IPV6_DEST_ADDR, *PQOS_FILTER_TLV_IPV6_DEST_ADDR; + +#define QOS_FILTER_IPV6_NEXT_HDR_PROTOCOL_TCP 0x06 +#define QOS_FILTER_IPV6_NEXT_HDR_PROTOCOL_UDP 0x11 + +typedef struct _QOS_FILTER_TLV_IPV6_TRAFFIC_CLASS +{ + UCHAR TLVType; // 0x19 + USHORT TLVLength; // 2 + UCHAR TrafficClass; + UCHAR TrafficClassMask; // compare the first 6 bits only +} QOS_FILTER_TLV_IPV6_TRAFFIC_CLASS, *PQOS_FILTER_TLV_IPV6_TRAFFIC_CLASS; + +typedef struct _QOS_FILTER_TLV_IPV6_FLOW_LABEL +{ + UCHAR TLVType; // 0x1A + USHORT TLVLength; // 4 + ULONG FlowLabel; +} QOS_FILTER_TLV_IPV6_FLOW_LABEL, *PQOS_FILTER_TLV_IPV6_FLOW_LABEL; + +#endif // QCQOS_IPV6 +#endif + +// ======================= WMS ============================== +#define QMIWMS_SET_EVENT_REPORT_REQ 0x0001 +#define QMIWMS_SET_EVENT_REPORT_RESP 0x0001 +#define QMIWMS_EVENT_REPORT_IND 0x0001 +#define QMIWMS_RAW_SEND_REQ 0x0020 +#define QMIWMS_RAW_SEND_RESP 0x0020 +#define QMIWMS_RAW_WRITE_REQ 0x0021 +#define QMIWMS_RAW_WRITE_RESP 0x0021 +#define QMIWMS_RAW_READ_REQ 0x0022 +#define QMIWMS_RAW_READ_RESP 0x0022 +#define QMIWMS_MODIFY_TAG_REQ 0x0023 +#define QMIWMS_MODIFY_TAG_RESP 0x0023 +#define QMIWMS_DELETE_REQ 0x0024 +#define QMIWMS_DELETE_RESP 0x0024 +#define QMIWMS_GET_MESSAGE_PROTOCOL_REQ 0x0030 +#define QMIWMS_GET_MESSAGE_PROTOCOL_RESP 0x0030 +#define QMIWMS_LIST_MESSAGES_REQ 0x0031 +#define QMIWMS_LIST_MESSAGES_RESP 0x0031 +#define QMIWMS_GET_SMSC_ADDRESS_REQ 0x0034 +#define QMIWMS_GET_SMSC_ADDRESS_RESP 0x0034 +#define QMIWMS_SET_SMSC_ADDRESS_REQ 0x0035 +#define QMIWMS_SET_SMSC_ADDRESS_RESP 0x0035 +#define QMIWMS_GET_STORE_MAX_SIZE_REQ 0x0036 +#define QMIWMS_GET_STORE_MAX_SIZE_RESP 0x0036 + + +#define WMS_MESSAGE_PROTOCOL_CDMA 0x00 +#define WMS_MESSAGE_PROTOCOL_WCDMA 0x01 + +#if 0 +typedef struct _QMIWMS_GET_MESSAGE_PROTOCOL_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; +} QMIWMS_GET_MESSAGE_PROTOCOL_REQ_MSG, *PQMIWMS_GET_MESSAGE_PROTOCOL_REQ_MSG; + +typedef struct _QMIWMS_GET_MESSAGE_PROTOCOL_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR MessageProtocol; +} QMIWMS_GET_MESSAGE_PROTOCOL_RESP_MSG, *PQMIWMS_GET_MESSAGE_PROTOCOL_RESP_MSG; + +typedef struct _QMIWMS_GET_STORE_MAX_SIZE_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR StorageType; +} QMIWMS_GET_STORE_MAX_SIZE_REQ_MSG, *PQMIWMS_GET_STORE_MAX_SIZE_REQ_MSG; + +typedef struct _QMIWMS_GET_STORE_MAX_SIZE_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + ULONG MemStoreMaxSize; +} QMIWMS_GET_STORE_MAX_SIZE_RESP_MSG, *PQMIWMS_GET_STORE_MAX_SIZE_RESP_MSG; + +typedef struct _REQUEST_TAG +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR TagType; +} REQUEST_TAG, *PREQUEST_TAG; + +typedef struct _QMIWMS_LIST_MESSAGES_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR StorageType; +} QMIWMS_LIST_MESSAGES_REQ_MSG, *PQMIWMS_LIST_MESSAGES_REQ_MSG; + +typedef struct _QMIWMS_MESSAGE +{ + ULONG MessageIndex; + UCHAR TagType; +} QMIWMS_MESSAGE, *PQMIWMS_MESSAGE; + +typedef struct _QMIWMS_LIST_MESSAGES_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + ULONG NumMessages; +} QMIWMS_LIST_MESSAGES_RESP_MSG, *PQMIWMS_LIST_MESSAGES_RESP_MSG; + +typedef struct _QMIWMS_RAW_READ_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR StorageType; + ULONG MemoryIndex; +} QMIWMS_RAW_READ_REQ_MSG, *PQMIWMS_RAW_READ_REQ_MSG; + +typedef struct _QMIWMS_RAW_READ_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR TagType; + UCHAR Format; + USHORT MessageLength; + UCHAR Message; +} QMIWMS_RAW_READ_RESP_MSG, *PQMIWMS_RAW_READ_RESP_MSG; + +typedef struct _QMIWMS_MODIFY_TAG_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR StorageType; + ULONG MemoryIndex; + UCHAR TagType; +} QMIWMS_MODIFY_TAG_REQ_MSG, *PQMIWMS_MODIFY_TAG_REQ_MSG; + +typedef struct _QMIWMS_MODIFY_TAG_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} QMIWMS_MODIFY_TAG_RESP_MSG, *PQMIWMS_MODIFY_TAG_RESP_MSG; + +typedef struct _QMIWMS_RAW_SEND_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR SmsFormat; + USHORT SmsLength; + UCHAR SmsMessage; +} QMIWMS_RAW_SEND_REQ_MSG, *PQMIWMS_RAW_SEND_REQ_MSG; + +typedef struct _RAW_SEND_CAUSE_CODE +{ + UCHAR TLVType; + USHORT TLVLength; + USHORT CauseCode; +} RAW_SEND_CAUSE_CODE, *PRAW_SEND_CAUSE_CODE; + + +typedef struct _QMIWMS_RAW_SEND_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} QMIWMS_RAW_SEND_RESP_MSG, *PQMIWMS_RAW_SEND_RESP_MSG; + + +typedef struct _WMS_DELETE_MESSAGE_INDEX +{ + UCHAR TLVType; + USHORT TLVLength; + ULONG MemoryIndex; +} WMS_DELETE_MESSAGE_INDEX, *PWMS_DELETE_MESSAGE_INDEX; + +typedef struct _WMS_DELETE_MESSAGE_TAG +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR MessageTag; +} WMS_DELETE_MESSAGE_TAG, *PWMS_DELETE_MESSAGE_TAG; + +typedef struct _QMIWMS_DELETE_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR StorageType; +} QMIWMS_DELETE_REQ_MSG, *PQMIWMS_DELETE_REQ_MSG; + +typedef struct _QMIWMS_DELETE_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} QMIWMS_DELETE_RESP_MSG, *PQMIWMS_DELETE_RESP_MSG; + + +typedef struct _QMIWMS_GET_SMSC_ADDRESS_REQ_MSG +{ + USHORT Type; + USHORT Length; +} QMIWMS_GET_SMSC_ADDRESS_REQ_MSG, *PQMIWMS_GET_SMSC_ADDRESS_REQ_MSG; + +typedef struct _QMIWMS_SMSC_ADDRESS +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR SMSCAddressType[3]; + UCHAR SMSCAddressLength; + UCHAR SMSCAddressDigits; +} QMIWMS_SMSC_ADDRESS, *PQMIWMS_SMSC_ADDRESS; + + +typedef struct _QMIWMS_GET_SMSC_ADDRESS_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; + UCHAR SMSCAddress; +} QMIWMS_GET_SMSC_ADDRESS_RESP_MSG, *PQMIWMS_GET_SMSC_ADDRESS_RESP_MSG; + +typedef struct _QMIWMS_SET_SMSC_ADDRESS_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR SMSCAddress; +} QMIWMS_SET_SMSC_ADDRESS_REQ_MSG, *PQMIWMS_SET_SMSC_ADDRESS_REQ_MSG; + +typedef struct _QMIWMS_SET_SMSC_ADDRESS_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} QMIWMS_SET_SMSC_ADDRESS_RESP_MSG, *PQMIWMS_SET_SMSC_ADDRESS_RESP_MSG; + +typedef struct _QMIWMS_SET_EVENT_REPORT_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR ReportNewMessage; +} QMIWMS_SET_EVENT_REPORT_REQ_MSG, *PQMIWMS_SET_EVENT_REPORT_REQ_MSG; + +typedef struct _QMIWMS_SET_EVENT_REPORT_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} QMIWMS_SET_EVENT_REPORT_RESP_MSG, *PQMIWMS_SET_EVENT_REPORT_RESP_MSG; + +typedef struct _QMIWMS_EVENT_REPORT_IND_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR StorageType; + ULONG StorageIndex; +} QMIWMS_EVENT_REPORT_IND_MSG, *PQMIWMS_EVENT_REPORT_IND_MSG; +#endif + +// ======================= End of WMS ============================== + + +// ======================= NAS ============================== +#define QMINAS_SET_EVENT_REPORT_REQ 0x0002 +#define QMINAS_SET_EVENT_REPORT_RESP 0x0002 +#define QMINAS_EVENT_REPORT_IND 0x0002 +#define QMINAS_GET_SIGNAL_STRENGTH_REQ 0x0020 +#define QMINAS_GET_SIGNAL_STRENGTH_RESP 0x0020 +#define QMINAS_PERFORM_NETWORK_SCAN_REQ 0x0021 +#define QMINAS_PERFORM_NETWORK_SCAN_RESP 0x0021 +#define QMINAS_INITIATE_NW_REGISTER_REQ 0x0022 +#define QMINAS_INITIATE_NW_REGISTER_RESP 0x0022 +#define QMINAS_INITIATE_ATTACH_REQ 0x0023 +#define QMINAS_INITIATE_ATTACH_RESP 0x0023 +#define QMINAS_GET_SERVING_SYSTEM_REQ 0x0024 +#define QMINAS_GET_SERVING_SYSTEM_RESP 0x0024 +#define QMINAS_SERVING_SYSTEM_IND 0x0024 +#define QMINAS_GET_HOME_NETWORK_REQ 0x0025 +#define QMINAS_GET_HOME_NETWORK_RESP 0x0025 +#define QMINAS_GET_PREFERRED_NETWORK_REQ 0x0026 +#define QMINAS_GET_PREFERRED_NETWORK_RESP 0x0026 +#define QMINAS_SET_PREFERRED_NETWORK_REQ 0x0027 +#define QMINAS_SET_PREFERRED_NETWORK_RESP 0x0027 +#define QMINAS_GET_FORBIDDEN_NETWORK_REQ 0x0028 +#define QMINAS_GET_FORBIDDEN_NETWORK_RESP 0x0028 +#define QMINAS_SET_FORBIDDEN_NETWORK_REQ 0x0029 +#define QMINAS_SET_FORBIDDEN_NETWORK_RESP 0x0029 +#define QMINAS_SET_TECHNOLOGY_PREF_REQ 0x002A +#define QMINAS_SET_TECHNOLOGY_PREF_RESP 0x002A +#define QMINAS_GET_RF_BAND_INFO_REQ 0x0031 +#define QMINAS_GET_RF_BAND_INFO_RESP 0x0031 +#define QMINAS_GET_PLMN_NAME_REQ 0x0044 +#define QMINAS_GET_PLMN_NAME_RESP 0x0044 +#define MEIG_PACKET_TRANSFER_START_IND 0X100 +#define MEIG_PACKET_TRANSFER_END_IND 0X101 +#define QMINAS_GET_SYS_INFO_REQ 0x004D +#define QMINAS_GET_SYS_INFO_RESP 0x004D +#define QMINAS_SYS_INFO_IND 0x004D + +typedef struct _QMINAS_GET_HOME_NETWORK_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; +} __attribute__ ((packed)) QMINAS_GET_HOME_NETWORK_REQ_MSG, *PQMINAS_GET_HOME_NETWORK_REQ_MSG; + +typedef struct _HOME_NETWORK_SYSTEMID +{ + UCHAR TLVType; + USHORT TLVLength; + USHORT SystemID; + USHORT NetworkID; +} __attribute__ ((packed)) HOME_NETWORK_SYSTEMID, *PHOME_NETWORK_SYSTEMID; + +typedef struct _HOME_NETWORK +{ + UCHAR TLVType; + USHORT TLVLength; + USHORT MobileCountryCode; + USHORT MobileNetworkCode; + UCHAR NetworkDesclen; + UCHAR NetworkDesc; +} __attribute__ ((packed)) HOME_NETWORK, *PHOME_NETWORK; + +#if 0 +typedef struct _HOME_NETWORK_EXT +{ + UCHAR TLVType; + USHORT TLVLength; + USHORT MobileCountryCode; + USHORT MobileNetworkCode; + UCHAR NetworkDescDisp; + UCHAR NetworkDescEncoding; + UCHAR NetworkDesclen; + UCHAR NetworkDesc; +} HOME_NETWORK_EXT, *PHOME_NETWORK_EXT; + +typedef struct _QMINAS_GET_HOME_NETWORK_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} QMINAS_GET_HOME_NETWORK_RESP_MSG, *PQMINAS_GET_HOME_NETWORK_RESP_MSG; + +typedef struct _QMINAS_GET_PREFERRED_NETWORK_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; +} QMINAS_GET_PREFERRED_NETWORK_REQ_MSG, *PQMINAS_GET_PREFERRED_NETWORK_REQ_MSG; + + +typedef struct _PREFERRED_NETWORK +{ + USHORT MobileCountryCode; + USHORT MobileNetworkCode; + USHORT RadioAccess; +} PREFERRED_NETWORK, *PPREFERRED_NETWORK; + +typedef struct _QMINAS_GET_PREFERRED_NETWORK_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; // 0x01 - required parameter + USHORT TLV2Length; // length of the mfr string + USHORT NumPreferredNetwork; +} QMINAS_GET_PREFERRED_NETWORK_RESP_MSG, *PQMINAS_GET_PREFERRED_NETWORK_RESP_MSG; + +typedef struct _QMINAS_GET_FORBIDDEN_NETWORK_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; +} QMINAS_GET_FORBIDDEN_NETWORK_REQ_MSG, *PQMINAS_GET_FORBIDDEN_NETWORK_REQ_MSG; + +typedef struct _FORBIDDEN_NETWORK +{ + USHORT MobileCountryCode; + USHORT MobileNetworkCode; +} FORBIDDEN_NETWORK, *PFORBIDDEN_NETWORK; + +typedef struct _QMINAS_GET_FORBIDDEN_NETWORK_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; // 0x01 - required parameter + USHORT TLV2Length; // length of the mfr string + USHORT NumForbiddenNetwork; +} QMINAS_GET_FORBIDDEN_NETWORK_RESP_MSG, *PQMINAS_GET_FORBIDDEN_NETWORK_RESP_MSG; + +typedef struct _QMINAS_GET_SERVING_SYSTEM_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; +} QMINAS_GET_SERVING_SYSTEM_REQ_MSG, *PQMINAS_GET_SERVING_SYSTEM_REQ_MSG; + +typedef struct _QMINAS_ROAMING_INDICATOR_MSG +{ + UCHAR TLVType; // 0x01 - required parameter + USHORT TLVLength; // length of the mfr string + UCHAR RoamingIndicator; +} QMINAS_ROAMING_INDICATOR_MSG, *PQMINAS_ROAMING_INDICATOR_MSG; +#endif + +typedef struct _QMINAS_DATA_CAP +{ + UCHAR TLVType; // 0x01 - required parameter + USHORT TLVLength; // length of the mfr string + UCHAR DataCapListLen; + UCHAR DataCap; +} __attribute__ ((packed)) QMINAS_DATA_CAP, *PQMINAS_DATA_CAP; + +typedef struct _QMINAS_CURRENT_PLMN_MSG +{ + UCHAR TLVType; // 0x01 - required parameter + USHORT TLVLength; // length of the mfr string + USHORT MobileCountryCode; + USHORT MobileNetworkCode; + UCHAR NetworkDesclen; + UCHAR NetworkDesc; +} __attribute__ ((packed)) QMINAS_CURRENT_PLMN_MSG, *PQMINAS_CURRENT_PLMN_MSG; + +typedef struct _QMINAS_GET_SERVING_SYSTEM_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} __attribute__ ((packed)) QMINAS_GET_SERVING_SYSTEM_RESP_MSG, *PQMINAS_GET_SERVING_SYSTEM_RESP_MSG; + +typedef struct _SERVING_SYSTEM +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR RegistrationState; + UCHAR CSAttachedState; + UCHAR PSAttachedState; + UCHAR RegistredNetwork; + UCHAR InUseRadioIF; + UCHAR RadioIF; +} __attribute__ ((packed)) SERVING_SYSTEM, *PSERVING_SYSTEM; + +typedef struct _QMINAS_GET_SYS_INFO_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} __attribute__ ((packed)) QMINAS_GET_SYS_INFO_RESP_MSG, *PQMINAS_GET_SYS_INFO_RESP_MSG; + +typedef struct _QMINAS_SYS_INFO_IND_MSG +{ + USHORT Type; + USHORT Length; +} __attribute__ ((packed)) QMINAS_SYS_INFO_IND_MSG, *PQMINAS_SYS_INFO_IND_MSG; + +typedef struct _SERVICE_STATUS_INFO +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR SrvStatus; + UCHAR true_srv_status; + UCHAR IsPrefDataPath; +} __attribute__ ((packed)) SERVICE_STATUS_INFO, *PSERVICE_STATUS_INFO; + +typedef struct _CDMA_SYSTEM_INFO +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR SrvDomainValid; + UCHAR SrvDomain; + UCHAR SrvCapabilityValid; + UCHAR SrvCapability; + UCHAR RoamStatusValid; + UCHAR RoamStatus; + UCHAR IsSysForbiddenValid; + UCHAR IsSysForbidden; + UCHAR IsSysPrlMatchValid; + UCHAR IsSysPrlMatch; + UCHAR PRevInUseValid; + UCHAR PRevInUse; + UCHAR BSPRevValid; + UCHAR BSPRev; + UCHAR CCSSupportedValid; + UCHAR CCSSupported; + UCHAR CDMASysIdValid; + USHORT SID; + USHORT NID; + UCHAR BSInfoValid; + USHORT BaseID; + ULONG BaseLAT; + ULONG BaseLONG; + UCHAR PacketZoneValid; + USHORT PacketZone; + UCHAR NetworkIdValid; + UCHAR MCC[3]; + UCHAR MNC[3]; +} __attribute__ ((packed)) CDMA_SYSTEM_INFO, *PCDMA_SYSTEM_INFO; + +typedef struct _HDR_SYSTEM_INFO +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR SrvDomainValid; + UCHAR SrvDomain; + UCHAR SrvCapabilityValid; + UCHAR SrvCapability; + UCHAR RoamStatusValid; + UCHAR RoamStatus; + UCHAR IsSysForbiddenValid; + UCHAR IsSysForbidden; + UCHAR IsSysPrlMatchValid; + UCHAR IsSysPrlMatch; + UCHAR HdrPersonalityValid; + UCHAR HdrPersonality; + UCHAR HdrActiveProtValid; + UCHAR HdrActiveProt; + UCHAR is856SysIdValid; + UCHAR is856SysId[16]; +} __attribute__ ((packed)) HDR_SYSTEM_INFO, *PHDR_SYSTEM_INFO; + +typedef struct _GSM_SYSTEM_INFO +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR SrvDomainValid; + UCHAR SrvDomain; + UCHAR SrvCapabilityValid; + UCHAR SrvCapability; + UCHAR RoamStatusValid; + UCHAR RoamStatus; + UCHAR IsSysForbiddenValid; + UCHAR IsSysForbidden; + UCHAR LacValid; + USHORT Lac; + UCHAR CellIdValid; + ULONG CellId; + UCHAR RegRejectInfoValid; + UCHAR RejectSrvDomain; + UCHAR RejCause; + UCHAR NetworkIdValid; + UCHAR MCC[3]; + UCHAR MNC[3]; + UCHAR EgprsSuppValid; + UCHAR EgprsSupp; + UCHAR DtmSuppValid; + UCHAR DtmSupp; +} __attribute__ ((packed)) GSM_SYSTEM_INFO, *PGSM_SYSTEM_INFO; + +typedef struct _WCDMA_SYSTEM_INFO +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR SrvDomainValid; + UCHAR SrvDomain; + UCHAR SrvCapabilityValid; + UCHAR SrvCapability; + UCHAR RoamStatusValid; + UCHAR RoamStatus; + UCHAR IsSysForbiddenValid; + UCHAR IsSysForbidden; + UCHAR LacValid; + USHORT Lac; + UCHAR CellIdValid; + ULONG CellId; + UCHAR RegRejectInfoValid; + UCHAR RejectSrvDomain; + UCHAR RejCause; + UCHAR NetworkIdValid; + UCHAR MCC[3]; + UCHAR MNC[3]; + UCHAR HsCallStatusValid; + UCHAR HsCallStatus; + UCHAR HsIndValid; + UCHAR HsInd; + UCHAR PscValid; + UCHAR Psc; +} __attribute__ ((packed)) WCDMA_SYSTEM_INFO, *PWCDMA_SYSTEM_INFO; + +typedef struct _LTE_SYSTEM_INFO +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR SrvDomainValid; + UCHAR SrvDomain; + UCHAR SrvCapabilityValid; + UCHAR SrvCapability; + UCHAR RoamStatusValid; + UCHAR RoamStatus; + UCHAR IsSysForbiddenValid; + UCHAR IsSysForbidden; + UCHAR LacValid; + USHORT Lac; + UCHAR CellIdValid; + ULONG CellId; + UCHAR RegRejectInfoValid; + UCHAR RejectSrvDomain; + UCHAR RejCause; + UCHAR NetworkIdValid; + UCHAR MCC[3]; + UCHAR MNC[3]; + UCHAR TacValid; + USHORT Tac; +} __attribute__ ((packed)) LTE_SYSTEM_INFO, *PLTE_SYSTEM_INFO; + +typedef struct _TDSCDMA_SYSTEM_INFO +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR SrvDomainValid; + UCHAR SrvDomain; + UCHAR SrvCapabilityValid; + UCHAR SrvCapability; + UCHAR RoamStatusValid; + UCHAR RoamStatus; + UCHAR IsSysForbiddenValid; + UCHAR IsSysForbidden; + UCHAR LacValid; + USHORT Lac; + UCHAR CellIdValid; + ULONG CellId; + UCHAR RegRejectInfoValid; + UCHAR RejectSrvDomain; + UCHAR RejCause; + UCHAR NetworkIdValid; + UCHAR MCC[3]; + UCHAR MNC[3]; + UCHAR HsCallStatusValid; + UCHAR HsCallStatus; + UCHAR HsIndValid; + UCHAR HsInd; + UCHAR CellParameterIdValid; + USHORT CellParameterId; + UCHAR CellBroadcastCapValid; + ULONG CellBroadcastCap; + UCHAR CsBarStatusValid; + ULONG CsBarStatus; + UCHAR PsBarStatusValid; + ULONG PsBarStatus; + UCHAR CipherDomainValid; + UCHAR CipherDomain; +} __attribute__ ((packed)) TDSCDMA_SYSTEM_INFO, *PTDSCDMA_SYSTEM_INFO; + +#if 0 +typedef struct _QMINAS_SERVING_SYSTEM_IND_MSG +{ + USHORT Type; + USHORT Length; +} QMINAS_SERVING_SYSTEM_IND_MSG, *PQMINAS_SERVING_SYSTEM_IND_MSG; + +typedef struct _QMINAS_SET_PREFERRED_NETWORK_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT NumPreferredNetwork; + USHORT MobileCountryCode; + USHORT MobileNetworkCode; + USHORT RadioAccess; +} QMINAS_SET_PREFERRED_NETWORK_REQ_MSG, *PQMINAS_SET_PREFERRED_NETWORK_REQ_MSG; + +typedef struct _QMINAS_SET_PREFERRED_NETWORK_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMINAS_SET_PREFERRED_NETWORK_RESP_MSG, *PQMINAS_SET_PREFERRED_NETWORK_RESP_MSG; + +typedef struct _QMINAS_SET_FORBIDDEN_NETWORK_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT NumForbiddenNetwork; + USHORT MobileCountryCode; + USHORT MobileNetworkCode; +} QMINAS_SET_FORBIDDEN_NETWORK_REQ_MSG, *PQMINAS_SET_FORBIDDEN_NETWORK_REQ_MSG; + +typedef struct _QMINAS_SET_FORBIDDEN_NETWORK_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMINAS_SET_FORBIDDEN_NETWORK_RESP_MSG, *PQMINAS_SET_FORBIDDEN_NETWORK_RESP_MSG; + +typedef struct _QMINAS_PERFORM_NETWORK_SCAN_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; +} QMINAS_PERFORM_NETWORK_SCAN_REQ_MSG, *PQMINAS_PERFORM_NETWORK_SCAN_REQ_MSG; + +typedef struct _VISIBLE_NETWORK +{ + USHORT MobileCountryCode; + USHORT MobileNetworkCode; + UCHAR NetworkStatus; + UCHAR NetworkDesclen; +} VISIBLE_NETWORK, *PVISIBLE_NETWORK; + +typedef struct _QMINAS_PERFORM_NETWORK_SCAN_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMINAS_PERFORM_NETWORK_SCAN_RESP_MSG, *PQMINAS_PERFORM_NETWORK_SCAN_RESP_MSG; + +typedef struct _QMINAS_PERFORM_NETWORK_SCAN_NETWORK_INFO +{ + UCHAR TLVType; // 0x010 - required parameter + USHORT TLVLength; // length + USHORT NumNetworkInstances; +} QMINAS_PERFORM_NETWORK_SCAN_NETWORK_INFO, *PQMINAS_PERFORM_NETWORK_SCAN_NETWORK_INFO; + +typedef struct _QMINAS_PERFORM_NETWORK_SCAN_RAT_INFO +{ + UCHAR TLVType; // 0x011 - required parameter + USHORT TLVLength; // length + USHORT NumInst; +} QMINAS_PERFORM_NETWORK_SCAN_RAT_INFO, *PQMINAS_PERFORM_NETWORK_SCAN_RAT_INFO; + +typedef struct _QMINAS_PERFORM_NETWORK_SCAN_RAT +{ + USHORT MCC; + USHORT MNC; + UCHAR RAT; +} QMINAS_PERFORM_NETWORK_SCAN_RAT, *PQMINAS_PERFORM_NETWORK_SCAN_RAT; + + +typedef struct _QMINAS_MANUAL_NW_REGISTER +{ + UCHAR TLV2Type; // 0x02 - result code + USHORT TLV2Length; // 4 + USHORT MobileCountryCode; + USHORT MobileNetworkCode; + UCHAR RadioAccess; +} QMINAS_MANUAL_NW_REGISTER, *PQMINAS_MANUAL_NW_REGISTER; + +typedef struct _QMINAS_INITIATE_NW_REGISTER_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + UCHAR RegisterAction; +} QMINAS_INITIATE_NW_REGISTER_REQ_MSG, *PQMINAS_INITIATE_NW_REGISTER_REQ_MSG; + +typedef struct _QMINAS_INITIATE_NW_REGISTER_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMINAS_INITIATE_NW_REGISTER_RESP_MSG, *PQMINAS_INITIATE_NW_REGISTER_RESP_MSG; + +typedef struct _QMINAS_SET_TECHNOLOGY_PREF_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT TechPref; + UCHAR Duration; +} QMINAS_SET_TECHNOLOGY_PREF_REQ_MSG, *PQMINAS_SET_TECHNOLOGY_PREF_REQ_MSG; + +typedef struct _QMINAS_SET_TECHNOLOGY_PREF_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMINAS_SET_TECHNOLOGY_PREF_RESP_MSG, *PQMINAS_SET_TECHNOLOGY_PREF_RESP_MSG; + +typedef struct _QMINAS_GET_SIGNAL_STRENGTH_REQ_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; +} QMINAS_GET_SIGNAL_STRENGTH_REQ_MSG, *PQMINAS_GET_SIGNAL_STRENGTH_REQ_MSG; + +typedef struct _QMINAS_SIGNAL_STRENGTH +{ + CHAR SigStrength; + UCHAR RadioIf; +} QMINAS_SIGNAL_STRENGTH, *PQMINAS_SIGNAL_STRENGTH; + +typedef struct _QMINAS_SIGNAL_STRENGTH_LIST +{ + UCHAR TLV3Type; + USHORT TLV3Length; + USHORT NumInstance; +} QMINAS_SIGNAL_STRENGTH_LIST, *PQMINAS_SIGNAL_STRENGTH_LIST; + + +typedef struct _QMINAS_GET_SIGNAL_STRENGTH_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; + USHORT TLV2Length; + CHAR SignalStrength; + UCHAR RadioIf; +} QMINAS_GET_SIGNAL_STRENGTH_RESP_MSG, *PQMINAS_GET_SIGNAL_STRENGTH_RESP_MSG; + + +typedef struct _QMINAS_SET_EVENT_REPORT_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR ReportSigStrength; + UCHAR NumTresholds; + CHAR TresholdList[2]; +} QMINAS_SET_EVENT_REPORT_REQ_MSG, *PQMINAS_SET_EVENT_REPORT_REQ_MSG; + +typedef struct _QMINAS_SET_EVENT_REPORT_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMINAS_SET_EVENT_REPORT_RESP_MSG, *PQMINAS_SET_EVENT_REPORT_RESP_MSG; + +typedef struct _QMINAS_SIGNAL_STRENGTH_TLV +{ + UCHAR TLVType; + USHORT TLVLength; + CHAR SigStrength; + UCHAR RadioIf; +} QMINAS_SIGNAL_STRENGTH_TLV, *PQMINAS_SIGNAL_STRENGTH_TLV; + +typedef struct _QMINAS_REJECT_CAUSE_TLV +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR ServiceDomain; + USHORT RejectCause; +} QMINAS_REJECT_CAUSE_TLV, *PQMINAS_REJECT_CAUSE_TLV; + +typedef struct _QMINAS_EVENT_REPORT_IND_MSG +{ + USHORT Type; + USHORT Length; +} QMINAS_EVENT_REPORT_IND_MSG, *PQMINAS_EVENT_REPORT_IND_MSG; + +typedef struct _QMINAS_GET_RF_BAND_INFO_REQ_MSG +{ + USHORT Type; + USHORT Length; +} QMINAS_GET_RF_BAND_INFO_REQ_MSG, *PQMINAS_GET_RF_BAND_INFO_REQ_MSG; + +typedef struct _QMINASRF_BAND_INFO +{ + UCHAR RadioIf; + USHORT ActiveBand; + USHORT ActiveChannel; +} QMINASRF_BAND_INFO, *PQMINASRF_BAND_INFO; + +typedef struct _QMINAS_GET_RF_BAND_INFO_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT + UCHAR TLV2Type; + USHORT TLV2Length; + UCHAR NumInstances; +} QMINAS_GET_RF_BAND_INFO_RESP_MSG, *PQMINAS_GET_RF_BAND_INFO_RESP_MSG; + + +typedef struct _QMINAS_GET_PLMN_NAME_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT MCC; + USHORT MNC; +} QMINAS_GET_PLMN_NAME_REQ_MSG, *PQMINAS_GET_PLMN_NAME_REQ_MSG; + +typedef struct _QMINAS_GET_PLMN_NAME_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMINAS_GET_PLMN_NAME_RESP_MSG, *PQMINAS_GET_PLMN_NAME_RESP_MSG; + +typedef struct _QMINAS_GET_PLMN_NAME_SPN +{ + UCHAR TLVType; + USHORT TLVLength; + UCHAR SPN_Enc; + UCHAR SPN_Len; +} QMINAS_GET_PLMN_NAME_SPN, *PQMINAS_GET_PLMN_NAME_SPN; + +typedef struct _QMINAS_GET_PLMN_NAME_PLMN +{ + UCHAR PLMN_Enc; + UCHAR PLMN_Ci; + UCHAR PLMN_SpareBits; + UCHAR PLMN_Len; +} QMINAS_GET_PLMN_NAME_PLMN, *PQMINAS_GET_PLMN_NAME_PLMN; + +typedef struct _QMINAS_INITIATE_ATTACH_REQ_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + UCHAR PsAttachAction; +} QMINAS_INITIATE_ATTACH_REQ_MSG, *PQMINAS_INITIATE_ATTACH_REQ_MSG; + +typedef struct _QMINAS_INITIATE_ATTACH_RESP_MSG +{ + USHORT Type; // QMUX type 0x0003 + USHORT Length; + UCHAR TLVType; // 0x02 - result code + USHORT TLVLength; // 4 + USHORT QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + USHORT QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} QMINAS_INITIATE_ATTACH_RESP_MSG, *PQMINAS_INITIATE_ATTACH_RESP_MSG; +#endif +// ======================= End of NAS ============================== + +// ======================= UIM ============================== +#define QMIUIM_READ_TRANSPARENT_REQ 0x0020 +#define QMIUIM_READ_TRANSPARENT_RESP 0x0020 +#define QMIUIM_READ_TRANSPARENT_IND 0x0020 +#define QMIUIM_READ_RECORD_REQ 0x0021 +#define QMIUIM_READ_RECORD_RESP 0x0021 +#define QMIUIM_READ_RECORD_IND 0x0021 +#define QMIUIM_WRITE_TRANSPARENT_REQ 0x0022 +#define QMIUIM_WRITE_TRANSPARENT_RESP 0x0022 +#define QMIUIM_WRITE_TRANSPARENT_IND 0x0022 +#define QMIUIM_WRITE_RECORD_REQ 0x0023 +#define QMIUIM_WRITE_RECORD_RESP 0x0023 +#define QMIUIM_WRITE_RECORD_IND 0x0023 +#define QMIUIM_SET_PIN_PROTECTION_REQ 0x0025 +#define QMIUIM_SET_PIN_PROTECTION_RESP 0x0025 +#define QMIUIM_SET_PIN_PROTECTION_IND 0x0025 +#define QMIUIM_VERIFY_PIN_REQ 0x0026 +#define QMIUIM_VERIFY_PIN_RESP 0x0026 +#define QMIUIM_VERIFY_PIN_IND 0x0026 +#define QMIUIM_UNBLOCK_PIN_REQ 0x0027 +#define QMIUIM_UNBLOCK_PIN_RESP 0x0027 +#define QMIUIM_UNBLOCK_PIN_IND 0x0027 +#define QMIUIM_CHANGE_PIN_REQ 0x0028 +#define QMIUIM_CHANGE_PIN_RESP 0x0028 +#define QMIUIM_CHANGE_PIN_IND 0x0028 +#define QMIUIM_DEPERSONALIZATION_REQ 0x0029 +#define QMIUIM_DEPERSONALIZATION_RESP 0x0029 +#define QMIUIM_EVENT_REG_REQ 0x002E +#define QMIUIM_EVENT_REG_RESP 0x002E +#define QMIUIM_GET_CARD_STATUS_REQ 0x002F +#define QMIUIM_GET_CARD_STATUS_RESP 0x002F +#define QMIUIM_STATUS_CHANGE_IND 0x0032 + + +typedef struct _QMIUIM_GET_CARD_STATUS_RESP_MSG +{ + USHORT Type; + USHORT Length; + UCHAR TLVType; + USHORT TLVLength; + USHORT QMUXResult; + USHORT QMUXError; +} __attribute__ ((packed)) QMIUIM_GET_CARD_STATUS_RESP_MSG, *PQMIUIM_GET_CARD_STATUS_RESP_MSG; + +typedef struct _QMIUIM_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 _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; +#if 0 + QMIWDS_GET_DATA_BEARER_REQ_MSG GetDataBearerReq; + QMIWDS_GET_DATA_BEARER_RESP_MSG GetDataBearerResp; + QMIWDS_DUN_CALL_INFO_REQ_MSG DunCallInfoReq; + QMIWDS_DUN_CALL_INFO_RESP_MSG DunCallInfoResp; +#endif + QMIWDS_BIND_MUX_DATA_PORT_REQ_MSG BindMuxDataPortReq; + + // QMIDMS Messages +#if 0 + QMIDMS_GET_DEVICE_MFR_REQ_MSG GetDeviceMfrReq; + QMIDMS_GET_DEVICE_MFR_RESP_MSG GetDeviceMfrRsp; + QMIDMS_GET_DEVICE_MODEL_ID_REQ_MSG GetDeviceModeIdReq; + QMIDMS_GET_DEVICE_MODEL_ID_RESP_MSG GetDeviceModeIdRsp; + QMIDMS_GET_DEVICE_REV_ID_REQ_MSG GetDeviceRevIdReq; + QMIDMS_GET_DEVICE_REV_ID_RESP_MSG GetDeviceRevIdRsp; + QMIDMS_GET_MSISDN_REQ_MSG GetMsisdnReq; + QMIDMS_GET_MSISDN_RESP_MSG GetMsisdnRsp; + QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ_MSG GetDeviceSerialNumReq; + QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP_MSG GetDeviceSerialNumRsp; + QMIDMS_GET_DEVICE_CAP_REQ_MSG GetDeviceCapReq; + QMIDMS_GET_DEVICE_CAP_RESP_MSG GetDeviceCapResp; + QMIDMS_GET_BAND_CAP_REQ_MSG GetBandCapReq; + QMIDMS_GET_BAND_CAP_RESP_MSG GetBandCapRsp; + QMIDMS_GET_ACTIVATED_STATUS_REQ_MSG GetActivatedStatusReq; + QMIDMS_GET_ACTIVATED_STATUS_RESP_MSG GetActivatedStatusResp; + QMIDMS_GET_OPERATING_MODE_REQ_MSG GetOperatingModeReq; + QMIDMS_GET_OPERATING_MODE_RESP_MSG GetOperatingModeResp; +#endif + QMIDMS_SET_OPERATING_MODE_REQ_MSG SetOperatingModeReq; + QMIDMS_SET_OPERATING_MODE_RESP_MSG SetOperatingModeResp; +#if 0 + QMIDMS_UIM_GET_ICCID_REQ_MSG GetICCIDReq; + QMIDMS_UIM_GET_ICCID_RESP_MSG GetICCIDResp; + QMIDMS_ACTIVATE_AUTOMATIC_REQ_MSG ActivateAutomaticReq; + QMIDMS_ACTIVATE_AUTOMATIC_RESP_MSG ActivateAutomaticResp; + QMIDMS_ACTIVATE_MANUAL_REQ_MSG ActivateManualReq; + QMIDMS_ACTIVATE_MANUAL_RESP_MSG ActivateManualResp; +#endif + QMIDMS_UIM_GET_PIN_STATUS_REQ_MSG UIMGetPinStatusReq; + QMIDMS_UIM_GET_PIN_STATUS_RESP_MSG UIMGetPinStatusResp; + QMIDMS_UIM_VERIFY_PIN_REQ_MSG UIMVerifyPinReq; + QMIDMS_UIM_VERIFY_PIN_RESP_MSG UIMVerifyPinResp; +#if 0 + QMIDMS_UIM_SET_PIN_PROTECTION_REQ_MSG UIMSetPinProtectionReq; + QMIDMS_UIM_SET_PIN_PROTECTION_RESP_MSG UIMSetPinProtectionResp; + QMIDMS_UIM_CHANGE_PIN_REQ_MSG UIMChangePinReq; + QMIDMS_UIM_CHANGE_PIN_RESP_MSG UIMChangePinResp; + QMIDMS_UIM_UNBLOCK_PIN_REQ_MSG UIMUnblockPinReq; + QMIDMS_UIM_UNBLOCK_PIN_RESP_MSG UIMUnblockPinResp; + QMIDMS_SET_EVENT_REPORT_REQ_MSG DmsSetEventReportReq; + QMIDMS_SET_EVENT_REPORT_RESP_MSG DmsSetEventReportResp; + QMIDMS_EVENT_REPORT_IND_MSG DmsEventReportInd; +#endif + QMIDMS_UIM_GET_STATE_REQ_MSG UIMGetStateReq; + QMIDMS_UIM_GET_STATE_RESP_MSG UIMGetStateResp; + QMIDMS_UIM_GET_IMSI_REQ_MSG UIMGetIMSIReq; + QMIDMS_UIM_GET_IMSI_RESP_MSG UIMGetIMSIResp; +#if 0 + QMIDMS_UIM_GET_CK_STATUS_REQ_MSG UIMGetCkStatusReq; + QMIDMS_UIM_GET_CK_STATUS_RESP_MSG UIMGetCkStatusResp; + QMIDMS_UIM_SET_CK_PROTECTION_REQ_MSG UIMSetCkProtectionReq; + QMIDMS_UIM_SET_CK_PROTECTION_RESP_MSG UIMSetCkProtectionResp; + QMIDMS_UIM_UNBLOCK_CK_REQ_MSG UIMUnblockCkReq; + QMIDMS_UIM_UNBLOCK_CK_RESP_MSG UIMUnblockCkResp; +#endif + + // QMIQOS Messages +#if 0 + QMI_QOS_SET_EVENT_REPORT_REQ_MSG QosSetEventReportReq; + QMI_QOS_SET_EVENT_REPORT_RESP_MSG QosSetEventReportRsp; + QMI_QOS_EVENT_REPORT_IND_MSG QosEventReportInd; +#endif + + // QMIWMS Messages +#if 0 + QMIWMS_GET_MESSAGE_PROTOCOL_REQ_MSG GetMessageProtocolReq; + QMIWMS_GET_MESSAGE_PROTOCOL_RESP_MSG GetMessageProtocolResp; + QMIWMS_GET_SMSC_ADDRESS_REQ_MSG GetSMSCAddressReq; + QMIWMS_GET_SMSC_ADDRESS_RESP_MSG GetSMSCAddressResp; + QMIWMS_SET_SMSC_ADDRESS_REQ_MSG SetSMSCAddressReq; + QMIWMS_SET_SMSC_ADDRESS_RESP_MSG SetSMSCAddressResp; + QMIWMS_GET_STORE_MAX_SIZE_REQ_MSG GetStoreMaxSizeReq; + QMIWMS_GET_STORE_MAX_SIZE_RESP_MSG GetStoreMaxSizeResp; + QMIWMS_LIST_MESSAGES_REQ_MSG ListMessagesReq; + QMIWMS_LIST_MESSAGES_RESP_MSG ListMessagesResp; + QMIWMS_RAW_READ_REQ_MSG RawReadMessagesReq; + QMIWMS_RAW_READ_RESP_MSG RawReadMessagesResp; + QMIWMS_SET_EVENT_REPORT_REQ_MSG WmsSetEventReportReq; + QMIWMS_SET_EVENT_REPORT_RESP_MSG WmsSetEventReportResp; + QMIWMS_EVENT_REPORT_IND_MSG WmsEventReportInd; + QMIWMS_DELETE_REQ_MSG WmsDeleteReq; + QMIWMS_DELETE_RESP_MSG WmsDeleteResp; + QMIWMS_RAW_SEND_REQ_MSG RawSendMessagesReq; + QMIWMS_RAW_SEND_RESP_MSG RawSendMessagesResp; + QMIWMS_MODIFY_TAG_REQ_MSG WmsModifyTagReq; + QMIWMS_MODIFY_TAG_RESP_MSG WmsModifyTagResp; +#endif + + // QMINAS Messages +#if 0 + QMINAS_GET_HOME_NETWORK_REQ_MSG GetHomeNetworkReq; + QMINAS_GET_HOME_NETWORK_RESP_MSG GetHomeNetworkResp; + QMINAS_GET_PREFERRED_NETWORK_REQ_MSG GetPreferredNetworkReq; + QMINAS_GET_PREFERRED_NETWORK_RESP_MSG GetPreferredNetworkResp; + QMINAS_GET_FORBIDDEN_NETWORK_REQ_MSG GetForbiddenNetworkReq; + QMINAS_GET_FORBIDDEN_NETWORK_RESP_MSG GetForbiddenNetworkResp; + QMINAS_GET_SERVING_SYSTEM_REQ_MSG GetServingSystemReq; +#endif + QMINAS_GET_SERVING_SYSTEM_RESP_MSG GetServingSystemResp; + QMINAS_GET_SYS_INFO_RESP_MSG GetSysInfoResp; + QMINAS_SYS_INFO_IND_MSG NasSysInfoInd; +#if 0 + QMINAS_SERVING_SYSTEM_IND_MSG NasServingSystemInd; + QMINAS_SET_PREFERRED_NETWORK_REQ_MSG SetPreferredNetworkReq; + QMINAS_SET_PREFERRED_NETWORK_RESP_MSG SetPreferredNetworkResp; + QMINAS_SET_FORBIDDEN_NETWORK_REQ_MSG SetForbiddenNetworkReq; + QMINAS_SET_FORBIDDEN_NETWORK_RESP_MSG SetForbiddenNetworkResp; + QMINAS_PERFORM_NETWORK_SCAN_REQ_MSG PerformNetworkScanReq; + QMINAS_PERFORM_NETWORK_SCAN_RESP_MSG PerformNetworkScanResp; + QMINAS_INITIATE_NW_REGISTER_REQ_MSG InitiateNwRegisterReq; + QMINAS_INITIATE_NW_REGISTER_RESP_MSG InitiateNwRegisterResp; + QMINAS_SET_TECHNOLOGY_PREF_REQ_MSG SetTechnologyPrefReq; + QMINAS_SET_TECHNOLOGY_PREF_RESP_MSG SetTechnologyPrefResp; + QMINAS_GET_SIGNAL_STRENGTH_REQ_MSG GetSignalStrengthReq; + QMINAS_GET_SIGNAL_STRENGTH_RESP_MSG GetSignalStrengthResp; + QMINAS_SET_EVENT_REPORT_REQ_MSG SetEventReportReq; + QMINAS_SET_EVENT_REPORT_RESP_MSG SetEventReportResp; + QMINAS_EVENT_REPORT_IND_MSG NasEventReportInd; + QMINAS_GET_RF_BAND_INFO_REQ_MSG GetRFBandInfoReq; + QMINAS_GET_RF_BAND_INFO_RESP_MSG GetRFBandInfoResp; + QMINAS_INITIATE_ATTACH_REQ_MSG InitiateAttachReq; + QMINAS_INITIATE_ATTACH_RESP_MSG InitiateAttachResp; + QMINAS_GET_PLMN_NAME_REQ_MSG GetPLMNNameReq; + QMINAS_GET_PLMN_NAME_RESP_MSG GetPLMNNameResp; +#endif + + // QMIUIM Messages + QMIUIM_GET_CARD_STATUS_RESP_MSG UIMGetCardStatus; + QMIUIM_VERIFY_PIN_REQ_MSG UIMUIMVerifyPinReq; + QMIUIM_VERIFY_PIN_RESP_MSG UIMUIMVerifyPinResp; +#if 0 + QMIUIM_SET_PIN_PROTECTION_REQ_MSG UIMUIMSetPinProtectionReq; + QMIUIM_SET_PIN_PROTECTION_RESP_MSG UIMUIMSetPinProtectionResp; + QMIUIM_CHANGE_PIN_REQ_MSG UIMUIMChangePinReq; + QMIUIM_CHANGE_PIN_RESP_MSG UIMUIMChangePinResp; + QMIUIM_UNBLOCK_PIN_REQ_MSG UIMUIMUnblockPinReq; + QMIUIM_UNBLOCK_PIN_RESP_MSG UIMUIMUnblockPinResp; +#endif + QMIUIM_READ_TRANSPARENT_REQ_MSG UIMUIMReadTransparentReq; + QMIUIM_READ_TRANSPARENT_RESP_MSG UIMUIMReadTransparentResp; + + QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG SetDataFormatReq; + + }; +} __attribute__ ((packed)) QMUX_MSG, *PQMUX_MSG; + +#pragma pack(pop) + +#endif // MPQMUX_H diff --git a/package/wwan/meig-cm/src/Makefile b/package/wwan/meig-cm/src/Makefile new file mode 100644 index 000000000..cfe7aa1c7 --- /dev/null +++ b/package/wwan/meig-cm/src/Makefile @@ -0,0 +1,25 @@ +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 + +MEIG_CM_SRC=QmiWwanCM.c GobiNetCM.c main.c MPQMUX.c QMIThread.c util.c udhcpc.c qmap_bridge_mode.c mbim-cm.c device.c + +release: clean qmi-proxy + $(CC) -Wall -s ${MEIG_CM_SRC} -o meig-cm -lpthread -ldl + +debug: clean + $(CC) -Wall -g -DCM_DEBUG ${MEIG_CM_SRC} -o meig-cm -lpthread -ldl + +qmi-proxy: + $(CC) -Wall -s meig-qmi-proxy.c -o meig-qmi-proxy -lpthread -ldl + +clean: + rm -rf meig-cm *~ + rm -rf meig-qmi-proxy diff --git a/package/wwan/meig-cm/src/QMIThread.c b/package/wwan/meig-cm/src/QMIThread.c new file mode 100644 index 000000000..2fadd5a27 --- /dev/null +++ b/package/wwan/meig-cm/src/QMIThread.c @@ -0,0 +1,1988 @@ +#include "QMIThread.h" +extern char *strndup (const char *__string, size_t __n); + +#define qmi_rsp_check_and_return() do { \ + if (err < 0 || pResponse == NULL) { \ + dbg_time("%s err = %d", __func__, err); \ + return err; \ + } \ + pMUXMsg = &pResponse->MUXMsg; \ + if (le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXResult) || le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError)) { \ + USHORT QMUXError = le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError); \ + dbg_time("%s QMUXResult = 0x%x, QMUXError = 0x%x", __func__, \ + le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXResult), QMUXError); \ + free(pResponse); \ + return QMUXError; \ + } \ +} while(0) + +#define qmi_rsp_check() do { \ + if (err < 0 || pResponse == NULL) { \ + dbg_time("%s err = %d", __func__, err); \ + return err; \ + } \ + pMUXMsg = &pResponse->MUXMsg; \ + if (le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXResult) || le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError)) { \ + USHORT QMUXError = le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError); \ + dbg_time("%s QMUXResult = 0x%x, QMUXError = 0x%x", __func__, \ + le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXResult), QMUXError); \ + } \ +} while(0) + +int qmiclientId[QMUX_TYPE_WDS_ADMIN + 1]; //GobiNet use fd to indicate client ID, so type of qmiclientId must be int +static uint32_t WdsConnectionIPv4Handle = 0; +static uint32_t WdsConnectionIPv6Handle = 0; +static int s_is_cdma = 0; +static int s_hdr_personality = 0; // 0x01-HRPD, 0x02-eHRPD +static char *qstrcpy(char *to, const char *from) { //no __strcpy_chk + char *save = to; + for (; (*to = *from) != '\0'; ++from, ++to); + return(save); +} + +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->QMIHdr.ClientId = qmiclientId[QMIType] & 0xFF; + + if (qmiclientId[QMIType] == 0) { + dbg_time("QMIType %d has no clientID", QMIType); + return NULL; + } + + pRequest->MUXMsg.QMUXHdr.CtlFlags = QMUX_CTL_FLAG_SINGLE_MSG | QMUX_CTL_FLAG_TYPE_CMD; + pRequest->MUXMsg.QMUXHdr.TransactionId = cpu_to_le16(GetQMUXTransactionId()); + pRequest->MUXMsg.QMUXMsgHdr.Type = cpu_to_le16(Type); + if (customQmuxMsgFunction) + pRequest->MUXMsg.QMUXMsgHdr.Length = cpu_to_le16(customQmuxMsgFunction(&pRequest->MUXMsg, arg) - sizeof(QCQMUX_MSG_HDR)); + else + pRequest->MUXMsg.QMUXMsgHdr.Length = cpu_to_le16(0x0000); + + pRequest->QMIHdr.Length = cpu_to_le16(le16_to_cpu(pRequest->MUXMsg.QMUXMsgHdr.Length) + sizeof(QCQMUX_MSG_HDR) + sizeof(QCQMUX_HDR) + + sizeof(QCQMI_HDR) - 1); + Length = le16_to_cpu(pRequest->QMIHdr.Length) + 1; + + pRequest = (PQCQMIMSG)malloc(Length); + if (pRequest == NULL) { + dbg_time("%s fail to malloc", __func__); + } else { + memcpy(pRequest, QMIBuf, Length); + } + + return pRequest; +} + +#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->pdp && !s_is_cdma) { //cdma only support one pdp, so no need to set profile index + PQMIWDS_PROFILE_IDENTIFIER pProfileIndex = (PQMIWDS_PROFILE_IDENTIFIER)(pTLV + TLVLength); + pProfileIndex->TLVLength = cpu_to_le16(0x01); + pProfileIndex->TLVType = 0x31; + pProfileIndex->ProfileIndex = profile->pdp; + if (s_is_cdma && s_hdr_personality == 0x02) { + pProfileIndex->TLVType = 0x32; //profile_index_3gpp2 + pProfileIndex->ProfileIndex = 101; + } + TLVLength += (le16_to_cpu(pProfileIndex->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + return sizeof(QMIWDS_START_NETWORK_INTERFACE_REQ_MSG) + TLVLength; +} + +static USHORT WdsStopNwInterfaceReq(PQMUX_MSG pMUXMsg, void *arg) { + pMUXMsg->StopNwInterfaceReq.TLVType = 0x01; + pMUXMsg->StopNwInterfaceReq.TLVLength = cpu_to_le16(0x04); + if (*((int *)arg) == IpFamilyV4) + pMUXMsg->StopNwInterfaceReq.Handle = cpu_to_le32(WdsConnectionIPv4Handle); + else + pMUXMsg->StopNwInterfaceReq.Handle = cpu_to_le32(WdsConnectionIPv6Handle); + return sizeof(QMIWDS_STOP_NETWORK_INTERFACE_REQ_MSG); +} + +static USHORT WdsSetClientIPFamilyPref(PQMUX_MSG pMUXMsg, void *arg) { + pMUXMsg->SetClientIpFamilyPrefReq.TLVType = 0x01; + pMUXMsg->SetClientIpFamilyPrefReq.TLVLength = cpu_to_le16(0x01); + pMUXMsg->SetClientIpFamilyPrefReq.IpPreference = *((UCHAR *)arg); + return sizeof(QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ_MSG); +} + +static USHORT WdsSetAutoConnect(PQMUX_MSG pMUXMsg, void *arg) { + pMUXMsg->SetAutoConnectReq.TLVType = 0x01; + pMUXMsg->SetAutoConnectReq.TLVLength = cpu_to_le16(0x01); + pMUXMsg->SetAutoConnectReq.autoconnect_setting = *((UCHAR *)arg); + return sizeof(QMIWDS_SET_AUTO_CONNECT_REQ_MSG); +} + +enum peripheral_ep_type { + DATA_EP_TYPE_RESERVED = 0x0, + DATA_EP_TYPE_HSIC = 0x1, + DATA_EP_TYPE_HSUSB = 0x2, + DATA_EP_TYPE_PCIE = 0x3, + DATA_EP_TYPE_EMBEDDED = 0x4, + DATA_EP_TYPE_BAM_DMUX = 0x5, +}; + +typedef struct { + UINT rx_urb_size; + enum peripheral_ep_type ep_type; + UINT iface_id; + UCHAR MuxId; +} QMAP_SETTING; +static USHORT WdsSetQMUXBindMuxDataPort(PQMUX_MSG pMUXMsg, void *arg) { + QMAP_SETTING *qmap_settings = (QMAP_SETTING *)arg; + + pMUXMsg->BindMuxDataPortReq.TLVType = 0x10; + pMUXMsg->BindMuxDataPortReq.TLVLength = cpu_to_le16(0x08); + pMUXMsg->BindMuxDataPortReq.ep_type = cpu_to_le32(qmap_settings->ep_type); + pMUXMsg->BindMuxDataPortReq.iface_id = cpu_to_le32(qmap_settings->iface_id); + pMUXMsg->BindMuxDataPortReq.TLV2Type = 0x11; + pMUXMsg->BindMuxDataPortReq.TLV2Length = cpu_to_le16(0x01); + pMUXMsg->BindMuxDataPortReq.MuxId = qmap_settings->MuxId; + pMUXMsg->BindMuxDataPortReq.TLV3Type = 0x13; + pMUXMsg->BindMuxDataPortReq.TLV3Length = cpu_to_le16(0x04); + pMUXMsg->BindMuxDataPortReq.client_type = cpu_to_le32(1); //WDS_CLIENT_TYPE_TETHERED + + return sizeof(QMIWDS_BIND_MUX_DATA_PORT_REQ_MSG); +} + +static USHORT WdaSetDataFormat(PQMUX_MSG pMUXMsg, void *arg) { + QMAP_SETTING *qmap_settings = (QMAP_SETTING *)arg; + + if (qmap_settings->rx_urb_size == 0) { + PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS pWdsAdminQosTlv; + PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV linkProto; + PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV dlTlp; + + pWdsAdminQosTlv = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS)(&pMUXMsg->QMUXMsgHdr + 1); + pWdsAdminQosTlv->TLVType = 0x10; + pWdsAdminQosTlv->TLVLength = cpu_to_le16(0x0001); + pWdsAdminQosTlv->QOSSetting = 0; /* no-QOS header */ + + linkProto = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)(pWdsAdminQosTlv + 1); + linkProto->TLVType = 0x11; + linkProto->TLVLength = cpu_to_le16(4); + linkProto->Value = cpu_to_le32(0x01); /* Set Ethernet mode */ + + dlTlp = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)(linkProto + 1);; + dlTlp->TLVType = 0x13; + dlTlp->TLVLength = cpu_to_le16(4); + dlTlp->Value = cpu_to_le32(0x00); + + if (sizeof(*linkProto) != 7 ) + dbg_time("%s sizeof(*linkProto) = %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(0x09); //UL QMAP is enabled //0x05 + //Downlink (DL) data aggregation protocol to be used for downlink data transfer + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationProtocolTlv.TLVType = 0x13; + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationProtocolTlv.TLVLength = cpu_to_le16(4); + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationProtocolTlv.Value = cpu_to_le32(0x05); //UL QMAP is enabled + //Maximum number of datagrams in a single aggregated packet on downlink + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.TLVType = 0x15; + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.TLVLength = cpu_to_le16(4); + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.Value = cpu_to_le32(qmap_settings->rx_urb_size/512); + //Maximum size in bytes of a single aggregated packet allowed on downlink + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.TLVType = 0x16; + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.TLVLength = cpu_to_le16(4); + pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.Value = cpu_to_le32(qmap_settings->rx_urb_size); + //Peripheral End Point ID + pMUXMsg->SetDataFormatReq.epTlv.TLVType = 0x17; + pMUXMsg->SetDataFormatReq.epTlv.TLVLength = cpu_to_le16(8); + pMUXMsg->SetDataFormatReq.epTlv.ep_type = cpu_to_le32(qmap_settings->ep_type); + pMUXMsg->SetDataFormatReq.epTlv.iface_id = cpu_to_le32(qmap_settings->iface_id); + + return sizeof(QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG); + } +} + +#ifdef CONFIG_SIM +static USHORT 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((PCHAR)&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((PCHAR)&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 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->pdp; + 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->pdp; + + pTLV = (UCHAR *)(&pMUXMsg->ModifyProfileSettingsReq + 1); + + pPdpType = (PQMIWDS_PDPTYPE)(pTLV + TLVLength); + pPdpType->TLVType = 0x11; + pPdpType->TLVLength = cpu_to_le16(0x01); +// 0 ?C PDP-IP (IPv4) +// 1 ?C PDP-PPP +// 2 ?C PDP-IPv6 +// 3 ?C PDP-IPv4v6 + if (profile->IsDualIPSupported) + pPdpType->PdpType = 3; + else if (profile->enable_ipv6) + pPdpType->PdpType = 2; + else + pPdpType->PdpType = 0; + TLVLength +=(le16_to_cpu(pPdpType->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + + // Set APN Name + if (profile->apn) { + PQMIWDS_APNNAME pApnName = (PQMIWDS_APNNAME)(pTLV + TLVLength); + pApnName->TLVType = 0x14; + pApnName->TLVLength = cpu_to_le16(strlen(profile->apn)); + qstrcpy((char *)&pApnName->ApnName, profile->apn); + TLVLength +=(le16_to_cpu(pApnName->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + // Set User Name + if (profile->user) { + PQMIWDS_USERNAME pUserName = (PQMIWDS_USERNAME)(pTLV + TLVLength); + pUserName->TLVType = 0x1B; + pUserName->TLVLength = cpu_to_le16(strlen(profile->user)); + qstrcpy((char *)&pUserName->UserName, profile->user); + TLVLength += (le16_to_cpu(pUserName->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + // Set Password + if (profile->password) { + PQMIWDS_PASSWD pPasswd = (PQMIWDS_PASSWD)(pTLV + TLVLength); + pPasswd->TLVType = 0x1C; + pPasswd->TLVLength = cpu_to_le16(strlen(profile->password)); + qstrcpy((char *)&pPasswd->Passwd, profile->password); + TLVLength +=(le16_to_cpu(pPasswd->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + // Set Auth Protocol + if (profile->user && profile->password) { + PQMIWDS_AUTH_PREFERENCE pAuthPref = (PQMIWDS_AUTH_PREFERENCE)(pTLV + TLVLength); + pAuthPref->TLVType = 0x1D; + pAuthPref->TLVLength = cpu_to_le16(0x01); + pAuthPref->AuthPreference = profile->auth; // 0 ~ None, 1 ~ Pap, 2 ~ Chap, 3 ~ MsChapV2 + TLVLength += (le16_to_cpu(pAuthPref->TLVLength) + sizeof(QCQMICTL_TLV_HDR)); + } + + return sizeof(QMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG) + TLVLength; +} +#endif + +static USHORT WdsGetRuntimeSettingReq(PQMUX_MSG pMUXMsg, void *arg) { + pMUXMsg->GetRuntimeSettingsReq.TLVType = 0x10; + pMUXMsg->GetRuntimeSettingsReq.TLVLength = cpu_to_le16(0x04); + // the following mask also applies to IPV6 + pMUXMsg->GetRuntimeSettingsReq.Mask = cpu_to_le32(QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4DNS_ADDR | + QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4_ADDR | + QMIWDS_GET_RUNTIME_SETTINGS_MASK_MTU | + QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4GATEWAY_ADDR); // | + // QMIWDS_GET_RUNTIME_SETTINGS_MASK_PCSCF_SV_ADDR | + // QMIWDS_GET_RUNTIME_SETTINGS_MASK_PCSCF_DOM_NAME; + + return sizeof(QMIWDS_GET_RUNTIME_SETTINGS_REQ_MSG); +} + +static PQCQMIMSG s_pRequest; +static PQCQMIMSG s_pResponse; +static pthread_mutex_t s_commandmutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t s_commandcond = PTHREAD_COND_INITIALIZER; + +static int is_response(const PQCQMIMSG pRequest, const PQCQMIMSG pResponse) { + if ((pRequest->QMIHdr.QMIType == pResponse->QMIHdr.QMIType) + && (pRequest->QMIHdr.ClientId == pResponse->QMIHdr.ClientId)) { + USHORT requestTID, responseTID; + if (pRequest->QMIHdr.QMIType == QMUX_TYPE_CTL) { + requestTID = pRequest->CTLMsg.QMICTLMsgHdr.TransactionId; + responseTID = pResponse->CTLMsg.QMICTLMsgHdr.TransactionId; + } else { + requestTID = le16_to_cpu(pRequest->MUXMsg.QMUXHdr.TransactionId); + responseTID = le16_to_cpu(pResponse->MUXMsg.QMUXHdr.TransactionId); + } + return (requestTID == responseTID); + } + return 0; +} + + +int (*qmidev_send)(PQCQMIMSG pRequest); + +int QmiThreadSendQMITimeout(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse, unsigned msecs) { + int ret; + + static int flag = 0; + if (!flag) { + cond_setclock_attr(&s_commandcond, CLOCK_MONOTONIC); + flag = 1; + } + + if (!pRequest) + return -EINVAL; + + pthread_mutex_lock(&s_commandmutex); + + if (ppResponse) + *ppResponse = NULL; + + dump_qmi(pRequest, le16_to_cpu(pRequest->QMIHdr.Length) + 1); + + s_pRequest = pRequest; + s_pResponse = NULL; + + ret = qmidev_send(pRequest); + + if (ret == 0) { + ret = pthread_cond_timeout_np(&s_commandcond, &s_commandmutex, msecs); + if (!ret) { + if (s_pResponse && ppResponse) { + *ppResponse = s_pResponse; + } else { + if (s_pResponse) { + free(s_pResponse); + s_pResponse = NULL; + } + } + } else { + dbg_time("%s pthread_cond_timeout_np timeout", __func__); + } + } + + pthread_mutex_unlock(&s_commandmutex); + + return ret; +} + +int QmiThreadSendQMI(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse) { + return QmiThreadSendQMITimeout(pRequest, ppResponse, 30 * 1000); +} + +void QmiThreadRecvQMI(PQCQMIMSG pResponse) { + pthread_mutex_lock(&s_commandmutex); + if (pResponse == NULL) { + if (s_pRequest) { + free(s_pRequest); + s_pRequest = NULL; + s_pResponse = NULL; + pthread_cond_signal(&s_commandcond); + } + pthread_mutex_unlock(&s_commandmutex); + return; + } + dump_qmi(pResponse, le16_to_cpu(pResponse->QMIHdr.Length) + 1); + if (s_pRequest && is_response(s_pRequest, pResponse)) { + free(s_pRequest); + s_pRequest = NULL; + s_pResponse = malloc(le16_to_cpu(pResponse->QMIHdr.Length) + 1); + if (s_pResponse != NULL) { + memcpy(s_pResponse, pResponse, le16_to_cpu(pResponse->QMIHdr.Length) + 1); + } + pthread_cond_signal(&s_commandcond); + } else if ((pResponse->QMIHdr.QMIType == QMUX_TYPE_NAS) + && (le16_to_cpu(pResponse->MUXMsg.QMUXMsgHdrResp.Type) == QMINAS_SERVING_SYSTEM_IND)) { + qmidevice_send_event_to_main(RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED); + } else if ((pResponse->QMIHdr.QMIType == QMUX_TYPE_WDS) + && (le16_to_cpu(pResponse->MUXMsg.QMUXMsgHdrResp.Type) == QMIWDS_GET_PKT_SRVC_STATUS_IND)) { + qmidevice_send_event_to_main(RIL_UNSOL_DATA_CALL_LIST_CHANGED); + } else if ((pResponse->QMIHdr.QMIType == QMUX_TYPE_NAS) + && (le16_to_cpu(pResponse->MUXMsg.QMUXMsgHdrResp.Type) == QMINAS_SYS_INFO_IND)) { + qmidevice_send_event_to_main(RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED); + } else { + if (debug_qmi) + dbg_time("nobody care this qmi msg!!"); + } + pthread_mutex_unlock(&s_commandmutex); +} + +int requestSetEthMode(PROFILE_T *profile) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse = NULL; + PQMUX_MSG pMUXMsg; + int err; + PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV linkProto; + UCHAR IpPreference; + UCHAR autoconnect_setting = 0; + QMAP_SETTING qmap_settings = {0, 0, 0, 0}; + + if (profile->qmap_mode) { + profile->rawIP = 1; + s_9x07 = profile->rawIP; + + qmap_settings.MuxId = profile->muxid; + + if (qmidev_is_pciemhi(profile->qmichannel)) { //SDX20_PCIE + qmap_settings.rx_urb_size = 32*1024; //SDX24&SDX55 support 32KB + qmap_settings.ep_type = DATA_EP_TYPE_PCIE; + qmap_settings.iface_id = 0x04; + } + else { // for MDM9x07&MDM9x40&SDX20 USB + qmap_settings.rx_urb_size = 32*1024; //SDX24&SDX55 support 32KB + qmap_settings.ep_type = DATA_EP_TYPE_HSUSB; + qmap_settings.iface_id = 0x04; + } + + if (qmidev_is_gobinet(profile->qmichannel)) { //GobiNet set data format in GobiNet driver + goto skip_WdaSetDataFormat; + } else if (profile->qmap_mode > 1) {//QMAP MUX enabled, set data format in meig-qmi-proxy + 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; + } + + 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 + } + + free(pResponse); + +skip_WdaSetDataFormat: + if (profile->enable_ipv6) { + if (profile->qmapnet_adapter) { + // bind wds mux data port + pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_BIND_MUX_DATA_PORT_REQ , WdsSetQMUXBindMuxDataPort, (void *)&qmap_settings); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + if (pResponse) free(pResponse); + } + + // set 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); + } else { + if (profile->qmapnet_adapter) { + // bind wds mux data port + pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_BIND_MUX_DATA_PORT_REQ , WdsSetQMUXBindMuxDataPort, (void *)&qmap_settings); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + if (pResponse) free(pResponse); + } + + // set 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->IsDualIPSupported) { + if (profile->qmapnet_adapter) { + // 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 +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 && qmiclientId[QMUX_TYPE_UIM]) + 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; +} + +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 && qmiclientId[QMUX_TYPE_UIM]) + 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 && qmiclientId[QMUX_TYPE_UIM]) + { + 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 (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; +} + +int requestEnterSimPin(const CHAR *pPinCode) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + + if (s_9x07 && qmiclientId[QMUX_TYPE_UIM]) + 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; +} + +#ifdef CONFIG_IMSI_ICCID +int requestGetICCID(void) { //RIL_REQUEST_GET_IMSI + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + PQMIUIM_CONTENT pUimContent; + int err; + + if (s_9x07 && qmiclientId[QMUX_TYPE_UIM]) { + 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; +} + +int requestGetIMSI(void) { //RIL_REQUEST_GET_IMSI + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + PQMIUIM_CONTENT pUimContent; + int err; + + if (s_9x07 && qmiclientId[QMUX_TYPE_UIM]) { + 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 +#endif + +#if 1 +static void meig_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 meig_convert_cdma_mnc_2_ascii_mnc( USHORT *p_mnc, USHORT imsi_11_12) +{ + unsigned int d1, d2, buf = imsi_11_12 + 11; + + if ( imsi_11_12 == 0x7F ) // wildcard + { + *p_mnc = 7; + } + else + { + d2 = buf % 10; + buf = ( d2 == 0 ) ? (buf-10)/10 : buf/10; + + d1 = ( buf == 10 ) ? 0 : buf; + + if ( d1<10 && d2<10 ) + { + *p_mnc = d1*10 + d2; + } + else + { + //dbg_time( "invalid digits %d %d", d1, d2, 0 ); + *p_mnc = 0; + } + } +} + +int requestGetHomeNetwork(USHORT *p_mcc, USHORT *p_mnc, USHORT *p_sid, USHORT *p_nid) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + PHOME_NETWORK pHomeNetwork; + PHOME_NETWORK_SYSTEMID pHomeNetworkSystemID; + + pRequest = ComposeQMUXMsg(QMUX_TYPE_NAS, QMINAS_GET_HOME_NETWORK_REQ, NULL, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + pHomeNetwork = (PHOME_NETWORK)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x01); + if (pHomeNetwork && p_mcc && p_mnc ) { + *p_mcc = le16_to_cpu(pHomeNetwork->MobileCountryCode); + *p_mnc = le16_to_cpu(pHomeNetwork->MobileNetworkCode); + //dbg_time("%s MobileCountryCode: %d, MobileNetworkCode: %d", __func__, *pMobileCountryCode, *pMobileNetworkCode); + } + + pHomeNetworkSystemID = (PHOME_NETWORK_SYSTEMID)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x10); + if (pHomeNetworkSystemID && p_sid && p_nid) { + *p_sid = le16_to_cpu(pHomeNetworkSystemID->SystemID); //china-hefei: sid 14451 + *p_nid = le16_to_cpu(pHomeNetworkSystemID->NetworkID); + //dbg_time("%s SystemID: %d, NetworkID: %d", __func__, *pSystemID, *pNetworkID); + } + + free(pResponse); + + return 0; +} +#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 + +struct wwan_data_class_str class2str[] = { + {WWAN_DATA_CLASS_NONE, "UNKNOWN"}, + {WWAN_DATA_CLASS_GPRS, "GPRS"}, + {WWAN_DATA_CLASS_EDGE, "EDGE"}, + {WWAN_DATA_CLASS_UMTS, "UMTS"}, + {WWAN_DATA_CLASS_HSDPA, "HSDPA"}, + {WWAN_DATA_CLASS_HSUPA, "HSUPA"}, + {WWAN_DATA_CLASS_LTE, "LTE"}, + {WWAN_DATA_CLASS_1XRTT, "1XRTT"}, + {WWAN_DATA_CLASS_1XEVDO, "1XEVDO"}, + {WWAN_DATA_CLASS_1XEVDO_REVA, "1XEVDO_REVA"}, + {WWAN_DATA_CLASS_1XEVDV, "1XEVDV"}, + {WWAN_DATA_CLASS_3XRTT, "3XRTT"}, + {WWAN_DATA_CLASS_1XEVDO_REVB, "1XEVDO_REVB"}, + {WWAN_DATA_CLASS_UMB, "UMB"}, + {WWAN_DATA_CLASS_CUSTOM, "CUSTOM"}, +}; + +CHAR *wwan_data_class2str(ULONG class) +{ + unsigned int i = 0; + for (i = 0; i < sizeof(class2str)/sizeof(class2str[0]); i++) { + if (class2str[i].class == class) { + return class2str[i].str; + } + } + return "UNKNOWN"; +} + +int requestRegistrationState2(UCHAR *pPSAttachedState) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + USHORT MobileCountryCode = 0; + USHORT MobileNetworkCode = 0; + const char *pDataCapStr = "UNKNOW"; + LONG remainingLen; + PSERVICE_STATUS_INFO pServiceStatusInfo; + int is_lte = 0; + PCDMA_SYSTEM_INFO pCdmaSystemInfo; + PHDR_SYSTEM_INFO pHdrSystemInfo; + PGSM_SYSTEM_INFO pGsmSystemInfo; + PWCDMA_SYSTEM_INFO pWcdmaSystemInfo; + PLTE_SYSTEM_INFO pLteSystemInfo; + PTDSCDMA_SYSTEM_INFO pTdscdmaSystemInfo; + UCHAR DeviceClass = 0; + ULONG DataCapList = 0; + + *pPSAttachedState = 0; + pRequest = ComposeQMUXMsg(QMUX_TYPE_NAS, QMINAS_GET_SYS_INFO_REQ, NULL, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + pServiceStatusInfo = (PSERVICE_STATUS_INFO)(((PCHAR)&pMUXMsg->GetSysInfoResp) + QCQMUX_MSG_HDR_SIZE); + remainingLen = le16_to_cpu(pMUXMsg->GetSysInfoResp.Length); + + s_is_cdma = 0; + s_hdr_personality = 0; + while (remainingLen > 0) { + switch (pServiceStatusInfo->TLVType) { + case 0x10: // CDMA + if (pServiceStatusInfo->SrvStatus == 0x02) { + DataCapList = WWAN_DATA_CLASS_1XRTT| + WWAN_DATA_CLASS_1XEVDO| + WWAN_DATA_CLASS_1XEVDO_REVA| + WWAN_DATA_CLASS_1XEVDV| + WWAN_DATA_CLASS_1XEVDO_REVB; + DeviceClass = DEVICE_CLASS_CDMA; + s_is_cdma = (0 == is_lte); + } + break; + case 0x11: // HDR + if (pServiceStatusInfo->SrvStatus == 0x02) { + DataCapList = WWAN_DATA_CLASS_3XRTT| + WWAN_DATA_CLASS_UMB; + DeviceClass = DEVICE_CLASS_CDMA; + s_is_cdma = (0 == is_lte); + } + break; + case 0x12: // GSM + if (pServiceStatusInfo->SrvStatus == 0x02) { + DataCapList = WWAN_DATA_CLASS_GPRS| + WWAN_DATA_CLASS_EDGE; + DeviceClass = DEVICE_CLASS_GSM; + } + break; + case 0x13: // WCDMA + if (pServiceStatusInfo->SrvStatus == 0x02) { + DataCapList = WWAN_DATA_CLASS_UMTS; + DeviceClass = DEVICE_CLASS_GSM; + } + break; + case 0x14: // LTE + if (pServiceStatusInfo->SrvStatus == 0x02) { + DataCapList = WWAN_DATA_CLASS_LTE; + DeviceClass = DEVICE_CLASS_GSM; + is_lte = 1; + s_is_cdma = 0; + } + break; + case 0x24: // TDSCDMA + if (pServiceStatusInfo->SrvStatus == 0x02) { + pDataCapStr = "TD-SCDMA"; + } + break; + case 0x15: // CDMA + // CDMA_SYSTEM_INFO + pCdmaSystemInfo = (PCDMA_SYSTEM_INFO)pServiceStatusInfo; + if (pCdmaSystemInfo->SrvDomainValid == 0x01) { + *pPSAttachedState = 0; + if (pCdmaSystemInfo->SrvDomain & 0x02) { + *pPSAttachedState = 1; + s_is_cdma = (0 == is_lte); + } + } +#if 0 + if (pCdmaSystemInfo->SrvCapabilityValid == 0x01) { + *pPSAttachedState = 0; + if (pCdmaSystemInfo->SrvCapability & 0x02) { + *pPSAttachedState = 1; + s_is_cdma = (0 == is_lte); + } + } +#endif + if (pCdmaSystemInfo->NetworkIdValid == 0x01) { + int i; + CHAR temp[10]; + strncpy(temp, (CHAR *)pCdmaSystemInfo->MCC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileCountryCode = (USHORT)atoi(temp); + + strncpy(temp, (CHAR *)pCdmaSystemInfo->MNC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileNetworkCode = (USHORT)atoi(temp); + } + break; + case 0x16: // HDR + // HDR_SYSTEM_INFO + pHdrSystemInfo = (PHDR_SYSTEM_INFO)pServiceStatusInfo; + if (pHdrSystemInfo->SrvDomainValid == 0x01) { + *pPSAttachedState = 0; + if (pHdrSystemInfo->SrvDomain & 0x02) { + *pPSAttachedState = 1; + s_is_cdma = (0 == is_lte); + } + } +#if 0 + if (pHdrSystemInfo->SrvCapabilityValid == 0x01) { + *pPSAttachedState = 0; + if (pHdrSystemInfo->SrvCapability & 0x02) { + *pPSAttachedState = 1; + s_is_cdma = (0 == is_lte); + } + } +#endif + if (*pPSAttachedState && pHdrSystemInfo->HdrPersonalityValid == 0x01) { + if (pHdrSystemInfo->HdrPersonality == 0x03) + s_hdr_personality = 0x02; + //else if (pHdrSystemInfo->HdrPersonality == 0x02) + // s_hdr_personality = 0x01; + } + USHORT cmda_mcc = 0, cdma_mnc = 0; + if(!requestGetHomeNetwork(&cmda_mcc, &cdma_mnc,NULL, NULL) && cmda_mcc) { + meig_convert_cdma_mcc_2_ascii_mcc(&MobileCountryCode, cmda_mcc); + meig_convert_cdma_mnc_2_ascii_mnc(&MobileNetworkCode, cdma_mnc); + } + break; + case 0x17: // GSM + // GSM_SYSTEM_INFO + pGsmSystemInfo = (PGSM_SYSTEM_INFO)pServiceStatusInfo; + if (pGsmSystemInfo->SrvDomainValid == 0x01) { + *pPSAttachedState = 0; + if (pGsmSystemInfo->SrvDomain & 0x02) { + *pPSAttachedState = 1; + } + } +#if 0 + if (pGsmSystemInfo->SrvCapabilityValid == 0x01) { + *pPSAttachedState = 0; + if (pGsmSystemInfo->SrvCapability & 0x02) { + *pPSAttachedState = 1; + } + } +#endif + if (pGsmSystemInfo->NetworkIdValid == 0x01) { + int i; + CHAR temp[10]; + strncpy(temp, (CHAR *)pGsmSystemInfo->MCC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileCountryCode = (USHORT)atoi(temp); + + strncpy(temp, (CHAR *)pGsmSystemInfo->MNC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileNetworkCode = (USHORT)atoi(temp); + } + break; + case 0x18: // WCDMA + // WCDMA_SYSTEM_INFO + pWcdmaSystemInfo = (PWCDMA_SYSTEM_INFO)pServiceStatusInfo; + if (pWcdmaSystemInfo->SrvDomainValid == 0x01) { + *pPSAttachedState = 0; + if (pWcdmaSystemInfo->SrvDomain & 0x02) { + *pPSAttachedState = 1; + } + } +#if 0 + if (pWcdmaSystemInfo->SrvCapabilityValid == 0x01) { + *pPSAttachedState = 0; + if (pWcdmaSystemInfo->SrvCapability & 0x02) { + *pPSAttachedState = 1; + } + } +#endif + if (pWcdmaSystemInfo->NetworkIdValid == 0x01) { + int i; + CHAR temp[10]; + strncpy(temp, (CHAR *)pWcdmaSystemInfo->MCC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileCountryCode = (USHORT)atoi(temp); + + strncpy(temp, (CHAR *)pWcdmaSystemInfo->MNC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileNetworkCode = (USHORT)atoi(temp); + } + break; + case 0x19: // LTE_SYSTEM_INFO + // LTE_SYSTEM_INFO + pLteSystemInfo = (PLTE_SYSTEM_INFO)pServiceStatusInfo; + if (pLteSystemInfo->SrvDomainValid == 0x01) { + *pPSAttachedState = 0; + if (pLteSystemInfo->SrvDomain & 0x02) { + *pPSAttachedState = 1; + is_lte = 1; + s_is_cdma = 0; + } + } +#if 0 + if (pLteSystemInfo->SrvCapabilityValid == 0x01) { + *pPSAttachedState = 0; + if (pLteSystemInfo->SrvCapability & 0x02) { + *pPSAttachedState = 1; + is_lte = 1; + s_is_cdma = 0; + } + } +#endif + if (pLteSystemInfo->NetworkIdValid == 0x01) { + int i; + CHAR temp[10]; + strncpy(temp, (CHAR *)pLteSystemInfo->MCC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileCountryCode = (USHORT)atoi(temp); + + strncpy(temp, (CHAR *)pLteSystemInfo->MNC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileNetworkCode = (USHORT)atoi(temp); + } + break; + case 0x25: // TDSCDMA + // TDSCDMA_SYSTEM_INFO + pTdscdmaSystemInfo = (PTDSCDMA_SYSTEM_INFO)pServiceStatusInfo; + if (pTdscdmaSystemInfo->SrvDomainValid == 0x01) { + *pPSAttachedState = 0; + if (pTdscdmaSystemInfo->SrvDomain & 0x02) { + *pPSAttachedState = 1; + } + } +#if 0 + if (pTdscdmaSystemInfo->SrvCapabilityValid == 0x01) { + *pPSAttachedState = 0; + if (pTdscdmaSystemInfo->SrvCapability & 0x02) { + *pPSAttachedState = 1; + } + } +#endif + if (pTdscdmaSystemInfo->NetworkIdValid == 0x01) { + int i; + CHAR temp[10]; + strncpy(temp, (CHAR *)pTdscdmaSystemInfo->MCC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileCountryCode = (USHORT)atoi(temp); + + strncpy(temp, (CHAR *)pTdscdmaSystemInfo->MNC, 3); + temp[3] = '\0'; + for (i = 0; i < 4; i++) { + if ((UCHAR)temp[i] == 0xFF) { + temp[i] = '\0'; + } + } + MobileNetworkCode = (USHORT)atoi(temp); + } + break; + default: + break; + } /* switch (pServiceStatusInfo->TLYType) */ + remainingLen -= (le16_to_cpu(pServiceStatusInfo->TLVLength) + 3); + pServiceStatusInfo = (PSERVICE_STATUS_INFO)((PCHAR)&pServiceStatusInfo->TLVLength + le16_to_cpu(pServiceStatusInfo->TLVLength) + sizeof(USHORT)); + } /* while (remainingLen > 0) */ + + if (DeviceClass == DEVICE_CLASS_CDMA) { + if (s_hdr_personality == 2) { + pDataCapStr = s_hdr_personality == 2 ? "eHRPD" : "HRPD"; + } else if (DataCapList & WWAN_DATA_CLASS_1XEVDO_REVB) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_1XEVDO_REVB); + } else if (DataCapList & WWAN_DATA_CLASS_1XEVDO_REVA) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_1XEVDO_REVA); + } else if (DataCapList & WWAN_DATA_CLASS_1XEVDO) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_1XEVDO); + } else if (DataCapList & WWAN_DATA_CLASS_1XRTT) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_1XRTT); + } else if (DataCapList & WWAN_DATA_CLASS_3XRTT) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_3XRTT); + } else if (DataCapList & WWAN_DATA_CLASS_UMB) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_UMB); + } + } else { + if (DataCapList & WWAN_DATA_CLASS_LTE) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_LTE); + } else if ((DataCapList & WWAN_DATA_CLASS_HSDPA) && (DataCapList & WWAN_DATA_CLASS_HSUPA)) { + pDataCapStr = "HSDPA_HSUPA"; + } else if (DataCapList & WWAN_DATA_CLASS_HSDPA) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_HSDPA); + } else if (DataCapList & WWAN_DATA_CLASS_HSUPA) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_HSUPA); + } else if (DataCapList & WWAN_DATA_CLASS_UMTS) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_UMTS); + } else if (DataCapList & WWAN_DATA_CLASS_EDGE) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_EDGE); + } else if (DataCapList & WWAN_DATA_CLASS_GPRS) { + pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_GPRS); + } + } + + dbg_time("%s MCC: %d, MNC: %d, PS: %s, DataCap: %s", __func__, + MobileCountryCode, MobileNetworkCode, (*pPSAttachedState == 1) ? "Attached" : "Detached" , pDataCapStr); + + free(pResponse); + + return 0; +} + +int requestRegistrationState(UCHAR *pPSAttachedState) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + PQMINAS_CURRENT_PLMN_MSG pCurrentPlmn; + PSERVING_SYSTEM pServingSystem; + PQMINAS_DATA_CAP pDataCap; + USHORT MobileCountryCode = 0; + USHORT MobileNetworkCode = 0; + const char *pDataCapStr = "UNKNOW"; + + 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) { + meig_convert_cdma_mcc_2_ascii_mcc(&MobileCountryCode, cmda_mcc); + meig_convert_cdma_mnc_2_ascii_mnc(&MobileNetworkCode, cdma_mnc); + } + if (1) { + PQCQMUX_TLV pTLV = (PQCQMUX_TLV)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x23); + if (pTLV) + s_hdr_personality = pTLV->Value; + else + s_hdr_personality = 0; + if (s_hdr_personality == 2) + pDataCapStr = "eHRPD"; + } + } + + dbg_time("%s MCC: %d, MNC: %d, PS: %s, DataCap: %s", __func__, + MobileCountryCode, MobileNetworkCode, (*pPSAttachedState == 1) ? "Attached" : "Detached" , pDataCapStr); + + free(pResponse); + + return 0; +} + +int requestQueryDataCall(UCHAR *pConnectionStatus, int curIpFamily) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + PQMIWDS_PKT_SRVC_TLV pPktSrvc; + UCHAR oldConnectionStatus = *pConnectionStatus; + UCHAR QMIType = (curIpFamily == IpFamilyV4) ? QMUX_TYPE_WDS : QMUX_TYPE_WDS_IPV6; + + pRequest = ComposeQMUXMsg(QMIType, QMIWDS_GET_PKT_SRVC_STATUS_REQ, NULL, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + *pConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; + pPktSrvc = (PQMIWDS_PKT_SRVC_TLV)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x01); + if (pPktSrvc) { + *pConnectionStatus = pPktSrvc->ConnectionStatus; + if ((le16_to_cpu(pPktSrvc->TLVLength) == 2) && (pPktSrvc->ReconfigReqd == 0x01)) + *pConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; + } + + if (*pConnectionStatus == QWDS_PKT_DATA_DISCONNECTED) { + if (curIpFamily == IpFamilyV4) + WdsConnectionIPv4Handle = 0; + else + WdsConnectionIPv6Handle = 0; + } + + if (oldConnectionStatus != *pConnectionStatus || debug_qmi) { + dbg_time("%s %sConnectionStatus: %s", __func__, (curIpFamily == IpFamilyV4) ? "IPv4" : "IPv6", + (*pConnectionStatus == QWDS_PKT_DATA_CONNECTED) ? "CONNECTED" : "DISCONNECTED"); + } + + free(pResponse); + return 0; +} + +int requestSetupDataCall(PROFILE_T *profile, int curIpFamily) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err = 0; + UCHAR QMIType = (curIpFamily == IpFamilyV4) ? QMUX_TYPE_WDS : QMUX_TYPE_WDS_IPV6; + +//DualIPSupported means can get ipv4 & ipv6 address at the same time, one wds for ipv4, the other wds for ipv6 + profile->curIpFamily = curIpFamily; + pRequest = ComposeQMUXMsg(QMIType, QMIWDS_START_NETWORK_INTERFACE_REQ, WdsStartNwInterfaceReq, profile); + err = QmiThreadSendQMITimeout(pRequest, &pResponse, 120 * 1000); + qmi_rsp_check(); + + if (le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXResult) || le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError)) { + PQMI_TLV_HDR pTLVHdr; + + pTLVHdr = GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x10); + if (pTLVHdr) { + uint16_t *data16 = (uint16_t *)(pTLVHdr+1); + uint16_t call_end_reason = le16_to_cpu(data16[0]); + dbg_time("call_end_reason is %d", call_end_reason); + } + + pTLVHdr = GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11); + if (pTLVHdr) { + uint16_t *data16 = (uint16_t *)(pTLVHdr+1); + uint16_t call_end_reason_type = le16_to_cpu(data16[0]); + uint16_t verbose_call_end_reason = le16_to_cpu(data16[1]); + + dbg_time("call_end_reason_type is %d", call_end_reason_type); + dbg_time("call_end_reason_verbose is %d", verbose_call_end_reason); + } + + free(pResponse); + return le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError); + } + + if (curIpFamily == IpFamilyV4) { + WdsConnectionIPv4Handle = le32_to_cpu(pResponse->MUXMsg.StartNwInterfaceResp.Handle); + dbg_time("%s WdsConnectionIPv4Handle: 0x%08x", __func__, WdsConnectionIPv4Handle); + } else { + WdsConnectionIPv6Handle = le32_to_cpu(pResponse->MUXMsg.StartNwInterfaceResp.Handle); + dbg_time("%s WdsConnectionIPv6Handle: 0x%08x", __func__, WdsConnectionIPv6Handle); + } + + free(pResponse); + + return 0; +} + +int requestDeactivateDefaultPDP(PROFILE_T *profile, int curIpFamily) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + UCHAR QMIType = (curIpFamily == 0x04) ? QMUX_TYPE_WDS : QMUX_TYPE_WDS_IPV6; + + if (curIpFamily == IpFamilyV4 && WdsConnectionIPv4Handle == 0) + return 0; + if (curIpFamily == IpFamilyV6 && WdsConnectionIPv6Handle == 0) + return 0; + + pRequest = ComposeQMUXMsg(QMIType, QMIWDS_STOP_NETWORK_INTERFACE_REQ , WdsStopNwInterfaceReq, &curIpFamily); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + if (curIpFamily == IpFamilyV4) + WdsConnectionIPv4Handle = 0; + else + WdsConnectionIPv6Handle = 0; + free(pResponse); + return 0; +} + +int requestGetIPAddress(PROFILE_T *profile, int curIpFamily) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR pIpv4Addr; + PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR pIpv6Addr = NULL; + PQMIWDS_GET_RUNTIME_SETTINGS_TLV_MTU pMtu; + IPV4_T *pIpv4 = &profile->ipv4; + IPV6_T *pIpv6 = &profile->ipv6; + UCHAR QMIType = (curIpFamily == 0x04) ? QMUX_TYPE_WDS : QMUX_TYPE_WDS_IPV6; + + if (curIpFamily == IpFamilyV4) { + memset(pIpv4, 0x00, sizeof(IPV4_T)); + if (WdsConnectionIPv4Handle == 0) + return 0; + } else if (curIpFamily == IpFamilyV6) { + memset(pIpv6, 0x00, sizeof(IPV6_T)); + if (WdsConnectionIPv6Handle == 0) + return 0; + } + + pRequest = ComposeQMUXMsg(QMIType, QMIWDS_GET_RUNTIME_SETTINGS_REQ, WdsGetRuntimeSettingReq, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + pIpv4Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4PRIMARYDNS); + if (pIpv4Addr) { + pIpv4->DnsPrimary = pIpv4Addr->IPV4Address; + } + + pIpv4Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4SECONDARYDNS); + if (pIpv4Addr) { + pIpv4->DnsSecondary = pIpv4Addr->IPV4Address; + } + + pIpv4Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4GATEWAY); + if (pIpv4Addr) { + pIpv4->Gateway = pIpv4Addr->IPV4Address; + } + + pIpv4Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4SUBNET); + if (pIpv4Addr) { + pIpv4->SubnetMask = pIpv4Addr->IPV4Address; + } + + pIpv4Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4); + if (pIpv4Addr) { + pIpv4->Address = pIpv4Addr->IPV4Address; + } + + 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) { + pIpv4->Mtu = pIpv6->Mtu = le32_to_cpu(pMtu->Mtu); + } + + free(pResponse); + return 0; +} + +#ifdef CONFIG_APN +int requestSetProfile(PROFILE_T *profile) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + + if (!profile->pdp) + return 0; + + dbg_time("%s[%d] %s/%s/%s/%d", __func__, profile->pdp, profile->apn, profile->user, profile->password, profile->auth); + pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_MODIFY_PROFILE_SETTINGS_REQ, WdsModifyProfileSettingsReq, profile); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + free(pResponse); + return 0; +} + +int requestGetProfile(PROFILE_T *profile) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + char *apn = NULL; + char *user = NULL; + char *password = NULL; + int auth = 0; + PQMIWDS_APNNAME pApnName; + PQMIWDS_USERNAME pUserName; + PQMIWDS_PASSWD pPassWd; + PQMIWDS_AUTH_PREFERENCE pAuthPref; + + if (!profile->pdp) + return 0; + + pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_GET_PROFILE_SETTINGS_REQ, WdsGetProfileSettingsReqSend, profile); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + pApnName = (PQMIWDS_APNNAME)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x14); + pUserName = (PQMIWDS_USERNAME)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x1B); + pPassWd = (PQMIWDS_PASSWD)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x1C); + pAuthPref = (PQMIWDS_AUTH_PREFERENCE)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x1D); + + if (pApnName/* && le16_to_cpu(pApnName->TLVLength)*/) + apn = strndup((const char *)(&pApnName->ApnName), le16_to_cpu(pApnName->TLVLength)); + if (pUserName/* && pUserName->UserName*/) + user = strndup((const char *)(&pUserName->UserName), le16_to_cpu(pUserName->TLVLength)); + if (pPassWd/* && le16_to_cpu(pPassWd->TLVLength)*/) + password = strndup((const char *)(&pPassWd->Passwd), le16_to_cpu(pPassWd->TLVLength)); + if (pAuthPref/* && le16_to_cpu(pAuthPref->TLVLength)*/) { + auth = pAuthPref->AuthPreference; + } + +#if 0 + if (profile) { + profile->apn = apn; + profile->user = user; + profile->password = password; + profile->auth = auth; + } +#endif + + dbg_time("%s[%d] %s/%s/%s/%d", __func__, profile->pdp, apn, user, password, auth); + + free(pResponse); + return 0; +} +#endif + +#ifdef CONFIG_VERSION +int requestBaseBandVersion(const char **pp_reversion) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + PDEVICE_REV_ID revId; + int err; + + if (pp_reversion) *pp_reversion = NULL; + + pRequest = ComposeQMUXMsg(QMUX_TYPE_DMS, QMIDMS_GET_DEVICE_REV_ID_REQ, NULL, NULL); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + revId = (PDEVICE_REV_ID)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x01); + + if (revId && le16_to_cpu(revId->TLVLength)) + { + char *DeviceRevisionID = strndup((const char *)(&revId->RevisionID), le16_to_cpu(revId->TLVLength)); + dbg_time("%s %s", __func__, DeviceRevisionID); + if (s_9x07 == -1) { //fail to get QMUX_TYPE_WDS_ADMIN + if (strncmp(DeviceRevisionID, "EC20", strlen("EC20"))) + s_9x07 = 1; + else + s_9x07 = DeviceRevisionID[5] == 'F' || DeviceRevisionID[6] == 'F'; //EC20CF,EC20EF,EC20CEF + } + if (pp_reversion) *pp_reversion = DeviceRevisionID; + } + + free(pResponse); + return 0; +} +#endif + +#ifdef CONFIG_RESET_RADIO +static USHORT DmsSetOperatingModeReq(PQMUX_MSG pMUXMsg, void *arg) { + pMUXMsg->SetOperatingModeReq.TLVType = 0x01; + pMUXMsg->SetOperatingModeReq.TLVLength = cpu_to_le16(1); + pMUXMsg->SetOperatingModeReq.OperatingMode = *((UCHAR *)arg); + + return sizeof(QMIDMS_SET_OPERATING_MODE_REQ_MSG); +} + +int requestSetOperatingMode(UCHAR OperatingMode) { + PQCQMIMSG pRequest; + PQCQMIMSG pResponse; + PQMUX_MSG pMUXMsg; + int err; + + dbg_time("%s(%d)", __func__, OperatingMode); + + pRequest = ComposeQMUXMsg(QMUX_TYPE_DMS, QMIDMS_SET_OPERATING_MODE_REQ, DmsSetOperatingModeReq, &OperatingMode); + err = QmiThreadSendQMI(pRequest, &pResponse); + qmi_rsp_check_and_return(); + + free(pResponse); + return 0; +} +#endif diff --git a/package/wwan/meig-cm/src/QMIThread.h b/package/wwan/meig-cm/src/QMIThread.h new file mode 100644 index 000000000..59208887d --- /dev/null +++ b/package/wwan/meig-cm/src/QMIThread.h @@ -0,0 +1,228 @@ +#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_DEFAULT_PDP 1 +//#define CONFIG_IMSI_ICCID + +#include <stdio.h> +#include <string.h> +#include <termios.h> +#include <stdio.h> +#include <ctype.h> +#include <time.h> +#include <fcntl.h> +#include <signal.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <pthread.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/epoll.h> +#include <sys/poll.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <stddef.h> + +#include "MPQMI.h" +#include "MPQCTL.h" +#include "MPQMUX.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_1XRTT 0x00010000 +#define WWAN_DATA_CLASS_1XEVDO 0x00020000 +#define WWAN_DATA_CLASS_1XEVDO_REVA 0x00040000 +#define WWAN_DATA_CLASS_1XEVDV 0x00080000 +#define WWAN_DATA_CLASS_3XRTT 0x00100000 +#define WWAN_DATA_CLASS_1XEVDO_REVB 0x00200000 /* for future use */ +#define WWAN_DATA_CLASS_UMB 0x00400000 +#define WWAN_DATA_CLASS_CUSTOM 0x80000000 + +struct wwan_data_class_str { + ULONG class; + CHAR *str; +}; + +#pragma pack(push, 1) + +typedef struct _QCQMIMSG { + QCQMI_HDR QMIHdr; + union { + QMICTL_MSG CTLMsg; + QMUX_MSG MUXMsg; + }; +} __attribute__ ((packed)) QCQMIMSG, *PQCQMIMSG; + +typedef struct __IPV4 { + uint32_t Address; + uint32_t Gateway; + uint32_t SubnetMask; + uint32_t DnsPrimary; + uint32_t DnsSecondary; + uint32_t Mtu; +} IPV4_T; + +typedef struct __IPV6 { + UCHAR Address[16]; + UCHAR Gateway[16]; + UCHAR SubnetMask[16]; + UCHAR DnsPrimary[16]; + UCHAR DnsSecondary[16]; + UCHAR PrefixLengthIPAddr; + UCHAR PrefixLengthGateway; + ULONG Mtu; +} IPV6_T; + +#define IpFamilyV4 (0x04) +#define IpFamilyV6 (0x06) + +struct __PROFILE; +struct qmi_device_ops { + int (*init)(struct __PROFILE *profile); + int (*deinit)(void); + int (*send)(PQCQMIMSG pRequest); + void* (*read)(void *pData); + + + // int (*thread_read)(struct __PROFILE *profile); + // int (*init)(struct __PROFILE *profile); + // int (*open)(struct __PROFILE *profile); + // int (*close)(struct __PROFILE *profile); + // int (*reopen)(struct __PROFILE *profile); + // int (*start_network)(struct __PROFILE *profile); + // int (*stop_network)(struct __PROFILE *profile); + // int (*query_network)(struct __PROFILE *profile); +}; +extern int (*qmidev_send)(PQCQMIMSG pRequest); + +typedef struct __PROFILE { + char *qmichannel; + char *usbnet_adapter; + char *qmapnet_adapter; + char *driver_name; + int qmap_mode; + int qmap_version; + const char *apn; + const char *user; + const char *password; + const char *pincode; + int auth; + int pdp; + int IsDualIPSupported; + int curIpFamily; + int rawIP; + int muxid; + IPV4_T ipv4; + IPV6_T ipv6; + int enable_ipv6; + int ipv4_flag; + int ipv6_flag; + int apntype; + const struct qmi_device_ops *qmi_ops; +} PROFILE_T; + +typedef enum { + SIM_ABSENT = 0, + SIM_NOT_READY = 1, + SIM_READY = 2, /* SIM_READY means the radio state is RADIO_STATE_SIM_READY */ + SIM_PIN = 3, + SIM_PUK = 4, + SIM_NETWORK_PERSONALIZATION = 5, + SIM_BAD = 6, +} SIM_Status; + +#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 + +extern int pthread_cond_timeout_np(pthread_cond_t *cond, pthread_mutex_t * mutex, unsigned msecs); +extern int QmiThreadSendQMI(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse); +extern int QmiThreadSendQMITimeout(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse, unsigned msecs); +extern void QmiThreadRecvQMI(PQCQMIMSG pResponse); +extern void udhcpc_start(PROFILE_T *profile); +extern void udhcpc_stop(PROFILE_T *profile); +extern void dump_qmi(void *dataBuffer, int dataLen); +extern void qmidevice_send_event_to_main(int triger_event); +extern int requestSetEthMode(PROFILE_T *profile); +extern int requestGetSIMStatus(SIM_Status *pSIMStatus); +extern int requestEnterSimPin(const CHAR *pPinCode); +extern int requestGetICCID(void); +extern int requestGetIMSI(void); +extern int requestRegistrationState(UCHAR *pPSAttachedState); +extern int requestQueryDataCall(UCHAR *pConnectionStatus, int curIpFamily); +extern int requestSetupDataCall(PROFILE_T *profile, int curIpFamily); +extern int requestDeactivateDefaultPDP(PROFILE_T *profile, int curIpFamily); +extern int requestSetProfile(PROFILE_T *profile); +extern int requestGetProfile(PROFILE_T *profile); +extern int requestBaseBandVersion(const char **pp_reversion); +extern int requestGetIPAddress(PROFILE_T *profile, int curIpFamily); +extern int requestSetOperatingMode(UCHAR OperatingMode); + +extern void cond_setclock_attr(pthread_cond_t *cond, clockid_t clock); +extern int mbim_main(PROFILE_T *profile); +extern int varify_driver(PROFILE_T *profile); +extern BOOL qmidevice_detect(char *qmichannel, char *usbnet_adapter, unsigned bufsize); +extern int meig_bridge_mode_detect(PROFILE_T *profile); +extern int meig_enable_qmi_wwan_rawip_mode(PROFILE_T *profile); +extern int meig_driver_type_detect(PROFILE_T *profile); +extern int meig_qmap_mode_detect(PROFILE_T *profile); +extern const struct qmi_device_ops gobi_qmidev_ops; +extern const struct qmi_device_ops qmiwwan_qmidev_ops; + +#define qmidev_is_gobinet(_qmichannel) (strncmp(_qmichannel, "/dev/qcqmi", strlen("/dev/qcqmi")) == 0) +#define qmidev_is_qmiwwan(_qmichannel) (strncmp(_qmichannel, "/dev/cdc-wdm", strlen("/dev/cdc-wdm")) == 0) +#define qmidev_is_pciemhi(_qmichannel) (strncmp(_qmichannel, "/dev/mhi_", strlen("/dev/mhi_")) == 0) + +#define driver_is_qmi(_drv_name) (strncasecmp(_drv_name, "qmi_wwan", strlen("qmi_wwan")) == 0) +#define driver_is_mbim(_drv_name) (strncasecmp(_drv_name, "cdc_mbim", strlen("cdc_mbim")) == 0) + +extern FILE *logfilefp; +extern int debug_qmi; +extern int qmidevice_control_fd[2]; +extern int qmiclientId[QMUX_TYPE_WDS_ADMIN + 1]; +extern USHORT le16_to_cpu(USHORT v16); +extern UINT le32_to_cpu (UINT v32); +extern UINT meig_swap32(UINT v32); +extern USHORT cpu_to_le16(USHORT v16); +extern UINT cpu_to_le32(UINT v32); +extern void update_resolv_conf(int iptype, const char *ifname, const char *dns1, const char *dns2); + +#define CM_MAX_BUFF 256 +#define strset(k, v) {if (k) free(k); k = strdup(v);} +#define mfree(v) {if (v) {free(v); v = NULL;} + +#ifdef CM_DEBUG +#define dbg_time(fmt, args...) do { \ + fprintf(stdout, "[%s-%04d: %s] " fmt "\n", __FILE__, __LINE__, get_time(), ##args); \ + 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); \ + if (logfilefp) fprintf(logfilefp, "[%s] " fmt "\n", get_time(), ##args); \ +} while(0); +#endif +#endif diff --git a/package/wwan/meig-cm/src/QmiWwanCM.c b/package/wwan/meig-cm/src/QmiWwanCM.c new file mode 100644 index 000000000..a8c979f21 --- /dev/null +++ b/package/wwan/meig-cm/src/QmiWwanCM.c @@ -0,0 +1,371 @@ +#include <stdio.h> +#include <string.h> +#include <termios.h> +#include <stdio.h> +#include <ctype.h> +typedef unsigned short sa_family_t; +#include <linux/un.h> +#include "QMIThread.h" + +#ifdef CONFIG_QMIWWAN +static int cdc_wdm_fd = -1; +static UCHAR GetQCTLTransactionId(void) { + static int TransactionId = 0; + if (++TransactionId > 0xFF) + TransactionId = 1; + return TransactionId; +} + +typedef USHORT (*CUSTOMQCTL)(PQMICTL_MSG pCTLMsg, void *arg); + +static PQCQMIMSG ComposeQCTLMsg(USHORT QMICTLType, CUSTOMQCTL customQctlMsgFunction, void *arg) { + UCHAR QMIBuf[WDM_DEFAULT_BUFSIZE]; + PQCQMIMSG pRequest = (PQCQMIMSG)QMIBuf; + int Length; + + pRequest->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI; + pRequest->QMIHdr.CtlFlags = 0x00; + pRequest->QMIHdr.QMIType = QMUX_TYPE_CTL; + pRequest->QMIHdr.ClientId= 0x00; + + pRequest->CTLMsg.QMICTLMsgHdr.CtlFlags = QMICTL_FLAG_REQUEST; + pRequest->CTLMsg.QMICTLMsgHdr.TransactionId = GetQCTLTransactionId(); + pRequest->CTLMsg.QMICTLMsgHdr.QMICTLType = cpu_to_le16(QMICTLType); + if (customQctlMsgFunction) + pRequest->CTLMsg.QMICTLMsgHdr.Length = cpu_to_le16(customQctlMsgFunction(&pRequest->CTLMsg, arg) - sizeof(QCQMICTL_MSG_HDR)); + else + pRequest->CTLMsg.QMICTLMsgHdr.Length = cpu_to_le16(0x0000); + + pRequest->QMIHdr.Length = cpu_to_le16(le16_to_cpu(pRequest->CTLMsg.QMICTLMsgHdr.Length) + sizeof(QCQMICTL_MSG_HDR) + sizeof(QCQMI_HDR) - 1); + Length = le16_to_cpu(pRequest->QMIHdr.Length) + 1; + + pRequest = (PQCQMIMSG)malloc(Length); + if (pRequest == NULL) { + dbg_time("%s fail to malloc", __func__); + } else { + memcpy(pRequest, QMIBuf, Length); + } + + return pRequest; +} + +static USHORT CtlGetVersionReq(PQMICTL_MSG QCTLMsg, void *arg) { + QCTLMsg->GetVersionReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER; + QCTLMsg->GetVersionReq.TLVLength = cpu_to_le16(0x0001); + QCTLMsg->GetVersionReq.QMUXTypes = QMUX_TYPE_ALL; + return sizeof(QMICTL_GET_VERSION_REQ_MSG); +} + +static USHORT CtlGetClientIdReq(PQMICTL_MSG QCTLMsg, void *arg) { + QCTLMsg->GetClientIdReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER; + QCTLMsg->GetClientIdReq.TLVLength = cpu_to_le16(0x0001); + QCTLMsg->GetClientIdReq.QMIType = ((UCHAR *)arg)[0]; + return sizeof(QMICTL_GET_CLIENT_ID_REQ_MSG); +} + +static USHORT CtlReleaseClientIdReq(PQMICTL_MSG QCTLMsg, void *arg) { + QCTLMsg->ReleaseClientIdReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER; + QCTLMsg->ReleaseClientIdReq.TLVLength = cpu_to_le16(0x0002); + QCTLMsg->ReleaseClientIdReq.QMIType = ((UCHAR *)arg)[0]; + QCTLMsg->ReleaseClientIdReq.ClientId = ((UCHAR *)arg)[1] ; + return sizeof(QMICTL_RELEASE_CLIENT_ID_REQ_MSG); +} + +static int QmiWwanSendQMI(PQCQMIMSG pRequest) { + struct pollfd pollfds[]= {{cdc_wdm_fd, POLLOUT, 0}}; + int ret; + + if (cdc_wdm_fd == -1) { + dbg_time("%s cdc_wdm_fd = -1", __func__); + return -ENODEV; + } + + if (pRequest->QMIHdr.QMIType == QMUX_TYPE_WDS_IPV6) + pRequest->QMIHdr.QMIType = QMUX_TYPE_WDS; + + do { + ret = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), 5000); + } while ((ret < 0) && (errno == EINTR)); + + if (pollfds[0].revents & POLLOUT) { + ssize_t nwrites = le16_to_cpu(pRequest->QMIHdr.Length) + 1; + ret = write(cdc_wdm_fd, pRequest, nwrites); + if (ret == nwrites) { + ret = 0; + } else { + dbg_time("%s write=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno)); + } + } else { + dbg_time("%s poll=%d, revents = 0x%x, errno: %d (%s)", __func__, ret, pollfds[0].revents, errno, strerror(errno)); + } + + return ret; +} + +static int QmiWwanGetClientID(UCHAR QMIType) { + PQCQMIMSG pResponse; + + QmiThreadSendQMI(ComposeQCTLMsg(QMICTL_GET_CLIENT_ID_REQ, CtlGetClientIdReq, &QMIType), &pResponse); + + if (pResponse) { + USHORT QMUXResult = cpu_to_le16(pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXResult); // QMI_RESULT_SUCCESS + USHORT QMUXError = cpu_to_le16(pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXError); // QMI_ERR_INVALID_ARG + //UCHAR QMIType = pResponse->CTLMsg.GetClientIdRsp.QMIType; + UCHAR ClientId = pResponse->CTLMsg.GetClientIdRsp.ClientId; + + if (!QMUXResult && !QMUXError && (QMIType == pResponse->CTLMsg.GetClientIdRsp.QMIType)) { + switch (QMIType) { + case QMUX_TYPE_WDS: dbg_time("Get clientWDS = %d", ClientId); break; + case QMUX_TYPE_DMS: dbg_time("Get clientDMS = %d", ClientId); break; + case QMUX_TYPE_NAS: dbg_time("Get clientNAS = %d", ClientId); break; + case QMUX_TYPE_QOS: dbg_time("Get clientQOS = %d", ClientId); break; + case QMUX_TYPE_WMS: dbg_time("Get clientWMS = %d", ClientId); break; + case QMUX_TYPE_PDS: dbg_time("Get clientPDS = %d", ClientId); break; + case QMUX_TYPE_UIM: dbg_time("Get clientUIM = %d", ClientId); break; + case QMUX_TYPE_WDS_ADMIN: dbg_time("Get clientWDA = %d", ClientId); + break; + default: break; + } + return ClientId; + } + } + return 0; +} + +static int QmiWwanReleaseClientID(QMI_SERVICE_TYPE QMIType, UCHAR ClientId) { + UCHAR argv[] = {QMIType, ClientId}; + QmiThreadSendQMI(ComposeQCTLMsg(QMICTL_RELEASE_CLIENT_ID_REQ, CtlReleaseClientIdReq, argv), NULL); + return 0; +} + +static int QmiWwanInit(PROFILE_T *profile) { + unsigned i; + int ret; + PQCQMIMSG pResponse; + + if (profile->qmap_mode == 0 || profile->qmap_mode == 1) + { + for (i = 0; i < 10; i++) { + ret = QmiThreadSendQMITimeout(ComposeQCTLMsg(QMICTL_SYNC_REQ, NULL, NULL), NULL, 1 * 1000); + if (!ret) + break; + sleep(1); + } + if (ret) + return ret; + } + + QmiThreadSendQMI(ComposeQCTLMsg(QMICTL_GET_VERSION_REQ, CtlGetVersionReq, NULL), &pResponse); + if (profile->qmap_mode) { + if (pResponse) { + if (pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXResult == 0 && pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXError == 0) { + uint8_t NumElements = 0; + + for (NumElements = 0; NumElements < pResponse->CTLMsg.GetVersionRsp.NumElements; NumElements++) { +#if 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 || profile->IsDualIPSupported) + qmiclientId[QMUX_TYPE_WDS_IPV6] = QmiWwanGetClientID(QMUX_TYPE_WDS); + qmiclientId[QMUX_TYPE_DMS] = QmiWwanGetClientID(QMUX_TYPE_DMS); + qmiclientId[QMUX_TYPE_NAS] = QmiWwanGetClientID(QMUX_TYPE_NAS); + qmiclientId[QMUX_TYPE_UIM] = QmiWwanGetClientID(QMUX_TYPE_UIM); + if (profile->qmap_mode == 0 || profile->qmap_mode == 1) + qmiclientId[QMUX_TYPE_WDS_ADMIN] = QmiWwanGetClientID(QMUX_TYPE_WDS_ADMIN); + + return 0; +} + +static int QmiWwanDeInit(void) { + unsigned int i; + for (i = 0; i < sizeof(qmiclientId)/sizeof(qmiclientId[0]); i++) + { + if (qmiclientId[i] != 0) + { + QmiWwanReleaseClientID(i, qmiclientId[i]); + qmiclientId[i] = 0; + } + } + + return 0; +} + +#define MEIG_QMI_PROXY "meig-qmi-proxy" +static int qmi_proxy_open(const char *name) { + int sockfd = -1; + int reuse_addr = 1; + struct sockaddr_un sockaddr; + socklen_t alen; + + /*Create server socket*/ + (sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)); + if (sockfd < 0) + return sockfd; + + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sun_family = AF_LOCAL; + sockaddr.sun_path[0] = 0; + memcpy(sockaddr.sun_path + 1, name, strlen(name) ); + + alen = strlen(name) + offsetof(struct sockaddr_un, sun_path) + 1; + if(connect(sockfd, (struct sockaddr *)&sockaddr, alen) < 0) { + close(sockfd); + dbg_time("%s connect %s errno: %d (%s)\n", __func__, name, errno, strerror(errno)); + return -1; + } + (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,sizeof(reuse_addr))); + + dbg_time("connect to %s sockfd = %d\n", name, sockfd); + + return sockfd; +} + +static ssize_t qmi_proxy_read (int fd, void *buf, size_t size) { + ssize_t nreads; + PQCQMI_HDR pHdr = (PQCQMI_HDR)buf; + + nreads = read(fd, pHdr, sizeof(QCQMI_HDR)); + if (nreads == sizeof(QCQMI_HDR)) { + nreads += read(fd, pHdr+1, le16_to_cpu(pHdr->Length) + 1 - sizeof(QCQMI_HDR)); + } + + return nreads; +} + +static void * QmiWwanThread(void *pData) { + PROFILE_T *profile = (PROFILE_T *)pData; + const char *cdc_wdm = (const char *)profile->qmichannel; + int wait_for_request_quit = 0; + + if (profile->qmap_mode == 0 || profile->qmap_mode == 1) + cdc_wdm_fd = open(cdc_wdm, O_RDWR | O_NONBLOCK | O_NOCTTY); + else + cdc_wdm_fd = qmi_proxy_open(MEIG_QMI_PROXY); + + if (cdc_wdm_fd == -1) { + dbg_time("%s Failed to open %s, errno: %d (%s)", __func__, cdc_wdm, errno, strerror(errno)); + qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED); + pthread_exit(NULL); + return NULL; + } + + fcntl(cdc_wdm_fd, F_SETFL, fcntl(cdc_wdm_fd,F_GETFL) | O_NONBLOCK); + fcntl(cdc_wdm_fd, F_SETFD, FD_CLOEXEC); + + dbg_time("cdc_wdm_fd = %d", cdc_wdm_fd); + + qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED); + while (1) { + struct pollfd pollfds[] = {{qmidevice_control_fd[1], POLLIN, 0}, {cdc_wdm_fd, POLLIN, 0}}; + int ne, ret, nevents = sizeof(pollfds)/sizeof(pollfds[0]); + + do { + ret = poll(pollfds, nevents, 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 SIGTERM: + case SIGHUP: + case SIGINT: + wait_for_request_quit = 1; + break; + default: + break; + } + } + } + + if (fd == cdc_wdm_fd) { + ssize_t nreads; + UCHAR QMIBuf[512]; + PQCQMIMSG pResponse = (PQCQMIMSG)QMIBuf; + + if (profile->qmap_mode == 0 || profile->qmap_mode == 1) + nreads = read(fd, QMIBuf, sizeof(QMIBuf)); + else + nreads = qmi_proxy_read(fd, QMIBuf, sizeof(QMIBuf)); + //dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads, errno, strerror(errno)); + if (nreads <= 0) { + dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads, errno, strerror(errno)); + break; + } + + if (nreads != (le16_to_cpu(pResponse->QMIHdr.Length) + 1)) { + dbg_time("%s nreads=%d, pQCQMI->QMIHdr.Length = %d", __func__, (int)nreads, le16_to_cpu(pResponse->QMIHdr.Length)); + continue; + } + + QmiThreadRecvQMI(pResponse); + } + } + } + +__QmiWwanThread_quit: + if (cdc_wdm_fd != -1) { close(cdc_wdm_fd); cdc_wdm_fd = -1; } + qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED); + QmiThreadRecvQMI(NULL); //main thread may pending on QmiThreadSendQMI() + dbg_time("%s exit", __func__); + pthread_exit(NULL); + return NULL; +} + +#else +static int QmiWwanSendQMI(PQCQMIMSG pRequest) {return -1;} +static int QmiWwanInit(PROFILE_T *profile) {return -1;} +static int QmiWwanDeInit(void) {return -1;} +static void * QmiWwanThread(void *pData) {dbg_time("please set CONFIG_QMIWWAN"); return NULL;} +#endif + +const struct qmi_device_ops qmiwwan_qmidev_ops = { + .init = QmiWwanInit, + .deinit = QmiWwanDeInit, + .send = QmiWwanSendQMI, + .read = QmiWwanThread, +}; diff --git a/package/wwan/meig-cm/src/ReleaseNote.txt b/package/wwan/meig-cm/src/ReleaseNote.txt new file mode 100644 index 000000000..c6f344c5a --- /dev/null +++ b/package/wwan/meig-cm/src/ReleaseNote.txt @@ -0,0 +1,61 @@ +Release Notes +[WCDMA<E_QConnectManager_Linux&Android_V1.2.1] +Date: 2019/02/26 +enhancement: +1. Implement help message. + +root@ubuntu:# ./meig-cm -h +[02-26_10:39:21:353] Usage: ./meig-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: ./meig-cm +[02-26_10:39:21:353] Example 2: ./meig-cm -s 3gnet +[02-26_10:39:21:353] Example 3: ./meig-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. meig-cm now support three dialing methods: IPV4 only, IPV6 only, IPV4V6. + ./meig-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 Meig FAE to get qmi_wwan.c patch. + when enable QMI_WWAN's QMAP IP Mux function, must run 'meig-qmi-proxy -d /dev/cdc-wdmX' before meig-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. + 'Meig_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/package/wwan/meig-cm/src/build.sh b/package/wwan/meig-cm/src/build.sh new file mode 100644 index 000000000..3d9f7fb88 --- /dev/null +++ b/package/wwan/meig-cm/src/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash +#export PATH=/home/zhaopengfei/ruiming/aarch64-himix200-linux/bin:$PATH +make clean +make CROSS_COMPILE=arm-hisiv500-linux- + diff --git a/package/wwan/meig-cm/src/default.script b/package/wwan/meig-cm/src/default.script new file mode 100644 index 000000000..ddce8d4c5 --- /dev/null +++ b/package/wwan/meig-cm/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/package/wwan/meig-cm/src/device.c b/package/wwan/meig-cm/src/device.c new file mode 100644 index 000000000..3f812333d --- /dev/null +++ b/package/wwan/meig-cm/src/device.c @@ -0,0 +1,231 @@ +#include <unistd.h> +#include <sys/types.h> +#include <fcntl.h> +#include <dirent.h> +#include <errno.h> +#include <strings.h> +#include <stdlib.h> +#include <limits.h> + +#include "QMIThread.h" + +#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) +{ + FILE *fp = NULL; + int hexnum; + 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); + + hexnum = strtol(buff, &endptr, 16); + if (errno == ERANGE && (hexnum == LONG_MAX || hexnum == LONG_MIN)) + goto error; + /* if there is no digit in buff */ + if (endptr == buff) + goto error; + return (int)hexnum; + +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 + */ +int dir_get_child(const char *dirname, char *buff, unsigned bufsize) +{ + struct dirent *entptr = NULL; + DIR *dirptr = opendir(dirname); + if (!dirptr) + goto error; + while ((entptr = readdir(dirptr))) { + if (entptr->d_name[0] == '.') + continue; + snprintf(buff, bufsize, "%s", entptr->d_name); + break; + } + + closedir(dirptr); + return 0; +error: + buff[0] = '\0'; + if (dirptr) closedir(dirptr); + return -1; +} + +int conf_get_val(const char *fname, const char *key) +{ + char buff[CM_MAX_BUFF] = {'\0'}; + FILE *fp = fopen(fname, "r"); + if (!fp) + goto error; + + while (fgets(buff, CM_MAX_BUFF, fp)) { + char prefix[CM_MAX_BUFF] = {'\0'}; + char tail[CM_MAX_BUFF] = {'\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); + } + } + +error: + fclose(fp); + return CM_INVALID_VAL; +} + +/* To detect the device info of the modem. + * return: + * FALSE -> fail + * TRUE -> ok + */ +BOOL qmidevice_detect(char *qmichannel, char *usbnet_adapter, unsigned bufsize) { + struct dirent* ent = NULL; + DIR *pDir; + const char *rootdir = "/sys/bus/usb/devices"; + struct { + char path[255*2]; + char uevent[255*3]; + } *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) { + int idVendor; + int idProduct; + char netcard[32+1] = {'\0'}; + char device[32+1] = {'\0'}; + char devname[32+1+6] = {'\0'}; + + snprintf(pl->path, sizeof(pl->path), "%s/%s/idVendor", rootdir, ent->d_name); + idVendor = file_get_value(pl->path); + + snprintf(pl->path, sizeof(pl->path), "%s/%s/idProduct", rootdir, ent->d_name); + idProduct = file_get_value(pl->path); + + if (idVendor != 0x05c6 && idVendor != 0x2c7c && idVendor != 0x2dee) + continue; + + dbg_time("Find %s/%s idVendor=0x%x idProduct=0x%x", rootdir, ent->d_name, idVendor, idProduct); + + /* get network interface */ + snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4/net", rootdir, ent->d_name); + dir_get_child(pl->path, netcard, sizeof(netcard)); + if (netcard[0] == '\0') + continue; + + if (usbnet_adapter[0] && strcmp(usbnet_adapter, netcard)) + continue; + + snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4/GobiQMI", rootdir, ent->d_name); + if (access(pl->path, R_OK)) { + snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4/usbmisc", rootdir, ent->d_name); + if (access(pl->path, R_OK)) { + snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4/usb", rootdir, ent->d_name); + if (access(pl->path, R_OK)) { + dbg_time("no GobiQMI/usbmic/usb found in %s/%s:1.4", rootdir, ent->d_name); + continue; + } + } + } + + /* get device */ + dir_get_child(pl->path, device, sizeof(device)); + if (device[0] == '\0') + continue; + + /* There is a chance that, no device(qcqmiX|cdc-wdmX) is generated. We should warn user about that! */ + snprintf(devname, sizeof(devname), "/dev/%s", device); + if (access(devname, R_OK | F_OK) && errno == ENOENT) { + int major; + int minor; + int ret; + + dbg_time("%s access failed, errno: %d (%s)", devname, errno, strerror(errno)); + snprintf(pl->uevent, sizeof(pl->uevent), "%s/%s/uevent", pl->path, device); + major = conf_get_val(pl->uevent, "MAJOR"); + minor = conf_get_val(pl->uevent, "MINOR"); + if(major == CM_INVALID_VAL || minor == CM_INVALID_VAL) + dbg_time("get major and minor failed"); + + ret = mknod(devname, S_IFCHR|0666, (((major & 0xfff) << 8) | (minor & 0xff) | ((minor & 0xfff00) << 12))); + if (ret) + dbg_time("please mknod %s c %d %d", devname, major, minor); + } + + if (netcard[0] && device[0]) { + snprintf(qmichannel, bufsize, "/dev/%s", device); + 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; +} + +#define USB_CLASS_COMM 2 +#define USB_CLASS_VENDOR_SPEC 0xff +#define USB_CDC_SUBCLASS_MBIM 0x0e + +/* + * To check whether the system load the wrong driver: + * error1: usbnet 2(MBIM) match the QMI driver(qmi_wwan|GobiNet) + * error2: usbnet 0(QMI) match the MBIM driver(cdc_mbim) + * return: + * 0 for ok, or ignorance + * others for failure or error + */ +int varify_driver(PROFILE_T *profile) +{ + char path[CM_MAX_PATHLEN+1] = {'\0'}; + int bInterfaceClass = -1; + + snprintf(path, sizeof(path), "/sys/class/net/%s/device/bInterfaceClass", profile->usbnet_adapter); + bInterfaceClass = file_get_value(path); + + /* QMI_WWAN */ + if (driver_is_qmi(profile->driver_name) && bInterfaceClass != USB_CLASS_VENDOR_SPEC) { + dbg_time("module register driver %s, but at+qcfg=\"usbnet\" is not QMI mode!", profile->driver_name); + return 1; + } + + /* CDC_MBIM */ + if (driver_is_mbim(profile->driver_name) && bInterfaceClass != USB_CLASS_COMM) { + dbg_time("module register driver %s, but at+qcfg=\"usbnet\" is not MBIM mode!", profile->driver_name); + return 1; + } + + return 0; +} diff --git a/package/wwan/meig-cm/src/dhcpclient.c b/package/wwan/meig-cm/src/dhcpclient.c new file mode 100644 index 000000000..de8602f33 --- /dev/null +++ b/package/wwan/meig-cm/src/dhcpclient.c @@ -0,0 +1,90 @@ +#ifdef ANDROID +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <arpa/inet.h> + +#include "QMIThread.h" +#ifdef USE_NDK +extern int (*ifc_init)(void); +extern void (*ifc_close)(void); +extern int (*do_dhcp)(const char *iname); +extern void (*get_dhcp_info)(uint32_t *ipaddr, uint32_t *gateway, uint32_t *prefixLength, + uint32_t *dns1, uint32_t *dns2, uint32_t *server, + uint32_t *lease); +extern int (*property_set)(const char *key, const char *value); +#else +#include <cutils/properties.h> +#include <netutils/ifc.h> +extern int do_dhcp(const char *iname); +extern void get_dhcp_info(uint32_t *ipaddr, uint32_t *gateway, uint32_t *prefixLength, + uint32_t *dns1, uint32_t *dns2, uint32_t *server, + uint32_t *lease); +#endif + +static const char *ipaddr_to_string(in_addr_t addr) +{ + struct in_addr in_addr; + + in_addr.s_addr = addr; + return inet_ntoa(in_addr); +} + +void do_dhcp_request(PROFILE_T *profile) { +#ifdef USE_NDK + if (!ifc_init ||!ifc_close ||!do_dhcp || !get_dhcp_info || !property_set) { + return; + } +#endif + + char *ifname = profile->usbnet_adapter; + uint32_t ipaddr, gateway, prefixLength, dns1, dns2, server, lease; + char propKey[128]; + +#if 0 + if (profile->rawIP && ((profile->IPType==0x04 && profile->ipv4.Address))) + { + snprintf(propKey, sizeof(propKey), "net.%s.dns1", ifname); + property_set(propKey, profile->ipv4.DnsPrimary ? ipaddr_to_string(meig_swap32(profile->ipv4.DnsPrimary)) : "8.8.8.8"); + snprintf(propKey, sizeof(propKey), "net.%s.dns2", ifname); + property_set(propKey, profile->ipv4.DnsSecondary ? ipaddr_to_string(meig_swap32(profile->ipv4.DnsSecondary)) : "8.8.8.8"); + snprintf(propKey, sizeof(propKey), "net.%s.gw", ifname); + property_set(propKey, profile->ipv4.Gateway ? ipaddr_to_string(meig_swap32(profile->ipv4.Gateway)) : "0.0.0.0"); + return; + } +#endif + + if(ifc_init()) { + dbg_time("failed to ifc_init(%s): %s\n", ifname, strerror(errno)); + } + + if (do_dhcp(ifname) < 0) { + dbg_time("failed to do_dhcp(%s): %s\n", ifname, strerror(errno)); + } + + ifc_close(); + + get_dhcp_info(&ipaddr, &gateway, &prefixLength, &dns1, &dns2, &server, &lease); + snprintf(propKey, sizeof(propKey), "net.%s.gw", ifname); + property_set(propKey, gateway ? ipaddr_to_string(gateway) : "0.0.0.0"); +} +#endif diff --git a/package/wwan/meig-cm/src/main.c b/package/wwan/meig-cm/src/main.c new file mode 100644 index 000000000..121c6e9a6 --- /dev/null +++ b/package/wwan/meig-cm/src/main.c @@ -0,0 +1,796 @@ +#include "QMIThread.h" +#include <sys/wait.h> +#include <sys/utsname.h> +#include <sys/time.h> +#include <dirent.h> + +#include "util.h" +//#define CONFIG_EXIT_WHEN_DIAL_FAILED +//#define CONFIG_BACKGROUND_WHEN_GET_IP +//#define CONFIG_PID_FILE_FORMAT "/var/run/meig-cm-%s.pid" //for example /var/run/meig-cm-wwan0.pid +#define MAJOR 1 +#define MINOR 3 +#define REVISION 8 +/* +* Generally, we do not modify version info, so several modifications will share the same version code. +* SUBVERSION is used for customized modification to distinguise this version from previous one. +* SUBVERSION adds up before you send the code to customers and it should be set to 0 if VERSION_STRING info is changed. +*/ +#define SUBVERSION 0 +#define STRINGIFY_HELPER(v) #v +#define STRINGIFY(v) STRINGIFY_HELPER(v) +#define VERSION_STRING() STRINGIFY(MAJOR) "." \ + STRINGIFY(MINOR) "." \ + STRINGIFY(REVISION) +int debug_qmi = 0; +int main_loop = 0; +int qmidevice_control_fd[2]; +static int signal_control_fd[2]; + +extern const struct qmi_device_ops gobi_qmidev_ops; +extern const struct qmi_device_ops qmiwwan_qmidev_ops; +extern int meig_ifconfig(int argc, char *argv[]); + +#ifdef CONFIG_BACKGROUND_WHEN_GET_IP +static int daemon_pipe_fd[2]; + +static void meig_prepare_daemon(void) { + pid_t daemon_child_pid; + + if (pipe(daemon_pipe_fd) < 0) { + dbg_time("%s Faild to create daemon_pipe_fd: %d (%s)", __func__, errno, strerror(errno)); + return; + } + + daemon_child_pid = fork(); + if (daemon_child_pid > 0) { + struct pollfd pollfds[] = {{daemon_pipe_fd[0], POLLIN, 0}, {0, POLLIN, 0}}; + int ne, ret, nevents = sizeof(pollfds)/sizeof(pollfds[0]); + int signo; + + //dbg_time("father"); + + close(daemon_pipe_fd[1]); + + 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; + } + + pollfds[1].fd = signal_control_fd[1]; + + while (1) { + do { + ret = poll(pollfds, nevents, -1); + } while ((ret < 0) && (errno == EINTR)); + + if (ret < 0) { + dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno)); + goto __daemon_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("poll fd = %d, events = 0x%04x", fd, revents); + if (revents & POLLHUP) + goto __daemon_quit; + } + + if ((revents & POLLIN) && read(fd, &signo, sizeof(signo)) == sizeof(signo)) { + if (signal_control_fd[1] == fd) { + if (signo == SIGCHLD) { + int status; + int pid = waitpid(daemon_child_pid, &status, 0); + dbg_time("waitpid pid=%d, status=%x", pid, status); + goto __daemon_quit; + } else { + kill(daemon_child_pid, signo); + } + } else if (daemon_pipe_fd[0] == fd) { + //dbg_time("daemon_pipe_signo = %d", signo); + goto __daemon_quit; + } + } + } + } +__daemon_quit: + //dbg_time("father exit"); + _exit(0); + } else if (daemon_child_pid == 0) { + close(daemon_pipe_fd[0]); + //dbg_time("child", getpid()); + } else { + close(daemon_pipe_fd[0]); + close(daemon_pipe_fd[1]); + dbg_time("%s Faild to create daemon_child_pid: %d (%s)", __func__, errno, strerror(errno)); + } +} + +static void meig_enter_daemon(int signo) { + if (daemon_pipe_fd[1] > 0) + if (signo) { + write(daemon_pipe_fd[1], &signo, sizeof(signo)); + sleep(1); + } + close(daemon_pipe_fd[1]); + daemon_pipe_fd[1] = -1; + setsid(); + } +#endif + +//UINT ifc_get_addr(const char *ifname); +static void usbnet_link_change(int link, PROFILE_T *profile) { + static int s_link = -1; + int curIpFamily = profile->enable_ipv6 ? IpFamilyV6 : IpFamilyV4; + + if (s_link == link) + return; + + s_link = link; + + if (link) { + requestGetIPAddress(profile, curIpFamily); + if (profile->IsDualIPSupported) + requestGetIPAddress(profile, IpFamilyV6); + udhcpc_start(profile); + } else { + udhcpc_stop(profile); + } + +#ifdef CONFIG_BACKGROUND_WHEN_GET_IP + if (link && daemon_pipe_fd[1] > 0) { + int timeout = 6; + while (timeout-- /*&& ifc_get_addr(profile->usbnet_adapter) == 0*/) { + sleep(1); + } + meig_enter_daemon(SIGUSR1); + } +#endif +} + +static int check_ipv4_address(PROFILE_T *now_profile) { + PROFILE_T new_profile_v; + PROFILE_T *new_profile = &new_profile_v; + + memcpy(new_profile, now_profile, sizeof(PROFILE_T)); + if (requestGetIPAddress(new_profile, 0x04) == 0) { + if (new_profile->ipv4.Address != now_profile->ipv4.Address || debug_qmi) { + unsigned char *l = (unsigned char *)&now_profile->ipv4.Address; + unsigned char *r = (unsigned char *)&new_profile->ipv4.Address; + dbg_time("localIP: %d.%d.%d.%d VS remoteIP: %d.%d.%d.%d", + l[3], l[2], l[1], l[0], r[3], r[2], r[1], r[0]); + } + return (new_profile->ipv4.Address == now_profile->ipv4.Address); + } + return 0; +} + +static void main_send_event_to_qmidevice(int triger_event) { + write(qmidevice_control_fd[0], &triger_event, sizeof(triger_event)); +} + +static void send_signo_to_main(int signo) { + write(signal_control_fd[0], &signo, sizeof(signo)); +} + +void qmidevice_send_event_to_main(int triger_event) { + write(qmidevice_control_fd[1], &triger_event, sizeof(triger_event)); +} + +#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]; + char filename[MAX_PATH]; + int linksize; + + snprintf(linkname, MAX_PATH, "%s/%s", dir, file); + linksize = readlink(linkname, filename, MAX_PATH); + if (linksize <= 0) + return 0; + + filename[linksize] = 0; + if (strcmp(filename, qmichannel)) + return 0; + + dbg_time("%s -> %s", linkname, filename); + return 1; +} + +static int is_brother_process(const char *dir, const char *file, void *argv[]) +{ + //const char *myself = (const char *)argv[0]; + char linkname[MAX_PATH]; + char filename[MAX_PATH]; + int linksize; + int i = 0, kill_timeout = 15; + pid_t pid; + + //dbg_time("%s", file); + while (file[i]) { + if (!isdigit(file[i])) + break; + i++; + } + + if (file[i]) { + //dbg_time("%s not digit", file); + return 0; + } + + snprintf(linkname, MAX_PATH, "%s/%s/exe", dir, file); + linksize = readlink(linkname, filename, MAX_PATH); + if (linksize <= 0) + return 0; + + filename[linksize] = 0; + + pid = atoi(file); + if (pid >= getpid()) + return 0; + + snprintf(linkname, MAX_PATH, "%s/%s/fd", dir, file); + if (!ls_dir(linkname, is_same_linkfile, argv)) + return 0; + + dbg_time("%s/%s/exe -> %s", dir, file, filename); + while (kill_timeout-- && !kill(pid, 0)) + { + kill(pid, SIGTERM); + sleep(1); + } + if (!kill(pid, 0)) + { + dbg_time("force kill %s/%s/exe -> %s", dir, file, filename); + kill(pid, SIGKILL); + sleep(1); + } + + return 1; +} + +static int kill_brothers(const char *qmichannel) +{ + char myself[MAX_PATH]; + int filenamesize; + void *argv[2] = {myself, (void *)qmichannel}; + + filenamesize = readlink("/proc/self/exe", myself, MAX_PATH); + if (filenamesize <= 0) + return 0; + myself[filenamesize] = 0; + + if (ls_dir("/proc", is_brother_process, argv)) + sleep(1); + + return 0; +} + +static void meig_sigaction(int signo) { + if (SIGCHLD == signo) + waitpid(-1, NULL, WNOHANG); + else if (SIGALRM == signo) + send_signo_to_main(SIGUSR1); + else + { + if (SIGTERM == signo || SIGHUP == signo || SIGINT == signo) + main_loop = 0; + send_signo_to_main(signo); + main_send_event_to_qmidevice(signo); //main may be wating qmi response + } +} + +pthread_t gQmiThreadID; + + +static int usage(const char *progname) { + dbg_time("Usage: %s [options]", progname); + dbg_time("-s [apn [user password auth]] Set apn/user/password/auth get from your network provider"); + dbg_time("-p pincode Verify sim card pin if sim card is locked"); + dbg_time("-f logfilename Save log message of this program to file"); + dbg_time("-i interface Specify network interface(default auto-detect)"); + dbg_time("-4 IPv4 protocol"); + dbg_time("-6 IPv6 protocol"); + dbg_time("-m muxID Specify muxid when set multi-pdn data connection."); + dbg_time("-n channelID Specify channelID when set multi-pdn data connection(default 1)."); + dbg_time("[Examples]"); + dbg_time("Example 1: %s ", progname); + dbg_time("Example 2: %s -s 3gnet ", progname); + dbg_time("Example 3: %s -s 3gnet carl 1234 0 -p 1234 -f gobinet_log.txt", progname); + return 0; +} + +int qmi_main(PROFILE_T *profile) +{ + int triger_event = 0; + int signo; +#ifdef CONFIG_SIM + SIM_Status SIMStatus; +#endif + UCHAR PSAttachedState; + UCHAR IPv4ConnectionStatus = 0xff; //unknow state + UCHAR IPV6ConnectionStatus = 0xff; //unknow state + int qmierr = 0; + char * save_usbnet_adapter = NULL; + + signal(SIGUSR1, meig_sigaction); + signal(SIGUSR2, meig_sigaction); + signal(SIGINT, meig_sigaction); + signal(SIGTERM, meig_sigaction); + signal(SIGHUP, meig_sigaction); + signal(SIGCHLD, meig_sigaction); + signal(SIGALRM, meig_sigaction); + +#ifdef CONFIG_BACKGROUND_WHEN_GET_IP + meig_prepare_daemon(); +#endif + + if (socketpair( AF_LOCAL, SOCK_STREAM, 0, signal_control_fd) < 0 ) { + dbg_time("%s Faild to create main_control_fd: %d (%s)", __func__, errno, strerror(errno)); + return -1; + } + + if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, qmidevice_control_fd ) < 0 ) { + dbg_time("%s Failed to create thread control socket pair: %d (%s)", __func__, errno, strerror(errno)); + return 0; + } + +//sudo apt-get install udhcpc +//sudo apt-get remove ModemManager +__main_loop: + while (!profile->qmichannel) { + char qmichannel[32+1] = {'\0'}; + char usbnet_adapter[32+1] = {'\0'}; + + if (!qmidevice_detect(qmichannel, usbnet_adapter, sizeof(qmichannel))) { + dbg_time("qmidevice_detect failed"); + continue; + } else { + if (!(profile->qmichannel)) + strset(profile->qmichannel, qmichannel); + if (!(profile->usbnet_adapter)) + strset(profile->usbnet_adapter, usbnet_adapter); + break; + } + if (main_loop) { + int wait_for_device = 3000; + dbg_time("Wait for Meig modules connect"); + while (wait_for_device && main_loop) { + wait_for_device -= 100; + usleep(100*1000); + } + continue; + } + dbg_time("Cannot find qmichannel(%s) usbnet_adapter(%s) for Meig modules", profile->qmichannel, profile->usbnet_adapter); + return -ENODEV; + } + + 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; + meig_qmap_mode_detect(profile); + dbg_time("[zpf]qmap_mode=%d", profile->qmap_mode); + if (profile->qmap_mode == 0 || profile->qmap_mode == 1) + kill_brothers(profile->qmichannel); + + if (pthread_create( &gQmiThreadID, 0, profile->qmi_ops->read, (void *)profile) != 0) { + dbg_time("%s Failed to create QMIThread: %d (%s)", __func__, errno, strerror(errno)); + return 0; + } + + if ((read(qmidevice_control_fd[0], &triger_event, sizeof(triger_event)) != sizeof(triger_event)) + || (triger_event != RIL_INDICATE_DEVICE_CONNECTED)) { + dbg_time("%s Failed to init QMIThread: %d (%s)", __func__, errno, strerror(errno)); + return 0; + } + + if (profile->qmi_ops->init && profile->qmi_ops->init(profile)) { + dbg_time("%s Failed to qmi init: %d (%s)", __func__, errno, strerror(errno)); + return 0; + } + +#ifdef CONFIG_VERSION + requestBaseBandVersion(NULL); +#endif + requestSetEthMode(profile); +#ifdef CONFIG_SIM + qmierr = requestGetSIMStatus(&SIMStatus); + while (qmierr == QMI_ERR_OP_DEVICE_UNSUPPORTED) { + sleep(1); + qmierr = requestGetSIMStatus(&SIMStatus); + } + if ((SIMStatus == SIM_PIN) && profile->pincode) { + requestEnterSimPin(profile->pincode); + } +#ifdef CONFIG_IMSI_ICCID + if (SIMStatus == SIM_READY) { + requestGetICCID(); + requestGetIMSI(); + } +#endif +#endif +#ifdef CONFIG_APN + if (profile->apn || profile->user || profile->password) { + requestSetProfile(profile); + } + requestGetProfile(profile); +#endif + requestRegistrationState(&PSAttachedState); + + if (!requestQueryDataCall(&IPv4ConnectionStatus, IpFamilyV4) && (QWDS_PKT_DATA_CONNECTED == IPv4ConnectionStatus)){ + usbnet_link_change(1, profile); + } else + usbnet_link_change(0, profile); + + send_signo_to_main(SIGUSR1); + +#ifdef CONFIG_PID_FILE_FORMAT + { + char cmd[255]; + sprintf(cmd, "echo %d > " CONFIG_PID_FILE_FORMAT, getpid(), profile->usbnet_adapter); + system(cmd); + } +#endif + + 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]); + UCHAR *pConnectionStatus = (profile->enable_ipv6) ? &IPV6ConnectionStatus : &IPv4ConnectionStatus; + int curIpFamily = (profile->enable_ipv6) ? IpFamilyV6 : IpFamilyV4; + + do { + ret = poll(pollfds, nevents, 15*1000); + } while ((ret < 0) && (errno == EINTR)); + + if (ret == 0) + { + send_signo_to_main(SIGUSR2); + continue; + } + + if (ret <= 0) { + dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno)); + goto __main_quit; + } + + for (ne = 0; ne < nevents; ne++) { + int fd = pollfds[ne].fd; + short revents = pollfds[ne].revents; + + 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 SIGUSR1: + requestQueryDataCall(pConnectionStatus, curIpFamily); + if (QWDS_PKT_DATA_CONNECTED != *pConnectionStatus) + { + usbnet_link_change(0, profile); + requestRegistrationState(&PSAttachedState); + + if (PSAttachedState == 1) { + qmierr = requestSetupDataCall(profile, curIpFamily); + + if ((qmierr > 0) && profile->user && profile->user[0] && profile->password && profile->password[0]) { + int old_auto = profile->auth; + + //may be fail because wrong auth mode, try pap->chap, or chap->pap + profile->auth = (profile->auth == 1) ? 2 : 1; + qmierr = requestSetupDataCall(profile, curIpFamily); + + if (qmierr) + profile->auth = old_auto; //still fail, restore old auth moe + } + + //succssful setup data call + if (!qmierr && profile->IsDualIPSupported) { + requestSetupDataCall(profile, IpFamilyV6); + } + + if (!qmierr) + continue; + } + +#ifdef CONFIG_EXIT_WHEN_DIAL_FAILED + kill(getpid(), SIGTERM); +#endif + alarm(5); //try to setup data call 5 seconds later + } + break; + + case SIGUSR2: + if (QWDS_PKT_DATA_CONNECTED == *pConnectionStatus) + requestQueryDataCall(pConnectionStatus, curIpFamily); + + //local ip is different with remote ip + if (QWDS_PKT_DATA_CONNECTED == IPv4ConnectionStatus && check_ipv4_address(profile) == 0) { + requestDeactivateDefaultPDP(profile, curIpFamily); + *pConnectionStatus = QWDS_PKT_DATA_DISCONNECTED; + } + + if (QWDS_PKT_DATA_CONNECTED != *pConnectionStatus) + send_signo_to_main(SIGUSR1); + break; + + case SIGTERM: + case SIGHUP: + case SIGINT: + if (QWDS_PKT_DATA_CONNECTED == *pConnectionStatus) { + requestDeactivateDefaultPDP(profile, curIpFamily); + if (profile->IsDualIPSupported) + requestDeactivateDefaultPDP(profile, IpFamilyV6); + } + usbnet_link_change(0, profile); + if (profile->qmi_ops->deinit) + profile->qmi_ops->deinit(); + main_send_event_to_qmidevice(RIL_REQUEST_QUIT); + goto __main_quit; + break; + + default: + break; + } + } + } + + if (fd == qmidevice_control_fd[0]) { + if (read(fd, &triger_event, sizeof(triger_event)) == sizeof(triger_event)) { + switch (triger_event) { + case RIL_INDICATE_DEVICE_DISCONNECTED: + usbnet_link_change(0, profile); + if (main_loop) + { + if (pthread_join(gQmiThreadID, NULL)) { + dbg_time("%s Error joining to listener thread (%s)", __func__, strerror(errno)); + } + profile->qmichannel = NULL; + profile->usbnet_adapter = save_usbnet_adapter; + goto __main_loop; + } + goto __main_quit; + break; + + case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED: + requestRegistrationState(&PSAttachedState); + if (PSAttachedState == 1 && QWDS_PKT_DATA_DISCONNECTED == *pConnectionStatus) + send_signo_to_main(SIGUSR1); + break; + + case RIL_UNSOL_DATA_CALL_LIST_CHANGED: + { + UCHAR oldConnectionStatus = *pConnectionStatus; + requestQueryDataCall(pConnectionStatus, curIpFamily); + if (profile->IsDualIPSupported) + requestQueryDataCall(&IPV6ConnectionStatus, IpFamilyV6); + if (QWDS_PKT_DATA_CONNECTED != *pConnectionStatus) + { + usbnet_link_change(0, profile); + //connected change to disconnect + if (oldConnectionStatus == QWDS_PKT_DATA_CONNECTED) + send_signo_to_main(SIGUSR1); + } else if (QWDS_PKT_DATA_CONNECTED == *pConnectionStatus) { + usbnet_link_change(1, profile); + if (oldConnectionStatus == QWDS_PKT_DATA_CONNECTED) { //receive two CONNECT IND? + send_signo_to_main(SIGUSR2); + } + } + } + break; + + default: + break; + } + } + } + } + } + +__main_quit: + usbnet_link_change(0, profile); + if (pthread_join(gQmiThreadID, NULL)) { + dbg_time("%s Error joining to listener thread (%s)", __func__, strerror(errno)); + } + close(signal_control_fd[0]); + close(signal_control_fd[1]); + close(qmidevice_control_fd[0]); + close(qmidevice_control_fd[1]); + dbg_time("%s exit", __func__); + if (logfilefp) + fclose(logfilefp); + +#ifdef CONFIG_PID_FILE_FORMAT + { + char cmd[255]; + sprintf(cmd, "rm " CONFIG_PID_FILE_FORMAT, profile.usbnet_adapter); + system(cmd); + } +#endif + + return 0; +} + +#define has_more_argv() ((opt < argc) && (argv[opt][0] != '-')) +int main(int argc, char *argv[]) +{ + int opt = 1; + char * save_usbnet_adapter = NULL; + PROFILE_T profile; + + dbg_time("Meig_QConnectManager_Linux_V%s", VERSION_STRING()); + memset(&profile, 0x00, sizeof(profile)); + profile.pdp = CONFIG_DEFAULT_PDP; + + if (!strcmp(argv[argc-1], "&")) + argc--; + + opt = 1; + 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()) + profile.auth = argv[opt++][0] - '0'; + break; + + case 'm': + if (has_more_argv()) + profile.muxid = argv[opt++][0] - '0'; + break; + + case 'p': + if (has_more_argv()) + profile.pincode = argv[opt++]; + break; + + case 'n': + if (has_more_argv()) + profile.pdp = argv[opt++][0] - '0'; + break; + + case 'f': + if (has_more_argv()) + { + const char * filename = argv[opt++]; + logfilefp = fopen(filename, "a+"); + if (!logfilefp) { + dbg_time("Fail to open %s, errno: %d(%s)", filename, errno, strerror(errno)); + } + } + break; + + case 'i': + if (has_more_argv()) + profile.usbnet_adapter = save_usbnet_adapter = argv[opt++]; + break; + + case 'v': + debug_qmi = 1; + break; + + case 'l': + main_loop = 1; + break; + + case '4': + profile.ipv4_flag = 1; + break; + + case '6': + profile.ipv6_flag = 1; + break; + + case 'd': + if (has_more_argv()) { + profile.qmichannel = argv[opt++]; + if (qmidev_is_pciemhi(profile.qmichannel)) + profile.usbnet_adapter = "mhi0.1"; + } + break; + + default: + return usage(argv[0]); + break; + } + } + + if (profile.ipv4_flag == 1 && profile.ipv6_flag == 1) { + profile.IsDualIPSupported |= (1 << IpFamilyV6); + } else if (profile.ipv6_flag) { + profile.enable_ipv6 = 1; + } + + if (profile.ipv4_flag != 1 && profile.ipv6_flag != 1) { // default enable IPv4 + profile.ipv4_flag = 1; + } + + if (!(profile.qmichannel) || !(profile.usbnet_adapter)) { + char qmichannel[32+1] = {'\0'}; + char usbnet_adapter[32+1] = {'\0'}; + + if (profile.usbnet_adapter) + strcpy(usbnet_adapter, profile.usbnet_adapter); + + if (!qmidevice_detect(qmichannel, usbnet_adapter, sizeof(qmichannel))) { + dbg_time("qmidevice_detect failed"); + goto error; + } + if (!(profile.qmichannel)) + strset(profile.qmichannel, qmichannel); + if (!(profile.usbnet_adapter)) + strset(profile.usbnet_adapter, usbnet_adapter); + } + + meig_qmap_mode_detect(&profile); + if (varify_driver(&profile)) + return -1; + + if (driver_is_mbim(profile.driver_name) || !strncmp(profile.qmichannel, "/dev/mhi_MBIM", strlen("/dev/mhi_MBIM"))) { + dbg_time("Modem works in MBIM mode"); + return mbim_main(&profile); + } else { + dbg_time("Modem works in QMI mode"); + return qmi_main(&profile); + } + +error: + return -1; +} diff --git a/package/wwan/meig-cm/src/mbim-cm.c b/package/wwan/meig-cm/src/mbim-cm.c new file mode 100644 index 000000000..278af7d91 --- /dev/null +++ b/package/wwan/meig-cm/src/mbim-cm.c @@ -0,0 +1,1765 @@ +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <fcntl.h> +#include <stddef.h> +#include <pthread.h> +#include <errno.h> +#include <time.h> +#include <signal.h> +#include <getopt.h> +#include <sys/poll.h> +#include <sys/time.h> +#include <endian.h> +#include <time.h> +#include <inttypes.h> + +#include "QMIThread.h" + +#define mbim_debug dbg_time + +#define UUID_BASIC_CONNECT "a289cc33-bcbb-8b4f-b6b0-133ec2aae6df" +#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_MBIMContextTypeInternet "7E5E2A7E-4E6F-7272-736B-656E7E5E2A7E" + +typedef unsigned char UINT8; +typedef unsigned short UINT16; +typedef unsigned int UINT32; +typedef unsigned long long UINT64; + +/* Donot use lexxtoh directly, it will change the variable itself, that's what we cannnot telerate */ +#define LE16TOH(v) ({UINT16 _v = (v); le32toh(_v);}) +#define LE32TOH(v) ({UINT32 _v = (v); le32toh(_v);}) +#define LE64TOH(v) ({UINT64 _v = (v); le64toh(_v);}) + +#define STRINGFY(v) #v +/* The function name will be _ENUM_NAMEStr */ +#define enumstrfunc(_ENUM_NAME, _ENUM_MEMS) \ +static const char *_ENUM_NAME##Str(int _val) { \ + struct { int val;char *name;} _enumstr[] = { _ENUM_MEMS }; \ + int idx; for (idx = 0; idx < sizeof(_enumstr)/sizeof(_enumstr[0]); idx++) { \ + if (_val == _enumstr[idx].val) return _enumstr[idx].name;} \ + return STRINGFY(_ENUM_NAME##Unknow); \ +} + +#pragma pack(4) +typedef enum { + MBIM_CID_CMD_TYPE_QUERY = 0, + MBIM_CID_CMD_TYPE_SET = 1, +} MBIM_CID_CMD_TYPE_E; + +//Set Query Notification +#define UUID_BASIC_CONNECT_CIDs \ + MBIM_ENUM_HELPER(MBIM_CID_DEVICE_CAPS, 1) \ + MBIM_ENUM_HELPER(MBIM_CID_SUBSCRIBER_READY_STATUS, 2) \ + MBIM_ENUM_HELPER(MBIM_CID_RADIO_STATE, 3) \ + MBIM_ENUM_HELPER(MBIM_CID_PIN, 4) \ + MBIM_ENUM_HELPER(MBIM_CID_PIN_LIS, 5) \ + MBIM_ENUM_HELPER(MBIM_CID_HOME_PROVIDER, 6) \ + MBIM_ENUM_HELPER(MBIM_CID_PREFERRED_PROVIDERS, 7) \ + MBIM_ENUM_HELPER(MBIM_CID_VISIBLE_PROVIDERS, 8) \ + MBIM_ENUM_HELPER(MBIM_CID_REGISTER_STATE, 9) \ + MBIM_ENUM_HELPER(MBIM_CID_PACKET_SERVICE, 10) \ + MBIM_ENUM_HELPER(MBIM_CID_SIGNAL_STATE, 11) \ + MBIM_ENUM_HELPER(MBIM_CID_CONNECT, 12) \ + MBIM_ENUM_HELPER(MBIM_CID_PROVISIONED_CONTEXTS, 13) \ + MBIM_ENUM_HELPER(MBIM_CID_SERVICE_ACTIVATION, 14) \ + MBIM_ENUM_HELPER(MBIM_CID_IP_CONFIGURATION, 15) \ + MBIM_ENUM_HELPER(MBIM_CID_DEVICE_SERVICES, 16) \ + MBIM_ENUM_HELPER(MBIM_CID_DEVICE_SERVICE_SUBSCRIBE_LIST, 19) \ + MBIM_ENUM_HELPER(MBIM_CID_PACKET_STATISTICS, 20) \ + MBIM_ENUM_HELPER(MBIM_CID_NETWORK_IDLE_HINT, 21) \ + MBIM_ENUM_HELPER(MBIM_CID_EMERGENCY_MODE, 22) \ + MBIM_ENUM_HELPER(MBIM_CID_IP_PACKET_FILTERS, 23) \ + MBIM_ENUM_HELPER(MBIM_CID_MULTICARRIER_PROVIDERS, 24) + +#define MBIM_ENUM_HELPER(k, v) k = v, +typedef enum{ + UUID_BASIC_CONNECT_CIDs +} UUID_BASIC_CONNECT_CID_E; +#undef MBIM_ENUM_HELPER +#define MBIM_ENUM_HELPER(k, v) {k, #k}, +enumstrfunc(CID2, UUID_BASIC_CONNECT_CIDs); +#undef MBIM_ENUM_HELPER + +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; + +#define MBIM_MSGS \ + MBIM_ENUM_HELPER(MBIM_OPEN_MSG, 1) \ + MBIM_ENUM_HELPER(MBIM_CLOSE_MSG, 2) \ + MBIM_ENUM_HELPER(MBIM_COMMAND_MSG, 3) \ + MBIM_ENUM_HELPER(MBIM_HOST_ERROR_MSG, 4) \ + \ + MBIM_ENUM_HELPER(MBIM_OPEN_DONE, 0x80000001) \ + MBIM_ENUM_HELPER(MBIM_CLOSE_DONE, 0x80000002) \ + MBIM_ENUM_HELPER(MBIM_COMMAND_DONE, 0x80000003) \ + MBIM_ENUM_HELPER(MBIM_FUNCTION_ERROR_MSG, 0x80000004) \ + MBIM_ENUM_HELPER(MBIM_INDICATE_STATUS_MSG, 0x80000007) + +#define MBIM_ENUM_HELPER(k, v) k = v, +typedef enum{ + MBIM_MSGS +} MBIM_MSG_Type_E; +#undef MBIM_ENUM_HELPER +#define MBIM_ENUM_HELPER(k, v) {k, #k}, +enumstrfunc(MBIMMSGType, MBIM_MSGS); +#undef MBIM_ENUM_HELPER + +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 < sizeof(_enumstr)/sizeof(_enumstr[0]); idx++) { + if (_val == _enumstr[idx].val) + return _enumstr[idx].name; + } + + return "Undefined"; +}; + +#define MBIMDataClasss \ + MBIM_ENUM_HELPER(MBIMDataClassNone, 0x0) \ + MBIM_ENUM_HELPER(MBIMDataClassGPRS, 0x1) \ + MBIM_ENUM_HELPER(MBIMDataClassEDGE, 0x2) \ + MBIM_ENUM_HELPER(MBIMDataClassUMTS, 0x4) \ + MBIM_ENUM_HELPER(MBIMDataClassHSDPA, 0x8) \ + MBIM_ENUM_HELPER(MBIMDataClassHSUPA, 0x10) \ + MBIM_ENUM_HELPER(MBIMDataClassLTE, 0x20) \ + \ + MBIM_ENUM_HELPER(MBIMDataClass1XRTT, 0x10000) \ + MBIM_ENUM_HELPER(MBIMDataClass1XEVDO, 0x20000) \ + MBIM_ENUM_HELPER(MBIMDataClass1XEVDORevA, 0x40000) \ + \ + MBIM_ENUM_HELPER(MBIMDataClass1XEVDV, 0x80000) \ + MBIM_ENUM_HELPER(MBIMDataClass3XRTT, 0x100000) \ + MBIM_ENUM_HELPER(MBIMDataClass1XEVDORevB, 0x200000) \ + MBIM_ENUM_HELPER(MBIMDataClassUMB, 0x400000) \ + MBIM_ENUM_HELPER(MBIMDataClassCustom, 0x80000000) + +#define MBIM_ENUM_HELPER(k, v) k = v, +typedef enum { + MBIMDataClasss +} MBIM_DATA_CLASS_E; +#undef MBIM_ENUM_HELPER +#define MBIM_ENUM_HELPER(k, v) {k, #k}, +enumstrfunc(MBIMDataClass, MBIMDataClasss); +#undef MBIM_ENUM_HELPER + +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 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 < 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 { + 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 < 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 < 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 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_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 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; + +#define MBIMContextIPTypes \ + MBIM_ENUM_HELPER(MBIMContextIPTypeDefault, 0) \ + MBIM_ENUM_HELPER(MBIMContextIPTypeIPv4, 1) \ + MBIM_ENUM_HELPER(MBIMContextIPTypeIPv6, 2) \ + MBIM_ENUM_HELPER(MBIMContextIPTypeIPv4v6, 3) \ + MBIM_ENUM_HELPER(MBIMContextIPTypeIPv4AndIPv6, 4) + +#define MBIM_ENUM_HELPER(k, v) k = v, +typedef enum { + MBIMContextIPTypes +} MBIM_CONTEXT_IP_TYPE_E; +#undef MBIM_ENUM_HELPER +#define MBIM_ENUM_HELPER(k, v) {k, #k}, +enumstrfunc(MBIMContextIPType, MBIMContextIPTypes); +#undef MBIM_ENUM_HELPER + +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 *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 < 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 < sizeof(_enumstr)/sizeof(_enumstr[0]); idx++) { + if (_val == _enumstr[idx].val) + return _enumstr[idx].name; + } + + return "Undefined"; +}; + +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 Rssi; + UINT32 ErrorRate; + UINT32 SignalStrengthInterval; + UINT32 RssiThreshold; + UINT32 ErrorRateThreshold; +} MBIM_SIGNAL_STATE_INFO_T; + +typedef struct { + UINT32 SignalStrengthInterval; + UINT32 RssiThreshold; + UINT32 ErrorRateThreshold; +} MBIM_SET_SIGNAL_STATE_T; + +#pragma pack() + +static pthread_t read_tid = 0; +static int mbim_verbose = 0; +static UINT32 TransactionId = 1; +static unsigned mbim_default_timeout = 10000; +static const char *mbim_netcard = "wwan0"; +static const char *mbim_dev = "/dev/cdc-wdm0"; +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_fd = -1; +static pthread_mutex_t mbim_command_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t mbim_command_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t mbim_state_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t mbim_state_cond = PTHREAD_COND_INITIALIZER; +static MBIM_MESSAGE_HEADER *mbim_pRequest; +static MBIM_MESSAGE_HEADER *mbim_pResponse; +extern int meig_ifconfig(int argc, char *argv[]); + +static int mysystem(const char *cmd) +{ + int status = system(cmd); + mbim_debug("system(%s)=%d", cmd, status); + + return status; +} + +static void mbim_ifconfig(int iptype, const char *ifname, const unsigned char *ipaddr, const unsigned char *gw, + const unsigned char *dns1, const unsigned char *dns2, UINT32 prefix, UINT32 mtu) { + char shell_cmd[256] = {'\0'}; + char dns1str[128], dns2str[128]; + + if (ipaddr) { + snprintf(shell_cmd, sizeof(shell_cmd), "busybox ip link set dev %s up", ifname); + mysystem(shell_cmd); + } else { + /* remove all address */ + snprintf(shell_cmd, sizeof(shell_cmd), "busybox ip address flush dev %s", ifname); + mysystem(shell_cmd); + snprintf(shell_cmd, sizeof(shell_cmd), "busybox ip link set dev %s down", ifname); + mysystem(shell_cmd); + + update_resolv_conf(4, ifname, NULL, NULL); + update_resolv_conf(6, ifname, NULL, NULL); + return; + } + + if (ipaddr) { + const unsigned char *d = ipaddr; + snprintf(shell_cmd, sizeof(shell_cmd), "busybox ip -%d address flush dev %s", iptype, ifname); + mysystem(shell_cmd); + if (iptype == 4) + snprintf(shell_cmd, sizeof(shell_cmd), "busybox ip -%d address add %u.%u.%u.%u/%u dev %s", + iptype, d[0], d[1], d[2], d[3], prefix, ifname); + if (iptype == 6) + snprintf(shell_cmd, sizeof(shell_cmd), + "busybox ip -%d address add %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x/%u dev %s", + iptype, 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], + prefix, ifname); + mysystem(shell_cmd); + } + + if (gw) { + const unsigned char *d = gw; + if (iptype == 4) + snprintf(shell_cmd, sizeof(shell_cmd), "busybox ip -%d route add default via %d.%d.%d.%d dev %s", + iptype, d[0], d[1], d[2], d[3], ifname); + if (iptype == 6) + snprintf(shell_cmd, sizeof(shell_cmd), + "busybox ip -%d route add default via %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x dev %s", + iptype, 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], + ifname); + mysystem(shell_cmd); + } else { + snprintf(shell_cmd, sizeof(shell_cmd), "busybox ip -%d route add default dev %s", iptype, ifname); + mysystem(shell_cmd); + } + + if (mtu) { + snprintf(shell_cmd, sizeof(shell_cmd), "busybox ip -%d link set dev %s mtu %u", iptype, ifname, mtu); + mysystem(shell_cmd); + } + + if (dns1) { + const unsigned char *d = dns1; + if (iptype == 4) + snprintf(dns1str, sizeof(dns1str), "%d.%d.%d.%d", d[0], d[1], d[2], d[3]); + if (iptype == 6) + snprintf(dns1str, sizeof(dns1str), "%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]); + } + + if (dns2) { + const unsigned char *d = dns2; + if (iptype == 4) + snprintf(dns2str, sizeof(dns2str), "%d.%d.%d.%d", d[0], d[1], d[2], d[3]); + if (iptype == 6) + snprintf(dns2str, sizeof(dns2str), "%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]); + } + + update_resolv_conf(iptype, ifname, dns1 ? dns1str : NULL, dns2 ? dns2str : NULL); +} + +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; +} + +#define mbim_alloc( _size) malloc(_size) +#define mbim_free(_mem) do { if (_mem) { free(_mem); _mem = NULL;}} while(0) + +static int mbim_quit = 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 void _notify_state_chage(void) { + pthread_mutex_lock(&mbim_state_mutex); + pthread_cond_signal(&mbim_state_cond); + pthread_mutex_unlock(&mbim_state_mutex); +} + +#define notify_state_chage(_var, _new) do {if (_var != _new) {_var = _new; _notify_state_chage();}} while (0) + +static int wait_state_change(uint32_t seconds) { + int retval = 0; + + pthread_mutex_lock(&mbim_state_mutex); + retval = pthread_cond_timeout_np(&mbim_state_cond, &mbim_state_mutex, seconds*1000); + pthread_mutex_unlock(&mbim_state_mutex); + + if (retval !=0 && retval != ETIMEDOUT) mbim_debug("seconds=%u, retval=%d", seconds, retval); + return retval; +} + +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 = MBIM_OPEN_MSG; + pRequest->MessageHeader.MessageLength = sizeof(MBIM_COMMAND_MSG_T); + pRequest->MessageHeader.TransactionId = TransactionId++; + pRequest->MaxControlTransfer = 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 = MBIM_CLOSE_MSG; + pRequest->MessageHeader.MessageLength = sizeof(MBIM_CLOSE_MSG_T); + pRequest->MessageHeader.TransactionId = 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); + + pRequest->MessageHeader.MessageType = MBIM_COMMAND_MSG; + pRequest->MessageHeader.MessageLength = (sizeof(MBIM_COMMAND_MSG_T) + InformationBufferLength); + pRequest->MessageHeader.TransactionId = TransactionId++; + + pRequest->FragmentHeader.TotalFragments = 1; + pRequest->FragmentHeader.CurrentFragment= 0; + + memcpy(pRequest->DeviceServiceId.uuid, str2uuid(UUID_BASIC_CONNECT), 16); + + pRequest->CID = CID; + pRequest->CommandType = CommandType; + if (InformationBufferLength && pInformationBuffer) { + pRequest->InformationBufferLength = InformationBufferLength; + memcpy(pRequest->InformationBuffer, pInformationBuffer, InformationBufferLength); + } else { + pRequest->InformationBufferLength = 0; + } + + 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 = "UUID_UNKNOW"; + + if (!strcmp(uuid2str(pUUID), UUID_BASIC_CONNECT)) + str = "UUID_BASIC_CONNECT"; + else if (!strcmp(uuid2str(pUUID), UUID_SMS)) + str = "UUID_SMS"; + else if (!strcmp(uuid2str(pUUID), UUID_USSD)) + str = "UUID_USSD"; + else if (!strcmp(uuid2str(pUUID), UUID_PHONEBOOK)) + str = "UUID_PHONEBOOK"; + else if (!strcmp(uuid2str(pUUID), UUID_STK)) + str = "UUID_STK"; + else if (!strcmp(uuid2str(pUUID), UUID_AUTH)) + str = "UUID_AUTH"; + else if (!strcmp(uuid2str(pUUID), UUID_DSS)) + str = "UUID_DSS"; + else + str = "UUID_UNKNOW"; + + 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 < 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_command_msg(MBIM_COMMAND_MSG_T *pCmdMsg, const char *direction) { + mbim_debug("%s DeviceServiceId = %s (%s)", direction, DeviceServiceId2str(&pCmdMsg->DeviceServiceId), uuid2str(&pCmdMsg->DeviceServiceId)); + mbim_debug("%s CID = %s (%u)", direction, CID2Str(LE32TOH(pCmdMsg->CID)), LE32TOH(pCmdMsg->CID)); + 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_debug("%s DeviceServiceId = %s (%s)", direction, DeviceServiceId2str(&pCmdDone->DeviceServiceId), uuid2str(&pCmdDone->DeviceServiceId)); + mbim_debug("%s CID = %s (%u)", direction, CID2Str(LE32TOH(pCmdDone->CID)), LE32TOH(pCmdDone->CID)); + 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_debug("%s DeviceServiceId = %s (%s)", direction, DeviceServiceId2str(&pIndMsg->DeviceServiceId), uuid2str(&pIndMsg->DeviceServiceId)); + mbim_debug("%s CID = %s (%u)", direction, CID2Str(LE32TOH(pIndMsg->CID)), LE32TOH(pIndMsg->CID)); + 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, pInfo->NwError); + mbim_debug("%s PacketServiceState = %s", direction, MBIMPacketServiceStateStr(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(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 */ + int 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(pInfo->RegisterState)); + mbim_debug("%s RegisterMode = %s", direction, MBIMRegisterModeStr(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 (pInfo->IPv4Mtu) mbim_debug("%s ipv4 mtu = %u", direction, 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 (pInfo->IPv6Mtu) mbim_debug("%s ipv6 mtu = %u", direction, pInfo->IPv6Mtu); +} + +static void mbim_dump(MBIM_MESSAGE_HEADER *pMsg, int mbim_verbose) { + unsigned char *data = (unsigned char *)pMsg; + const char *direction = (pMsg->MessageType & 0x80000000) ? "<" : ">"; + + if (!mbim_verbose) + return; + + if (mbim_verbose) { + unsigned i; + static char _tmp[4096] = {'\0'}; + _tmp[0] = (pMsg->MessageType & 0x80000000) ? '<' : '>'; + _tmp[1] = '\0'; + for (i = 0; i < pMsg->MessageLength && i < 4096; i++) + snprintf(_tmp + strlen(_tmp), 4096 - strlen(_tmp), "%02X:", data[i]); + mbim_debug("%s", _tmp); + } + + mbim_dump_header(pMsg, direction); + if (pMsg->MessageType == MBIM_OPEN_MSG) { + MBIM_OPEN_MSG_T *pOpenMsg = (MBIM_OPEN_MSG_T *)pMsg; + mbim_debug("%s MaxControlTransfer = %u", direction, LE32TOH(pOpenMsg->MaxControlTransfer)); + } + else if (pMsg->MessageType == MBIM_OPEN_DONE) { + MBIM_OPEN_DONE_T *pOpenDone = (MBIM_OPEN_DONE_T *)pMsg; + mbim_debug("%s Status = %u", direction, LE32TOH(pOpenDone->Status)); + } + else if (pMsg->MessageType == MBIM_CLOSE_MSG) { + + } + else if (pMsg->MessageType == MBIM_CLOSE_DONE) { + MBIM_CLOSE_DONE_T *pCloseDone = (MBIM_CLOSE_DONE_T *)pMsg; + mbim_debug("%s Status = %u", direction, LE32TOH(pCloseDone->Status)); + } + else if (pMsg->MessageType == 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)) { + + if (pCmdMsg->CID == MBIM_CID_CONNECT) { + MBIM_SET_CONNECT_T *pInfo = (MBIM_SET_CONNECT_T *)pCmdMsg->InformationBuffer; + mbim_debug("%s SessionId = %u", direction, LE32TOH(pInfo->SessionId)); + } + else if (pCmdMsg->CID == 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)); + } + } + } + else if (pMsg->MessageType == MBIM_COMMAND_DONE) { + MBIM_COMMAND_DONE_T *pCmdDone = (MBIM_COMMAND_DONE_T *)pMsg; + + mbim_dump_command_done(pCmdDone, direction); + if (!memcmp(pCmdDone->DeviceServiceId.uuid, str2uuid(UUID_BASIC_CONNECT), 16)) { + if (pCmdDone->CID == MBIM_CID_CONNECT) { + MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pCmdDone->InformationBuffer; + mbim_dump_connect(pInfo, direction); + } + else if (pCmdDone->CID == MBIM_CID_IP_CONFIGURATION) { + MBIM_IP_CONFIGURATION_INFO_T *pInfo = (MBIM_IP_CONFIGURATION_INFO_T *)pCmdDone->InformationBuffer; + mbim_dump_ipconfig(pInfo, direction); + } + else if (pCmdDone->CID == MBIM_CID_PACKET_SERVICE) { + MBIM_PACKET_SERVICE_INFO_T *pInfo = (MBIM_PACKET_SERVICE_INFO_T *)pCmdDone->InformationBuffer; + mbim_dump_packet_service(pInfo, direction); + } + else if (pCmdDone->CID == MBIM_CID_SUBSCRIBER_READY_STATUS) { + MBIM_SUBSCRIBER_READY_STATUS_T *pInfo = (MBIM_SUBSCRIBER_READY_STATUS_T *)pCmdDone->InformationBuffer; + mbim_dump_subscriber_status(pInfo, direction); + } + else if (pCmdDone->CID == MBIM_CID_REGISTER_STATE) { + MBIM_REGISTRATION_STATE_INFO_T *pInfo = (MBIM_REGISTRATION_STATE_INFO_T *)pCmdDone->InformationBuffer; + mbim_dump_regiester_status(pInfo, direction); + } + } + } + else if (pMsg->MessageType == MBIM_INDICATE_STATUS_MSG) { + MBIM_INDICATE_STATUS_MSG_T *pIndMsg = (MBIM_INDICATE_STATUS_MSG_T *)pMsg; + + mbim_dump_indicate_msg(pIndMsg, direction); + if (!memcmp(pIndMsg->DeviceServiceId.uuid, str2uuid(UUID_BASIC_CONNECT), 16)) { + if (pIndMsg->CID == MBIM_CID_CONNECT) { + MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pIndMsg->InformationBuffer; + mbim_dump_connect(pInfo, direction); + } + else if (pIndMsg->CID == MBIM_CID_SIGNAL_STATE) { + MBIM_SIGNAL_STATE_INFO_T *pInfo = (MBIM_SIGNAL_STATE_INFO_T *)pIndMsg->InformationBuffer; + mbim_dump_signal_state(pInfo, direction); + } + else if (pIndMsg->CID == MBIM_CID_SUBSCRIBER_READY_STATUS) { + MBIM_SUBSCRIBER_READY_STATUS_T *pInfo = (MBIM_SUBSCRIBER_READY_STATUS_T *)pIndMsg->InformationBuffer; + mbim_dump_subscriber_status(pInfo, direction); + } + else if (pIndMsg->CID == MBIM_CID_REGISTER_STATE) { + MBIM_REGISTRATION_STATE_INFO_T *pInfo = (MBIM_REGISTRATION_STATE_INFO_T *)pIndMsg->InformationBuffer; + mbim_dump_regiester_status(pInfo, direction); + } + else if (pIndMsg->CID == MBIM_CID_PACKET_SERVICE) { + MBIM_PACKET_SERVICE_INFO_T *pInfo = (MBIM_PACKET_SERVICE_INFO_T *)pIndMsg->InformationBuffer; + mbim_dump_packet_service(pInfo, direction); + } + } + } + else if (pMsg->MessageType == MBIM_FUNCTION_ERROR_MSG) { + MBIM_FUNCTION_ERROR_MSG_T *pErrMsg = (MBIM_FUNCTION_ERROR_MSG_T*)pMsg; + mbim_debug("%s ErrorStatusCode = %u", direction, pErrMsg->ErrorStatusCode); + } +} + +static void mbim_recv_command(MBIM_MESSAGE_HEADER *pResponse, unsigned 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 && mbim_pRequest->TransactionId == pResponse->TransactionId) { + mbim_pResponse = mbim_alloc(pResponse->MessageLength); + if (mbim_pResponse) + memcpy(mbim_pResponse, pResponse, pResponse->MessageLength); + pthread_cond_signal(&mbim_command_cond); + } + else if (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)) { + if (pIndMsg->CID == MBIM_CID_CONNECT) { + MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pIndMsg->InformationBuffer; + notify_state_chage(ActivationState, pInfo->ActivationState); + } + else if (pIndMsg->CID == MBIM_CID_SUBSCRIBER_READY_STATUS) { + MBIM_SUBSCRIBER_READY_STATUS_T *pInfo = (MBIM_SUBSCRIBER_READY_STATUS_T *)pIndMsg->InformationBuffer; + notify_state_chage(ReadyState, pInfo->ReadyState); + } + else if (pIndMsg->CID == MBIM_CID_REGISTER_STATE) { + MBIM_REGISTRATION_STATE_INFO_T *pInfo = (MBIM_REGISTRATION_STATE_INFO_T *)pIndMsg->InformationBuffer; + notify_state_chage(RegisterState, pInfo->RegisterState); + } + else if (pIndMsg->CID == MBIM_CID_PACKET_SERVICE) { + MBIM_PACKET_SERVICE_INFO_T *pInfo = (MBIM_PACKET_SERVICE_INFO_T *)pIndMsg->InformationBuffer; + notify_state_chage(PacketServiceState, pInfo->PacketServiceState); + } + else if (pIndMsg->CID == MBIM_CID_CONNECT) { + MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pIndMsg->InformationBuffer; + if (pInfo->ActivationState == MBIMActivationStateDeactivated || pInfo->ActivationState == MBIMActivationStateDeactivating) + mbim_ifconfig(4, mbim_netcard, NULL, NULL, NULL, NULL, 0, 0); + } + } + } + + 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 (!pRequest) + return -ENOMEM; + + pthread_mutex_lock(&mbim_command_mutex); + + if (pRequest) + mbim_dump(pRequest, mbim_verbose); + + mbim_pRequest = pRequest; + mbim_pResponse = NULL; + + ret = write(mbim_fd, pRequest, pRequest->MessageLength); + + if (ret == 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 UINT32 mbim_recv_buf[1024]; +static void * mbim_read_thread(void *param) { + mbim_debug("%s is created", __func__); + + while (mbim_fd > 0) { + struct pollfd pollfds[] = {{mbim_fd, POLLIN, 0}}; + int ne, ret, nevents = 1; + + ret = poll(pollfds, nevents, -1); + + if (ret <= 0) { + if (mbim_quit == 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 *) mbim_recv_buf; + + nreads = read(fd, pResponse, sizeof(mbim_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); + } + } + } + +__quit: + read_tid = 0; + mbim_quit++; + mbim_recv_command(NULL, 0); + mbim_debug("%s exit", __func__); + _notify_state_chage(); + + return NULL; +} + +static int mbim_status_code(MBIM_MESSAGE_HEADER *pMsgHdr) { + int status = 0; + + if (!pMsgHdr) + return 0; + + if (pMsgHdr->MessageType == MBIM_OPEN_DONE) { + MBIM_OPEN_DONE_T *pOpenDone = (MBIM_OPEN_DONE_T *)pMsgHdr; + status = pOpenDone->Status; + } + else if (pMsgHdr->MessageType == MBIM_CLOSE_DONE) { + MBIM_CLOSE_DONE_T *pCloseDone = (MBIM_CLOSE_DONE_T *)pMsgHdr; + status = pCloseDone->Status; + } + else if (pMsgHdr->MessageType == MBIM_COMMAND_DONE) { + MBIM_COMMAND_DONE_T *pCmdDone = (MBIM_COMMAND_DONE_T *)pMsgHdr; + status = pCmdDone->Status; + } + else if (pMsgHdr->MessageType == MBIM_FUNCTION_ERROR_MSG) { + MBIM_FUNCTION_ERROR_MSG_T *pErrMsg = (MBIM_FUNCTION_ERROR_MSG_T *)pMsgHdr; + status = pErrMsg->ErrorStatusCode; + if (pErrMsg->ErrorStatusCode == MBIM_ERROR_NOT_OPENED) + mbim_open_state = 0; //EM06ELAR03A05M4G when suspend/resume, may get this error + } + + return status; +} + +#define mbim_check_err(err, pRequest, pCmdDone) do { \ + int _status = mbim_status_code(&pCmdDone->MessageHeader); \ + if ((err || _status) && pCmdDone) { mbim_dump(&pCmdDone->MessageHeader, (mbim_verbose == 0)); } \ + if (err || _status) {mbim_debug("%s:%d err=%d, Status=%d", __func__, __LINE__, err, _status); } \ + if (err || pCmdDone == NULL) { mbim_free(pRequest); mbim_free(pCmdDone); return (err ? err : 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; + + mbim_debug("%s()", __func__); + pRequest = compose_open_command(MaxControlTransfer); + err = mbim_send_command(pRequest, (MBIM_COMMAND_DONE_T **)&pOpenDone, mbim_default_timeout); + mbim_check_err(err, pRequest, pOpenDone); + + err = 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 = 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; + + mbim_debug("%s(sessionID=%d)", __func__, sessionID); + 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 (pCmdDone->InformationBufferLength) + { + MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pCmdDone->InformationBuffer; + ActivationState = pInfo->ActivationState; + } + mbim_free(pRequest); mbim_free(pCmdDone); + return err; +} + +static int mbim_device_caps_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_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 (pCmdDone->InformationBufferLength) { + MBIM_DEVICE_CAPS_INFO_T *pInfo = (MBIM_DEVICE_CAPS_INFO_T *)pCmdDone->InformationBuffer; + char tmp[32]; + UINT32 i; + + if (pInfo->DeviceIdOffset && pInfo->DeviceIdSize) { + for (i = 0; i < 32 && i < (pInfo->DeviceIdSize/2); i++) + tmp[i] = pInfo->DataBuffer[pInfo->DeviceIdOffset - sizeof(MBIM_DEVICE_CAPS_INFO_T) + i*2]; + tmp[i] = '\0'; + mbim_debug("DeviceId: %s", tmp); + } + if (pInfo->FirmwareInfoOffset && pInfo->FirmwareInfoSize) { + for (i = 0; i < 32 && i < (pInfo->FirmwareInfoSize/2); i++) + tmp[i] = pInfo->DataBuffer[pInfo->FirmwareInfoOffset - sizeof(MBIM_DEVICE_CAPS_INFO_T) + i*2]; + tmp[i] = '\0'; + mbim_debug("FirmwareInfo: %s", tmp); + } + if (pInfo->HardwareInfoOffset && pInfo->HardwareInfoSize) { + for (i = 0; i < 32 && i < (pInfo->HardwareInfoSize/2); i++) + tmp[i] = pInfo->DataBuffer[pInfo->HardwareInfoOffset - sizeof(MBIM_DEVICE_CAPS_INFO_T) + i*2]; + tmp[i] = '\0'; + mbim_debug("HardwareInfo: %s", tmp); + } + } + 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 (pCmdDone->InformationBufferLength) { + MBIM_SUBSCRIBER_READY_STATUS_T *pInfo = (MBIM_SUBSCRIBER_READY_STATUS_T *)pCmdDone->InformationBuffer; + ReadyState = pInfo->ReadyState; + } + 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 (pCmdDone->InformationBufferLength) { + MBIM_REGISTRATION_STATE_INFO_T *pInfo = (MBIM_REGISTRATION_STATE_INFO_T *)pCmdDone->InformationBuffer;; + RegisterState = pInfo->RegisterState; + } + 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 (pCmdDone->InformationBufferLength) { + MBIM_PACKET_SERVICE_INFO_T *pInfo = (MBIM_PACKET_SERVICE_INFO_T *)pCmdDone->InformationBuffer; + PacketServiceState = pInfo->PacketServiceState; + } + mbim_free(pRequest); mbim_free(pCmdDone); + return err; +} + +static int mbim_packet_service_set(void) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_COMMAND_DONE_T *pCmdDone = NULL; + int err; + + mbim_debug("%s()", __func__); + UINT32 PacketServiceAction = MBIMPacketServiceActionAttach; + pRequest = compose_basic_connect_command(MBIM_CID_PACKET_SERVICE, MBIM_CID_CMD_TYPE_SET, &PacketServiceAction, sizeof(PacketServiceAction)); + err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout); + mbim_check_err(err, pRequest, pCmdDone); + + if (pCmdDone->InformationBufferLength) { + MBIM_PACKET_SERVICE_INFO_T *pInfo = (MBIM_PACKET_SERVICE_INFO_T *)pCmdDone->InformationBuffer; + PacketServiceState = pInfo->PacketServiceState; + } + mbim_free(pRequest); mbim_free(pCmdDone); + return err; +} + +static int mbim_populate_connect_data(MBIM_SET_CONNECT_T **connect_req_ptr) { + int offset; + int buflen = 0; + int i; + + if (mbim_apn) buflen += 2*strlen(mbim_apn); + if (mbim_user) buflen += 2*strlen(mbim_user); + if (mbim_passwd) buflen += 2*strlen(mbim_passwd); + + *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)); + for (i = 0; i < strlen(mbim_apn); i++) { + (*connect_req_ptr)->DataBuffer[offset + i*2] = mbim_apn[i]; + (*connect_req_ptr)->DataBuffer[offset + i*2 + 1] = 0; + } + } + + offset += (*connect_req_ptr)->AccessStringSize; + 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)); + for (i = 0; i < strlen(mbim_user); i++) { + (*connect_req_ptr)->DataBuffer[offset + i*2] = mbim_user[i]; + (*connect_req_ptr)->DataBuffer[offset + i*2 + 1] = 0; + } + } + + offset += (*connect_req_ptr)->UserNameSize; + 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)); + for (i = 0; i < strlen(mbim_passwd); i++) { + (*connect_req_ptr)->DataBuffer[offset + i*2] = mbim_passwd[i]; + (*connect_req_ptr)->DataBuffer[offset + i*2 + 1] = 0; + } + } + + 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 (pCmdDone->InformationBufferLength) { + MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pCmdDone->InformationBuffer; + ActivationState = pInfo->ActivationState; + } + + mbim_free(pRequest); mbim_free(pCmdDone); + return err; +} + +static int mbim_ip_config(int sessionID) { + MBIM_MESSAGE_HEADER *pRequest = NULL; + MBIM_COMMAND_DONE_T *pCmdDone = NULL; + MBIM_IP_CONFIGURATION_INFO_T ip_info; + int err; + + 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 (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; + + if (mbim_verbose == 0) mbim_dump_ipconfig(pInfo, "<"); + + /* IPv4 network configration */ + if (pInfo->IPv4ConfigurationAvailable&0x1) { + MBIM_IPV4_ELEMENT_T *pAddress = (MBIM_IPV4_ELEMENT_T *)(&pInfo->DataBuffer[pInfo->IPv4AddressOffset-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + prefix = pAddress->OnLinkPrefixLength; + ipv4 = pAddress->IPv4Address; + + if (pInfo->IPv4ConfigurationAvailable&0x2) + gw = (UINT8 *)(&pInfo->DataBuffer[pInfo->IPv4GatewayOffset-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + + if (pInfo->IPv4ConfigurationAvailable&0x4) { + dns1 = (UINT8 *)(&pInfo->DataBuffer[pInfo->IPv4DnsServerOffset-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + if (pInfo->IPv4DnsServerCount == 2) + dns2 = dns1 + 4; + } + + if (pInfo->IPv4ConfigurationAvailable&0x8) + mtu = pInfo->IPv4Mtu; + + mbim_ifconfig(4, mbim_netcard, ipv4, gw, dns1, dns2, prefix, mtu); + } + + /* IPv6 network configration */ + if (pInfo->IPv6ConfigurationAvailable&0x1) { + MBIM_IPV6_ELEMENT_T *pAddress = (MBIM_IPV6_ELEMENT_T *)(&pInfo->DataBuffer[pInfo->IPv6AddressOffset-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + prefix = pAddress->OnLinkPrefixLength; + ipv6 = pAddress->IPv6Address; + + if (pInfo->IPv6ConfigurationAvailable&0x2) + gw = (UINT8 *)(&pInfo->DataBuffer[pInfo->IPv6GatewayOffset-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + + if (pInfo->IPv6ConfigurationAvailable&0x4) { + dns1 = (UINT8 *)(&pInfo->DataBuffer[pInfo->IPv6DnsServerOffset-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]); + if (pInfo->IPv6DnsServerCount == 2) + dns2 = dns1 + 16; + } + + if (pInfo->IPv6ConfigurationAvailable&0x8) + mtu = pInfo->IPv6Mtu; + + mbim_ifconfig(6, mbim_netcard, ipv6, gw, dns1, dns2, prefix, mtu); + } + } + return err; +} + +static void meig_sigaction(int signo) { + mbim_debug("MBIM catch signo %d", signo); + if (SIGTERM == signo || SIGHUP == signo || SIGINT == signo) { + mbim_quit++; + _notify_state_chage(); + } +} + +static void mbim_reset_state(void) { + ReadyState = oldReadyState = MBIMSubscriberReadyStateNotInitialized; + RegisterState = oldRegisterState = MBIMRegisterStateUnknown; + PacketServiceState = oldPacketServiceState = MBIMPacketServiceStateUnknown; + ActivationState = oldActivationState = MBIMActivationStateUnknown; +} + +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 (ActivationState != oldActivationState) { + mbim_debug("ActivationState %s -> %s ", MBIMActivationStateStr(oldActivationState), MBIMActivationStateStr(ActivationState)); + oldActivationState = ActivationState; chages++; + } + + return chages; +} + +int mbim_main(PROFILE_T *profile) +{ + int retval; + int sessionID = 0; + + signal(SIGINT, meig_sigaction); + signal(SIGTERM, meig_sigaction); + signal(SIGHUP, meig_sigaction); + if (profile->qmichannel) + mbim_dev = profile->qmichannel; + if (profile->usbnet_adapter) + mbim_netcard = profile->usbnet_adapter; + 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->ipv4_flag) + mbim_iptype = MBIMContextIPTypeIPv4; + if (profile->ipv6_flag) + mbim_iptype = MBIMContextIPTypeIPv6; + if (profile->ipv4_flag && profile->ipv6_flag) + mbim_iptype = MBIMContextIPTypeIPv4AndIPv6; + mbim_verbose = debug_qmi; + mbim_debug("apn %s, user %s, passwd %s, auth %d", mbim_apn, mbim_user, mbim_passwd, mbim_auth); + mbim_debug("IP Proto %s", MBIMContextIPTypeStr(mbim_iptype)) + + /* set relative time, for pthread_cond_timedwait */ + cond_setclock_attr(&mbim_state_cond, CLOCK_MONOTONIC); + cond_setclock_attr(&mbim_command_cond, CLOCK_MONOTONIC); + + mbim_fd = open(mbim_dev, O_RDWR | O_NONBLOCK | O_NOCTTY); + if (mbim_fd <= 0) { + mbim_debug("fail to open (%s), errno: %d (%s)", mbim_dev, errno, strerror(errno)); + goto exit; + } + pthread_create(&read_tid, NULL, mbim_read_thread, (void *)mbim_dev); + mbim_open_state = 0; + + while (mbim_quit == 0 && read_tid != 0) { + uint32_t wait_time = 24*60*60; + + if (mbim_open_state == 0) { + mbim_ifconfig(4, mbim_netcard, NULL, NULL, NULL, NULL, 0, 0); + TransactionId = 1; + retval = mbim_open_device(4096); + if (retval) goto exit; + mbim_open_state = 1; + mbim_reset_state(); + retval = mbim_device_caps_query(); + if (retval) goto exit; + } + + if (ReadyState != MBIMSubscriberReadyStateInitialized) { + retval = mbim_subscriber_status_query(); + if (retval) goto exit; + mbim_update_state(); + } + if (ReadyState != MBIMSubscriberReadyStateInitialized) goto _wait_state_change; + + if (RegisterState != MBIMRegisterStateHome && RegisterState != MBIMRegisterStateRoaming) { + retval = mbim_register_state_query(); + if (retval) goto exit; + mbim_update_state(); + } + if (RegisterState != MBIMRegisterStateHome && RegisterState != MBIMRegisterStateRoaming) goto _wait_state_change; + + if (PacketServiceState != MBIMPacketServiceStateAttached) { + retval = mbim_packet_service_query(); + if (retval) goto exit; + mbim_update_state(); + if (PacketServiceState == MBIMPacketServiceStateDetached) { + retval = mbim_packet_service_set(); //at+cgatt=0/1 + if (retval) goto exit; + } + mbim_update_state(); + } + if (PacketServiceState != MBIMPacketServiceStateAttached) goto _wait_state_change; + + if (ActivationState == MBIMActivationStateUnknown) { + retval = mbim_query_connect(sessionID); + if (retval) goto exit; + mbim_update_state(); + } + + if (ActivationState != MBIMActivationStateActivated && ActivationState != MBIMActivationStateActivating) { + retval = mbim_set_connect(1, sessionID); + if (retval) goto exit; + mbim_update_state(); + if (ActivationState == MBIMActivationStateActivated) { + retval = mbim_ip_config(sessionID); + if (retval) goto exit; + mbim_update_state(); + } + else { + wait_time = 30; //retry call mbim_set_connect 30 seconds later + } + } + if (ActivationState != MBIMActivationStateActivated) goto _wait_state_change; + +_wait_state_change: + wait_state_change(wait_time); + do { + mbim_update_state(); + } while (mbim_quit == 0 && read_tid != 0 && wait_state_change(1) != ETIMEDOUT); + } + +exit: + if (read_tid > 0) { + if (ActivationState == MBIMActivationStateActivated || ActivationState == MBIMActivationStateActivating) + mbim_set_connect(0, sessionID); + mbim_close_device(); + pthread_kill(read_tid, SIGTERM); + pthread_join(read_tid, NULL); + mbim_ifconfig(4, mbim_netcard, NULL, NULL, NULL, NULL, 0, 0); + } + + if (mbim_fd > 0) { + close(mbim_fd); + } + pthread_mutex_destroy(&mbim_command_mutex); + pthread_mutex_destroy(&mbim_state_mutex); + pthread_cond_destroy(&mbim_command_cond); + pthread_cond_destroy(&mbim_state_cond); + + mbim_debug("MBIM CM exit...\n"); + return 0; +} diff --git a/package/wwan/meig-cm/src/meig-cm b/package/wwan/meig-cm/src/meig-cm new file mode 100644 index 000000000..0239c0c6b Binary files /dev/null and b/package/wwan/meig-cm/src/meig-cm differ diff --git a/package/wwan/meig-cm/src/meig-qmi-proxy b/package/wwan/meig-cm/src/meig-qmi-proxy new file mode 100644 index 000000000..4d3626ae5 Binary files /dev/null and b/package/wwan/meig-cm/src/meig-qmi-proxy differ diff --git a/package/wwan/meig-cm/src/meig-qmi-proxy.c b/package/wwan/meig-cm/src/meig-qmi-proxy.c new file mode 100644 index 000000000..688933e4a --- /dev/null +++ b/package/wwan/meig-cm/src/meig-qmi-proxy.c @@ -0,0 +1,1277 @@ +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdarg.h> +#include <stddef.h> +#include <fcntl.h> +#include <pthread.h> +#include <sys/epoll.h> +#include <sys/poll.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <linux/un.h> +#include <dirent.h> +#include <signal.h> + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; + +#define cpu_to_le16(x) (x) +#define cpu_to_le32(x) (x) +#define le16_to_cpu(x) (x) +#define le32_to_cpu(x) (x) + +#define dprintf(fmt, arg...) do { printf(fmt, ##arg); } while(0) +#define SYSCHECK(c) do{if((c)<0) {dprintf("%s %d error: '%s' (code: %d)\n", __func__, __LINE__, strerror(errno), errno); return -1;}}while(0) +#define cfmakenoblock(fd) do{fcntl(fd, F_SETFL, fcntl(fd,F_GETFL) | O_NONBLOCK);}while(0) + +typedef struct _QCQMI_HDR +{ + uint8_t IFType; + uint16_t Length; + uint8_t CtlFlags; // reserved + uint8_t QMIType; + uint8_t ClientId; +} __attribute__ ((packed)) QCQMI_HDR, *PQCQMI_HDR; + +typedef struct _QMICTL_SYNC_REQ_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_REQUEST + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_CTL_SYNC_REQ + uint16_t Length; // 0 +} __attribute__ ((packed)) QMICTL_SYNC_REQ_MSG, *PQMICTL_SYNC_REQ_MSG; + +typedef struct _QMICTL_SYNC_RESP_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_RESPONSE + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_CTL_SYNC_RESP + uint16_t Length; + uint8_t TLVType; // QCTLV_TYPE_RESULT_CODE + uint16_t TLVLength; // 0x0004 + uint16_t QMIResult; + uint16_t QMIError; +} __attribute__ ((packed)) QMICTL_SYNC_RESP_MSG, *PQMICTL_SYNC_RESP_MSG; + +typedef struct _QMICTL_SYNC_IND_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_INDICATION + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND + uint16_t Length; +} __attribute__ ((packed)) QMICTL_SYNC_IND_MSG, *PQMICTL_SYNC_IND_MSG; + +typedef struct _QMICTL_GET_CLIENT_ID_REQ_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_REQUEST + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_GET_CLIENT_ID_REQ + uint16_t Length; + uint8_t TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + uint16_t TLVLength; // 1 + uint8_t QMIType; // QMUX type +} __attribute__ ((packed)) QMICTL_GET_CLIENT_ID_REQ_MSG, *PQMICTL_GET_CLIENT_ID_REQ_MSG; + +typedef struct _QMICTL_GET_CLIENT_ID_RESP_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_RESPONSE + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_GET_CLIENT_ID_RESP + uint16_t Length; + uint8_t TLVType; // QCTLV_TYPE_RESULT_CODE + uint16_t TLVLength; // 0x0004 + uint16_t QMIResult; // result code + uint16_t QMIError; // error code + uint8_t TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER + uint16_t TLV2Length; // 2 + uint8_t QMIType; + uint8_t ClientId; +} __attribute__ ((packed)) QMICTL_GET_CLIENT_ID_RESP_MSG, *PQMICTL_GET_CLIENT_ID_RESP_MSG; + +typedef struct _QMICTL_RELEASE_CLIENT_ID_REQ_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_REQUEST + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_RELEASE_CLIENT_ID_REQ + uint16_t Length; + uint8_t TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + uint16_t TLVLength; // 0x0002 + uint8_t QMIType; + uint8_t ClientId; +} __attribute__ ((packed)) QMICTL_RELEASE_CLIENT_ID_REQ_MSG, *PQMICTL_RELEASE_CLIENT_ID_REQ_MSG; + +typedef struct _QMICTL_RELEASE_CLIENT_ID_RESP_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_RESPONSE + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_RELEASE_CLIENT_ID_RESP + uint16_t Length; + uint8_t TLVType; // QCTLV_TYPE_RESULT_CODE + uint16_t TLVLength; // 0x0004 + uint16_t QMIResult; // result code + uint16_t QMIError; // error code + uint8_t TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER + uint16_t TLV2Length; // 2 + uint8_t QMIType; + uint8_t ClientId; +} __attribute__ ((packed)) QMICTL_RELEASE_CLIENT_ID_RESP_MSG, *PQMICTL_RELEASE_CLIENT_ID_RESP_MSG; + +// QMICTL Control Flags +#define QMICTL_CTL_FLAG_CMD 0x00 +#define QMICTL_CTL_FLAG_RSP 0x01 +#define QMICTL_CTL_FLAG_IND 0x02 + +typedef struct _QCQMICTL_MSG_HDR +{ + uint8_t CtlFlags; // 00-cmd, 01-rsp, 10-ind + uint8_t TransactionId; + uint16_t QMICTLType; + uint16_t Length; +} __attribute__ ((packed)) QCQMICTL_MSG_HDR, *PQCQMICTL_MSG_HDR; + +#define QCQMICTL_MSG_HDR_SIZE sizeof(QCQMICTL_MSG_HDR) + +typedef struct _QCQMICTL_MSG_HDR_RESP +{ + uint8_t CtlFlags; // 00-cmd, 01-rsp, 10-ind + uint8_t TransactionId; + uint16_t QMICTLType; + uint16_t Length; + uint8_t TLVType; // 0x02 - result code + uint16_t TLVLength; // 4 + uint16_t QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + uint16_t QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} __attribute__ ((packed)) QCQMICTL_MSG_HDR_RESP, *PQCQMICTL_MSG_HDR_RESP; + +typedef struct _QMICTL_GET_VERSION_REQ_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_REQUEST + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_GET_VERSION_REQ + uint16_t Length; // 0 + uint8_t TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER + uint16_t TLVLength; // var + uint8_t QMUXTypes; // List of one byte QMUX_TYPE values + // 0xFF returns a list of versions for all + // QMUX_TYPEs implemented on the device +} __attribute__ ((packed)) QMICTL_GET_VERSION_REQ_MSG, *PQMICTL_GET_VERSION_REQ_MSG; + +typedef struct _QMUX_TYPE_VERSION_STRUCT +{ + uint8_t QMUXType; + uint16_t MajorVersion; + uint16_t MinorVersion; +} __attribute__ ((packed)) QMUX_TYPE_VERSION_STRUCT, *PQMUX_TYPE_VERSION_STRUCT; + +typedef struct _QMICTL_GET_VERSION_RESP_MSG +{ + uint8_t CtlFlags; // QMICTL_FLAG_RESPONSE + uint8_t TransactionId; + uint16_t QMICTLType; // QMICTL_GET_VERSION_RESP + uint16_t Length; + uint8_t TLVType; // QCTLV_TYPE_RESULT_CODE + uint16_t TLVLength; // 0x0004 + uint16_t QMIResult; + uint16_t QMIError; + uint8_t TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER + uint16_t TLV2Length; // var + uint8_t NumElements; // Num of QMUX_TYPE_VERSION_STRUCT + QMUX_TYPE_VERSION_STRUCT TypeVersion[0]; +} __attribute__ ((packed)) QMICTL_GET_VERSION_RESP_MSG, *PQMICTL_GET_VERSION_RESP_MSG; + + +typedef struct _QMICTL_MSG +{ + union + { + // Message Header + QCQMICTL_MSG_HDR QMICTLMsgHdr; + QCQMICTL_MSG_HDR_RESP QMICTLMsgHdrRsp; + + // QMICTL Message + //QMICTL_SET_INSTANCE_ID_REQ_MSG SetInstanceIdReq; + //QMICTL_SET_INSTANCE_ID_RESP_MSG SetInstanceIdRsp; + QMICTL_GET_VERSION_REQ_MSG GetVersionReq; + QMICTL_GET_VERSION_RESP_MSG GetVersionRsp; + QMICTL_GET_CLIENT_ID_REQ_MSG GetClientIdReq; + QMICTL_GET_CLIENT_ID_RESP_MSG GetClientIdRsp; + //QMICTL_RELEASE_CLIENT_ID_REQ_MSG ReleaseClientIdReq; + QMICTL_RELEASE_CLIENT_ID_RESP_MSG ReleaseClientIdRsp; + //QMICTL_REVOKE_CLIENT_ID_IND_MSG RevokeClientIdInd; + //QMICTL_INVALID_CLIENT_ID_IND_MSG InvalidClientIdInd; + //QMICTL_SET_DATA_FORMAT_REQ_MSG SetDataFormatReq; + //QMICTL_SET_DATA_FORMAT_RESP_MSG SetDataFormatRsp; + QMICTL_SYNC_REQ_MSG SyncReq; + QMICTL_SYNC_RESP_MSG SyncRsp; + QMICTL_SYNC_IND_MSG SyncInd; + }; +} __attribute__ ((packed)) QMICTL_MSG, *PQMICTL_MSG; + +typedef struct _QCQMUX_MSG_HDR +{ + uint8_t CtlFlags; // 0: single QMUX Msg; 1: + uint16_t TransactionId; + uint16_t Type; + uint16_t Length; + uint8_t payload[0]; +} __attribute__ ((packed)) QCQMUX_MSG_HDR, *PQCQMUX_MSG_HDR; + +typedef struct _QCQMUX_MSG_HDR_RESP +{ + uint8_t CtlFlags; // 0: single QMUX Msg; 1: + uint16_t TransactionId; + uint16_t Type; + uint16_t Length; + uint8_t TLVType; // 0x02 - result code + uint16_t TLVLength; // 4 + uint16_t QMUXResult; // QMI_RESULT_SUCCESS + // QMI_RESULT_FAILURE + uint16_t QMUXError; // QMI_ERR_INVALID_ARG + // QMI_ERR_NO_MEMORY + // QMI_ERR_INTERNAL + // QMI_ERR_FAULT +} __attribute__ ((packed)) QCQMUX_MSG_HDR_RESP, *PQCQMUX_MSG_HDR_RESP; + +typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT +{ + uint16_t Type; // QMUX type 0x0000 + uint16_t Length; +} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT, *PQMIWDS_ADMIN_SET_DATA_FORMAT; + +typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS +{ + uint8_t TLVType; + uint16_t TLVLength; + uint8_t QOSSetting; +} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS, *PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS; + +typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_TLV +{ + uint8_t TLVType; + uint16_t TLVLength; + uint32_t Value; +} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_TLV, *PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV; + +typedef struct _QMIWDS_ENDPOINT_TLV +{ + uint8_t TLVType; + uint16_t TLVLength; + uint32_t ep_type; + uint32_t iface_id; +} __attribute__ ((packed)) QMIWDS_ENDPOINT_TLV, *PQMIWDS_ENDPOINT_TLV; + +typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG +{ + uint8_t CtlFlags; // 0: single QMUX Msg; 1: + uint16_t TransactionId; + uint16_t Type; + uint16_t Length; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS QosDataFormatTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UnderlyingLinkLayerProtocolTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationProtocolTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationProtocolTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationMaxDatagramsTlv; + QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationMaxSizeTlv; + QMIWDS_ENDPOINT_TLV epTlv; +} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG, *PQMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG; + +typedef struct _QCQMUX_TLV +{ + uint8_t Type; + uint16_t Length; + uint8_t Value[0]; +} __attribute__ ((packed)) QCQMUX_TLV, *PQCQMUX_TLV; + +typedef struct _QMUX_MSG +{ + union + { + // Message Header + QCQMUX_MSG_HDR QMUXMsgHdr; + QCQMUX_MSG_HDR_RESP QMUXMsgHdrResp; + QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG SetDataFormatReq; + }; +} __attribute__ ((packed)) QMUX_MSG, *PQMUX_MSG; + +typedef struct _QCQMIMSG { + QCQMI_HDR QMIHdr; + union { + QMICTL_MSG CTLMsg; + QMUX_MSG MUXMsg; + }; +} __attribute__ ((packed)) QCQMIMSG, *PQCQMIMSG; + + +// QMUX Message Definitions -- QMI SDU +#define QMUX_CTL_FLAG_SINGLE_MSG 0x00 +#define QMUX_CTL_FLAG_COMPOUND_MSG 0x01 +#define QMUX_CTL_FLAG_TYPE_CMD 0x00 +#define QMUX_CTL_FLAG_TYPE_RSP 0x02 +#define QMUX_CTL_FLAG_TYPE_IND 0x04 +#define QMUX_CTL_FLAG_MASK_COMPOUND 0x01 +#define QMUX_CTL_FLAG_MASK_TYPE 0x06 // 00-cmd, 01-rsp, 10-ind + +#define USB_CTL_MSG_TYPE_QMI 0x01 + +#define QMICTL_FLAG_REQUEST 0x00 +#define QMICTL_FLAG_RESPONSE 0x01 +#define QMICTL_FLAG_INDICATION 0x02 + +// QMICTL Type +#define QMICTL_SET_INSTANCE_ID_REQ 0x0020 +#define QMICTL_SET_INSTANCE_ID_RESP 0x0020 +#define QMICTL_GET_VERSION_REQ 0x0021 +#define QMICTL_GET_VERSION_RESP 0x0021 +#define QMICTL_GET_CLIENT_ID_REQ 0x0022 +#define QMICTL_GET_CLIENT_ID_RESP 0x0022 +#define QMICTL_RELEASE_CLIENT_ID_REQ 0x0023 +#define QMICTL_RELEASE_CLIENT_ID_RESP 0x0023 +#define QMICTL_REVOKE_CLIENT_ID_IND 0x0024 +#define QMICTL_INVALID_CLIENT_ID_IND 0x0025 +#define QMICTL_SET_DATA_FORMAT_REQ 0x0026 +#define QMICTL_SET_DATA_FORMAT_RESP 0x0026 +#define QMICTL_SYNC_REQ 0x0027 +#define QMICTL_SYNC_RESP 0x0027 +#define QMICTL_SYNC_IND 0x0027 + +#define QCTLV_TYPE_REQUIRED_PARAMETER 0x01 + +// Define QMI Type +typedef enum _QMI_SERVICE_TYPE +{ + QMUX_TYPE_CTL = 0x00, + QMUX_TYPE_WDS = 0x01, + QMUX_TYPE_DMS = 0x02, + QMUX_TYPE_NAS = 0x03, + QMUX_TYPE_QOS = 0x04, + QMUX_TYPE_WMS = 0x05, + QMUX_TYPE_PDS = 0x06, + QMUX_TYPE_UIM = 0x0B, + QMUX_TYPE_WDS_IPV6 = 0x11, + QMUX_TYPE_WDS_ADMIN = 0x1A, + QMUX_TYPE_MAX = 0xFF, + QMUX_TYPE_ALL = 0xFF +} QMI_SERVICE_TYPE; + +#define QMIWDS_ADMIN_SET_DATA_FORMAT_REQ 0x0020 +#define QMIWDS_ADMIN_SET_DATA_FORMAT_RESP 0x0020 + +struct qlistnode +{ + struct qlistnode *next; + struct qlistnode *prev; +}; + +#define qnode_to_item(node, container, member) \ + (container *) (((char*) (node)) - offsetof(container, member)) + +#define qlist_for_each(node, list) \ + for (node = (list)->next; node != (list); node = node->next) + +#define qlist_empty(list) ((list) == (list)->next) +#define qlist_head(list) ((list)->next) +#define qlist_tail(list) ((list)->prev) + +typedef struct { + struct qlistnode qnode; + uint8_t ClientFd; + QCQMIMSG qmi[0]; +} QMI_PROXY_MSG; + +typedef struct { + struct qlistnode qnode; + uint8_t QMIType; + uint8_t ClientId; + unsigned AccessTime; +} QMI_PROXY_CLINET; + +typedef struct { + struct qlistnode qnode; + struct qlistnode client_qnode; + uint8_t ClientFd; + unsigned AccessTime; +} QMI_PROXY_CONNECTION; + +static void qlist_init(struct qlistnode *node) +{ + node->next = node; + node->prev = node; +} + +static void qlist_add_tail(struct qlistnode *head, struct qlistnode *item) +{ + item->next = head; + item->prev = head->prev; + head->prev->next = item; + head->prev = item; +} + +static void qlist_remove(struct qlistnode *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; +} + +static int qmi_proxy_quit = 0; +static pthread_t thread_id = 0; +static int cdc_wdm_fd = -1; +static int qmi_proxy_server_fd = -1; +static struct qlistnode qmi_proxy_connection; +static struct qlistnode qmi_proxy_ctl_msg; +static PQCQMIMSG s_pCtlReq; +static PQCQMIMSG s_pCtlRsq; +static pthread_mutex_t s_ctlmutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t s_ctlcond = PTHREAD_COND_INITIALIZER; + +static void setTimespecRelative(struct timespec *p_ts, long long msec) +{ + struct timeval tv; + + gettimeofday(&tv, (struct timezone *) NULL); + + /* what's really funny about this is that I know + pthread_cond_timedwait just turns around and makes this + a relative time again */ + p_ts->tv_sec = tv.tv_sec + (msec / 1000); + p_ts->tv_nsec = (tv.tv_usec + (msec % 1000) * 1000L ) * 1000L; +} + +static int pthread_cond_timeout_np(pthread_cond_t *cond, pthread_mutex_t * mutex, unsigned msecs) { + if (msecs != 0) { + struct timespec ts; + setTimespecRelative(&ts, msecs); + return pthread_cond_timedwait(cond, mutex, &ts); + } else { + return pthread_cond_wait(cond, mutex); + } +} + +static int create_local_server(const char *name) { + int sockfd = -1; + int reuse_addr = 1; + struct sockaddr_un sockaddr; + socklen_t alen; + + /*Create server socket*/ + SYSCHECK(sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)); + + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sun_family = AF_LOCAL; + sockaddr.sun_path[0] = 0; + memcpy(sockaddr.sun_path + 1, name, strlen(name) ); + + alen = strlen(name) + offsetof(struct sockaddr_un, sun_path) + 1; + SYSCHECK(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,sizeof(reuse_addr))); + if(bind(sockfd, (struct sockaddr *)&sockaddr, alen) < 0) { + close(sockfd); + dprintf("%s bind %s errno: %d (%s)\n", __func__, name, errno, strerror(errno)); + return -1; + } + + dprintf("local server: %s sockfd = %d\n", name, sockfd); + cfmakenoblock(sockfd); + listen(sockfd, 1); + + return sockfd; +} + +static void accept_qmi_connection(int serverfd) { + int clientfd = -1; + unsigned char addr[128]; + socklen_t alen = sizeof(addr); + QMI_PROXY_CONNECTION *qmi_con; + + clientfd = accept(serverfd, (struct sockaddr *)addr, &alen); + + qmi_con = (QMI_PROXY_CONNECTION *)malloc(sizeof(QMI_PROXY_CONNECTION)); + if (qmi_con) { + qlist_init(&qmi_con->qnode); + qlist_init(&qmi_con->client_qnode); + qmi_con->ClientFd= clientfd; + qmi_con->AccessTime = 0; + dprintf("+++ ClientFd=%d\n", qmi_con->ClientFd); + qlist_add_tail(&qmi_proxy_connection, &qmi_con->qnode); + } + + cfmakenoblock(clientfd); +} + +static void cleanup_qmi_connection(int clientfd) { + struct qlistnode *con_node, *qmi_node; + + qlist_for_each(con_node, &qmi_proxy_connection) { + QMI_PROXY_CONNECTION *qmi_con = qnode_to_item(con_node, QMI_PROXY_CONNECTION, qnode); + + if (qmi_con->ClientFd == clientfd) { + while (!qlist_empty(&qmi_con->client_qnode)) { + QMI_PROXY_CLINET *qmi_client = qnode_to_item(qlist_head(&qmi_con->client_qnode), QMI_PROXY_CLINET, qnode); + + dprintf("xxx ClientFd=%d QMIType=%d ClientId=%d\n", qmi_con->ClientFd, qmi_client->QMIType, qmi_client->ClientId); + + qlist_remove(&qmi_client->qnode); + free(qmi_client); + } + + qlist_for_each(qmi_node, &qmi_proxy_ctl_msg) { + QMI_PROXY_MSG *qmi_msg = qnode_to_item(qmi_node, QMI_PROXY_MSG, qnode); + + if (qmi_msg->ClientFd == qmi_con->ClientFd) { + qlist_remove(&qmi_msg->qnode); + free(qmi_msg); + break; + } + } + + dprintf("--- ClientFd=%d\n", qmi_con->ClientFd); + close(qmi_con->ClientFd); + qlist_remove(&qmi_con->qnode); + free(qmi_con); + break; + } + } +} + +static void get_client_id(QMI_PROXY_CONNECTION *qmi_con, PQMICTL_GET_CLIENT_ID_RESP_MSG pClient) { + if (pClient->QMIResult == 0 && pClient->QMIError == 0) { + QMI_PROXY_CLINET *qmi_client = (QMI_PROXY_CLINET *)malloc(sizeof(QMI_PROXY_CLINET)); + + qlist_init(&qmi_client->qnode); + qmi_client->QMIType = pClient->QMIType; + qmi_client->ClientId = pClient->ClientId; + qmi_client->AccessTime = 0; + + dprintf("+++ ClientFd=%d QMIType=%d ClientId=%d\n", qmi_con->ClientFd, qmi_client->QMIType, qmi_client->ClientId); + qlist_add_tail(&qmi_con->client_qnode, &qmi_client->qnode); + } +} + +static void release_client_id(QMI_PROXY_CONNECTION *qmi_con, PQMICTL_RELEASE_CLIENT_ID_RESP_MSG pClient) { + struct qlistnode *client_node; + + if (pClient->QMIResult == 0 && pClient->QMIError == 0) { + qlist_for_each (client_node, &qmi_con->client_qnode) { + QMI_PROXY_CLINET *qmi_client = qnode_to_item(client_node, QMI_PROXY_CLINET, qnode); + + if (pClient->QMIType == qmi_client->QMIType && pClient->ClientId == qmi_client->ClientId) { + dprintf("--- ClientFd=%d QMIType=%d ClientId=%d\n", qmi_con->ClientFd, qmi_client->QMIType, qmi_client->ClientId); + qlist_remove(&qmi_client->qnode); + free(qmi_client); + break; + } + } + } +} + +static int verbose_debug = 0; +static int send_qmi_to_cdc_wdm(PQCQMIMSG pQMI) { + struct pollfd pollfds[]= {{cdc_wdm_fd, POLLOUT, 0}}; + ssize_t ret = 0; + + do { + ret = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), 5000); + } while ((ret < 0) && (errno == EINTR)); + + if (pollfds[0].revents & POLLOUT) { + ssize_t size = le16_to_cpu(pQMI->QMIHdr.Length) + 1; + ret = write(cdc_wdm_fd, pQMI, size); + if (verbose_debug) + { + ssize_t i; + printf("w %d %zd: ", cdc_wdm_fd, ret); + for (i = 0; i < 16; i++) + printf("%02x ", ((uint8_t *)pQMI)[i]); + printf("\n"); + } + } + + return ret; +} + +static int send_qmi_to_client(PQCQMIMSG pQMI, int clientFd) { + struct pollfd pollfds[]= {{clientFd, POLLOUT, 0}}; + ssize_t ret = 0; + + do { + ret = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), 5000); + } while ((ret < 0) && (errno == EINTR)); + + if (pollfds[0].revents & POLLOUT) { + ssize_t size = le16_to_cpu(pQMI->QMIHdr.Length) + 1; + ret = write(clientFd, pQMI, size); + if (verbose_debug) + { + ssize_t i; + printf("w %d %zd: ", clientFd, ret); + for (i = 0; i < 16; i++) + printf("%02x ", ((uint8_t *)pQMI)[i]); + printf("\n"); + } + } + + return ret; +} + + +static void recv_qmi(PQCQMIMSG pQMI, unsigned size) { + struct qlistnode *con_node, *client_node; + + if (qmi_proxy_server_fd <= 0) { + pthread_mutex_lock(&s_ctlmutex); + + if (s_pCtlReq != NULL) { + if (pQMI->QMIHdr.QMIType == QMUX_TYPE_CTL + && s_pCtlReq->CTLMsg.QMICTLMsgHdrRsp.TransactionId == pQMI->CTLMsg.QMICTLMsgHdrRsp.TransactionId) { + s_pCtlRsq = malloc(size); + memcpy(s_pCtlRsq, pQMI, size); + pthread_cond_signal(&s_ctlcond); + } + else if (pQMI->QMIHdr.QMIType != QMUX_TYPE_CTL + && s_pCtlReq->MUXMsg.QMUXMsgHdr.TransactionId == pQMI->MUXMsg.QMUXMsgHdr.TransactionId) { + s_pCtlRsq = malloc(size); + memcpy(s_pCtlRsq, pQMI, size); + pthread_cond_signal(&s_ctlcond); + } + } + + pthread_mutex_unlock(&s_ctlmutex); + } + else if (pQMI->QMIHdr.QMIType == QMUX_TYPE_CTL) { + if (pQMI->CTLMsg.QMICTLMsgHdr.CtlFlags == QMICTL_CTL_FLAG_RSP) { + if (!qlist_empty(&qmi_proxy_ctl_msg)) { + QMI_PROXY_MSG *qmi_msg = qnode_to_item(qlist_head(&qmi_proxy_ctl_msg), QMI_PROXY_MSG, qnode); + + qlist_for_each(con_node, &qmi_proxy_connection) { + QMI_PROXY_CONNECTION *qmi_con = qnode_to_item(con_node, QMI_PROXY_CONNECTION, qnode); + + if (qmi_con->ClientFd == qmi_msg->ClientFd) { + send_qmi_to_client(pQMI, qmi_msg->ClientFd); + + if (pQMI->CTLMsg.QMICTLMsgHdrRsp.QMICTLType == QMICTL_GET_CLIENT_ID_RESP) + get_client_id(qmi_con, &pQMI->CTLMsg.GetClientIdRsp); + else if (pQMI->CTLMsg.QMICTLMsgHdrRsp.QMICTLType == QMICTL_RELEASE_CLIENT_ID_RESP) + release_client_id(qmi_con, &pQMI->CTLMsg.ReleaseClientIdRsp); + else { + } + } + } + + qlist_remove(&qmi_msg->qnode); + free(qmi_msg); + } + } + + if (!qlist_empty(&qmi_proxy_ctl_msg)) { + QMI_PROXY_MSG *qmi_msg = qnode_to_item(qlist_head(&qmi_proxy_ctl_msg), QMI_PROXY_MSG, qnode); + + qlist_for_each(con_node, &qmi_proxy_connection) { + QMI_PROXY_CONNECTION *qmi_con = qnode_to_item(con_node, QMI_PROXY_CONNECTION, qnode); + + if (qmi_con->ClientFd == qmi_msg->ClientFd) { + send_qmi_to_cdc_wdm(qmi_msg->qmi); + } + } + } + } + else { + qlist_for_each(con_node, &qmi_proxy_connection) { + QMI_PROXY_CONNECTION *qmi_con = qnode_to_item(con_node, QMI_PROXY_CONNECTION, qnode); + + qlist_for_each(client_node, &qmi_con->client_qnode) { + QMI_PROXY_CLINET *qmi_client = qnode_to_item(client_node, QMI_PROXY_CLINET, qnode); + if (pQMI->QMIHdr.QMIType == qmi_client->QMIType) { + if (pQMI->QMIHdr.ClientId == 0 || pQMI->QMIHdr.ClientId == qmi_client->ClientId) { + send_qmi_to_client(pQMI, qmi_con->ClientFd); + } + } + } + } + } +} + +static int send_qmi(PQCQMIMSG pQMI, unsigned size, int clientfd) { + if (qmi_proxy_server_fd <= 0) { + send_qmi_to_cdc_wdm(pQMI); + } + else if (pQMI->QMIHdr.QMIType == QMUX_TYPE_CTL) { + QMI_PROXY_MSG *qmi_msg; + + if (qlist_empty(&qmi_proxy_ctl_msg)) + send_qmi_to_cdc_wdm(pQMI); + + qmi_msg = malloc(sizeof(QMI_PROXY_MSG) + size); + qlist_init(&qmi_msg->qnode); + qmi_msg->ClientFd = clientfd; + memcpy(qmi_msg->qmi, pQMI, size); + qlist_add_tail(&qmi_proxy_ctl_msg, &qmi_msg->qnode); + } + else { + send_qmi_to_cdc_wdm(pQMI); + } + + return 0; +} + +static int send_qmi_timeout(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse, unsigned mseconds) { + int ret; + + pthread_mutex_lock(&s_ctlmutex); + + s_pCtlReq = pRequest; + s_pCtlRsq = NULL; + if (ppResponse) *ppResponse = NULL; + + send_qmi_to_cdc_wdm(pRequest); + ret = pthread_cond_timeout_np(&s_ctlcond, &s_ctlmutex, mseconds); + if (!ret) { + if (s_pCtlRsq && ppResponse) { + *ppResponse = s_pCtlRsq; + } else if (s_pCtlRsq) { + free(s_pCtlRsq); + } + } else { + dprintf("%s ret=%d\n", __func__, ret); + } + + s_pCtlReq = NULL; + pthread_mutex_unlock(&s_ctlmutex); + + return ret; +} + +static PQCQMUX_TLV qmi_find_tlv (PQCQMIMSG pQMI, uint8_t TLVType) { + int Length = 0; + + while (Length < le16_to_cpu(pQMI->MUXMsg.QMUXMsgHdr.Length)) { + PQCQMUX_TLV pTLV = (PQCQMUX_TLV)(&pQMI->MUXMsg.QMUXMsgHdr.payload[Length]); + + //dprintf("TLV {%02x, %04x}\n", pTLV->Type, pTLV->Length); + if (pTLV->Type == TLVType) { + return pTLV; + } + + Length += (le16_to_cpu((pTLV->Length)) + sizeof(QCQMUX_TLV)); + } + + return NULL; +} + +static int qmi_proxy_init(void) { + unsigned i; + int ret; + QCQMIMSG _QMI; + PQCQMIMSG pQMI = &_QMI; + PQCQMIMSG pRsp; + uint8_t TransactionId = 0xC1; + uint8_t WDAClientId = 0; + unsigned rx_urb_size = 0; + unsigned ep_type, iface_id; + + dprintf("%s enter\n", __func__); + + pQMI->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI; + pQMI->QMIHdr.CtlFlags = 0x00; + pQMI->QMIHdr.QMIType = QMUX_TYPE_CTL; + pQMI->QMIHdr.ClientId= 0x00; + + pQMI->CTLMsg.QMICTLMsgHdr.CtlFlags = QMICTL_FLAG_REQUEST; + + for (i = 0; i < 10; i++) { + pQMI->CTLMsg.SyncReq.TransactionId = TransactionId++; + pQMI->CTLMsg.SyncReq.QMICTLType = QMICTL_SYNC_REQ; + pQMI->CTLMsg.SyncReq.Length = 0; + + pQMI->QMIHdr.Length = pQMI->CTLMsg.QMICTLMsgHdr.Length + sizeof(QCQMI_HDR) + sizeof(QCQMICTL_MSG_HDR) - 1; + + ret = send_qmi_timeout(pQMI, NULL, 1000); + if (!ret) + break; + } + + if (ret) + goto qmi_proxy_init_fail; + + pQMI->CTLMsg.GetVersionReq.TransactionId = TransactionId++; + pQMI->CTLMsg.GetVersionReq.QMICTLType = QMICTL_GET_VERSION_REQ; + pQMI->CTLMsg.GetVersionReq.Length = 0x0004; + pQMI->CTLMsg.GetVersionReq.TLVType= QCTLV_TYPE_REQUIRED_PARAMETER; + pQMI->CTLMsg.GetVersionReq.TLVLength = 0x0001; + pQMI->CTLMsg.GetVersionReq.QMUXTypes = QMUX_TYPE_ALL; + + pQMI->QMIHdr.Length = pQMI->CTLMsg.QMICTLMsgHdr.Length + sizeof(QCQMI_HDR) + sizeof(QCQMICTL_MSG_HDR) - 1; + + ret = send_qmi_timeout(pQMI, &pRsp, 3000); + if (ret || (pRsp == NULL)) + goto qmi_proxy_init_fail; + + if (pRsp) { + uint8_t NumElements = 0; + if (pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXResult ||pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXError) { + dprintf("QMICTL_GET_VERSION_REQ QMUXResult=%d, QMUXError=%d\n", + pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXResult, pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXError); + goto qmi_proxy_init_fail; + } + + for (NumElements = 0; NumElements < pRsp->CTLMsg.GetVersionRsp.NumElements; NumElements++) { + if (verbose_debug) + dprintf("QMUXType = %02x Version = %d.%d\n", + pRsp->CTLMsg.GetVersionRsp.TypeVersion[NumElements].QMUXType, + pRsp->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MajorVersion, + pRsp->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MinorVersion); + } + } + free(pRsp); + + pQMI->CTLMsg.GetClientIdReq.TransactionId = TransactionId++; + pQMI->CTLMsg.GetClientIdReq.QMICTLType = QMICTL_GET_CLIENT_ID_REQ; + pQMI->CTLMsg.GetClientIdReq.Length = 0x0004; + pQMI->CTLMsg.GetClientIdReq.TLVType= QCTLV_TYPE_REQUIRED_PARAMETER; + pQMI->CTLMsg.GetClientIdReq.TLVLength = 0x0001; + pQMI->CTLMsg.GetClientIdReq.QMIType = QMUX_TYPE_WDS_ADMIN; + + pQMI->QMIHdr.Length = pQMI->CTLMsg.QMICTLMsgHdr.Length + sizeof(QCQMI_HDR) + sizeof(QCQMICTL_MSG_HDR) - 1; + + ret = send_qmi_timeout(pQMI, &pRsp, 3000); + if (ret || (pRsp == NULL)) + goto qmi_proxy_init_fail; + + if (pRsp) { + if (pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXResult ||pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXError) { + dprintf("QMICTL_GET_CLIENT_ID_REQ QMUXResult=%d, QMUXError=%d\n", + pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXResult, pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXError); + goto qmi_proxy_init_fail; + } + + WDAClientId = pRsp->CTLMsg.GetClientIdRsp.ClientId; + if (verbose_debug) dprintf("WDAClientId = %d\n", WDAClientId); + } + free(pRsp); + + rx_urb_size = 32*1024; //must same as rx_urb_size defined in GobiNet&qmi_wwan driver //SDX24&SDX55 support 32KB + ep_type = 0x02; + iface_id = 0x04; + + pQMI->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI; + pQMI->QMIHdr.CtlFlags = 0x00; + pQMI->QMIHdr.QMIType = QMUX_TYPE_WDS_ADMIN; + pQMI->QMIHdr.ClientId= WDAClientId; + + pQMI->MUXMsg.QMUXMsgHdr.CtlFlags = QMICTL_FLAG_REQUEST; + pQMI->MUXMsg.QMUXMsgHdr.TransactionId = TransactionId++; + + pQMI->MUXMsg.SetDataFormatReq.Type = QMIWDS_ADMIN_SET_DATA_FORMAT_REQ; + pQMI->MUXMsg.SetDataFormatReq.Length = sizeof(QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG) - sizeof(QCQMUX_MSG_HDR); + +//Indicates whether the Quality of Service(QOS) data format is used by the client. + pQMI->MUXMsg.SetDataFormatReq.QosDataFormatTlv.TLVType = 0x10; + pQMI->MUXMsg.SetDataFormatReq.QosDataFormatTlv.TLVLength = cpu_to_le16(0x0001); + pQMI->MUXMsg.SetDataFormatReq.QosDataFormatTlv.QOSSetting = 0; /* no-QOS header */ +//Underlying Link Layer Protocol + pQMI->MUXMsg.SetDataFormatReq.UnderlyingLinkLayerProtocolTlv.TLVType = 0x11; + pQMI->MUXMsg.SetDataFormatReq.UnderlyingLinkLayerProtocolTlv.TLVLength = cpu_to_le16(4); + pQMI->MUXMsg.SetDataFormatReq.UnderlyingLinkLayerProtocolTlv.Value = cpu_to_le32(0x02); /* Set IP mode */ +//Uplink (UL) data aggregation protocol to be used for uplink data transfer. + pQMI->MUXMsg.SetDataFormatReq.UplinkDataAggregationProtocolTlv.TLVType = 0x12; + pQMI->MUXMsg.SetDataFormatReq.UplinkDataAggregationProtocolTlv.TLVLength = cpu_to_le16(4); + pQMI->MUXMsg.SetDataFormatReq.UplinkDataAggregationProtocolTlv.Value = cpu_to_le32(0x05); //UL QMAP is enabled +//Downlink (DL) data aggregation protocol to be used for downlink data transfer + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationProtocolTlv.TLVType = 0x13; + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationProtocolTlv.TLVLength = cpu_to_le16(4); + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationProtocolTlv.Value = cpu_to_le32(0x05); //UL QMAP is enabled +//Maximum number of datagrams in a single aggregated packet on downlink + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.TLVType = 0x15; + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.TLVLength = cpu_to_le16(4); + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.Value = cpu_to_le32((rx_urb_size>2048)?(rx_urb_size/1024):1); +//Maximum size in bytes of a single aggregated packet allowed on downlink + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.TLVType = 0x16; + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.TLVLength = cpu_to_le16(4); + pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.Value = cpu_to_le32(rx_urb_size); +//Peripheral End Point ID + pQMI->MUXMsg.SetDataFormatReq.epTlv.TLVType = 0x17; + pQMI->MUXMsg.SetDataFormatReq.epTlv.TLVLength = cpu_to_le16(8); + pQMI->MUXMsg.SetDataFormatReq.epTlv.ep_type = cpu_to_le32(ep_type); // DATA_EP_TYPE_BAM_DMUX + pQMI->MUXMsg.SetDataFormatReq.epTlv.iface_id = cpu_to_le32(iface_id); + + pQMI->QMIHdr.Length = pQMI->MUXMsg.QMUXMsgHdr.Length + sizeof(QCQMI_HDR) + sizeof(QCQMUX_MSG_HDR) - 1; + + ret = send_qmi_timeout(pQMI, &pRsp, 3000); + if (ret || (pRsp == NULL)) + goto qmi_proxy_init_fail; + + if (pRsp) { + PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV pFormat; + + if (pRsp->MUXMsg.QMUXMsgHdrResp.QMUXResult || pRsp->MUXMsg.QMUXMsgHdrResp.QMUXError) { + dprintf("QMIWDS_ADMIN_SET_DATA_FORMAT_REQ QMUXResult=%d, QMUXError=%d\n", + pRsp->MUXMsg.QMUXMsgHdrResp.QMUXResult, pRsp->MUXMsg.QMUXMsgHdrResp.QMUXError); + goto qmi_proxy_init_fail; + } + + pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x11); + if (pFormat) + dprintf("link_prot %d\n", le32_to_cpu(pFormat->Value)); + pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x12); + if (pFormat) + dprintf("ul_data_aggregation_protocol %d\n", le32_to_cpu(pFormat->Value)); + pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x13); + if (pFormat) + dprintf("dl_data_aggregation_protocol %d\n", le32_to_cpu(pFormat->Value)); + pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x15); + if (pFormat) + dprintf("dl_data_aggregation_max_datagrams %d\n", le32_to_cpu(pFormat->Value)); + pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x16); + if (pFormat) { + dprintf("dl_data_aggregation_max_size %d\n", le32_to_cpu(pFormat->Value)); + rx_urb_size = le32_to_cpu(pFormat->Value); + } + pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x17); + if (pFormat) + dprintf("ul_data_aggregation_max_datagrams %d\n", le32_to_cpu(pFormat->Value)); + pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x18); + if (pFormat) + dprintf("ul_data_aggregation_max_size %d\n", le32_to_cpu(pFormat->Value)); + } + free(pRsp); + + dprintf("%s finished, rx_urb_size is %u\n", __func__, rx_urb_size); + return 0; + +qmi_proxy_init_fail: + dprintf("%s failed\n", __func__); + return -1; +} + +static void qmi_start_server(void) { + qmi_proxy_server_fd = create_local_server("meig-qmi-proxy"); + printf("%s: qmi_proxy_server_fd = %d\n", __func__, qmi_proxy_server_fd); + if (qmi_proxy_server_fd == -1) { + dprintf("%s Failed to create %s, errno: %d (%s)\n", __func__, "meig-qmi-proxy", errno, strerror(errno)); + } +} + +static void qmi_close_server(void) { + if (qmi_proxy_server_fd != -1) { + dprintf("%s %s close server\n", __func__, "meig-qmi-proxy"); + close(qmi_proxy_server_fd); + qmi_proxy_server_fd = -1; + } +} + +static void *qmi_proxy_loop(void *param) +{ + static uint8_t qmi_buf[2048]; + PQCQMIMSG pQMI = (PQCQMIMSG)qmi_buf; + struct qlistnode *con_node; + QMI_PROXY_CONNECTION *qmi_con; + + dprintf("%s enter thread_id %ld\n", __func__, pthread_self()); + + qlist_init(&qmi_proxy_connection); + qlist_init(&qmi_proxy_ctl_msg); + + while (cdc_wdm_fd > 0 && qmi_proxy_quit == 0) { + struct pollfd pollfds[2+64]; + int ne, ret, nevents = 0; + ssize_t nreads; + + pollfds[nevents].fd = cdc_wdm_fd; + pollfds[nevents].events = POLLIN; + pollfds[nevents].revents= 0; + nevents++; + + if (qmi_proxy_server_fd > 0) { + pollfds[nevents].fd = qmi_proxy_server_fd; + pollfds[nevents].events = POLLIN; + pollfds[nevents].revents= 0; + nevents++; + } + + qlist_for_each(con_node, &qmi_proxy_connection) { + qmi_con = qnode_to_item(con_node, QMI_PROXY_CONNECTION, qnode); + + pollfds[nevents].fd = qmi_con->ClientFd; + pollfds[nevents].events = POLLIN; + pollfds[nevents].revents= 0; + nevents++; + + if (nevents == (sizeof(pollfds)/sizeof(pollfds[0]))) + break; + } + +#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 < 0 && errno == EINTR && qmi_proxy_quit == 0); + + if (ret < 0) { + dprintf("%s poll=%d, errno: %d (%s)\n", __func__, ret, errno, strerror(errno)); + goto qmi_proxy_loop_exit; + } + + for (ne = 0; ne < nevents; ne++) { + int fd = pollfds[ne].fd; + short revents = pollfds[ne].revents; + + if (revents & (POLLERR | POLLHUP | POLLNVAL)) { + dprintf("%s poll fd = %d, revents = %04x\n", __func__, fd, revents); + if (fd == cdc_wdm_fd) { + goto qmi_proxy_loop_exit; + } else if(fd == qmi_proxy_server_fd) { + + } else { + cleanup_qmi_connection(fd); + } + + continue; + } + + if (!(pollfds[ne].revents & POLLIN)) { + continue; + } + + if (fd == qmi_proxy_server_fd) { + accept_qmi_connection(fd); + } + else if (fd == cdc_wdm_fd) { + nreads = read(fd, pQMI, sizeof(qmi_buf)); + if (verbose_debug) + { + ssize_t i; + printf("r %d %zd: ", fd, nreads); + for (i = 0; i < 16; i++) + printf("%02x ", ((uint8_t *)pQMI)[i]); + printf("\n"); + } + if (nreads <= 0) { + dprintf("%s read=%d errno: %d (%s)\n", __func__, (int)nreads, errno, strerror(errno)); + goto qmi_proxy_loop_exit; + } + + if (nreads != ((pQMI->QMIHdr.Length) + 1)) { + dprintf("%s nreads=%d, pQCQMI->QMIHdr.Length = %d\n", __func__, (int)nreads, (pQMI->QMIHdr.Length)); + continue; + } + + recv_qmi(pQMI, nreads); + } + else { + nreads = read(fd, pQMI, sizeof(qmi_buf)); + if (verbose_debug) + { + ssize_t i; + printf("r %d %zd: ", fd, nreads); + for (i = 0; i < 16; i++) + printf("%02x ", ((uint8_t *)pQMI)[i]); + printf("\n"); + } + if (nreads <= 0) { + dprintf("%s read=%d errno: %d (%s)", __func__, (int)nreads, errno, strerror(errno)); + cleanup_qmi_connection(fd); + break; + } + + if (nreads != ((pQMI->QMIHdr.Length) + 1)) { + dprintf("%s nreads=%d, pQCQMI->QMIHdr.Length = %d\n", __func__, (int)nreads, (pQMI->QMIHdr.Length)); + continue; + } + + send_qmi(pQMI, nreads, fd); + } + } + } + +qmi_proxy_loop_exit: + while (!qlist_empty(&qmi_proxy_connection)) { + QMI_PROXY_CONNECTION *qmi_con = qnode_to_item(qlist_head(&qmi_proxy_connection), QMI_PROXY_CONNECTION, qnode); + + cleanup_qmi_connection(qmi_con->ClientFd); + } + + dprintf("%s exit, thread_id %ld\n", __func__, pthread_self()); + + return NULL; +} + +static void qmidevice_detect(char **device_name) { + struct dirent* ent = NULL; + DIR *pDir; + + char dir[255] = "/sys/bus/usb/devices"; + pDir = opendir(dir); + if (pDir) { + struct { + char subdir[255 * 3]; + char qmifile[255 * 2]; + } *pl; + pl = (typeof(pl)) malloc(sizeof(*pl)); + memset(pl, 0x00, sizeof(*pl)); + + while ((ent = readdir(pDir)) != NULL) { + struct dirent* subent = NULL; + DIR *psubDir; + char idVendor[4+1] = {0}; + char idProduct[4+1] = {0}; + int fd = 0; + + memset(pl, 0x00, sizeof(*pl)); + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s/idVendor", dir, ent->d_name); + fd = open(pl->subdir, O_RDONLY); + if (fd > 0) { + read(fd, idVendor, 4); + close(fd); + } + + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s/idProduct", dir, ent->d_name); + fd = open(pl->subdir, O_RDONLY); + if (fd > 0) { + read(fd, idProduct, 4); + close(fd); + } + + if (strncasecmp(idVendor, "05c6", 4) && strncasecmp(idVendor, "2c7c", 4) && strncasecmp(idVendor, "2dee", 4)) + continue; + + dprintf("Find %s/%s idVendor=%s idProduct=%s\n", dir, ent->d_name, idVendor, idProduct); + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s:1.4/usbmisc", dir, ent->d_name); + if (access(pl->subdir, R_OK)) { + snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s:1.4/usb", dir, ent->d_name); + if (access(pl->subdir, R_OK)) { + dprintf("no GobiQMI/usbmic/usb found in %s/%s:1.4\n", dir, ent->d_name); + continue; + } + } + + psubDir = opendir(pl->subdir); + if (pDir == NULL) { + dprintf("Cannot open directory: %s, errno: %d (%s)\n", dir, errno, strerror(errno)); + continue; + } + + while ((subent = readdir(psubDir)) != NULL) { + if (subent->d_name[0] == '.') + continue; + dprintf("Find %s/%s\n", pl->subdir, subent->d_name); + snprintf(pl->qmifile, sizeof(pl->qmifile), "/dev/%s", subent->d_name); + break; + } + *device_name = strdup(pl->qmifile); + closedir(psubDir); + } + closedir(pDir); + free(pl); + } +} + +static void usage(void) { + dprintf(" -d <device_name> A valid qmi device\n" + " default /dev/cdc-wdm0, but cdc-wdm0 may be invalid\n" + " -v Will show all details\n"); +} + +static void sig_action(int sig) { + if (qmi_proxy_quit == 0) { + qmi_proxy_quit = 1; + if (thread_id) + pthread_kill(thread_id, sig); + } +} + +int main(int argc, char *argv[]) { + int opt; + char *cdc_wdm = NULL; + int retry_times = 0; + + optind = 1; + + signal(SIGINT, sig_action); + + while ( -1 != (opt = getopt(argc, argv, "d:v"))) { + switch (opt) { + case 'd': + cdc_wdm = strdup(optarg); + break; + case 'v': + verbose_debug = 1; + break; + default: + usage(); + break; + } + } + + if (cdc_wdm == NULL) + qmidevice_detect(&cdc_wdm); + + if (cdc_wdm == NULL) { + dprintf("Fail to find any /dev/cdc-wdm device. break\n"); + return -1; + } + + if (access(cdc_wdm, R_OK | W_OK)) { + dprintf("Fail to access %s, errno: %d (%s). break\n", cdc_wdm, errno, strerror(errno)); + free(cdc_wdm); + return -1; + } + + while (qmi_proxy_quit == 0) { + if (access(cdc_wdm, R_OK | W_OK)) { + dprintf("Fail to access %s, errno: %d (%s). continue\n", cdc_wdm, errno, strerror(errno)); + // wait device + sleep(3); + continue; + } + + dprintf("Will use cdc-wdm %s\n", cdc_wdm); + + cdc_wdm_fd = open(cdc_wdm, O_RDWR | O_NONBLOCK | O_NOCTTY); + if (cdc_wdm_fd == -1) { + dprintf("Failed to open %s, errno: %d (%s). break\n", cdc_wdm, errno, strerror(errno)); + return -1; + } + cfmakenoblock(cdc_wdm_fd); + + /* no qmi_proxy_loop lives, create one */ + pthread_create(&thread_id, NULL, qmi_proxy_loop, NULL); + /* try to redo init if failed, init function must be successfully */ + while (qmi_proxy_init() != 0) { + if (retry_times < 5) { + dprintf("fail to init proxy, try again in 2 seconds.\n"); + sleep(2); + retry_times++; + } else { + dprintf("has failed too much times, restart the modem and have a try...\n"); + break; + } + /* break loop if modem is detached */ + if (access(cdc_wdm, F_OK|R_OK|W_OK)) + break; + } + retry_times = 0; + qmi_start_server(); + pthread_join(thread_id, NULL); + + /* close local server at last */ + qmi_close_server(); + close(cdc_wdm_fd); + } + + if (cdc_wdm) { + free(cdc_wdm); + } + return 0; +} diff --git a/package/wwan/meig-cm/src/ql-ifconfig.c b/package/wwan/meig-cm/src/ql-ifconfig.c new file mode 100644 index 000000000..a9def8920 --- /dev/null +++ b/package/wwan/meig-cm/src/ql-ifconfig.c @@ -0,0 +1,494 @@ +/* ifconfig + * + * Similar to the standard Unix ifconfig, but with only the necessary + * parts for AF_INET, and without any printing of if info (for now). + * + * Bjorn Wesen, Axis Communications AB + * + * + * Authors of the original ifconfig was: + * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * $Id: ifconfig.c,v 1.12 2001/08/10 06:02:23 mjn3 Exp $ + * + */ + +/* + * Heavily modified by Manuel Novoa III Mar 6, 2001 + * + * From initial port to busybox, removed most of the redundancy by + * converting to a table-driven approach. Added several (optional) + * args missing from initial port. + * + * Still missing: media, tunnel. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> // strcmp and friends +#include <ctype.h> // isdigit and friends +#include <stddef.h> /* offsetof */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <linux/if.h> +#include <net/if_arp.h> +#include <linux/if_ether.h> + +#include "QMIThread.h" + +#ifdef BB_FEATURE_IFCONFIG_SLIP +#include <linux/if_slip.h> +#endif +#define LOG_TAG "IFCONFIG" +/* I don't know if this is needed for busybox or not. Anyone? */ +#define QUESTIONABLE_ALIAS_CASE + + +/* Defines for glibc2.0 users. */ +#ifndef SIOCSIFTXQLEN +#define SIOCSIFTXQLEN 0x8943 +#define SIOCGIFTXQLEN 0x8942 +#endif + +/* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */ +#ifndef ifr_qlen +#define ifr_qlen ifr_ifru.ifru_mtu +#endif + +#ifndef IFF_DYNAMIC +#define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */ +#endif + +/* + * Here are the bit masks for the "flags" member of struct options below. + * N_ signifies no arg prefix; M_ signifies arg prefixed by '-'. + * CLR clears the flag; SET sets the flag; ARG signifies (optional) arg. + */ +#define N_CLR 0x01 +#define M_CLR 0x02 +#define N_SET 0x04 +#define M_SET 0x08 +#define N_ARG 0x10 +#define M_ARG 0x20 + +#define M_MASK (M_CLR | M_SET | M_ARG) +#define N_MASK (N_CLR | N_SET | N_ARG) +#define SET_MASK (N_SET | M_SET) +#define CLR_MASK (N_CLR | M_CLR) +#define SET_CLR_MASK (SET_MASK | CLR_MASK) +#define ARG_MASK (M_ARG | N_ARG) + +/* + * Here are the bit masks for the "arg_flags" member of struct options below. + */ + +/* + * cast type: + * 00 int + * 01 char * + * 02 HOST_COPY in_ether + * 03 HOST_COPY INET_resolve + */ +#define A_CAST_TYPE 0x03 +/* + * map type: + * 00 not a map type (mem_start, io_addr, irq) + * 04 memstart (unsigned long) + * 08 io_addr (unsigned short) + * 0C irq (unsigned char) + */ +#define A_MAP_TYPE 0x0C +#define A_ARG_REQ 0x10 /* Set if an arg is required. */ +#define A_NETMASK 0x20 /* Set if netmask (check for multiple sets). */ +#define A_SET_AFTER 0x40 /* Set a flag at the end. */ +#define A_COLON_CHK 0x80 /* Is this needed? See below. */ + +/* + * These defines are for dealing with the A_CAST_TYPE field. + */ +#define A_CAST_CHAR_PTR 0x01 +#define A_CAST_RESOLVE 0x01 +#define A_CAST_HOST_COPY 0x02 +#define A_CAST_HOST_COPY_IN_ETHER A_CAST_HOST_COPY +#define A_CAST_HOST_COPY_RESOLVE (A_CAST_HOST_COPY | A_CAST_RESOLVE) + +/* + * These defines are for dealing with the A_MAP_TYPE field. + */ +#define A_MAP_ULONG 0x04 /* memstart */ +#define A_MAP_USHORT 0x08 /* io_addr */ +#define A_MAP_UCHAR 0x0C /* irq */ + +/* + * Define the bit masks signifying which operations to perform for each arg. + */ + +#define ARG_METRIC (A_ARG_REQ /*| A_CAST_INT*/) +#define ARG_MTU (A_ARG_REQ /*| A_CAST_INT*/) +#define ARG_TXQUEUELEN (A_ARG_REQ /*| A_CAST_INT*/) +#define ARG_MEM_START (A_ARG_REQ | A_MAP_ULONG) +#define ARG_IO_ADDR (A_ARG_REQ | A_MAP_USHORT) +#define ARG_IRQ (A_ARG_REQ | A_MAP_UCHAR) +#define ARG_DSTADDR (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE) +#define ARG_NETMASK (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_NETMASK) +#define ARG_BROADCAST (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER) +#define ARG_HW (A_ARG_REQ | A_CAST_HOST_COPY_IN_ETHER) +#define ARG_POINTOPOINT (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER) +#define ARG_KEEPALIVE (A_ARG_REQ | A_CAST_CHAR_PTR) +#define ARG_OUTFILL (A_ARG_REQ | A_CAST_CHAR_PTR) +#define ARG_HOSTNAME (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_COLON_CHK) + + +/* + * Set up the tables. Warning! They must have corresponding order! + */ + +struct arg1opt { + const char *name; + unsigned short selector; + unsigned short ifr_offset; +}; + +struct options { + const char *name; + const unsigned char flags; + const unsigned char arg_flags; + const unsigned short selector; +}; + +#define ifreq_offsetof(x) offsetof(struct ifreq, x) + +static const struct arg1opt Arg1Opt[] = { + {"SIOCSIFMETRIC", SIOCSIFMETRIC, ifreq_offsetof(ifr_metric)}, + {"SIOCSIFMTU", SIOCSIFMTU, ifreq_offsetof(ifr_mtu)}, + {"SIOCSIFTXQLEN", SIOCSIFTXQLEN, ifreq_offsetof(ifr_qlen)}, + {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)}, + {"SIOCSIFNETMASK", SIOCSIFNETMASK, ifreq_offsetof(ifr_netmask)}, + {"SIOCSIFBRDADDR", SIOCSIFBRDADDR, ifreq_offsetof(ifr_broadaddr)}, +#ifdef BB_FEATURE_IFCONFIG_HW + {"SIOCSIFHWADDR", SIOCSIFHWADDR, ifreq_offsetof(ifr_hwaddr)}, +#endif + {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)}, +#ifdef SIOCSKEEPALIVE + {"SIOCSKEEPALIVE", SIOCSKEEPALIVE, ifreq_offsetof(ifr_data)}, +#endif +#ifdef SIOCSOUTFILL + {"SIOCSOUTFILL", SIOCSOUTFILL, ifreq_offsetof(ifr_data)}, +#endif +#ifdef BB_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ + {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.mem_start)}, + {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.base_addr)}, + {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.irq)}, +#endif + /* Last entry if for unmatched (possibly hostname) arg. */ + {"SIOCSIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr)}, +}; + +static const struct options OptArray[] = { + {"metric", N_ARG, ARG_METRIC, 0}, + {"mtu", N_ARG, ARG_MTU, 0}, + {"txqueuelen", N_ARG, ARG_TXQUEUELEN, 0}, + {"dstaddr", N_ARG, ARG_DSTADDR, 0}, + {"netmask", N_ARG, ARG_NETMASK, 0}, + {"broadcast", N_ARG | M_CLR, ARG_BROADCAST, IFF_BROADCAST}, +#ifdef BB_FEATURE_IFCONFIG_HW + {"hw", N_ARG, ARG_HW, 0}, +#endif + {"pointopoint", N_ARG | M_CLR, ARG_POINTOPOINT, IFF_POINTOPOINT}, +#ifdef SIOCSKEEPALIVE + {"keepalive", N_ARG, ARG_KEEPALIVE, 0}, +#endif +#ifdef SIOCSOUTFILL + {"outfill", N_ARG, ARG_OUTFILL, 0}, +#endif +#ifdef BB_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ + {"mem_start", N_ARG, ARG_MEM_START, 0}, + {"io_addr", N_ARG, ARG_IO_ADDR, 0}, + {"irq", N_ARG, ARG_IRQ, 0}, +#endif + {"arp", N_CLR | M_SET, 0, IFF_NOARP}, + {"trailers", N_CLR | M_SET, 0, IFF_NOTRAILERS}, + {"promisc", N_SET | M_CLR, 0, IFF_PROMISC}, + {"multicast", N_SET | M_CLR, 0, IFF_MULTICAST}, + {"allmulti", N_SET | M_CLR, 0, IFF_ALLMULTI}, + {"dynamic", N_SET | M_CLR, 0, IFF_DYNAMIC}, + {"up", N_SET , 0, (IFF_UP | IFF_RUNNING)}, + {"down", N_CLR , 0, IFF_UP}, + { NULL, 0, ARG_HOSTNAME, (IFF_UP | IFF_RUNNING)} +}; + +/* + * A couple of prototypes. + */ + +#ifdef BB_FEATURE_IFCONFIG_HW +static int in_ether(char *bufp, struct sockaddr *sap); +#endif + +#ifdef BB_FEATURE_IFCONFIG_STATUS +extern int interface_opt_a; +extern int display_interfaces(char *ifname); +#endif + +/* + * Our main function. + */ + +int meig_ifconfig(int argc, char **argv) +{ + struct ifreq ifr; + struct sockaddr_in sai; +#ifdef BB_FEATURE_IFCONFIG_HW + struct sockaddr sa; +#endif + const struct arg1opt *a1op; + const struct options *op; + int sockfd; /* socket fd we use to manipulate stuff with */ + int goterr; + int selector; + char *p; + char host[128]; + unsigned char mask; + unsigned char did_flags; + + + goterr = 0; + did_flags = 0; + +#ifdef BB_FEATURE_IFCONFIG_STATUS + if ((argc > 0) && (strcmp(*argv,"-a") == 0)) { + interface_opt_a = 1; + --argc; + ++argv; + } +#endif + + if(argc <= 1) { +#ifdef BB_FEATURE_IFCONFIG_STATUS + return display_interfaces(argc ? *argv : NULL); +#else + //error_msg_and_die( "ifconfig was not compiled with interface status display support."); +#endif + } + + /* Create a channel to the NET kernel. */ + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + //perror_msg_and_die("socket"); + } + + /* get interface name */ + strncpy(ifr.ifr_name, *argv, IFNAMSIZ); + + /* Process the remaining arguments. */ + while (*++argv != (char *) NULL) { + p = *argv; + mask = N_MASK; + + if (*p == '-') { /* If the arg starts with '-'... */ + ++p; /* advance past it and */ + mask = M_MASK; /* set the appropriate mask. */ + } + + for (op = OptArray ; op->name ; op++) { /* Find table entry. */ + if (strcmp(p,op->name) == 0) { /* If name matches... */ + if ((mask &= op->flags)) { /* set the mask and go. */ + goto FOUND_ARG;; + } + /* If we get here, there was a valid arg with an */ + /* invalid '-' prefix. */ + ++goterr; + goto __LOOP; + } + } + /* We fell through, so treat as possible hostname. */ + a1op = Arg1Opt + (sizeof(Arg1Opt) / sizeof(Arg1Opt[0])) - 1; + mask = op->arg_flags; + goto HOSTNAME; + + FOUND_ARG: + if (mask & ARG_MASK) { + mask = op->arg_flags; + a1op = Arg1Opt + (op - OptArray); + if (mask & A_NETMASK & did_flags) { + //show_usage(); + } + if (*++argv == NULL) { + if (mask & A_ARG_REQ) { + //show_usage(); + } else { + --argv; + mask &= A_SET_AFTER; /* just for broadcast */ + } + } else { /* got an arg so process it */ + HOSTNAME: + did_flags |= (mask & A_NETMASK); + if (mask & A_CAST_HOST_COPY) { +#ifdef BB_FEATURE_IFCONFIG_HW + if (mask & A_CAST_RESOLVE) { +#endif + strncpy(host, *argv, (sizeof host)); + sai.sin_family = AF_INET; + sai.sin_port = 0; + if (!strcmp(host, "default")) { + /* Default is special, meaning 0.0.0.0. */ + sai.sin_addr.s_addr = INADDR_ANY; + } else if (inet_aton(host, &sai.sin_addr) == 0) { + /* It's not a dotted quad. */ + ++goterr; + continue; + } + p = (char *) &sai; +#ifdef BB_FEATURE_IFCONFIG_HW + } else { /* A_CAST_HOST_COPY_IN_ETHER */ + /* This is the "hw" arg case. */ + if (strcmp("ether", *argv) || (*++argv == NULL)) { + //show_usage(); + } + strncpy(host, *argv, (sizeof host)); + if (in_ether(host, &sa)) { + fprintf(stderr, "invalid hw-addr %s\n", host); + ++goterr; + continue; + } + p = (char *) &sa; + } +#endif + memcpy((((char *)(&ifr)) + a1op->ifr_offset), + p, sizeof(struct sockaddr)); + + } else { + unsigned int i = strtoul(*argv,NULL,0); + p = ((char *)(&ifr)) + a1op->ifr_offset; +#ifdef BB_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ + if (mask & A_MAP_TYPE) { + if (ioctl(sockfd, SIOCGIFMAP, &ifr) < 0) { + ++goterr; + continue; + } + if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR) { + *((unsigned char *) p) = i; + } else if (mask & A_MAP_USHORT) { + *((unsigned short *) p) = i; + } else { + *((unsigned long *) p) = i; + } + } else +#endif + if (mask & A_CAST_CHAR_PTR) { + *((caddr_t *) p) = (caddr_t) i; + } else { /* A_CAST_INT */ + *((int *) p) = i; + } + } + if (ioctl(sockfd, a1op->selector, &ifr) < 0) { + ++goterr; + continue; + } + +#ifdef QUESTIONABLE_ALIAS_CASE + if (mask & A_COLON_CHK) { + /* + * Don't do the set_flag() if the address is an alias with + * a - at the end, since it's deleted already! - Roman + * + * Should really use regex.h here, not sure though how well + * it'll go with the cross-platform support etc. + */ + char *ptr; + short int found_colon = 0; + for (ptr = ifr.ifr_name; *ptr; ptr++ ) { + if (*ptr == ':') { + found_colon++; + } + } + if (found_colon && *(ptr - 1) == '-') { + continue; + } + } +#endif + } + if (!(mask & A_SET_AFTER)) { + continue; + } + mask = N_SET; + } + + if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) { + printf("SIOCGIFFLAGS"); + ++goterr; + } else { + selector = op->selector; + if (mask & SET_MASK) { + ifr.ifr_flags |= selector; + } else { + ifr.ifr_flags &= ~selector; + } + if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) { + printf("SIOCSIFFLAGS"); + ++goterr; + } + } + __LOOP: + dbg_time("func = %s, line = %d", __func__, __LINE__); + } /* end of while-loop */ + dbg_time("func = %s, line = %d, goterr = %d", __func__, __LINE__, goterr); + + return goterr; +} + +#ifdef BB_FEATURE_IFCONFIG_HW +/* Input an Ethernet address and convert to binary. */ +static int +in_ether(char *bufp, struct sockaddr *sap) +{ + unsigned char *ptr; + int i, j; + unsigned char val; + unsigned char c; + + sap->sa_family = ARPHRD_ETHER; + ptr = sap->sa_data; + + for (i = 0 ; i < ETH_ALEN ; i++) { + val = 0; + + /* We might get a semicolon here - not required. */ + if (i && (*bufp == ':')) { + bufp++; + } + + for (j=0 ; j<2 ; j++) { + c = *bufp; + if (c >= '0' && c <= '9') { + c -= '0'; + } else if (c >= 'a' && c <= 'f') { + c -= ('a' - 10); + } else if (c >= 'A' && c <= 'F') { + c -= ('A' - 10); + } else if (j && (c == ':' || c == 0)) { + break; + } else { + return -1; + } + ++bufp; + val <<= 4; + val += c; + } + *ptr++ = val; + } + + return (int) (*bufp); /* Error if we don't end at end of string. */ +} +#endif + + diff --git a/package/wwan/meig-cm/src/qmap_bridge_mode.c b/package/wwan/meig-cm/src/qmap_bridge_mode.c new file mode 100644 index 000000000..009d819ea --- /dev/null +++ b/package/wwan/meig-cm/src/qmap_bridge_mode.c @@ -0,0 +1,264 @@ +#include "QMIThread.h" + +static size_t meig_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 meig_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; +} + +static int meig_iface_is_in_bridge(const char *iface) { + char filename[256]; + + snprintf(filename, sizeof(filename), "/sys/class/net/%s/brport", iface); + return (access(filename, F_OK) == 0 || errno != ENOENT); +} + +int meig_bridge_mode_detect(PROFILE_T *profile) { + const char *ifname = profile->qmapnet_adapter ? profile->qmapnet_adapter : profile->usbnet_adapter; + const char *driver; + char bridge_mode[128]; + char bridge_ipv4[128]; + char ipv4[128]; + char buf[64]; + size_t n; + int in_bridge; + + driver = profile->driver_name; + snprintf(bridge_mode, sizeof(bridge_mode), "/sys/class/net/%s/bridge_mode", ifname); + snprintf(bridge_ipv4, sizeof(bridge_ipv4), "/sys/class/net/%s/bridge_ipv4", ifname); + + if (access(bridge_mode, F_OK) && errno == ENOENT) { + snprintf(bridge_mode, sizeof(bridge_mode), "/sys/module/%s/parameters/bridge_mode", driver); + snprintf(bridge_ipv4, sizeof(bridge_ipv4), "/sys/module/%s/parameters/bridge_ipv4", driver); + + if (access(bridge_mode, F_OK) && errno == ENOENT) { + bridge_mode[0] = '\0'; + } + } + + in_bridge = meig_iface_is_in_bridge(ifname); + if (in_bridge) { + dbg_time("notice: iface %s had add to bridge\n", ifname); + } else { + return 0; + } + + if (in_bridge && bridge_mode[0] == '\0') { + dbg_time("warnning: can not find bride_mode file for %s\n", ifname); + return 1; + } + + n = meig_fread(bridge_mode, buf, sizeof(buf)); + + if (in_bridge) { + if (n <= 0 || buf[0] == '0') { + dbg_time("warnning: should set 1 to bride_mode file for %s\n", ifname); + return 1; + } + } + else { + if (buf[0] == '0') { + return 0; + } + } + + memset(ipv4, 0, sizeof(ipv4)); + + if (strstr(bridge_ipv4, "/sys/class/net/") || profile->qmap_mode == 0 || profile->qmap_mode == 1) { + snprintf(ipv4, sizeof(ipv4), "0x%x", profile->ipv4.Address); + dbg_time("echo '%s' > %s", ipv4, bridge_ipv4); + meig_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); + meig_fwrite(bridge_ipv4, ipv4, strlen(ipv4)); + } + + return 1; +} + +int meig_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 = meig_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 meig_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; +} + +int meig_qmap_mode_detect(PROFILE_T *profile) { + char buf[128]; + int n; + char qmap_netcard[128]; + struct { + char filename[255 * 2]; + char linkname[255 * 2]; + } *pl; + + pl = (typeof(pl)) malloc(sizeof(*pl)); + + snprintf(pl->linkname, sizeof(pl->linkname), "/sys/class/net/%s/device/driver", profile->usbnet_adapter); + n = readlink(pl->linkname, pl->filename, sizeof(pl->filename)); + pl->filename[n] = '\0'; + while (pl->filename[n] != '/') + n--; + strset(profile->driver_name, &pl->filename[n+1]); + + if (qmidev_is_gobinet(profile->qmichannel)) + { + snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_mode", profile->usbnet_adapter); + + n = meig_fread(pl->filename, buf, sizeof(buf)); + if (n > 0) { + profile->qmap_mode = atoi(buf); + + if (profile->qmap_mode > 1) { + profile->muxid = profile->pdp + 0x80; //muxis is 0x8X for PDN-X + sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter, profile->pdp); + profile->qmapnet_adapter = strdup(qmap_netcard); + } if (profile->qmap_mode == 1) { + profile->muxid = 0x81; + profile->qmapnet_adapter = strdup(profile->usbnet_adapter); + } + } + } + else if(qmidev_is_qmiwwan(profile->qmichannel)) + { + snprintf(pl->filename, sizeof(pl->filename), "/sys/module/%s/parameters/qmap_mode", profile->driver_name); + + if (access(pl->filename, R_OK) == 0) { + //Meig Style QMAP qmi_wwan.c + + if (meig_fread(pl->filename, buf, sizeof(buf))) { + profile->qmap_mode = atoi(buf); + + if (profile->qmap_mode > 1) { + profile->muxid = profile->pdp + 0x80; //muxis is 0x8X for PDN-X + sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter, profile->pdp); + profile->qmapnet_adapter = strdup(qmap_netcard); +#if 1 //TODO Ubuntu qmi_wwan 1-1.3:1.4 wwp0s26u1u3i4: renamed from wwan0 + if (access(qmap_netcard, R_OK) && errno == ENOENT) { + sprintf(qmap_netcard, "%s.%d", "wwan0", profile->pdp); + free(profile->qmapnet_adapter); + profile->qmapnet_adapter = strdup(qmap_netcard); + } +#endif + } + else if (profile->qmap_mode == 1) { + profile->muxid = 0x81; + profile->qmapnet_adapter = strdup(profile->usbnet_adapter); + } + } + else if (errno != ENOENT) { + dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno, strerror(errno)); + } + else { + snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/qmimux%d", profile->pdp - 1); + + if (access(pl->filename, R_OK) == 0) { + //upstream Kernel Style QMAP qmi_wwan.c + + snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmi/add_mux", profile->usbnet_adapter); + + n = meig_fread(pl->filename, buf, sizeof(buf)); + if (n >= 3) { + profile->qmap_mode = n/3; + if (profile->qmap_mode > 1) { + //PDN-X map to qmimux-X + profile->muxid = (buf[3*(profile->pdp - 1) + 0] - '0')*16 + (buf[3*(profile->pdp - 1) + 1] - '0'); + sprintf(qmap_netcard, "qmimux%d", profile->pdp - 1); + profile->qmapnet_adapter = strdup(qmap_netcard); + } else if (profile->qmap_mode == 1){ + profile->muxid = (buf[3*0 + 0] - '0')*16 + (buf[3*0 + 1] - '0'); + sprintf(qmap_netcard, "qmimux%d", 0); + profile->qmapnet_adapter = strdup(qmap_netcard); + } + } + } + } + } + else if (errno != ENOENT) { + dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno, strerror(errno)); + } + } + else if (qmidev_is_pciemhi(profile->qmichannel)) { + profile->qmap_mode = 1; + profile->muxid = 0x81; + profile->qmapnet_adapter = strdup(profile->usbnet_adapter); + } + + if (profile->qmap_mode) { + dbg_time("qmap_mode = %d, muxid = 0x%02x, qmap_netcard = %s", + profile->qmap_mode, profile->muxid, profile->qmapnet_adapter); + } + + free(pl); + + return 0; +} diff --git a/package/wwan/meig-cm/src/udhcpc.c b/package/wwan/meig-cm/src/udhcpc.c new file mode 100644 index 000000000..acb81b50a --- /dev/null +++ b/package/wwan/meig-cm/src/udhcpc.c @@ -0,0 +1,306 @@ +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/types.h> +#include <net/if.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <endian.h> + +#include "util.h" +#include "QMIThread.h" + +static int meig_system(const char *shell_cmd) { + dbg_time("%s", shell_cmd); + return system(shell_cmd); +} + +static void meig_set_mtu(const char *ifname, int ifru_mtu) { + int inet_sock; + struct ifreq ifr; + + inet_sock = socket(AF_INET, SOCK_DGRAM, 0); + + if (inet_sock > 0) { + strcpy(ifr.ifr_name, ifname); + + if (!ioctl(inet_sock, SIOCGIFMTU, &ifr)) { + if (ifr.ifr_ifru.ifru_mtu != ifru_mtu) { + dbg_time("change mtu %d -> %d", ifr.ifr_ifru.ifru_mtu , ifru_mtu); + ifr.ifr_ifru.ifru_mtu = ifru_mtu; + ioctl(inet_sock, SIOCSIFMTU, &ifr); + } + } + + close(inet_sock); + } +} + +static void* udhcpc_thread_function(void* arg) { + FILE * udhcpc_fp; + char *udhcpc_cmd = (char *)arg; + + if (udhcpc_cmd == NULL) + return NULL; + + dbg_time("%s", udhcpc_cmd); + udhcpc_fp = popen(udhcpc_cmd, "r"); + free(udhcpc_cmd); + if (udhcpc_fp) { + char buf[0xff]; + + buf[sizeof(buf)-1] = '\0'; + while((fgets(buf, sizeof(buf)-1, udhcpc_fp)) != NULL) { + if ((strlen(buf) > 1) && (buf[strlen(buf) - 1] == '\n')) + buf[strlen(buf) - 1] = '\0'; + dbg_time("%s", buf); + } + + pclose(udhcpc_fp); + } + + return NULL; +} + +//#define USE_DHCLIENT +#ifdef USE_DHCLIENT +static int dhclient_alive = 0; +#endif +static int dibbler_client_alive = 0; + +void udhcpc_start(PROFILE_T *profile) { + char *ifname = profile->usbnet_adapter; + char shell_cmd[128]; + + if (profile->qmapnet_adapter) { + ifname = profile->qmapnet_adapter; + } + + if (profile->rawIP && profile->ipv4.Address && profile->ipv4.Mtu) { + meig_set_mtu(ifname, (profile->ipv4.Mtu)); + } + + if (strcmp(ifname, profile->usbnet_adapter)) { + snprintf(shell_cmd, sizeof(shell_cmd), "ifconfig %s up", profile->usbnet_adapter); + meig_system(shell_cmd); + } + + snprintf(shell_cmd, sizeof(shell_cmd), "ifconfig %s up", ifname); + meig_system(shell_cmd); + +#if 1 //for bridge mode, only one public IP, so do udhcpc manually + if (meig_bridge_mode_detect(profile)) { + return; + } +#endif + +/* Do DHCP using Routing Discovery Protocol */ +/* This is needed for IPv6 */ +//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 +//zpf +#if 1 + if (profile->rawIP != 0) //mdm9x07/ec25,ec20 R2.0 + { + if (profile->ipv4.Address) { + unsigned char *ip = (unsigned char *)&profile->ipv4.Address; + unsigned char *gw = (unsigned char *)&profile->ipv4.Gateway; + unsigned char *netmask = (unsigned char *)&profile->ipv4.SubnetMask; + unsigned char *dns1 = (unsigned char *)&profile->ipv4.DnsPrimary; + unsigned char *dns2 = (unsigned char *)&profile->ipv4.DnsSecondary; + + snprintf(shell_cmd, sizeof(shell_cmd), "ifconfig %s %d.%d.%d.%d netmask %d.%d.%d.%d",ifname, + ip[3], ip[2], ip[1], ip[0], netmask[3], netmask[2], netmask[1], netmask[0]); + meig_system(shell_cmd); + + //Resetting default routes + snprintf(shell_cmd, sizeof(shell_cmd), "route del default gw 0.0.0.0 dev %s", ifname); + while(!system(shell_cmd)); + + snprintf(shell_cmd, sizeof(shell_cmd), "route add default gw %d.%d.%d.%d dev %s metric 0", gw[3], gw[2], gw[1], gw[0], ifname); + meig_system(shell_cmd); + + //Adding DNS + if (profile->ipv4.DnsSecondary == 0) + profile->ipv4.DnsSecondary = profile->ipv4.DnsPrimary; + + if (dns1[0]) { + dbg_time("Adding DNS %d.%d.%d.%d %d.%d.%d.%d", dns1[3], dns1[2], dns1[1], dns1[0], dns2[3], dns2[2], dns2[1], dns2[0]); + snprintf(shell_cmd, sizeof(shell_cmd), "echo -n \"nameserver %d.%d.%d.%d\nnameserver %d.%d.%d.%d\n\" > /etc/resolv.conf", + dns1[3], dns1[2], dns1[1], dns1[0], dns2[3], dns2[2], dns2[1], dns2[0]); + system(shell_cmd); + } + } + +/* + if (profile->ipv6.Address[0] && profile->ipv6.PrefixLengthIPAddr) { + unsigned char *ip = (unsigned char *)&profile->ipv4.Address; +#if 1 + snprintf(shell_cmd, sizeof(shell_cmd), "ifconfig %s inet6 add %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x/%d", + ifname, ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15], profile->ipv6.PrefixLengthIPAddr); +#else + snprintf(shell_cmd, sizeof(shell_cmd), "ip -6 addr add %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x/%d dev %s", + ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15], profile->ipv6.PrefixLengthIPAddr, ifname); +#endif + meig_system(shell_cmd); + snprintf(shell_cmd, sizeof(shell_cmd), "route -A inet6 add default dev %s", ifname); + meig_system(shell_cmd); + } +*/ + return; + } +#endif + +/* Do DHCP using busybox tools */ + { + char udhcpc_cmd[128]; + pthread_attr_t udhcpc_thread_attr; + pthread_t udhcpc_thread_id; + + pthread_attr_init(&udhcpc_thread_attr); + pthread_attr_setdetachstate(&udhcpc_thread_attr, PTHREAD_CREATE_DETACHED); + + if (profile->ipv4.Address) { +#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)) { + dbg_time("Fail to access /usr/share/udhcpc/default.script, errno: %d (%s)", errno, strerror(errno)); + } + + //-f,--foreground Run in foreground + //-b,--background Background if lease is not obtained + //-n,--now Exit if lease is not obtained + //-q,--quit Exit after obtaining lease + //-t,--retries N Send up to N discover packets (default 3) + snprintf(udhcpc_cmd, sizeof(udhcpc_cmd), "busybox udhcpc -f -n -q -t 5 -i %s", ifname); +#endif + +#if 1 //for OpenWrt + if (!access("/lib/netifd/dhcp.script", X_OK) && !access("/sbin/ifup", X_OK) && !access("/sbin/ifstatus", X_OK)) { + dbg_time("you are use OpenWrt?"); + dbg_time("should not calling udhcpc manually?"); + dbg_time("should modify /etc/config/network as below?"); + dbg_time("config interface wan"); + dbg_time("\toption ifname %s", ifname); + dbg_time("\toption proto dhcp"); + dbg_time("should use \"/sbin/ifstaus wan\" to check %s 's status?", ifname); + } +#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); +#endif + } + + if (profile->ipv6.Address[0] && profile->ipv6.PrefixLengthIPAddr) { + const unsigned char *d = (unsigned char *)&profile->ipv6.Address; +#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]; + 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); + } + + snprintf(shell_cmd, sizeof(shell_cmd), + "ip -%d address add %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x/%u dev %s", + 6, 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], + profile->ipv6.PrefixLengthIPAddr, ifname); + meig_system(shell_cmd); + + //ping6 www.qq.com + d = (unsigned char *)&profile->ipv6.Gateway; + snprintf(shell_cmd, sizeof(shell_cmd), + "ip -%d route add default via %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x dev %s", + 6, 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], + ifname); + meig_system(shell_cmd); + + if (profile->ipv6.DnsPrimary[0] || profile->ipv6.DnsSecondary[0]) { + char dns1str[128], dns2str[128]; + + if (profile->ipv6.DnsPrimary[0]) { + d = profile->ipv6.DnsPrimary; + snprintf(dns1str, sizeof(dns1str), "%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]); + } + + if (profile->ipv6.DnsSecondary[0]) { + d = profile->ipv6.DnsSecondary; + snprintf(dns2str, sizeof(dns2str), "%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]); + } + + update_resolv_conf(6, ifname, profile->ipv6.DnsPrimary[0] ? dns1str : NULL, profile->ipv6.DnsSecondary ? dns2str : NULL); + } +#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); + meig_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]; + + if (profile->qmapnet_adapter) { + ifname = profile->qmapnet_adapter; + } + +#ifdef USE_DHCLIENT + if (dhclient_alive) { + system("killall dhclient"); + dhclient_alive = 0; + } +#endif + if (dibbler_client_alive) { + system("killall dibbler-client"); + dibbler_client_alive = 0; + } + + snprintf(shell_cmd, sizeof(shell_cmd), "ifconfig %s down", ifname); + meig_system(shell_cmd); + snprintf(shell_cmd, sizeof(shell_cmd), "ifconfig %s 0.0.0.0", ifname); + meig_system(shell_cmd); +} diff --git a/package/wwan/meig-cm/src/util.c b/package/wwan/meig-cm/src/util.c new file mode 100644 index 000000000..0d55a70b6 --- /dev/null +++ b/package/wwan/meig-cm/src/util.c @@ -0,0 +1,208 @@ +#include <sys/time.h> +#if defined(__STDC__) +#include <stdarg.h> +#define __V(x) x +#else +#include <varargs.h> +#define __V(x) (va_alist) va_dcl +#define const +#define volatile +#endif + +#include <syslog.h> + +#include "QMIThread.h" +/* + * Use clock_gettime instead of gettimeofday + * -- Warnning: + * There is a risk that, the system may sync time at anytime. + * if gettimeofday may returns just before the sync time point, + * the funtion pthread_cond_timedwait will failed. + * -- use clock_gettime to get relative time will avoid the risk. + */ + +void setTimespecRelative(struct timespec *p_ts, long long msec) +{ + struct timespec ts; + + /* + CLOCK_REALTIME system time from 1970-1-1 + CLOCK_MONOTONIC system start time, cannot be changed + CLOCK_PROCESS_CPUTIME_ID the process running time + CLOCK_THREAD_CPUTIME_ID the thread running time + */ + + clock_gettime(CLOCK_MONOTONIC, &ts); + p_ts->tv_sec = ts.tv_sec + (msec / 1000); + p_ts->tv_nsec = ts.tv_nsec + (msec % 1000) * 1000L * 1000L; +} + +int pthread_cond_timeout_np(pthread_cond_t *cond, pthread_mutex_t * mutex, unsigned msecs) { + if (msecs != 0) { + struct timespec ts; + setTimespecRelative(&ts, msecs); + return pthread_cond_timedwait(cond, mutex, &ts); + } else { + return pthread_cond_wait(cond, mutex); + } +} + +void cond_setclock_attr(pthread_cond_t *cond, clockid_t clock) +{ + /* set relative time, for pthread_cond_timedwait */ + pthread_condattr_t attr; + pthread_condattr_init (&attr); + pthread_condattr_setclock(&attr, clock); + pthread_cond_init(cond, &attr); + pthread_condattr_destroy (&attr); +} + +const char * get_time(void) { + static char time_buf[50]; + struct timeval tv; + time_t time; + suseconds_t millitm; + struct tm *ti; + + gettimeofday (&tv, NULL); + + time= tv.tv_sec; + millitm = (tv.tv_usec + 500) / 1000; + + if (millitm == 1000) { + ++time; + millitm = 0; + } + + ti = localtime(&time); + sprintf(time_buf, "%02d-%02d_%02d:%02d:%02d:%03d", ti->tm_mon+1, ti->tm_mday, ti->tm_hour, ti->tm_min, ti->tm_sec, (int)millitm); + return time_buf; +} + +FILE *logfilefp = NULL; + +const int i = 1; +#define is_bigendian() ( (*(char*)&i) == 0 ) + +USHORT le16_to_cpu(USHORT v16) { + USHORT tmp = v16; + if (is_bigendian()) { + unsigned char *s = (unsigned char *)(&v16); + unsigned char *d = (unsigned char *)(&tmp); + d[0] = s[1]; + d[1] = s[0]; + } + return tmp; +} + +UINT le32_to_cpu (UINT v32) { + UINT tmp = v32; + if (is_bigendian()) { + unsigned char *s = (unsigned char *)(&v32); + unsigned char *d = (unsigned char *)(&tmp); + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; + } + return tmp; +} + +UINT meig_swap32(UINT v32) { + UINT tmp = v32; + { + unsigned char *s = (unsigned char *)(&v32); + unsigned char *d = (unsigned char *)(&tmp); + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; + } + return tmp; +} + +USHORT cpu_to_le16(USHORT v16) { + USHORT tmp = v16; + if (is_bigendian()) { + unsigned char *s = (unsigned char *)(&v16); + unsigned char *d = (unsigned char *)(&tmp); + d[0] = s[1]; + d[1] = s[0]; + } + return tmp; +} + +UINT cpu_to_le32 (UINT v32) { + UINT tmp = v32; + if (is_bigendian()) { + unsigned char *s = (unsigned char *)(&v32); + unsigned char *d = (unsigned char *)(&tmp); + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; + } + return tmp; +} + +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]); +} diff --git a/package/wwan/meig-cm/src/util.h b/package/wwan/meig-cm/src/util.h new file mode 100644 index 000000000..17802fabc --- /dev/null +++ b/package/wwan/meig-cm/src/util.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UTILS_H_ +#define _UTILS_H_ + +#include <stddef.h> + +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); +#endif diff --git a/package/wwan/quectel_MHI/Makefile b/package/wwan/quectel_MHI/Makefile index 6322519d6..7716efbba 100644 --- a/package/wwan/quectel_MHI/Makefile +++ b/package/wwan/quectel_MHI/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=pcie_mhi -PKG_VERSION:=1.3.6 +PKG_VERSION:=1.3.8 PKG_RELEASE:=1 include $(INCLUDE_DIR)/kernel.mk diff --git a/package/wwan/quectel_MHI/src/ReleaseNote.txt b/package/wwan/quectel_MHI/src/ReleaseNote.txt index 877d24cb8..8a55788d1 100644 --- a/package/wwan/quectel_MHI/src/ReleaseNote.txt +++ b/package/wwan/quectel_MHI/src/ReleaseNote.txt @@ -1,126 +1,132 @@ -Release Notes -[V1.3.7] -Date: 27/03/2024 -enhancement: - 1. support SDX35's PID/VID - 2. support IPQ QSDK MHI used with rmnetdata driver - -Release Notes -[V1.3.6] -Date: 01/08/2023 -enhancement: - 1. support Linux Kernel V6.4 - 2. support change mtu -fix: - 1. fix compile error on ipq's spf12.x - -Release Notes -[V1.3.5] -Date: 25/02/2023 -enhancement: - 1. support efuse SDX sleep - 2. support IPQ9574 SFE -fix: - 1. fix cannot find the node when dialing. Nodes in the /sys/bus/mhi_q/devices directory named hex - -[V1.3.4] -Date: 12/8/2022 -enhancement: - 1. only allow to enable autosuspend when module is in MHI_EE_AMSS - 2. show pcie link speed and width when driver probe - 3. check pcie link status by read pcie vid and pid when driver probe, - if pcie link is down, return -EIO - 4. support RM520 (1eac:1004) - 5. support qmap command packet -fix: - 1. fix tx queue is wrong stop when do uplink TPUT - 2. fix after QFirehose, module fail to bootup at very small probability - 3. mhi uci add mutex lock for concurrent reads/writes - -[V1.3.3] -Date: 30/6/2022 -enhancement: - 1. remove one un-necessary kmalloc when do qfirehose - 2. support mhi monitor (like usbmon), usage: cat /sys/kernel/debug/mhi_q/0306_00\:01.00/mhimon - 3. set ring size of event 0 to 256 (from 1024), required by x6x - 4. support PCIE local network card mhi_swip0 (chan 46/47), default disabled - 5. porting IPQ5018 mhi rate controll code from spf11.5 - 6. set pcie rmnet download max qmap packet size to 15KB (same to IPQ MHI Driver) - 7. support set different mac address for rmnet net card - 8. when mhi netdev fail to malloc, use delay_work instead work - 9. optimize code for 'when driver load, modem is still in MHI_EE_PTHRU' -fix: - 1. Fix not synchronize access rp/wp when mhi_queue_xxx and mhi_process_xxx_ring run on different CPU - 2. set dma mask when driver probe, some SOC like rpi_4 need it - -[V1.3.2] -Date: 12/16/2021 -enhancement: - 1. support Linux Kernel V5.14 - 2. mhi_netdev_quectel.c do not print log in softirq context - -[V1.3.1] -Date: 9/26/2021 -enhancement: -fix: - -[V1.3.0.19] -Date: 9/18/2021 -enhancement: - 1. support sdx62 (17cb:0308) - 2. support IPQ5018's NSS - 3. use 'qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c' instead myself rmnet_nss.c - and pcie_mhi.ko must load after then rmnet_nss.ko - 4. allow bhi irq is not 0 (for ipq5018) -fix: - -[V1.3.0.18] -Date: 4/14/2021 -enhancement: - 1. support mbim multiple call, usage: - # insmod pcie_mhi.ko mhi_mbim_enabeld=1 qmap_mode=4 - # quectel-mbim-proxy -d /dev/mhi_MBIM & - # quectel-CM -n X -fix: - -[V1.3.0.17] -Date: 3/11/2021 -enhancement: -fix: - 1. fix CPU loading very high when TPUT test when only one MSI interrupt - 2. fix error on latest X24 modem - -[V1.3.0.16] -Date: 11/18/2020 -enhancement: -fix: - 1. add ring size to 32, for in-bound chan, if one ring is full, modem will not generate MSI interrupt for all chan - -[V1.3.0.15] -Date: 10/30/2020 -enhancement: - 1. support multi-modems, named as /dev/mhi_<chan_name>X -fix: - 1. fix compile error on kernel v5.8 - -[V1.3.0.14] -Date: 10/9/2020 -enhancement: - 1. suppport EM120&EM160 -fix: - 1. fix compile error on kernel v5.6 - 2. support runtime suspend - -[V1.3.0.13] -Date: 9/7/2020 -enhancement: - 1. suppport EM120&EM160 -fix: - 1. fix error on X55 + PCIE2.0(e.g IPQ4019) - 2. support runtime suspend - -[V1.3.0.12] -Date: 7/7/2020 -enhancement: - 1. suppport create only none netcard (enabled by marco MHI_NETDEV_ONE_CARD_MODE), +Release Notes +[V1.3.8] +Date: 27/06/2024 +enhancement: + 1. support SDX7x's PID/VID + +Release Notes +[V1.3.7] +Date: 27/03/2024 +enhancement: + 1. support SDX35's PID/VID + 2. support IPQ QSDK MHI used with rmnetdata driver + +Release Notes +[V1.3.6] +Date: 01/08/2023 +enhancement: + 1. support Linux Kernel V6.4 + 2. support change mtu +fix: + 1. fix compile error on ipq's spf12.x + +Release Notes +[V1.3.5] +Date: 25/02/2023 +enhancement: + 1. support efuse SDX sleep + 2. support IPQ9574 SFE +fix: + 1. fix cannot find the node when dialing. Nodes in the /sys/bus/mhi_q/devices directory named hex + +[V1.3.4] +Date: 12/8/2022 +enhancement: + 1. only allow to enable autosuspend when module is in MHI_EE_AMSS + 2. show pcie link speed and width when driver probe + 3. check pcie link status by read pcie vid and pid when driver probe, + if pcie link is down, return -EIO + 4. support RM520 (1eac:1004) + 5. support qmap command packet +fix: + 1. fix tx queue is wrong stop when do uplink TPUT + 2. fix after QFirehose, module fail to bootup at very small probability + 3. mhi uci add mutex lock for concurrent reads/writes + +[V1.3.3] +Date: 30/6/2022 +enhancement: + 1. remove one un-necessary kmalloc when do qfirehose + 2. support mhi monitor (like usbmon), usage: cat /sys/kernel/debug/mhi_q/0306_00\:01.00/mhimon + 3. set ring size of event 0 to 256 (from 1024), required by x6x + 4. support PCIE local network card mhi_swip0 (chan 46/47), default disabled + 5. porting IPQ5018 mhi rate controll code from spf11.5 + 6. set pcie rmnet download max qmap packet size to 15KB (same to IPQ MHI Driver) + 7. support set different mac address for rmnet net card + 8. when mhi netdev fail to malloc, use delay_work instead work + 9. optimize code for 'when driver load, modem is still in MHI_EE_PTHRU' +fix: + 1. Fix not synchronize access rp/wp when mhi_queue_xxx and mhi_process_xxx_ring run on different CPU + 2. set dma mask when driver probe, some SOC like rpi_4 need it + +[V1.3.2] +Date: 12/16/2021 +enhancement: + 1. support Linux Kernel V5.14 + 2. mhi_netdev_quectel.c do not print log in softirq context + +[V1.3.1] +Date: 9/26/2021 +enhancement: +fix: + +[V1.3.0.19] +Date: 9/18/2021 +enhancement: + 1. support sdx62 (17cb:0308) + 2. support IPQ5018's NSS + 3. use 'qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c' instead myself rmnet_nss.c + and pcie_mhi.ko must load after then rmnet_nss.ko + 4. allow bhi irq is not 0 (for ipq5018) +fix: + +[V1.3.0.18] +Date: 4/14/2021 +enhancement: + 1. support mbim multiple call, usage: + # insmod pcie_mhi.ko mhi_mbim_enabeld=1 qmap_mode=4 + # quectel-mbim-proxy -d /dev/mhi_MBIM & + # quectel-CM -n X +fix: + +[V1.3.0.17] +Date: 3/11/2021 +enhancement: +fix: + 1. fix CPU loading very high when TPUT test when only one MSI interrupt + 2. fix error on latest X24 modem + +[V1.3.0.16] +Date: 11/18/2020 +enhancement: +fix: + 1. add ring size to 32, for in-bound chan, if one ring is full, modem will not generate MSI interrupt for all chan + +[V1.3.0.15] +Date: 10/30/2020 +enhancement: + 1. support multi-modems, named as /dev/mhi_<chan_name>X +fix: + 1. fix compile error on kernel v5.8 + +[V1.3.0.14] +Date: 10/9/2020 +enhancement: + 1. suppport EM120&EM160 +fix: + 1. fix compile error on kernel v5.6 + 2. support runtime suspend + +[V1.3.0.13] +Date: 9/7/2020 +enhancement: + 1. suppport EM120&EM160 +fix: + 1. fix error on X55 + PCIE2.0(e.g IPQ4019) + 2. support runtime suspend + +[V1.3.0.12] +Date: 7/7/2020 +enhancement: + 1. suppport create only none netcard (enabled by marco MHI_NETDEV_ONE_CARD_MODE), fix: \ No newline at end of file diff --git a/package/wwan/quectel_MHI/src/controllers/mhi_qcom.c b/package/wwan/quectel_MHI/src/controllers/mhi_qcom.c index df6ce193c..70dd9172b 100644 --- a/package/wwan/quectel_MHI/src/controllers/mhi_qcom.c +++ b/package/wwan/quectel_MHI/src/controllers/mhi_qcom.c @@ -1,715 +1,715 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/debugfs.h> -#include <linux/device.h> -#include <linux/dma-direction.h> -#include <linux/list.h> -#include <linux/of.h> -#include <linux/memblock.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/pm_runtime.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/uaccess.h> -#include <linux/interrupt.h> -#include <linux/version.h> -#include "../core/mhi.h" -#include "mhi_qcom.h" - -#if 1 -#ifndef PCI_IRQ_MSI -#define PCI_IRQ_MSI (1 << 1) /* Allow MSI interrupts */ - -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,53 )) -int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec) -{ - int nvec = maxvec; - int rc; - - if (maxvec < minvec) - return -ERANGE; - - do { - rc = pci_enable_msi_block(dev, nvec); - if (rc < 0) { - return rc; - } else if (rc > 0) { - if (rc < minvec) - return -ENOSPC; - nvec = rc; - } - } while (rc); - - return nvec; -} -#endif - -static int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs, - unsigned int max_vecs, unsigned int flags) -{ - return pci_enable_msi_range(dev, min_vecs, max_vecs); -} - -static void pci_free_irq_vectors(struct pci_dev *dev) -{ - pci_disable_msi(dev); -} - -static int pci_irq_vector(struct pci_dev *dev, unsigned int nr) -{ - return dev->irq + nr; -} -#endif -#endif - -static struct pci_device_id mhi_pcie_device_id[] = { - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0303)}, //SDX20 - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0304)}, //SDX24 - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0305)}, - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0306)}, //SDX55 - {PCI_DEVICE(0x2C7C, 0x0512)}, - {PCI_DEVICE(MHI_PCIE_VENDOR_ID, MHI_PCIE_DEBUG_ID)}, - {0}, -}; - -MODULE_DEVICE_TABLE(pci, mhi_pcie_device_id); - -static struct pci_driver mhi_pcie_driver; - -void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl) -{ - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - struct pci_dev *pci_dev = mhi_dev->pci_dev; - - pci_free_irq_vectors(pci_dev); - iounmap(mhi_cntrl->regs); - mhi_cntrl->regs = NULL; - pci_clear_master(pci_dev); - pci_release_region(pci_dev, mhi_dev->resn); - pci_disable_device(pci_dev); -} - -static int mhi_init_pci_dev(struct mhi_controller *mhi_cntrl) -{ - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - struct pci_dev *pci_dev = mhi_dev->pci_dev; - int ret; - resource_size_t start, len; - int i; - - mhi_dev->resn = MHI_PCI_BAR_NUM; - ret = pci_assign_resource(pci_dev, mhi_dev->resn); - if (ret) { - MHI_ERR("Error assign pci resources, ret:%d\n", ret); - return ret; - } - - ret = pci_enable_device(pci_dev); - if (ret) { - MHI_ERR("Error enabling device, ret:%d\n", ret); - goto error_enable_device; - } - - ret = pci_request_region(pci_dev, mhi_dev->resn, "mhi"); - if (ret) { - MHI_ERR("Error pci_request_region, ret:%d\n", ret); - goto error_request_region; - } - - pci_set_master(pci_dev); - - start = pci_resource_start(pci_dev, mhi_dev->resn); - len = pci_resource_len(pci_dev, mhi_dev->resn); - mhi_cntrl->regs = ioremap_nocache(start, len); - MHI_LOG("mhi_cntrl->regs = %p\n", mhi_cntrl->regs); - if (!mhi_cntrl->regs) { - MHI_ERR("Error ioremap region\n"); - goto error_ioremap; - } - - ret = pci_alloc_irq_vectors(pci_dev, 1, mhi_cntrl->msi_required, PCI_IRQ_MSI); - if (IS_ERR_VALUE((ulong)ret) || ret < mhi_cntrl->msi_required) { - if (ret == -ENOSPC) { - /* imx_3.14.52_1.1.0_ga - diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c - index f06e8f0..6a9614f 100644 - --- a/drivers/pci/host/pcie-designware.c - +++ b/drivers/pci/host/pcie-designware.c - @@ -376,6 +376,13 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, - if (msgvec > 5) - msgvec = 0; - - +#if 1 //Add by Quectel 20190419 - + if (msgvec > 0 && pdev->vendor == 0x17cb) { - + dev_info(&pdev->dev, "%s quectel fixup pos=%d, msg_ctr=%04x, msgvec=%d\n", __func__, desc->msi_attrib.pos, msg_ctr, msgvec); - + msgvec = 0; - + } - +#endif - + - irq = assign_irq((1 << msgvec), desc, &pos); - if (irq < 0) - return irq; - */ - } - //imx_4.1.15_2.0.0_ga & DELL_OPTIPLEX_7010 only alloc one msi interrupt for one pcie device - if (ret != 1) { - MHI_ERR("Failed to enable MSI, ret=%d, msi_required=%d\n", ret, mhi_cntrl->msi_required); - goto error_req_msi; - } - } - - mhi_cntrl->msi_allocated = ret; - MHI_LOG("msi_required = %d, msi_allocated = %d, msi_irq = %u\n", mhi_cntrl->msi_required, mhi_cntrl->msi_allocated, pci_dev->irq); - - for (i = 0; i < mhi_cntrl->msi_allocated; i++) { - mhi_cntrl->irq[i] = pci_irq_vector(pci_dev, i); - if (mhi_cntrl->irq[i] < 0) { - ret = mhi_cntrl->irq[i]; - goto error_get_irq_vec; - } - } - -#if 0 - /* configure runtime pm */ - pm_runtime_set_autosuspend_delay(&pci_dev->dev, MHI_RPM_SUSPEND_TMR_MS); - pm_runtime_dont_use_autosuspend(&pci_dev->dev); - pm_suspend_ignore_children(&pci_dev->dev, true); - - /* - * pci framework will increment usage count (twice) before - * calling local device driver probe function. - * 1st pci.c pci_pm_init() calls pm_runtime_forbid - * 2nd pci-driver.c local_pci_probe calls pm_runtime_get_sync - * Framework expect pci device driver to call - * pm_runtime_put_noidle to decrement usage count after - * successful probe and and call pm_runtime_allow to enable - * runtime suspend. - */ - pm_runtime_mark_last_busy(&pci_dev->dev); - pm_runtime_put_noidle(&pci_dev->dev); -#endif - - return 0; - -error_get_irq_vec: - pci_free_irq_vectors(pci_dev); - -error_req_msi: - iounmap(mhi_cntrl->regs); - -error_ioremap: - pci_clear_master(pci_dev); - -error_request_region: - pci_disable_device(pci_dev); - -error_enable_device: - pci_release_region(pci_dev, mhi_dev->resn); - - return ret; -} - -#ifdef CONFIG_PM -static int mhi_runtime_idle(struct device *dev) -{ - struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); - - MHI_LOG("Entered returning -EBUSY\n"); - - /* - * RPM framework during runtime resume always calls - * rpm_idle to see if device ready to suspend. - * If dev.power usage_count count is 0, rpm fw will call - * rpm_idle cb to see if device is ready to suspend. - * if cb return 0, or cb not defined the framework will - * assume device driver is ready to suspend; - * therefore, fw will schedule runtime suspend. - * In MHI power management, MHI host shall go to - * runtime suspend only after entering MHI State M2, even if - * usage count is 0. Return -EBUSY to disable automatic suspend. - */ - return -EBUSY; -} - -static int mhi_runtime_suspend(struct device *dev) -{ - int ret = 0; - struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); - - MHI_LOG("Enter\n"); - - mutex_lock(&mhi_cntrl->pm_mutex); - - ret = mhi_pm_suspend(mhi_cntrl); - if (ret) { - MHI_LOG("Abort due to ret:%d\n", ret); - goto exit_runtime_suspend; - } - - ret = mhi_arch_link_off(mhi_cntrl, true); - if (ret) - MHI_ERR("Failed to Turn off link ret:%d\n", ret); - -exit_runtime_suspend: - mutex_unlock(&mhi_cntrl->pm_mutex); - MHI_LOG("Exited with ret:%d\n", ret); - - return ret; -} - -static int mhi_runtime_resume(struct device *dev) -{ - int ret = 0; - struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - MHI_LOG("Enter\n"); - - mutex_lock(&mhi_cntrl->pm_mutex); - - if (!mhi_dev->powered_on) { - MHI_LOG("Not fully powered, return success\n"); - mutex_unlock(&mhi_cntrl->pm_mutex); - return 0; - } - - /* turn on link */ - ret = mhi_arch_link_on(mhi_cntrl); - if (ret) - goto rpm_resume_exit; - - /* enter M0 state */ - ret = mhi_pm_resume(mhi_cntrl); - -rpm_resume_exit: - mutex_unlock(&mhi_cntrl->pm_mutex); - MHI_LOG("Exited with :%d\n", ret); - - return ret; -} - -static int mhi_system_resume(struct device *dev) -{ - int ret = 0; - struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); - - ret = mhi_runtime_resume(dev); - if (ret) { - MHI_ERR("Failed to resume link\n"); - } else { - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - } - - return ret; -} - -int mhi_system_suspend(struct device *dev) -{ - struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); - - MHI_LOG("Entered\n"); - - /* if rpm status still active then force suspend */ - if (!pm_runtime_status_suspended(dev)) - return mhi_runtime_suspend(dev); - - pm_runtime_set_suspended(dev); - pm_runtime_disable(dev); - - MHI_LOG("Exit\n"); - return 0; -} -#endif - -/* checks if link is down */ -static int mhi_link_status(struct mhi_controller *mhi_cntrl, void *priv) -{ - struct mhi_dev *mhi_dev = priv; - u16 dev_id; - int ret; - - /* try reading device id, if dev id don't match, link is down */ - ret = pci_read_config_word(mhi_dev->pci_dev, PCI_DEVICE_ID, &dev_id); - - return (ret || dev_id != mhi_cntrl->dev_id) ? -EIO : 0; -} - -static int mhi_runtime_get(struct mhi_controller *mhi_cntrl, void *priv) -{ - struct mhi_dev *mhi_dev = priv; - struct device *dev = &mhi_dev->pci_dev->dev; - - return pm_runtime_get(dev); -} - -static void mhi_runtime_put(struct mhi_controller *mhi_cntrl, void *priv) -{ - struct mhi_dev *mhi_dev = priv; - struct device *dev = &mhi_dev->pci_dev->dev; - - pm_runtime_put_noidle(dev); -} - -static void mhi_status_cb(struct mhi_controller *mhi_cntrl, - void *priv, - enum MHI_CB reason) -{ - struct mhi_dev *mhi_dev = priv; - struct device *dev = &mhi_dev->pci_dev->dev; - - if (reason == MHI_CB_IDLE) { - MHI_LOG("Schedule runtime suspend 1\n"); - pm_runtime_mark_last_busy(dev); - pm_request_autosuspend(dev); - } -} - -int mhi_debugfs_trigger_m0(void *data, u64 val) -{ - struct mhi_controller *mhi_cntrl = data; - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - MHI_LOG("Trigger M3 Exit\n"); - pm_runtime_get(&mhi_dev->pci_dev->dev); - pm_runtime_put(&mhi_dev->pci_dev->dev); - - return 0; -} - -int mhi_debugfs_trigger_m3(void *data, u64 val) -{ - struct mhi_controller *mhi_cntrl = data; - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - MHI_LOG("Trigger M3 Entry\n"); - pm_runtime_mark_last_busy(&mhi_dev->pci_dev->dev); - pm_request_autosuspend(&mhi_dev->pci_dev->dev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m0_fops, NULL, - mhi_debugfs_trigger_m0, "%llu\n"); - -DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m3_fops, NULL, - mhi_debugfs_trigger_m3, "%llu\n"); - -static int mhi_init_debugfs_trigger_go(void *data, u64 val) -{ - struct mhi_controller *mhi_cntrl = data; - - MHI_LOG("Trigger power up sequence\n"); - - mhi_async_power_up(mhi_cntrl); - - return 0; -} -DEFINE_SIMPLE_ATTRIBUTE(mhi_init_debugfs_trigger_go_fops, NULL, - mhi_init_debugfs_trigger_go, "%llu\n"); - - -int mhi_init_debugfs_debug_show(struct seq_file *m, void *d) -{ - seq_puts(m, "Enable debug mode to debug external soc\n"); - seq_puts(m, - "Usage: echo 'devid,timeout,domain,smmu_cfg' > debug_mode\n"); - seq_puts(m, "No spaces between parameters\n"); - seq_puts(m, "\t1. devid : 0 or pci device id to register\n"); - seq_puts(m, "\t2. timeout: mhi cmd/state transition timeout\n"); - seq_puts(m, "\t3. domain: Rootcomplex\n"); - seq_puts(m, "\t4. smmu_cfg: smmu configuration mask:\n"); - seq_puts(m, "\t\t- BIT0: ATTACH\n"); - seq_puts(m, "\t\t- BIT1: S1 BYPASS\n"); - seq_puts(m, "\t\t-BIT2: FAST_MAP\n"); - seq_puts(m, "\t\t-BIT3: ATOMIC\n"); - seq_puts(m, "\t\t-BIT4: FORCE_COHERENT\n"); - seq_puts(m, "\t\t-BIT5: GEOMETRY\n"); - seq_puts(m, "\tAll timeout are in ms, enter 0 to keep default\n"); - seq_puts(m, "Examples inputs: '0x307,10000'\n"); - seq_puts(m, "\techo '0,10000,1'\n"); - seq_puts(m, "\techo '0x307,10000,0,0x3d'\n"); - seq_puts(m, "firmware image name will be changed to debug.mbn\n"); - - return 0; -} - -static int mhi_init_debugfs_debug_open(struct inode *node, struct file *file) -{ - return single_open(file, mhi_init_debugfs_debug_show, NULL); -} - -static ssize_t mhi_init_debugfs_debug_write(struct file *fp, - const char __user *ubuf, - size_t count, - loff_t *pos) -{ - char *buf = kmalloc(count + 1, GFP_KERNEL); - /* #,devid,timeout,domain,smmu-cfg */ - int args[5] = {0}; - static char const *dbg_fw = "debug.mbn"; - int ret; - struct mhi_controller *mhi_cntrl = fp->f_inode->i_private; - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - struct pci_device_id *id; - - if (!buf) - return -ENOMEM; - - ret = copy_from_user(buf, ubuf, count); - if (ret) - goto error_read; - buf[count] = 0; - get_options(buf, ARRAY_SIZE(args), args); - kfree(buf); - - /* override default parameters */ - mhi_cntrl->fw_image = dbg_fw; - mhi_cntrl->edl_image = dbg_fw; - - if (args[0] >= 2 && args[2]) - mhi_cntrl->timeout_ms = args[2]; - - if (args[0] >= 3 && args[3]) - mhi_cntrl->domain = args[3]; - - if (args[0] >= 4 && args[4]) - mhi_dev->smmu_cfg = args[4]; - - /* If it's a new device id register it */ - if (args[0] && args[1]) { - /* find the debug_id and overwrite it */ - for (id = mhi_pcie_device_id; id->vendor; id++) - if (id->device == MHI_PCIE_DEBUG_ID) { - id->device = args[1]; - pci_unregister_driver(&mhi_pcie_driver); - ret = pci_register_driver(&mhi_pcie_driver); - } - } - - mhi_dev->debug_mode = true; - debugfs_create_file("go", 0444, mhi_cntrl->parent, mhi_cntrl, - &mhi_init_debugfs_trigger_go_fops); - pr_info( - "%s: ret:%d pcidev:0x%x smm_cfg:%u timeout:%u\n", - __func__, ret, args[1], mhi_dev->smmu_cfg, - mhi_cntrl->timeout_ms); - return count; - -error_read: - kfree(buf); - return ret; -} - -static const struct file_operations debugfs_debug_ops = { - .open = mhi_init_debugfs_debug_open, - .release = single_release, - .read = seq_read, - .write = mhi_init_debugfs_debug_write, -}; - -static struct mhi_controller * mhi_platform_probe(struct pci_dev *pci_dev) -{ - struct mhi_controller *mhi_cntrl; - struct mhi_dev *mhi_dev; - u64 addr_win[2]; - int ret; - - mhi_cntrl = mhi_alloc_controller(sizeof(*mhi_dev)); - if (!mhi_cntrl) { - pr_err("mhi_alloc_controller fail\n"); - return NULL; - } - - mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - mhi_cntrl->dev_id = pci_dev->device; - mhi_cntrl->domain = pci_domain_nr(pci_dev->bus); - mhi_cntrl->bus = pci_dev->bus->number; - mhi_cntrl->slot = PCI_SLOT(pci_dev->devfn); - mhi_dev->smmu_cfg = 0; - #if 0 //def CONFIG_HAVE_MEMBLOCK - addr_win[0] = memblock_start_of_DRAM(); - addr_win[1] = memblock_end_of_DRAM(); - #else -#define MHI_MEM_BASE_DEFAULT 0x000000000 -#define MHI_MEM_SIZE_DEFAULT 0x2000000000 - addr_win[0] = MHI_MEM_BASE_DEFAULT; - addr_win[1] = MHI_MEM_SIZE_DEFAULT; - if (sizeof(dma_addr_t) == 4) { - addr_win[1] = 0xFFFFFFFF; - } - #endif - - mhi_cntrl->iova_start = addr_win[0]; - mhi_cntrl->iova_stop = addr_win[1]; - - mhi_dev->pci_dev = pci_dev; - mhi_cntrl->pci_dev = pci_dev; - - /* setup power management apis */ - mhi_cntrl->status_cb = mhi_status_cb; - mhi_cntrl->runtime_get = mhi_runtime_get; - mhi_cntrl->runtime_put = mhi_runtime_put; - mhi_cntrl->link_status = mhi_link_status; - - ret = mhi_arch_platform_init(mhi_dev); - if (ret) - goto error_probe; - - ret = mhi_register_mhi_controller(mhi_cntrl); - if (ret) - goto error_register; - - if (mhi_cntrl->parent) - debugfs_create_file("debug_mode", 0444, mhi_cntrl->parent, - mhi_cntrl, &debugfs_debug_ops); - - return mhi_cntrl; - -error_register: - mhi_arch_platform_deinit(mhi_dev); - -error_probe: - mhi_free_controller(mhi_cntrl); - - return NULL; -} - -int mhi_pci_probe(struct pci_dev *pci_dev, - const struct pci_device_id *device_id) -{ - struct mhi_controller *mhi_cntrl = NULL; - u32 domain = pci_domain_nr(pci_dev->bus); - u32 bus = pci_dev->bus->number; - u32 slot = PCI_SLOT(pci_dev->devfn); - struct mhi_dev *mhi_dev; - int ret; - - pr_info("%s pci_dev->name = %s, domain=%d, bus=%d, slot=%d, vendor=%04X, device=%04X\n", - __func__, dev_name(&pci_dev->dev), domain, bus, slot, pci_dev->vendor, pci_dev->device); - - mhi_cntrl = mhi_platform_probe(pci_dev); - if (!mhi_cntrl) { - pr_err("mhi_platform_probe fail\n"); - return -EPROBE_DEFER; - } - - mhi_cntrl->dev_id = pci_dev->device; - mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - mhi_dev->pci_dev = pci_dev; - mhi_dev->powered_on = true; - - ret = mhi_arch_pcie_init(mhi_cntrl); - if (ret) { - MHI_ERR("Error mhi_arch_pcie_init, ret:%d\n", ret); - return ret; - } - - ret = mhi_arch_iommu_init(mhi_cntrl); - if (ret) { - MHI_ERR("Error mhi_arch_iommu_init, ret:%d\n", ret); - goto error_iommu_init; - } - - ret = mhi_init_pci_dev(mhi_cntrl); - if (ret) { - MHI_ERR("Error mhi_init_pci_dev, ret:%d\n", ret); - goto error_init_pci; - } - - /* start power up sequence if not in debug mode */ - if (!mhi_dev->debug_mode) { - ret = mhi_async_power_up(mhi_cntrl); - if (ret) { - MHI_ERR("Error mhi_async_power_up, ret:%d\n", ret); - goto error_power_up; - } - } - -#if 0 - pm_runtime_mark_last_busy(&pci_dev->dev); - pm_runtime_allow(&pci_dev->dev); - pm_runtime_disable(&pci_dev->dev); -#endif - - if (mhi_cntrl->dentry) { - debugfs_create_file("m0", 0444, mhi_cntrl->dentry, mhi_cntrl, - &debugfs_trigger_m0_fops); - debugfs_create_file("m3", 0444, mhi_cntrl->dentry, mhi_cntrl, - &debugfs_trigger_m3_fops); - } - - dev_set_drvdata(&pci_dev->dev, mhi_cntrl); - MHI_LOG("Return successful\n"); - - return 0; - -error_power_up: - mhi_deinit_pci_dev(mhi_cntrl); - -error_init_pci: - mhi_arch_iommu_deinit(mhi_cntrl); - -error_iommu_init: - mhi_arch_pcie_deinit(mhi_cntrl); - - return ret; -} - -static void mhi_pci_remove(struct pci_dev *pci_dev) -{ - struct mhi_controller *mhi_cntrl = (struct mhi_controller *)dev_get_drvdata(&pci_dev->dev); - - if (mhi_cntrl && mhi_cntrl->pci_dev == pci_dev) { - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - MHI_LOG("%s\n", dev_name(&pci_dev->dev)); - if (!mhi_dev->debug_mode) { - mhi_power_down(mhi_cntrl, 1); - } - mhi_deinit_pci_dev(mhi_cntrl); - mhi_arch_iommu_deinit(mhi_cntrl); - mhi_arch_pcie_deinit(mhi_cntrl); - mhi_unregister_mhi_controller(mhi_cntrl); - } -} - -static const struct dev_pm_ops pm_ops = { - SET_RUNTIME_PM_OPS(mhi_runtime_suspend, - mhi_runtime_resume, - mhi_runtime_idle) - SET_SYSTEM_SLEEP_PM_OPS(mhi_system_suspend, mhi_system_resume) -}; - -static struct pci_driver mhi_pcie_driver = { - .name = "mhi", - .id_table = mhi_pcie_device_id, - .probe = mhi_pci_probe, - .remove = mhi_pci_remove, - .driver = { - .pm = &pm_ops - } -}; - -int __init mhi_controller_qcom_init(void) -{ - return pci_register_driver(&mhi_pcie_driver); -}; - -void mhi_controller_qcom_exit(void) -{ - pr_info("%s enter\n", __func__); - pci_unregister_driver(&mhi_pcie_driver); - pr_info("%s exit\n", __func__); -} +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/dma-direction.h> +#include <linux/list.h> +#include <linux/of.h> +#include <linux/memblock.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/pm_runtime.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/interrupt.h> +#include <linux/version.h> +#include "../core/mhi.h" +#include "mhi_qcom.h" + +#if 1 +#ifndef PCI_IRQ_MSI +#define PCI_IRQ_MSI (1 << 1) /* Allow MSI interrupts */ + +#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,53 )) +int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec) +{ + int nvec = maxvec; + int rc; + + if (maxvec < minvec) + return -ERANGE; + + do { + rc = pci_enable_msi_block(dev, nvec); + if (rc < 0) { + return rc; + } else if (rc > 0) { + if (rc < minvec) + return -ENOSPC; + nvec = rc; + } + } while (rc); + + return nvec; +} +#endif + +static int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs, + unsigned int max_vecs, unsigned int flags) +{ + return pci_enable_msi_range(dev, min_vecs, max_vecs); +} + +static void pci_free_irq_vectors(struct pci_dev *dev) +{ + pci_disable_msi(dev); +} + +static int pci_irq_vector(struct pci_dev *dev, unsigned int nr) +{ + return dev->irq + nr; +} +#endif +#endif + +static struct pci_device_id mhi_pcie_device_id[] = { + {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0303)}, //SDX20 + {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0304)}, //SDX24 + {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0305)}, + {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0306)}, //SDX55 + {PCI_DEVICE(0x2C7C, 0x0512)}, + {PCI_DEVICE(MHI_PCIE_VENDOR_ID, MHI_PCIE_DEBUG_ID)}, + {0}, +}; + +MODULE_DEVICE_TABLE(pci, mhi_pcie_device_id); + +static struct pci_driver mhi_pcie_driver; + +void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl) +{ + struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); + struct pci_dev *pci_dev = mhi_dev->pci_dev; + + pci_free_irq_vectors(pci_dev); + iounmap(mhi_cntrl->regs); + mhi_cntrl->regs = NULL; + pci_clear_master(pci_dev); + pci_release_region(pci_dev, mhi_dev->resn); + pci_disable_device(pci_dev); +} + +static int mhi_init_pci_dev(struct mhi_controller *mhi_cntrl) +{ + struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); + struct pci_dev *pci_dev = mhi_dev->pci_dev; + int ret; + resource_size_t start, len; + int i; + + mhi_dev->resn = MHI_PCI_BAR_NUM; + ret = pci_assign_resource(pci_dev, mhi_dev->resn); + if (ret) { + MHI_ERR("Error assign pci resources, ret:%d\n", ret); + return ret; + } + + ret = pci_enable_device(pci_dev); + if (ret) { + MHI_ERR("Error enabling device, ret:%d\n", ret); + goto error_enable_device; + } + + ret = pci_request_region(pci_dev, mhi_dev->resn, "mhi"); + if (ret) { + MHI_ERR("Error pci_request_region, ret:%d\n", ret); + goto error_request_region; + } + + pci_set_master(pci_dev); + + start = pci_resource_start(pci_dev, mhi_dev->resn); + len = pci_resource_len(pci_dev, mhi_dev->resn); + mhi_cntrl->regs = ioremap_nocache(start, len); + MHI_LOG("mhi_cntrl->regs = %p\n", mhi_cntrl->regs); + if (!mhi_cntrl->regs) { + MHI_ERR("Error ioremap region\n"); + goto error_ioremap; + } + + ret = pci_alloc_irq_vectors(pci_dev, 1, mhi_cntrl->msi_required, PCI_IRQ_MSI); + if (IS_ERR_VALUE((ulong)ret) || ret < mhi_cntrl->msi_required) { + if (ret == -ENOSPC) { + /* imx_3.14.52_1.1.0_ga + diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c + index f06e8f0..6a9614f 100644 + --- a/drivers/pci/host/pcie-designware.c + +++ b/drivers/pci/host/pcie-designware.c + @@ -376,6 +376,13 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, + if (msgvec > 5) + msgvec = 0; + + +#if 1 //Add by Quectel 20190419 + + if (msgvec > 0 && pdev->vendor == 0x17cb) { + + dev_info(&pdev->dev, "%s quectel fixup pos=%d, msg_ctr=%04x, msgvec=%d\n", __func__, desc->msi_attrib.pos, msg_ctr, msgvec); + + msgvec = 0; + + } + +#endif + + + irq = assign_irq((1 << msgvec), desc, &pos); + if (irq < 0) + return irq; + */ + } + //imx_4.1.15_2.0.0_ga & DELL_OPTIPLEX_7010 only alloc one msi interrupt for one pcie device + if (ret != 1) { + MHI_ERR("Failed to enable MSI, ret=%d, msi_required=%d\n", ret, mhi_cntrl->msi_required); + goto error_req_msi; + } + } + + mhi_cntrl->msi_allocated = ret; + MHI_LOG("msi_required = %d, msi_allocated = %d, msi_irq = %u\n", mhi_cntrl->msi_required, mhi_cntrl->msi_allocated, pci_dev->irq); + + for (i = 0; i < mhi_cntrl->msi_allocated; i++) { + mhi_cntrl->irq[i] = pci_irq_vector(pci_dev, i); + if (mhi_cntrl->irq[i] < 0) { + ret = mhi_cntrl->irq[i]; + goto error_get_irq_vec; + } + } + +#if 0 + /* configure runtime pm */ + pm_runtime_set_autosuspend_delay(&pci_dev->dev, MHI_RPM_SUSPEND_TMR_MS); + pm_runtime_dont_use_autosuspend(&pci_dev->dev); + pm_suspend_ignore_children(&pci_dev->dev, true); + + /* + * pci framework will increment usage count (twice) before + * calling local device driver probe function. + * 1st pci.c pci_pm_init() calls pm_runtime_forbid + * 2nd pci-driver.c local_pci_probe calls pm_runtime_get_sync + * Framework expect pci device driver to call + * pm_runtime_put_noidle to decrement usage count after + * successful probe and and call pm_runtime_allow to enable + * runtime suspend. + */ + pm_runtime_mark_last_busy(&pci_dev->dev); + pm_runtime_put_noidle(&pci_dev->dev); +#endif + + return 0; + +error_get_irq_vec: + pci_free_irq_vectors(pci_dev); + +error_req_msi: + iounmap(mhi_cntrl->regs); + +error_ioremap: + pci_clear_master(pci_dev); + +error_request_region: + pci_disable_device(pci_dev); + +error_enable_device: + pci_release_region(pci_dev, mhi_dev->resn); + + return ret; +} + +#ifdef CONFIG_PM +static int mhi_runtime_idle(struct device *dev) +{ + struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); + + MHI_LOG("Entered returning -EBUSY\n"); + + /* + * RPM framework during runtime resume always calls + * rpm_idle to see if device ready to suspend. + * If dev.power usage_count count is 0, rpm fw will call + * rpm_idle cb to see if device is ready to suspend. + * if cb return 0, or cb not defined the framework will + * assume device driver is ready to suspend; + * therefore, fw will schedule runtime suspend. + * In MHI power management, MHI host shall go to + * runtime suspend only after entering MHI State M2, even if + * usage count is 0. Return -EBUSY to disable automatic suspend. + */ + return -EBUSY; +} + +static int mhi_runtime_suspend(struct device *dev) +{ + int ret = 0; + struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); + + MHI_LOG("Enter\n"); + + mutex_lock(&mhi_cntrl->pm_mutex); + + ret = mhi_pm_suspend(mhi_cntrl); + if (ret) { + MHI_LOG("Abort due to ret:%d\n", ret); + goto exit_runtime_suspend; + } + + ret = mhi_arch_link_off(mhi_cntrl, true); + if (ret) + MHI_ERR("Failed to Turn off link ret:%d\n", ret); + +exit_runtime_suspend: + mutex_unlock(&mhi_cntrl->pm_mutex); + MHI_LOG("Exited with ret:%d\n", ret); + + return ret; +} + +static int mhi_runtime_resume(struct device *dev) +{ + int ret = 0; + struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); + struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); + + MHI_LOG("Enter\n"); + + mutex_lock(&mhi_cntrl->pm_mutex); + + if (!mhi_dev->powered_on) { + MHI_LOG("Not fully powered, return success\n"); + mutex_unlock(&mhi_cntrl->pm_mutex); + return 0; + } + + /* turn on link */ + ret = mhi_arch_link_on(mhi_cntrl); + if (ret) + goto rpm_resume_exit; + + /* enter M0 state */ + ret = mhi_pm_resume(mhi_cntrl); + +rpm_resume_exit: + mutex_unlock(&mhi_cntrl->pm_mutex); + MHI_LOG("Exited with :%d\n", ret); + + return ret; +} + +static int mhi_system_resume(struct device *dev) +{ + int ret = 0; + struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); + + ret = mhi_runtime_resume(dev); + if (ret) { + MHI_ERR("Failed to resume link\n"); + } else { + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + } + + return ret; +} + +int mhi_system_suspend(struct device *dev) +{ + struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); + + MHI_LOG("Entered\n"); + + /* if rpm status still active then force suspend */ + if (!pm_runtime_status_suspended(dev)) + return mhi_runtime_suspend(dev); + + pm_runtime_set_suspended(dev); + pm_runtime_disable(dev); + + MHI_LOG("Exit\n"); + return 0; +} +#endif + +/* checks if link is down */ +static int mhi_link_status(struct mhi_controller *mhi_cntrl, void *priv) +{ + struct mhi_dev *mhi_dev = priv; + u16 dev_id; + int ret; + + /* try reading device id, if dev id don't match, link is down */ + ret = pci_read_config_word(mhi_dev->pci_dev, PCI_DEVICE_ID, &dev_id); + + return (ret || dev_id != mhi_cntrl->dev_id) ? -EIO : 0; +} + +static int mhi_runtime_get(struct mhi_controller *mhi_cntrl, void *priv) +{ + struct mhi_dev *mhi_dev = priv; + struct device *dev = &mhi_dev->pci_dev->dev; + + return pm_runtime_get(dev); +} + +static void mhi_runtime_put(struct mhi_controller *mhi_cntrl, void *priv) +{ + struct mhi_dev *mhi_dev = priv; + struct device *dev = &mhi_dev->pci_dev->dev; + + pm_runtime_put_noidle(dev); +} + +static void mhi_status_cb(struct mhi_controller *mhi_cntrl, + void *priv, + enum MHI_CB reason) +{ + struct mhi_dev *mhi_dev = priv; + struct device *dev = &mhi_dev->pci_dev->dev; + + if (reason == MHI_CB_IDLE) { + MHI_LOG("Schedule runtime suspend 1\n"); + pm_runtime_mark_last_busy(dev); + pm_request_autosuspend(dev); + } +} + +int mhi_debugfs_trigger_m0(void *data, u64 val) +{ + struct mhi_controller *mhi_cntrl = data; + struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); + + MHI_LOG("Trigger M3 Exit\n"); + pm_runtime_get(&mhi_dev->pci_dev->dev); + pm_runtime_put(&mhi_dev->pci_dev->dev); + + return 0; +} + +int mhi_debugfs_trigger_m3(void *data, u64 val) +{ + struct mhi_controller *mhi_cntrl = data; + struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); + + MHI_LOG("Trigger M3 Entry\n"); + pm_runtime_mark_last_busy(&mhi_dev->pci_dev->dev); + pm_request_autosuspend(&mhi_dev->pci_dev->dev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m0_fops, NULL, + mhi_debugfs_trigger_m0, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m3_fops, NULL, + mhi_debugfs_trigger_m3, "%llu\n"); + +static int mhi_init_debugfs_trigger_go(void *data, u64 val) +{ + struct mhi_controller *mhi_cntrl = data; + + MHI_LOG("Trigger power up sequence\n"); + + mhi_async_power_up(mhi_cntrl); + + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(mhi_init_debugfs_trigger_go_fops, NULL, + mhi_init_debugfs_trigger_go, "%llu\n"); + + +int mhi_init_debugfs_debug_show(struct seq_file *m, void *d) +{ + seq_puts(m, "Enable debug mode to debug external soc\n"); + seq_puts(m, + "Usage: echo 'devid,timeout,domain,smmu_cfg' > debug_mode\n"); + seq_puts(m, "No spaces between parameters\n"); + seq_puts(m, "\t1. devid : 0 or pci device id to register\n"); + seq_puts(m, "\t2. timeout: mhi cmd/state transition timeout\n"); + seq_puts(m, "\t3. domain: Rootcomplex\n"); + seq_puts(m, "\t4. smmu_cfg: smmu configuration mask:\n"); + seq_puts(m, "\t\t- BIT0: ATTACH\n"); + seq_puts(m, "\t\t- BIT1: S1 BYPASS\n"); + seq_puts(m, "\t\t-BIT2: FAST_MAP\n"); + seq_puts(m, "\t\t-BIT3: ATOMIC\n"); + seq_puts(m, "\t\t-BIT4: FORCE_COHERENT\n"); + seq_puts(m, "\t\t-BIT5: GEOMETRY\n"); + seq_puts(m, "\tAll timeout are in ms, enter 0 to keep default\n"); + seq_puts(m, "Examples inputs: '0x307,10000'\n"); + seq_puts(m, "\techo '0,10000,1'\n"); + seq_puts(m, "\techo '0x307,10000,0,0x3d'\n"); + seq_puts(m, "firmware image name will be changed to debug.mbn\n"); + + return 0; +} + +static int mhi_init_debugfs_debug_open(struct inode *node, struct file *file) +{ + return single_open(file, mhi_init_debugfs_debug_show, NULL); +} + +static ssize_t mhi_init_debugfs_debug_write(struct file *fp, + const char __user *ubuf, + size_t count, + loff_t *pos) +{ + char *buf = kmalloc(count + 1, GFP_KERNEL); + /* #,devid,timeout,domain,smmu-cfg */ + int args[5] = {0}; + static char const *dbg_fw = "debug.mbn"; + int ret; + struct mhi_controller *mhi_cntrl = fp->f_inode->i_private; + struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); + struct pci_device_id *id; + + if (!buf) + return -ENOMEM; + + ret = copy_from_user(buf, ubuf, count); + if (ret) + goto error_read; + buf[count] = 0; + get_options(buf, ARRAY_SIZE(args), args); + kfree(buf); + + /* override default parameters */ + mhi_cntrl->fw_image = dbg_fw; + mhi_cntrl->edl_image = dbg_fw; + + if (args[0] >= 2 && args[2]) + mhi_cntrl->timeout_ms = args[2]; + + if (args[0] >= 3 && args[3]) + mhi_cntrl->domain = args[3]; + + if (args[0] >= 4 && args[4]) + mhi_dev->smmu_cfg = args[4]; + + /* If it's a new device id register it */ + if (args[0] && args[1]) { + /* find the debug_id and overwrite it */ + for (id = mhi_pcie_device_id; id->vendor; id++) + if (id->device == MHI_PCIE_DEBUG_ID) { + id->device = args[1]; + pci_unregister_driver(&mhi_pcie_driver); + ret = pci_register_driver(&mhi_pcie_driver); + } + } + + mhi_dev->debug_mode = true; + debugfs_create_file("go", 0444, mhi_cntrl->parent, mhi_cntrl, + &mhi_init_debugfs_trigger_go_fops); + pr_info( + "%s: ret:%d pcidev:0x%x smm_cfg:%u timeout:%u\n", + __func__, ret, args[1], mhi_dev->smmu_cfg, + mhi_cntrl->timeout_ms); + return count; + +error_read: + kfree(buf); + return ret; +} + +static const struct file_operations debugfs_debug_ops = { + .open = mhi_init_debugfs_debug_open, + .release = single_release, + .read = seq_read, + .write = mhi_init_debugfs_debug_write, +}; + +static struct mhi_controller * mhi_platform_probe(struct pci_dev *pci_dev) +{ + struct mhi_controller *mhi_cntrl; + struct mhi_dev *mhi_dev; + u64 addr_win[2]; + int ret; + + mhi_cntrl = mhi_alloc_controller(sizeof(*mhi_dev)); + if (!mhi_cntrl) { + pr_err("mhi_alloc_controller fail\n"); + return NULL; + } + + mhi_dev = mhi_controller_get_devdata(mhi_cntrl); + + mhi_cntrl->dev_id = pci_dev->device; + mhi_cntrl->domain = pci_domain_nr(pci_dev->bus); + mhi_cntrl->bus = pci_dev->bus->number; + mhi_cntrl->slot = PCI_SLOT(pci_dev->devfn); + mhi_dev->smmu_cfg = 0; + #if 0 //def CONFIG_HAVE_MEMBLOCK + addr_win[0] = memblock_start_of_DRAM(); + addr_win[1] = memblock_end_of_DRAM(); + #else +#define MHI_MEM_BASE_DEFAULT 0x000000000 +#define MHI_MEM_SIZE_DEFAULT 0x2000000000 + addr_win[0] = MHI_MEM_BASE_DEFAULT; + addr_win[1] = MHI_MEM_SIZE_DEFAULT; + if (sizeof(dma_addr_t) == 4) { + addr_win[1] = 0xFFFFFFFF; + } + #endif + + mhi_cntrl->iova_start = addr_win[0]; + mhi_cntrl->iova_stop = addr_win[1]; + + mhi_dev->pci_dev = pci_dev; + mhi_cntrl->pci_dev = pci_dev; + + /* setup power management apis */ + mhi_cntrl->status_cb = mhi_status_cb; + mhi_cntrl->runtime_get = mhi_runtime_get; + mhi_cntrl->runtime_put = mhi_runtime_put; + mhi_cntrl->link_status = mhi_link_status; + + ret = mhi_arch_platform_init(mhi_dev); + if (ret) + goto error_probe; + + ret = mhi_register_mhi_controller(mhi_cntrl); + if (ret) + goto error_register; + + if (mhi_cntrl->parent) + debugfs_create_file("debug_mode", 0444, mhi_cntrl->parent, + mhi_cntrl, &debugfs_debug_ops); + + return mhi_cntrl; + +error_register: + mhi_arch_platform_deinit(mhi_dev); + +error_probe: + mhi_free_controller(mhi_cntrl); + + return NULL; +} + +int mhi_pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *device_id) +{ + struct mhi_controller *mhi_cntrl = NULL; + u32 domain = pci_domain_nr(pci_dev->bus); + u32 bus = pci_dev->bus->number; + u32 slot = PCI_SLOT(pci_dev->devfn); + struct mhi_dev *mhi_dev; + int ret; + + pr_info("%s pci_dev->name = %s, domain=%d, bus=%d, slot=%d, vendor=%04X, device=%04X\n", + __func__, dev_name(&pci_dev->dev), domain, bus, slot, pci_dev->vendor, pci_dev->device); + + mhi_cntrl = mhi_platform_probe(pci_dev); + if (!mhi_cntrl) { + pr_err("mhi_platform_probe fail\n"); + return -EPROBE_DEFER; + } + + mhi_cntrl->dev_id = pci_dev->device; + mhi_dev = mhi_controller_get_devdata(mhi_cntrl); + mhi_dev->pci_dev = pci_dev; + mhi_dev->powered_on = true; + + ret = mhi_arch_pcie_init(mhi_cntrl); + if (ret) { + MHI_ERR("Error mhi_arch_pcie_init, ret:%d\n", ret); + return ret; + } + + ret = mhi_arch_iommu_init(mhi_cntrl); + if (ret) { + MHI_ERR("Error mhi_arch_iommu_init, ret:%d\n", ret); + goto error_iommu_init; + } + + ret = mhi_init_pci_dev(mhi_cntrl); + if (ret) { + MHI_ERR("Error mhi_init_pci_dev, ret:%d\n", ret); + goto error_init_pci; + } + + /* start power up sequence if not in debug mode */ + if (!mhi_dev->debug_mode) { + ret = mhi_async_power_up(mhi_cntrl); + if (ret) { + MHI_ERR("Error mhi_async_power_up, ret:%d\n", ret); + goto error_power_up; + } + } + +#if 0 + pm_runtime_mark_last_busy(&pci_dev->dev); + pm_runtime_allow(&pci_dev->dev); + pm_runtime_disable(&pci_dev->dev); +#endif + + if (mhi_cntrl->dentry) { + debugfs_create_file("m0", 0444, mhi_cntrl->dentry, mhi_cntrl, + &debugfs_trigger_m0_fops); + debugfs_create_file("m3", 0444, mhi_cntrl->dentry, mhi_cntrl, + &debugfs_trigger_m3_fops); + } + + dev_set_drvdata(&pci_dev->dev, mhi_cntrl); + MHI_LOG("Return successful\n"); + + return 0; + +error_power_up: + mhi_deinit_pci_dev(mhi_cntrl); + +error_init_pci: + mhi_arch_iommu_deinit(mhi_cntrl); + +error_iommu_init: + mhi_arch_pcie_deinit(mhi_cntrl); + + return ret; +} + +static void mhi_pci_remove(struct pci_dev *pci_dev) +{ + struct mhi_controller *mhi_cntrl = (struct mhi_controller *)dev_get_drvdata(&pci_dev->dev); + + if (mhi_cntrl && mhi_cntrl->pci_dev == pci_dev) { + struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); + MHI_LOG("%s\n", dev_name(&pci_dev->dev)); + if (!mhi_dev->debug_mode) { + mhi_power_down(mhi_cntrl, 1); + } + mhi_deinit_pci_dev(mhi_cntrl); + mhi_arch_iommu_deinit(mhi_cntrl); + mhi_arch_pcie_deinit(mhi_cntrl); + mhi_unregister_mhi_controller(mhi_cntrl); + } +} + +static const struct dev_pm_ops pm_ops = { + SET_RUNTIME_PM_OPS(mhi_runtime_suspend, + mhi_runtime_resume, + mhi_runtime_idle) + SET_SYSTEM_SLEEP_PM_OPS(mhi_system_suspend, mhi_system_resume) +}; + +static struct pci_driver mhi_pcie_driver = { + .name = "mhi", + .id_table = mhi_pcie_device_id, + .probe = mhi_pci_probe, + .remove = mhi_pci_remove, + .driver = { + .pm = &pm_ops + } +}; + +int __init mhi_controller_qcom_init(void) +{ + return pci_register_driver(&mhi_pcie_driver); +}; + +void mhi_controller_qcom_exit(void) +{ + pr_info("%s enter\n", __func__); + pci_unregister_driver(&mhi_pcie_driver); + pr_info("%s exit\n", __func__); +} diff --git a/package/wwan/quectel_MHI/src/controllers/mhi_qcom.h b/package/wwan/quectel_MHI/src/controllers/mhi_qcom.h index bced45b38..65ec5b8d2 100644 --- a/package/wwan/quectel_MHI/src/controllers/mhi_qcom.h +++ b/package/wwan/quectel_MHI/src/controllers/mhi_qcom.h @@ -1,92 +1,92 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#ifndef _MHI_QCOM_ -#define _MHI_QCOM_ - -/* iova cfg bitmask */ -#define MHI_SMMU_ATTACH BIT(0) -#define MHI_SMMU_S1_BYPASS BIT(1) -#define MHI_SMMU_FAST BIT(2) -#define MHI_SMMU_ATOMIC BIT(3) -#define MHI_SMMU_FORCE_COHERENT BIT(4) - -#define MHI_PCIE_VENDOR_ID (0x17cb) -#define MHI_PCIE_DEBUG_ID (0xffff) -#define MHI_RPM_SUSPEND_TMR_MS (3000) -#define MHI_PCI_BAR_NUM (0) - -struct mhi_dev { - struct pci_dev *pci_dev; - u32 smmu_cfg; - int resn; - void *arch_info; - bool powered_on; - bool debug_mode; -}; - -void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl); -int mhi_pci_probe(struct pci_dev *pci_dev, - const struct pci_device_id *device_id); - -#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,65 )) -static inline int dma_set_mask_and_coherent(struct device *dev, u64 mask) -{ - int rc = dma_set_mask(dev, mask); - if (rc == 0) - dma_set_coherent_mask(dev, mask); - return rc; -} -#endif - -static inline int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl) -{ - struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); - - mhi_cntrl->dev = &mhi_dev->pci_dev->dev; - - return dma_set_mask_and_coherent(mhi_cntrl->dev, DMA_BIT_MASK(64)); -} - -static inline void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl) -{ -} - -static inline int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl) -{ - return 0; -} - -static inline void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl) -{ -} - -static inline int mhi_arch_platform_init(struct mhi_dev *mhi_dev) -{ - return 0; -} - -static inline void mhi_arch_platform_deinit(struct mhi_dev *mhi_dev) -{ -} - -static inline int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, - bool graceful) -{ - return 0; -} - -static inline int mhi_arch_link_on(struct mhi_controller *mhi_cntrl) -{ - return 0; -} - -#endif /* _MHI_QCOM_ */ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _MHI_QCOM_ +#define _MHI_QCOM_ + +/* iova cfg bitmask */ +#define MHI_SMMU_ATTACH BIT(0) +#define MHI_SMMU_S1_BYPASS BIT(1) +#define MHI_SMMU_FAST BIT(2) +#define MHI_SMMU_ATOMIC BIT(3) +#define MHI_SMMU_FORCE_COHERENT BIT(4) + +#define MHI_PCIE_VENDOR_ID (0x17cb) +#define MHI_PCIE_DEBUG_ID (0xffff) +#define MHI_RPM_SUSPEND_TMR_MS (3000) +#define MHI_PCI_BAR_NUM (0) + +struct mhi_dev { + struct pci_dev *pci_dev; + u32 smmu_cfg; + int resn; + void *arch_info; + bool powered_on; + bool debug_mode; +}; + +void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl); +int mhi_pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *device_id); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,65 )) +static inline int dma_set_mask_and_coherent(struct device *dev, u64 mask) +{ + int rc = dma_set_mask(dev, mask); + if (rc == 0) + dma_set_coherent_mask(dev, mask); + return rc; +} +#endif + +static inline int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl) +{ + struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); + + mhi_cntrl->dev = &mhi_dev->pci_dev->dev; + + return dma_set_mask_and_coherent(mhi_cntrl->dev, DMA_BIT_MASK(64)); +} + +static inline void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl) +{ +} + +static inline int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl) +{ + return 0; +} + +static inline void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl) +{ +} + +static inline int mhi_arch_platform_init(struct mhi_dev *mhi_dev) +{ + return 0; +} + +static inline void mhi_arch_platform_deinit(struct mhi_dev *mhi_dev) +{ +} + +static inline int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, + bool graceful) +{ + return 0; +} + +static inline int mhi_arch_link_on(struct mhi_controller *mhi_cntrl) +{ + return 0; +} + +#endif /* _MHI_QCOM_ */ diff --git a/package/wwan/quectel_MHI/src/controllers/mhi_qti.c b/package/wwan/quectel_MHI/src/controllers/mhi_qti.c index 981b46fa4..21bcd04bc 100644 --- a/package/wwan/quectel_MHI/src/controllers/mhi_qti.c +++ b/package/wwan/quectel_MHI/src/controllers/mhi_qti.c @@ -1067,6 +1067,7 @@ static struct pci_device_id mhi_pcie_device_id[] = { {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0306)}, //SDX55 {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0308)}, //SDX62 {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x011a)}, //SDX35 + {PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0309)}, //SDX7X {PCI_DEVICE(0x1eac, 0x1001)}, //EM120 {PCI_DEVICE(0x1eac, 0x1002)}, //EM160 {PCI_DEVICE(0x1eac, 0x1004)}, //RM520 diff --git a/package/wwan/quectel_MHI/src/core/mhi.h b/package/wwan/quectel_MHI/src/core/mhi.h index fe330d59d..f9671af4a 100644 --- a/package/wwan/quectel_MHI/src/core/mhi.h +++ b/package/wwan/quectel_MHI/src/core/mhi.h @@ -4,7 +4,7 @@ #ifndef _MHI_H_ #define _MHI_H_ -#define PCIE_MHI_DRIVER_VERSION "V1.3.7" +#define PCIE_MHI_DRIVER_VERSION "V1.3.8" #define ENABLE_MHI_MON //#define ENABLE_IP_SW0 diff --git a/package/wwan/quectel_MHI/src/core/mhi_sdx20.h b/package/wwan/quectel_MHI/src/core/mhi_sdx20.h index 5a92efa4c..2bc5dc7b3 100644 --- a/package/wwan/quectel_MHI/src/core/mhi_sdx20.h +++ b/package/wwan/quectel_MHI/src/core/mhi_sdx20.h @@ -1,362 +1,362 @@ -#ifndef __SDX20_MHI_H -#define __SDX20_MHI_H - -#include <linux/types.h> - -/* MHI control data structures alloted by the host, including - * channel context array, event context array, command context and rings */ - -/* Channel context state */ -enum mhi_dev_ch_ctx_state { - MHI_DEV_CH_STATE_DISABLED, - MHI_DEV_CH_STATE_ENABLED, - MHI_DEV_CH_STATE_RUNNING, - MHI_DEV_CH_STATE_SUSPENDED, - MHI_DEV_CH_STATE_STOP, - MHI_DEV_CH_STATE_ERROR, - MHI_DEV_CH_STATE_RESERVED, - MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF -}; - -/* Channel type */ -enum mhi_dev_ch_ctx_type { - MHI_DEV_CH_TYPE_NONE, - MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL, - MHI_DEV_CH_TYPE_INBOUND_CHANNEL, - MHI_DEV_CH_RESERVED -}; - -/* Channel context type */ -struct mhi_dev_ch_ctx { - enum mhi_dev_ch_ctx_state ch_state; - enum mhi_dev_ch_ctx_type ch_type; - uint32_t err_indx; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -enum mhi_dev_ring_element_type_id { - MHI_DEV_RING_EL_INVALID = 0, - MHI_DEV_RING_EL_NOOP = 1, - MHI_DEV_RING_EL_TRANSFER = 2, - MHI_DEV_RING_EL_RESET = 16, - MHI_DEV_RING_EL_STOP = 17, - MHI_DEV_RING_EL_START = 18, - MHI_DEV_RING_EL_MHI_STATE_CHG = 32, - MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33, - MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34, - MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64, - MHI_DEV_RING_EL_UNDEF -}; - -enum mhi_dev_ring_state { - RING_STATE_UINT = 0, - RING_STATE_IDLE, - RING_STATE_PENDING, -}; - -enum mhi_dev_ring_type { - RING_TYPE_CMD = 0, - RING_TYPE_ER, - RING_TYPE_CH, - RING_TYPE_INVAL -}; - -/* Event context interrupt moderation */ -enum mhi_dev_evt_ctx_int_mod_timer { - MHI_DEV_EVT_INT_MODERATION_DISABLED -}; - -/* Event ring type */ -enum mhi_dev_evt_ctx_event_ring_type { - MHI_DEV_EVT_TYPE_DEFAULT, - MHI_DEV_EVT_TYPE_VALID, - MHI_DEV_EVT_RESERVED -}; - -/* Event ring context type */ -struct mhi_dev_ev_ctx { - uint32_t res1:16; - enum mhi_dev_evt_ctx_int_mod_timer intmodt:16; - enum mhi_dev_evt_ctx_event_ring_type ertype; - uint32_t msivec; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -/* Command context */ -struct mhi_dev_cmd_ctx { - uint32_t res1; - uint32_t res2; - uint32_t res3; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -/* generic context */ -struct mhi_dev_gen_ctx { - uint32_t res1; - uint32_t res2; - uint32_t res3; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -/* Transfer ring element */ -struct mhi_dev_transfer_ring_element { - uint64_t data_buf_ptr; - uint32_t len:16; - uint32_t res1:16; - uint32_t chain:1; - uint32_t res2:7; - uint32_t ieob:1; - uint32_t ieot:1; - uint32_t bei:1; - uint32_t res3:5; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res4:8; -} __packed; - -/* Command ring element */ -/* Command ring No op command */ -struct mhi_dev_cmd_ring_op { - uint64_t res1; - uint32_t res2; - uint32_t res3:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command ring reset channel command */ -struct mhi_dev_cmd_ring_reset_channel_cmd { - uint64_t res1; - uint32_t res2; - uint32_t res3:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command ring stop channel command */ -struct mhi_dev_cmd_ring_stop_channel_cmd { - uint64_t res1; - uint32_t res2; - uint32_t res3:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command ring start channel command */ -struct mhi_dev_cmd_ring_start_channel_cmd { - uint64_t res1; - uint32_t seqnum; - uint32_t reliable:1; - uint32_t res2:15; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -enum mhi_dev_cmd_completion_code { - MHI_CMD_COMPL_CODE_INVALID = 0, - MHI_CMD_COMPL_CODE_SUCCESS = 1, - MHI_CMD_COMPL_CODE_EOT = 2, - MHI_CMD_COMPL_CODE_OVERFLOW = 3, - MHI_CMD_COMPL_CODE_EOB = 4, - MHI_CMD_COMPL_CODE_UNDEFINED = 16, - MHI_CMD_COMPL_CODE_RING_EL = 17, - MHI_CMD_COMPL_CODE_RES -}; - -/* Event ring elements */ -/* Transfer completion event */ -struct mhi_dev_event_ring_transfer_completion { - uint64_t ptr; - uint32_t len:16; - uint32_t res1:8; - enum mhi_dev_cmd_completion_code code:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command completion event */ -struct mhi_dev_event_ring_cmd_completion { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_cmd_completion_code code:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res3:8; -} __packed; - -enum mhi_dev_state { - MHI_DEV_RESET_STATE = 0, - MHI_DEV_READY_STATE, - MHI_DEV_M0_STATE, - MHI_DEV_M1_STATE, - MHI_DEV_M2_STATE, - MHI_DEV_M3_STATE, - MHI_DEV_MAX_STATE, - MHI_DEV_SYSERR_STATE = 0xff -}; - -/* MHI state change event */ -struct mhi_dev_event_ring_state_change { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_state mhistate:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res3:8; -} __packed; - -enum mhi_dev_execenv { - MHI_DEV_SBL_EE = 1, - MHI_DEV_AMSS_EE = 2, - MHI_DEV_UNRESERVED -}; - -/* EE state change event */ -struct mhi_dev_event_ring_ee_state_change { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_execenv execenv:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res3:8; -} __packed; - -/* Generic cmd to parse common details like type and channel id */ -struct mhi_dev_ring_generic { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_state mhistate:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -struct mhi_config { - uint32_t mhi_reg_len; - uint32_t version; - uint32_t event_rings; - uint32_t channels; - uint32_t chdb_offset; - uint32_t erdb_offset; -}; - -#define NUM_CHANNELS 128 -#define HW_CHANNEL_BASE 100 -#define HW_CHANNEL_END 107 -#define MHI_ENV_VALUE 2 -#define MHI_MASK_ROWS_CH_EV_DB 4 -#define TRB_MAX_DATA_SIZE 8192 -#define MHI_CTRL_STATE 25 -#define IPA_DMA_SYNC 1 -#define IPA_DMA_ASYNC 0 - -/*maximum trasnfer completion events buffer*/ -#define MAX_TR_EVENTS 50 -/*maximum event requests */ -#define MHI_MAX_EVT_REQ 50 - -/* Possible ring element types */ -union mhi_dev_ring_element_type { - struct mhi_dev_cmd_ring_op cmd_no_op; - struct mhi_dev_cmd_ring_reset_channel_cmd cmd_reset; - struct mhi_dev_cmd_ring_stop_channel_cmd cmd_stop; - struct mhi_dev_cmd_ring_start_channel_cmd cmd_start; - struct mhi_dev_transfer_ring_element cmd_transfer; - struct mhi_dev_event_ring_transfer_completion evt_tr_comp; - struct mhi_dev_event_ring_cmd_completion evt_cmd_comp; - struct mhi_dev_event_ring_state_change evt_state_change; - struct mhi_dev_event_ring_ee_state_change evt_ee_state; - struct mhi_dev_ring_generic generic; -}; - -/* Transfer ring element type */ -union mhi_dev_ring_ctx { - struct mhi_dev_cmd_ctx cmd; - struct mhi_dev_ev_ctx ev; - struct mhi_dev_ch_ctx ch; - struct mhi_dev_gen_ctx generic; -}; - -/* MHI host Control and data address region */ -struct mhi_host_addr { - uint32_t ctrl_base_lsb; - uint32_t ctrl_base_msb; - uint32_t ctrl_limit_lsb; - uint32_t ctrl_limit_msb; - uint32_t data_base_lsb; - uint32_t data_base_msb; - uint32_t data_limit_lsb; - uint32_t data_limit_msb; -}; - -/* MHI physical and virtual address region */ -struct mhi_meminfo { - struct device *dev; - uintptr_t pa_aligned; - uintptr_t pa_unaligned; - uintptr_t va_aligned; - uintptr_t va_unaligned; - uintptr_t size; -}; - -struct mhi_addr { - uint64_t host_pa; - uintptr_t device_pa; - uintptr_t device_va; - size_t size; - dma_addr_t phy_addr; - void *virt_addr; - bool use_ipa_dma; -}; - -struct mhi_interrupt_state { - uint32_t mask; - uint32_t status; -}; - -enum mhi_dev_channel_state { - MHI_DEV_CH_UNINT, - MHI_DEV_CH_STARTED, - MHI_DEV_CH_PENDING_START, - MHI_DEV_CH_PENDING_STOP, - MHI_DEV_CH_STOPPED, - MHI_DEV_CH_CLOSED, -}; - -enum mhi_dev_ch_operation { - MHI_DEV_OPEN_CH, - MHI_DEV_CLOSE_CH, - MHI_DEV_READ_CH, - MHI_DEV_READ_WR, - MHI_DEV_POLL, -}; - -enum mhi_ctrl_info { - MHI_STATE_CONFIGURED = 0, - MHI_STATE_CONNECTED = 1, - MHI_STATE_DISCONNECTED = 2, - MHI_STATE_INVAL, -}; - -enum mhi_dev_tr_compl_evt_type { - SEND_EVENT_BUFFER, - SEND_EVENT_RD_OFFSET, -}; - -enum mhi_dev_transfer_type { - MHI_DEV_DMA_SYNC, - MHI_DEV_DMA_ASYNC, -}; -#endif /* _SDX20_MHI_H_ */ +#ifndef __SDX20_MHI_H +#define __SDX20_MHI_H + +#include <linux/types.h> + +/* MHI control data structures alloted by the host, including + * channel context array, event context array, command context and rings */ + +/* Channel context state */ +enum mhi_dev_ch_ctx_state { + MHI_DEV_CH_STATE_DISABLED, + MHI_DEV_CH_STATE_ENABLED, + MHI_DEV_CH_STATE_RUNNING, + MHI_DEV_CH_STATE_SUSPENDED, + MHI_DEV_CH_STATE_STOP, + MHI_DEV_CH_STATE_ERROR, + MHI_DEV_CH_STATE_RESERVED, + MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF +}; + +/* Channel type */ +enum mhi_dev_ch_ctx_type { + MHI_DEV_CH_TYPE_NONE, + MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL, + MHI_DEV_CH_TYPE_INBOUND_CHANNEL, + MHI_DEV_CH_RESERVED +}; + +/* Channel context type */ +struct mhi_dev_ch_ctx { + enum mhi_dev_ch_ctx_state ch_state; + enum mhi_dev_ch_ctx_type ch_type; + uint32_t err_indx; + uint64_t rbase; + uint64_t rlen; + uint64_t rp; + uint64_t wp; +} __packed; + +enum mhi_dev_ring_element_type_id { + MHI_DEV_RING_EL_INVALID = 0, + MHI_DEV_RING_EL_NOOP = 1, + MHI_DEV_RING_EL_TRANSFER = 2, + MHI_DEV_RING_EL_RESET = 16, + MHI_DEV_RING_EL_STOP = 17, + MHI_DEV_RING_EL_START = 18, + MHI_DEV_RING_EL_MHI_STATE_CHG = 32, + MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33, + MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34, + MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64, + MHI_DEV_RING_EL_UNDEF +}; + +enum mhi_dev_ring_state { + RING_STATE_UINT = 0, + RING_STATE_IDLE, + RING_STATE_PENDING, +}; + +enum mhi_dev_ring_type { + RING_TYPE_CMD = 0, + RING_TYPE_ER, + RING_TYPE_CH, + RING_TYPE_INVAL +}; + +/* Event context interrupt moderation */ +enum mhi_dev_evt_ctx_int_mod_timer { + MHI_DEV_EVT_INT_MODERATION_DISABLED +}; + +/* Event ring type */ +enum mhi_dev_evt_ctx_event_ring_type { + MHI_DEV_EVT_TYPE_DEFAULT, + MHI_DEV_EVT_TYPE_VALID, + MHI_DEV_EVT_RESERVED +}; + +/* Event ring context type */ +struct mhi_dev_ev_ctx { + uint32_t res1:16; + enum mhi_dev_evt_ctx_int_mod_timer intmodt:16; + enum mhi_dev_evt_ctx_event_ring_type ertype; + uint32_t msivec; + uint64_t rbase; + uint64_t rlen; + uint64_t rp; + uint64_t wp; +} __packed; + +/* Command context */ +struct mhi_dev_cmd_ctx { + uint32_t res1; + uint32_t res2; + uint32_t res3; + uint64_t rbase; + uint64_t rlen; + uint64_t rp; + uint64_t wp; +} __packed; + +/* generic context */ +struct mhi_dev_gen_ctx { + uint32_t res1; + uint32_t res2; + uint32_t res3; + uint64_t rbase; + uint64_t rlen; + uint64_t rp; + uint64_t wp; +} __packed; + +/* Transfer ring element */ +struct mhi_dev_transfer_ring_element { + uint64_t data_buf_ptr; + uint32_t len:16; + uint32_t res1:16; + uint32_t chain:1; + uint32_t res2:7; + uint32_t ieob:1; + uint32_t ieot:1; + uint32_t bei:1; + uint32_t res3:5; + enum mhi_dev_ring_element_type_id type:8; + uint32_t res4:8; +} __packed; + +/* Command ring element */ +/* Command ring No op command */ +struct mhi_dev_cmd_ring_op { + uint64_t res1; + uint32_t res2; + uint32_t res3:16; + enum mhi_dev_ring_element_type_id type:8; + uint32_t chid:8; +} __packed; + +/* Command ring reset channel command */ +struct mhi_dev_cmd_ring_reset_channel_cmd { + uint64_t res1; + uint32_t res2; + uint32_t res3:16; + enum mhi_dev_ring_element_type_id type:8; + uint32_t chid:8; +} __packed; + +/* Command ring stop channel command */ +struct mhi_dev_cmd_ring_stop_channel_cmd { + uint64_t res1; + uint32_t res2; + uint32_t res3:16; + enum mhi_dev_ring_element_type_id type:8; + uint32_t chid:8; +} __packed; + +/* Command ring start channel command */ +struct mhi_dev_cmd_ring_start_channel_cmd { + uint64_t res1; + uint32_t seqnum; + uint32_t reliable:1; + uint32_t res2:15; + enum mhi_dev_ring_element_type_id type:8; + uint32_t chid:8; +} __packed; + +enum mhi_dev_cmd_completion_code { + MHI_CMD_COMPL_CODE_INVALID = 0, + MHI_CMD_COMPL_CODE_SUCCESS = 1, + MHI_CMD_COMPL_CODE_EOT = 2, + MHI_CMD_COMPL_CODE_OVERFLOW = 3, + MHI_CMD_COMPL_CODE_EOB = 4, + MHI_CMD_COMPL_CODE_UNDEFINED = 16, + MHI_CMD_COMPL_CODE_RING_EL = 17, + MHI_CMD_COMPL_CODE_RES +}; + +/* Event ring elements */ +/* Transfer completion event */ +struct mhi_dev_event_ring_transfer_completion { + uint64_t ptr; + uint32_t len:16; + uint32_t res1:8; + enum mhi_dev_cmd_completion_code code:8; + uint32_t res2:16; + enum mhi_dev_ring_element_type_id type:8; + uint32_t chid:8; +} __packed; + +/* Command completion event */ +struct mhi_dev_event_ring_cmd_completion { + uint64_t ptr; + uint32_t res1:24; + enum mhi_dev_cmd_completion_code code:8; + uint32_t res2:16; + enum mhi_dev_ring_element_type_id type:8; + uint32_t res3:8; +} __packed; + +enum mhi_dev_state { + MHI_DEV_RESET_STATE = 0, + MHI_DEV_READY_STATE, + MHI_DEV_M0_STATE, + MHI_DEV_M1_STATE, + MHI_DEV_M2_STATE, + MHI_DEV_M3_STATE, + MHI_DEV_MAX_STATE, + MHI_DEV_SYSERR_STATE = 0xff +}; + +/* MHI state change event */ +struct mhi_dev_event_ring_state_change { + uint64_t ptr; + uint32_t res1:24; + enum mhi_dev_state mhistate:8; + uint32_t res2:16; + enum mhi_dev_ring_element_type_id type:8; + uint32_t res3:8; +} __packed; + +enum mhi_dev_execenv { + MHI_DEV_SBL_EE = 1, + MHI_DEV_AMSS_EE = 2, + MHI_DEV_UNRESERVED +}; + +/* EE state change event */ +struct mhi_dev_event_ring_ee_state_change { + uint64_t ptr; + uint32_t res1:24; + enum mhi_dev_execenv execenv:8; + uint32_t res2:16; + enum mhi_dev_ring_element_type_id type:8; + uint32_t res3:8; +} __packed; + +/* Generic cmd to parse common details like type and channel id */ +struct mhi_dev_ring_generic { + uint64_t ptr; + uint32_t res1:24; + enum mhi_dev_state mhistate:8; + uint32_t res2:16; + enum mhi_dev_ring_element_type_id type:8; + uint32_t chid:8; +} __packed; + +struct mhi_config { + uint32_t mhi_reg_len; + uint32_t version; + uint32_t event_rings; + uint32_t channels; + uint32_t chdb_offset; + uint32_t erdb_offset; +}; + +#define NUM_CHANNELS 128 +#define HW_CHANNEL_BASE 100 +#define HW_CHANNEL_END 107 +#define MHI_ENV_VALUE 2 +#define MHI_MASK_ROWS_CH_EV_DB 4 +#define TRB_MAX_DATA_SIZE 8192 +#define MHI_CTRL_STATE 25 +#define IPA_DMA_SYNC 1 +#define IPA_DMA_ASYNC 0 + +/*maximum trasnfer completion events buffer*/ +#define MAX_TR_EVENTS 50 +/*maximum event requests */ +#define MHI_MAX_EVT_REQ 50 + +/* Possible ring element types */ +union mhi_dev_ring_element_type { + struct mhi_dev_cmd_ring_op cmd_no_op; + struct mhi_dev_cmd_ring_reset_channel_cmd cmd_reset; + struct mhi_dev_cmd_ring_stop_channel_cmd cmd_stop; + struct mhi_dev_cmd_ring_start_channel_cmd cmd_start; + struct mhi_dev_transfer_ring_element cmd_transfer; + struct mhi_dev_event_ring_transfer_completion evt_tr_comp; + struct mhi_dev_event_ring_cmd_completion evt_cmd_comp; + struct mhi_dev_event_ring_state_change evt_state_change; + struct mhi_dev_event_ring_ee_state_change evt_ee_state; + struct mhi_dev_ring_generic generic; +}; + +/* Transfer ring element type */ +union mhi_dev_ring_ctx { + struct mhi_dev_cmd_ctx cmd; + struct mhi_dev_ev_ctx ev; + struct mhi_dev_ch_ctx ch; + struct mhi_dev_gen_ctx generic; +}; + +/* MHI host Control and data address region */ +struct mhi_host_addr { + uint32_t ctrl_base_lsb; + uint32_t ctrl_base_msb; + uint32_t ctrl_limit_lsb; + uint32_t ctrl_limit_msb; + uint32_t data_base_lsb; + uint32_t data_base_msb; + uint32_t data_limit_lsb; + uint32_t data_limit_msb; +}; + +/* MHI physical and virtual address region */ +struct mhi_meminfo { + struct device *dev; + uintptr_t pa_aligned; + uintptr_t pa_unaligned; + uintptr_t va_aligned; + uintptr_t va_unaligned; + uintptr_t size; +}; + +struct mhi_addr { + uint64_t host_pa; + uintptr_t device_pa; + uintptr_t device_va; + size_t size; + dma_addr_t phy_addr; + void *virt_addr; + bool use_ipa_dma; +}; + +struct mhi_interrupt_state { + uint32_t mask; + uint32_t status; +}; + +enum mhi_dev_channel_state { + MHI_DEV_CH_UNINT, + MHI_DEV_CH_STARTED, + MHI_DEV_CH_PENDING_START, + MHI_DEV_CH_PENDING_STOP, + MHI_DEV_CH_STOPPED, + MHI_DEV_CH_CLOSED, +}; + +enum mhi_dev_ch_operation { + MHI_DEV_OPEN_CH, + MHI_DEV_CLOSE_CH, + MHI_DEV_READ_CH, + MHI_DEV_READ_WR, + MHI_DEV_POLL, +}; + +enum mhi_ctrl_info { + MHI_STATE_CONFIGURED = 0, + MHI_STATE_CONNECTED = 1, + MHI_STATE_DISCONNECTED = 2, + MHI_STATE_INVAL, +}; + +enum mhi_dev_tr_compl_evt_type { + SEND_EVENT_BUFFER, + SEND_EVENT_RD_OFFSET, +}; + +enum mhi_dev_transfer_type { + MHI_DEV_DMA_SYNC, + MHI_DEV_DMA_ASYNC, +}; +#endif /* _SDX20_MHI_H_ */ diff --git a/package/wwan/quectel_MHI/src/core/sdx20_mhi.h b/package/wwan/quectel_MHI/src/core/sdx20_mhi.h index a7d37839f..29c4a3a25 100644 --- a/package/wwan/quectel_MHI/src/core/sdx20_mhi.h +++ b/package/wwan/quectel_MHI/src/core/sdx20_mhi.h @@ -1,426 +1,426 @@ -#ifndef __SDX20_MHI_H -#define __SDX20_MHI_H - -#include <linux/types.h> - -/* MHI control data structures alloted by the host, including - * channel context array, event context array, command context and rings */ - -/* Channel context state */ -enum mhi_dev_ch_ctx_state { - MHI_DEV_CH_STATE_DISABLED, - MHI_DEV_CH_STATE_ENABLED, - MHI_DEV_CH_STATE_RUNNING, - MHI_DEV_CH_STATE_SUSPENDED, - MHI_DEV_CH_STATE_STOP, - MHI_DEV_CH_STATE_ERROR, - MHI_DEV_CH_STATE_RESERVED, - MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF -}; - -/* Channel type */ -enum mhi_dev_ch_ctx_type { - MHI_DEV_CH_TYPE_NONE, - MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL, - MHI_DEV_CH_TYPE_INBOUND_CHANNEL, - MHI_DEV_CH_RESERVED -}; - -/* Channel context type */ -struct mhi_dev_ch_ctx { - enum mhi_dev_ch_ctx_state ch_state; - enum mhi_dev_ch_ctx_type ch_type; - uint32_t err_indx; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -enum mhi_dev_ring_element_type_id { - MHI_DEV_RING_EL_INVALID = 0, - MHI_DEV_RING_EL_NOOP = 1, - MHI_DEV_RING_EL_TRANSFER = 2, - MHI_DEV_RING_EL_RESET = 16, - MHI_DEV_RING_EL_STOP = 17, - MHI_DEV_RING_EL_START = 18, - MHI_DEV_RING_EL_MHI_STATE_CHG = 32, - MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33, - MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34, - MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64, - MHI_DEV_RING_EL_UNDEF -}; - -enum mhi_dev_ring_state { - RING_STATE_UINT = 0, - RING_STATE_IDLE, - RING_STATE_PENDING, -}; - -enum mhi_dev_ring_type { - RING_TYPE_CMD = 0, - RING_TYPE_ER, - RING_TYPE_CH, - RING_TYPE_INVAL -}; - -/* Event context interrupt moderation */ -enum mhi_dev_evt_ctx_int_mod_timer { - MHI_DEV_EVT_INT_MODERATION_DISABLED -}; - -/* Event ring type */ -enum mhi_dev_evt_ctx_event_ring_type { - MHI_DEV_EVT_TYPE_DEFAULT, - MHI_DEV_EVT_TYPE_VALID, - MHI_DEV_EVT_RESERVED -}; - -/* Event ring context type */ -struct mhi_dev_ev_ctx { - uint32_t res1:16; - enum mhi_dev_evt_ctx_int_mod_timer intmodt:16; - enum mhi_dev_evt_ctx_event_ring_type ertype; - uint32_t msivec; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -/* Command context */ -struct mhi_dev_cmd_ctx { - uint32_t res1; - uint32_t res2; - uint32_t res3; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -/* generic context */ -struct mhi_dev_gen_ctx { - uint32_t res1; - uint32_t res2; - uint32_t res3; - uint64_t rbase; - uint64_t rlen; - uint64_t rp; - uint64_t wp; -} __packed; - -/* Transfer ring element */ -struct mhi_dev_transfer_ring_element { - uint64_t data_buf_ptr; - uint32_t len:16; - uint32_t res1:16; - uint32_t chain:1; - uint32_t res2:7; - uint32_t ieob:1; - uint32_t ieot:1; - uint32_t bei:1; - uint32_t res3:5; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res4:8; -} __packed; - -/* Command ring element */ -/* Command ring No op command */ -struct mhi_dev_cmd_ring_op { - uint64_t res1; - uint32_t res2; - uint32_t res3:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command ring reset channel command */ -struct mhi_dev_cmd_ring_reset_channel_cmd { - uint64_t res1; - uint32_t res2; - uint32_t res3:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command ring stop channel command */ -struct mhi_dev_cmd_ring_stop_channel_cmd { - uint64_t res1; - uint32_t res2; - uint32_t res3:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command ring start channel command */ -struct mhi_dev_cmd_ring_start_channel_cmd { - uint64_t res1; - uint32_t seqnum; - uint32_t reliable:1; - uint32_t res2:15; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -enum mhi_dev_cmd_completion_code { - MHI_CMD_COMPL_CODE_INVALID = 0, - MHI_CMD_COMPL_CODE_SUCCESS = 1, - MHI_CMD_COMPL_CODE_EOT = 2, - MHI_CMD_COMPL_CODE_OVERFLOW = 3, - MHI_CMD_COMPL_CODE_EOB = 4, - MHI_CMD_COMPL_CODE_UNDEFINED = 16, - MHI_CMD_COMPL_CODE_RING_EL = 17, - MHI_CMD_COMPL_CODE_RES -}; - -/* Event ring elements */ -/* Transfer completion event */ -struct mhi_dev_event_ring_transfer_completion { - uint64_t ptr; - uint32_t len:16; - uint32_t res1:8; - enum mhi_dev_cmd_completion_code code:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -/* Command completion event */ -struct mhi_dev_event_ring_cmd_completion { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_cmd_completion_code code:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res3:8; -} __packed; - -enum mhi_dev_state { - MHI_DEV_RESET_STATE = 0, - MHI_DEV_READY_STATE, - MHI_DEV_M0_STATE, - MHI_DEV_M1_STATE, - MHI_DEV_M2_STATE, - MHI_DEV_M3_STATE, - MHI_DEV_MAX_STATE, - MHI_DEV_SYSERR_STATE = 0xff -}; - -/* MHI state change event */ -struct mhi_dev_event_ring_state_change { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_state mhistate:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res3:8; -} __packed; - -enum mhi_dev_execenv { - MHI_DEV_SBL_EE = 1, - MHI_DEV_AMSS_EE = 2, - MHI_DEV_UNRESERVED -}; - -/* EE state change event */ -struct mhi_dev_event_ring_ee_state_change { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_execenv execenv:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t res3:8; -} __packed; - -/* Generic cmd to parse common details like type and channel id */ -struct mhi_dev_ring_generic { - uint64_t ptr; - uint32_t res1:24; - enum mhi_dev_state mhistate:8; - uint32_t res2:16; - enum mhi_dev_ring_element_type_id type:8; - uint32_t chid:8; -} __packed; - -struct mhi_config { - uint32_t mhi_reg_len; - uint32_t version; - uint32_t event_rings; - uint32_t channels; - uint32_t chdb_offset; - uint32_t erdb_offset; -}; - -#define NUM_CHANNELS 128 -#define HW_CHANNEL_BASE 100 -#define HW_CHANNEL_END 107 -#define MHI_ENV_VALUE 2 -#define MHI_MASK_ROWS_CH_EV_DB 4 -#define TRB_MAX_DATA_SIZE 8192 -#define MHI_CTRL_STATE 25 -#define IPA_DMA_SYNC 1 -#define IPA_DMA_ASYNC 0 - -/*maximum trasnfer completion events buffer*/ -#define MAX_TR_EVENTS 50 -/*maximum event requests */ -#define MHI_MAX_EVT_REQ 50 - -/* Possible ring element types */ -union mhi_dev_ring_element_type { - struct mhi_dev_cmd_ring_op cmd_no_op; - struct mhi_dev_cmd_ring_reset_channel_cmd cmd_reset; - struct mhi_dev_cmd_ring_stop_channel_cmd cmd_stop; - struct mhi_dev_cmd_ring_start_channel_cmd cmd_start; - struct mhi_dev_transfer_ring_element tre; - struct mhi_dev_event_ring_transfer_completion evt_tr_comp; - struct mhi_dev_event_ring_cmd_completion evt_cmd_comp; - struct mhi_dev_event_ring_state_change evt_state_change; - struct mhi_dev_event_ring_ee_state_change evt_ee_state; - struct mhi_dev_ring_generic generic; -}; - -/* Transfer ring element type */ -union mhi_dev_ring_ctx { - struct mhi_dev_cmd_ctx cmd; - struct mhi_dev_ev_ctx ev; - struct mhi_dev_ch_ctx ch; - struct mhi_dev_gen_ctx generic; -}; - -/* MHI host Control and data address region */ -struct mhi_host_addr { - uint32_t ctrl_base_lsb; - uint32_t ctrl_base_msb; - uint32_t ctrl_limit_lsb; - uint32_t ctrl_limit_msb; - uint32_t data_base_lsb; - uint32_t data_base_msb; - uint32_t data_limit_lsb; - uint32_t data_limit_msb; -}; - -/* MHI physical and virtual address region */ -struct mhi_meminfo { - struct device *dev; - uintptr_t pa_aligned; - uintptr_t pa_unaligned; - uintptr_t va_aligned; - uintptr_t va_unaligned; - uintptr_t size; -}; - -struct mhi_addr { - uint64_t host_pa; - uintptr_t device_pa; - uintptr_t device_va; - size_t size; - dma_addr_t phy_addr; - void *virt_addr; - bool use_ipa_dma; -}; - -struct mhi_interrupt_state { - uint32_t mask; - uint32_t status; -}; - -enum mhi_dev_channel_state { - MHI_DEV_CH_UNINT, - MHI_DEV_CH_STARTED, - MHI_DEV_CH_PENDING_START, - MHI_DEV_CH_PENDING_STOP, - MHI_DEV_CH_STOPPED, - MHI_DEV_CH_CLOSED, -}; - -enum mhi_dev_ch_operation { - MHI_DEV_OPEN_CH, - MHI_DEV_CLOSE_CH, - MHI_DEV_READ_CH, - MHI_DEV_READ_WR, - MHI_DEV_POLL, -}; - -enum mhi_ctrl_info { - MHI_STATE_CONFIGURED = 0, - MHI_STATE_CONNECTED = 1, - MHI_STATE_DISCONNECTED = 2, - MHI_STATE_INVAL, -}; - -enum mhi_dev_tr_compl_evt_type { - SEND_EVENT_BUFFER, - SEND_EVENT_RD_OFFSET, -}; - -enum mhi_dev_transfer_type { - MHI_DEV_DMA_SYNC, - MHI_DEV_DMA_ASYNC, -}; - -#if 0 -/* SW channel client list */ -enum mhi_client_channel { - MHI_CLIENT_LOOPBACK_OUT = 0, - MHI_CLIENT_LOOPBACK_IN = 1, - MHI_CLIENT_SAHARA_OUT = 2, - MHI_CLIENT_SAHARA_IN = 3, - MHI_CLIENT_DIAG_OUT = 4, - MHI_CLIENT_DIAG_IN = 5, - MHI_CLIENT_SSR_OUT = 6, - MHI_CLIENT_SSR_IN = 7, - MHI_CLIENT_QDSS_OUT = 8, - MHI_CLIENT_QDSS_IN = 9, - MHI_CLIENT_EFS_OUT = 10, - MHI_CLIENT_EFS_IN = 11, - MHI_CLIENT_MBIM_OUT = 12, - MHI_CLIENT_MBIM_IN = 13, - MHI_CLIENT_QMI_OUT = 14, - MHI_CLIENT_QMI_IN = 15, - MHI_CLIENT_IP_CTRL_0_OUT = 16, - MHI_CLIENT_IP_CTRL_0_IN = 17, - MHI_CLIENT_IP_CTRL_1_OUT = 18, - MHI_CLIENT_IP_CTRL_1_IN = 19, - MHI_CLIENT_DCI_OUT = 20, - MHI_CLIENT_DCI_IN = 21, - MHI_CLIENT_IP_CTRL_3_OUT = 22, - MHI_CLIENT_IP_CTRL_3_IN = 23, - MHI_CLIENT_IP_CTRL_4_OUT = 24, - MHI_CLIENT_IP_CTRL_4_IN = 25, - MHI_CLIENT_IP_CTRL_5_OUT = 26, - MHI_CLIENT_IP_CTRL_5_IN = 27, - MHI_CLIENT_IP_CTRL_6_OUT = 28, - MHI_CLIENT_IP_CTRL_6_IN = 29, - MHI_CLIENT_IP_CTRL_7_OUT = 30, - MHI_CLIENT_IP_CTRL_7_IN = 31, - MHI_CLIENT_DUN_OUT = 32, - MHI_CLIENT_DUN_IN = 33, - MHI_CLIENT_IP_SW_0_OUT = 34, - MHI_CLIENT_IP_SW_0_IN = 35, - MHI_CLIENT_IP_SW_1_OUT = 36, - MHI_CLIENT_IP_SW_1_IN = 37, - MHI_CLIENT_IP_SW_2_OUT = 38, - MHI_CLIENT_IP_SW_2_IN = 39, - MHI_CLIENT_IP_SW_3_OUT = 40, - MHI_CLIENT_IP_SW_3_IN = 41, - MHI_CLIENT_CSVT_OUT = 42, - MHI_CLIENT_CSVT_IN = 43, - MHI_CLIENT_SMCT_OUT = 44, - MHI_CLIENT_SMCT_IN = 45, - MHI_CLIENT_IP_SW_4_OUT = 46, - MHI_CLIENT_IP_SW_4_IN = 47, - MHI_MAX_SOFTWARE_CHANNELS = 48, - MHI_CLIENT_TEST_OUT = 60, - MHI_CLIENT_TEST_IN = 61, - MHI_CLIENT_RESERVED_1_LOWER = 62, - MHI_CLIENT_RESERVED_1_UPPER = 99, - MHI_CLIENT_IP_HW_0_OUT = 100, - MHI_CLIENT_IP_HW_0_IN = 101, - MHI_CLIENT_RESERVED_2_LOWER = 102, - MHI_CLIENT_RESERVED_2_UPPER = 127, - MHI_MAX_CHANNELS = 102, -}; -#endif -#endif /* _SDX20_MHI_H_ */ +#ifndef __SDX20_MHI_H +#define __SDX20_MHI_H + +#include <linux/types.h> + +/* MHI control data structures alloted by the host, including + * channel context array, event context array, command context and rings */ + +/* Channel context state */ +enum mhi_dev_ch_ctx_state { + MHI_DEV_CH_STATE_DISABLED, + MHI_DEV_CH_STATE_ENABLED, + MHI_DEV_CH_STATE_RUNNING, + MHI_DEV_CH_STATE_SUSPENDED, + MHI_DEV_CH_STATE_STOP, + MHI_DEV_CH_STATE_ERROR, + MHI_DEV_CH_STATE_RESERVED, + MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF +}; + +/* Channel type */ +enum mhi_dev_ch_ctx_type { + MHI_DEV_CH_TYPE_NONE, + MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL, + MHI_DEV_CH_TYPE_INBOUND_CHANNEL, + MHI_DEV_CH_RESERVED +}; + +/* Channel context type */ +struct mhi_dev_ch_ctx { + enum mhi_dev_ch_ctx_state ch_state; + enum mhi_dev_ch_ctx_type ch_type; + uint32_t err_indx; + uint64_t rbase; + uint64_t rlen; + uint64_t rp; + uint64_t wp; +} __packed; + +enum mhi_dev_ring_element_type_id { + MHI_DEV_RING_EL_INVALID = 0, + MHI_DEV_RING_EL_NOOP = 1, + MHI_DEV_RING_EL_TRANSFER = 2, + MHI_DEV_RING_EL_RESET = 16, + MHI_DEV_RING_EL_STOP = 17, + MHI_DEV_RING_EL_START = 18, + MHI_DEV_RING_EL_MHI_STATE_CHG = 32, + MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33, + MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34, + MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64, + MHI_DEV_RING_EL_UNDEF +}; + +enum mhi_dev_ring_state { + RING_STATE_UINT = 0, + RING_STATE_IDLE, + RING_STATE_PENDING, +}; + +enum mhi_dev_ring_type { + RING_TYPE_CMD = 0, + RING_TYPE_ER, + RING_TYPE_CH, + RING_TYPE_INVAL +}; + +/* Event context interrupt moderation */ +enum mhi_dev_evt_ctx_int_mod_timer { + MHI_DEV_EVT_INT_MODERATION_DISABLED +}; + +/* Event ring type */ +enum mhi_dev_evt_ctx_event_ring_type { + MHI_DEV_EVT_TYPE_DEFAULT, + MHI_DEV_EVT_TYPE_VALID, + MHI_DEV_EVT_RESERVED +}; + +/* Event ring context type */ +struct mhi_dev_ev_ctx { + uint32_t res1:16; + enum mhi_dev_evt_ctx_int_mod_timer intmodt:16; + enum mhi_dev_evt_ctx_event_ring_type ertype; + uint32_t msivec; + uint64_t rbase; + uint64_t rlen; + uint64_t rp; + uint64_t wp; +} __packed; + +/* Command context */ +struct mhi_dev_cmd_ctx { + uint32_t res1; + uint32_t res2; + uint32_t res3; + uint64_t rbase; + uint64_t rlen; + uint64_t rp; + uint64_t wp; +} __packed; + +/* generic context */ +struct mhi_dev_gen_ctx { + uint32_t res1; + uint32_t res2; + uint32_t res3; + uint64_t rbase; + uint64_t rlen; + uint64_t rp; + uint64_t wp; +} __packed; + +/* Transfer ring element */ +struct mhi_dev_transfer_ring_element { + uint64_t data_buf_ptr; + uint32_t len:16; + uint32_t res1:16; + uint32_t chain:1; + uint32_t res2:7; + uint32_t ieob:1; + uint32_t ieot:1; + uint32_t bei:1; + uint32_t res3:5; + enum mhi_dev_ring_element_type_id type:8; + uint32_t res4:8; +} __packed; + +/* Command ring element */ +/* Command ring No op command */ +struct mhi_dev_cmd_ring_op { + uint64_t res1; + uint32_t res2; + uint32_t res3:16; + enum mhi_dev_ring_element_type_id type:8; + uint32_t chid:8; +} __packed; + +/* Command ring reset channel command */ +struct mhi_dev_cmd_ring_reset_channel_cmd { + uint64_t res1; + uint32_t res2; + uint32_t res3:16; + enum mhi_dev_ring_element_type_id type:8; + uint32_t chid:8; +} __packed; + +/* Command ring stop channel command */ +struct mhi_dev_cmd_ring_stop_channel_cmd { + uint64_t res1; + uint32_t res2; + uint32_t res3:16; + enum mhi_dev_ring_element_type_id type:8; + uint32_t chid:8; +} __packed; + +/* Command ring start channel command */ +struct mhi_dev_cmd_ring_start_channel_cmd { + uint64_t res1; + uint32_t seqnum; + uint32_t reliable:1; + uint32_t res2:15; + enum mhi_dev_ring_element_type_id type:8; + uint32_t chid:8; +} __packed; + +enum mhi_dev_cmd_completion_code { + MHI_CMD_COMPL_CODE_INVALID = 0, + MHI_CMD_COMPL_CODE_SUCCESS = 1, + MHI_CMD_COMPL_CODE_EOT = 2, + MHI_CMD_COMPL_CODE_OVERFLOW = 3, + MHI_CMD_COMPL_CODE_EOB = 4, + MHI_CMD_COMPL_CODE_UNDEFINED = 16, + MHI_CMD_COMPL_CODE_RING_EL = 17, + MHI_CMD_COMPL_CODE_RES +}; + +/* Event ring elements */ +/* Transfer completion event */ +struct mhi_dev_event_ring_transfer_completion { + uint64_t ptr; + uint32_t len:16; + uint32_t res1:8; + enum mhi_dev_cmd_completion_code code:8; + uint32_t res2:16; + enum mhi_dev_ring_element_type_id type:8; + uint32_t chid:8; +} __packed; + +/* Command completion event */ +struct mhi_dev_event_ring_cmd_completion { + uint64_t ptr; + uint32_t res1:24; + enum mhi_dev_cmd_completion_code code:8; + uint32_t res2:16; + enum mhi_dev_ring_element_type_id type:8; + uint32_t res3:8; +} __packed; + +enum mhi_dev_state { + MHI_DEV_RESET_STATE = 0, + MHI_DEV_READY_STATE, + MHI_DEV_M0_STATE, + MHI_DEV_M1_STATE, + MHI_DEV_M2_STATE, + MHI_DEV_M3_STATE, + MHI_DEV_MAX_STATE, + MHI_DEV_SYSERR_STATE = 0xff +}; + +/* MHI state change event */ +struct mhi_dev_event_ring_state_change { + uint64_t ptr; + uint32_t res1:24; + enum mhi_dev_state mhistate:8; + uint32_t res2:16; + enum mhi_dev_ring_element_type_id type:8; + uint32_t res3:8; +} __packed; + +enum mhi_dev_execenv { + MHI_DEV_SBL_EE = 1, + MHI_DEV_AMSS_EE = 2, + MHI_DEV_UNRESERVED +}; + +/* EE state change event */ +struct mhi_dev_event_ring_ee_state_change { + uint64_t ptr; + uint32_t res1:24; + enum mhi_dev_execenv execenv:8; + uint32_t res2:16; + enum mhi_dev_ring_element_type_id type:8; + uint32_t res3:8; +} __packed; + +/* Generic cmd to parse common details like type and channel id */ +struct mhi_dev_ring_generic { + uint64_t ptr; + uint32_t res1:24; + enum mhi_dev_state mhistate:8; + uint32_t res2:16; + enum mhi_dev_ring_element_type_id type:8; + uint32_t chid:8; +} __packed; + +struct mhi_config { + uint32_t mhi_reg_len; + uint32_t version; + uint32_t event_rings; + uint32_t channels; + uint32_t chdb_offset; + uint32_t erdb_offset; +}; + +#define NUM_CHANNELS 128 +#define HW_CHANNEL_BASE 100 +#define HW_CHANNEL_END 107 +#define MHI_ENV_VALUE 2 +#define MHI_MASK_ROWS_CH_EV_DB 4 +#define TRB_MAX_DATA_SIZE 8192 +#define MHI_CTRL_STATE 25 +#define IPA_DMA_SYNC 1 +#define IPA_DMA_ASYNC 0 + +/*maximum trasnfer completion events buffer*/ +#define MAX_TR_EVENTS 50 +/*maximum event requests */ +#define MHI_MAX_EVT_REQ 50 + +/* Possible ring element types */ +union mhi_dev_ring_element_type { + struct mhi_dev_cmd_ring_op cmd_no_op; + struct mhi_dev_cmd_ring_reset_channel_cmd cmd_reset; + struct mhi_dev_cmd_ring_stop_channel_cmd cmd_stop; + struct mhi_dev_cmd_ring_start_channel_cmd cmd_start; + struct mhi_dev_transfer_ring_element tre; + struct mhi_dev_event_ring_transfer_completion evt_tr_comp; + struct mhi_dev_event_ring_cmd_completion evt_cmd_comp; + struct mhi_dev_event_ring_state_change evt_state_change; + struct mhi_dev_event_ring_ee_state_change evt_ee_state; + struct mhi_dev_ring_generic generic; +}; + +/* Transfer ring element type */ +union mhi_dev_ring_ctx { + struct mhi_dev_cmd_ctx cmd; + struct mhi_dev_ev_ctx ev; + struct mhi_dev_ch_ctx ch; + struct mhi_dev_gen_ctx generic; +}; + +/* MHI host Control and data address region */ +struct mhi_host_addr { + uint32_t ctrl_base_lsb; + uint32_t ctrl_base_msb; + uint32_t ctrl_limit_lsb; + uint32_t ctrl_limit_msb; + uint32_t data_base_lsb; + uint32_t data_base_msb; + uint32_t data_limit_lsb; + uint32_t data_limit_msb; +}; + +/* MHI physical and virtual address region */ +struct mhi_meminfo { + struct device *dev; + uintptr_t pa_aligned; + uintptr_t pa_unaligned; + uintptr_t va_aligned; + uintptr_t va_unaligned; + uintptr_t size; +}; + +struct mhi_addr { + uint64_t host_pa; + uintptr_t device_pa; + uintptr_t device_va; + size_t size; + dma_addr_t phy_addr; + void *virt_addr; + bool use_ipa_dma; +}; + +struct mhi_interrupt_state { + uint32_t mask; + uint32_t status; +}; + +enum mhi_dev_channel_state { + MHI_DEV_CH_UNINT, + MHI_DEV_CH_STARTED, + MHI_DEV_CH_PENDING_START, + MHI_DEV_CH_PENDING_STOP, + MHI_DEV_CH_STOPPED, + MHI_DEV_CH_CLOSED, +}; + +enum mhi_dev_ch_operation { + MHI_DEV_OPEN_CH, + MHI_DEV_CLOSE_CH, + MHI_DEV_READ_CH, + MHI_DEV_READ_WR, + MHI_DEV_POLL, +}; + +enum mhi_ctrl_info { + MHI_STATE_CONFIGURED = 0, + MHI_STATE_CONNECTED = 1, + MHI_STATE_DISCONNECTED = 2, + MHI_STATE_INVAL, +}; + +enum mhi_dev_tr_compl_evt_type { + SEND_EVENT_BUFFER, + SEND_EVENT_RD_OFFSET, +}; + +enum mhi_dev_transfer_type { + MHI_DEV_DMA_SYNC, + MHI_DEV_DMA_ASYNC, +}; + +#if 0 +/* SW channel client list */ +enum mhi_client_channel { + MHI_CLIENT_LOOPBACK_OUT = 0, + MHI_CLIENT_LOOPBACK_IN = 1, + MHI_CLIENT_SAHARA_OUT = 2, + MHI_CLIENT_SAHARA_IN = 3, + MHI_CLIENT_DIAG_OUT = 4, + MHI_CLIENT_DIAG_IN = 5, + MHI_CLIENT_SSR_OUT = 6, + MHI_CLIENT_SSR_IN = 7, + MHI_CLIENT_QDSS_OUT = 8, + MHI_CLIENT_QDSS_IN = 9, + MHI_CLIENT_EFS_OUT = 10, + MHI_CLIENT_EFS_IN = 11, + MHI_CLIENT_MBIM_OUT = 12, + MHI_CLIENT_MBIM_IN = 13, + MHI_CLIENT_QMI_OUT = 14, + MHI_CLIENT_QMI_IN = 15, + MHI_CLIENT_IP_CTRL_0_OUT = 16, + MHI_CLIENT_IP_CTRL_0_IN = 17, + MHI_CLIENT_IP_CTRL_1_OUT = 18, + MHI_CLIENT_IP_CTRL_1_IN = 19, + MHI_CLIENT_DCI_OUT = 20, + MHI_CLIENT_DCI_IN = 21, + MHI_CLIENT_IP_CTRL_3_OUT = 22, + MHI_CLIENT_IP_CTRL_3_IN = 23, + MHI_CLIENT_IP_CTRL_4_OUT = 24, + MHI_CLIENT_IP_CTRL_4_IN = 25, + MHI_CLIENT_IP_CTRL_5_OUT = 26, + MHI_CLIENT_IP_CTRL_5_IN = 27, + MHI_CLIENT_IP_CTRL_6_OUT = 28, + MHI_CLIENT_IP_CTRL_6_IN = 29, + MHI_CLIENT_IP_CTRL_7_OUT = 30, + MHI_CLIENT_IP_CTRL_7_IN = 31, + MHI_CLIENT_DUN_OUT = 32, + MHI_CLIENT_DUN_IN = 33, + MHI_CLIENT_IP_SW_0_OUT = 34, + MHI_CLIENT_IP_SW_0_IN = 35, + MHI_CLIENT_IP_SW_1_OUT = 36, + MHI_CLIENT_IP_SW_1_IN = 37, + MHI_CLIENT_IP_SW_2_OUT = 38, + MHI_CLIENT_IP_SW_2_IN = 39, + MHI_CLIENT_IP_SW_3_OUT = 40, + MHI_CLIENT_IP_SW_3_IN = 41, + MHI_CLIENT_CSVT_OUT = 42, + MHI_CLIENT_CSVT_IN = 43, + MHI_CLIENT_SMCT_OUT = 44, + MHI_CLIENT_SMCT_IN = 45, + MHI_CLIENT_IP_SW_4_OUT = 46, + MHI_CLIENT_IP_SW_4_IN = 47, + MHI_MAX_SOFTWARE_CHANNELS = 48, + MHI_CLIENT_TEST_OUT = 60, + MHI_CLIENT_TEST_IN = 61, + MHI_CLIENT_RESERVED_1_LOWER = 62, + MHI_CLIENT_RESERVED_1_UPPER = 99, + MHI_CLIENT_IP_HW_0_OUT = 100, + MHI_CLIENT_IP_HW_0_IN = 101, + MHI_CLIENT_RESERVED_2_LOWER = 102, + MHI_CLIENT_RESERVED_2_UPPER = 127, + MHI_MAX_CHANNELS = 102, +}; +#endif +#endif /* _SDX20_MHI_H_ */ diff --git a/package/wwan/quectel_MHI/src/devices/mhi_netdev_quectel.c b/package/wwan/quectel_MHI/src/devices/mhi_netdev_quectel.c index 34c8befee..f38742ffb 100644 --- a/package/wwan/quectel_MHI/src/devices/mhi_netdev_quectel.c +++ b/package/wwan/quectel_MHI/src/devices/mhi_netdev_quectel.c @@ -206,6 +206,8 @@ static void qmap_hex_dump(const char *tag, unsigned char *data, unsigned len) { } #endif +#define MBIM_MUX_ID_SDX7X 112 //sdx7x is 112-126, others is 0-14 + static uint __read_mostly mhi_mbim_enabled = 0; module_param(mhi_mbim_enabled, uint, S_IRUGO); int mhi_netdev_mbin_enabled(void) { return mhi_mbim_enabled; } @@ -341,6 +343,7 @@ struct mhi_netdev { #endif MHI_MBIM_CTX mbim_ctx; + u32 mbim_mux_id; u32 mru; u32 max_mtu; @@ -652,7 +655,7 @@ static struct sk_buff * add_mbim_hdr(struct sk_buff *skb, u8 mux_id) { struct mhi_mbim_hdr *mhdr; __le32 sign; u8 *c; - u16 tci = mux_id - QUECTEL_QMAP_MUX_ID; + u16 tci = mux_id; unsigned int skb_len = skb->len; if (qmap_mode > 1) @@ -1305,12 +1308,12 @@ static void rmnet_mbim_rx_handler(void *dev, struct sk_buff *skb_in) goto error; } - if ((qmap_mode == 1 && tci != 0) || (qmap_mode > 1 && tci > qmap_mode)) { + if ((qmap_mode == 1 && tci != mhi_netdev->mbim_mux_id) || (qmap_mode > 1 && (tci - mhi_netdev->mbim_mux_id) > qmap_mode)){ MSG_ERR("unsupported tci %d by now\n", tci); goto error; } tci = abs(tci); - qmap_net = pQmapDev->mpQmapNetDev[qmap_mode == 1 ? 0 : tci - 1]; + qmap_net = pQmapDev->mpQmapNetDev[qmap_mode == 1 ? 0 : tci - 1 - mhi_netdev->mbim_mux_id]; dpe16 = ndp16->dpe16; @@ -2389,7 +2392,7 @@ static netdev_tx_t mhi_netdev_xmit(struct sk_buff *skb, struct net_device *dev) } if (mhi_netdev->net_type == MHI_NET_MBIM) { - if (add_mbim_hdr(skb, QUECTEL_QMAP_MUX_ID) == NULL) { + if (add_mbim_hdr(skb, mhi_netdev->mbim_mux_id) == NULL) { dev_kfree_skb_any (skb); return NETDEV_TX_OK; } @@ -2587,8 +2590,8 @@ static void mhi_netdev_get_drvinfo (struct net_device *ndev, struct ethtool_drvi { //struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev); - strlcpy (info->driver, "pcie_mhi", sizeof info->driver); - strlcpy (info->version, PCIE_MHI_DRIVER_VERSION, sizeof info->version); + strscpy (info->driver, "pcie_mhi", sizeof info->driver); + strscpy (info->version, PCIE_MHI_DRIVER_VERSION, sizeof info->version); } static const struct ethtool_ops mhi_netdev_ethtool_ops = { @@ -3162,10 +3165,12 @@ static void mhi_netdev_remove(struct mhi_device *mhi_dev) { struct mhi_netdev *mhi_netdev = mhi_device_get_devdata(mhi_dev); struct sk_buff *skb; - unsigned i; MSG_LOG("Remove notification received\n"); +#ifndef MHI_NETDEV_ONE_CARD_MODE +#ifndef CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY + unsigned i; write_lock_irq(&mhi_netdev->pm_lock); mhi_netdev->enabled = false; write_unlock_irq(&mhi_netdev->pm_lock); @@ -3183,7 +3188,8 @@ static void mhi_netdev_remove(struct mhi_device *mhi_dev) && rtnl_dereference(mhi_netdev->ndev->rx_handler) == rmnet_rx_handler) netdev_rx_handler_unregister(mhi_netdev->ndev); rtnl_unlock(); - +#endif +#endif while ((skb = skb_dequeue (&mhi_netdev->skb_chain))) dev_kfree_skb_any(skb); while ((skb = skb_dequeue (&mhi_netdev->qmap_chain))) @@ -3274,6 +3280,7 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev, || (mhi_dev->vendor == 0x1eac && mhi_dev->dev_id == 0x1004) || (mhi_dev->vendor == 0x17cb && mhi_dev->dev_id == 0x011a) || (mhi_dev->vendor == 0x1eac && mhi_dev->dev_id == 0x100b) + || (mhi_dev->vendor == 0x17cb && mhi_dev->dev_id == 0x0309) ) { mhi_netdev->qmap_version = 9; } @@ -3282,6 +3289,11 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev, mhi_netdev->qmap_version = 0; mhi_netdev->use_rmnet_usb = 0; } + + mhi_netdev->mbim_mux_id = 0; + if (mhi_dev->vendor == 0x17cb && mhi_dev->dev_id == 0x0309) { + mhi_netdev->mbim_mux_id = MBIM_MUX_ID_SDX7X; + } rmnet_info_set(mhi_netdev, &mhi_netdev->rmnet_info); mhi_netdev->rx_queue = mhi_netdev_alloc_skb; @@ -3314,8 +3326,12 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev, mhi_netdev->mpQmapNetDev[0] = mhi_netdev->ndev; strcpy(mhi_netdev->rmnet_info.ifname[0], mhi_netdev->mpQmapNetDev[0]->name); mhi_netdev->rmnet_info.mux_id[0] = QUECTEL_QMAP_MUX_ID; + if (mhi_mbim_enabled) { + mhi_netdev->rmnet_info.mux_id[0] = mhi_netdev->mbim_mux_id; + } } -#endif +#else + #ifdef CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY else if (1) { BUG_ON(mhi_netdev->net_type != MHI_NET_RMNET); @@ -3327,7 +3343,10 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev, #endif else if (mhi_netdev->use_rmnet_usb) { for (i = 0; i < mhi_netdev->qmap_mode; i++) { - u8 mux_id = QUECTEL_QMAP_MUX_ID+i; + u8 mux_id = QUECTEL_QMAP_MUX_ID + i; + if (mhi_mbim_enabled) { + mux_id = mhi_netdev->mbim_mux_id + i; + } mhi_netdev->mpQmapNetDev[i] = rmnet_vnd_register_device(mhi_netdev, i, mux_id); if (mhi_netdev->mpQmapNetDev[i]) { strcpy(mhi_netdev->rmnet_info.ifname[i], mhi_netdev->mpQmapNetDev[i]->name); @@ -3344,6 +3363,7 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev, #if defined(CONFIG_IPQ5018_RATE_CONTROL) mhi_netdev->mhi_rate_control = 1; +#endif #endif return 0; diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/Kconfig b/package/wwan/quectel_MHI/src/devices/rmnet/Kconfig deleted file mode 100644 index 9bb06d284..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -# -# RMNET MAP driver -# - -menuconfig RMNET - tristate "RmNet MAP driver" - default n - select GRO_CELLS - ---help--- - If you select this, you will enable the RMNET module which is used - for handling data in the multiplexing and aggregation protocol (MAP) - format in the embedded data path. RMNET devices can be attached to - any IP mode physical device. diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/Makefile b/package/wwan/quectel_MHI/src/devices/rmnet/Makefile deleted file mode 100644 index b175fbb7f..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# -# Makefile for the RMNET module -# - -rmnet-y := rmnet_config.o -rmnet-y += rmnet_vnd.o -rmnet-y += rmnet_handlers.o -rmnet-y += rmnet_map_data.o -rmnet-y += rmnet_map_command.o -rmnet-y += rmnet_descriptor.o -obj-$(CONFIG_RMNET) += rmnet.o diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_config.c b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_config.c deleted file mode 100644 index c5ec0c892..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_config.c +++ /dev/null @@ -1,141 +0,0 @@ -/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * RMNET configuration engine - * - */ - -#include <net/sock.h> -#include <linux/module.h> -#include <linux/netlink.h> -#include <linux/netdevice.h> -#include <linux/hashtable.h> -#include "rmnet_config.h" -#include "rmnet_handlers.h" -#include "rmnet_vnd.h" -#include "rmnet_private.h" -#include "rmnet_map.h" -#include "rmnet_descriptor.h" - -/* Locking scheme - - * The shared resource which needs to be protected is realdev->rx_handler_data. - * For the writer path, this is using rtnl_lock(). The writer paths are - * rmnet_newlink(), rmnet_dellink() and rmnet_force_unassociate_device(). These - * paths are already called with rtnl_lock() acquired in. There is also an - * ASSERT_RTNL() to ensure that we are calling with rtnl acquired. For - * dereference here, we will need to use rtnl_dereference(). Dev list writing - * needs to happen with rtnl_lock() acquired for netdev_master_upper_dev_link(). - * For the reader path, the real_dev->rx_handler_data is called in the TX / RX - * path. We only need rcu_read_lock() for these scenarios. In these cases, - * the rcu_read_lock() is held in __dev_queue_xmit() and - * netif_receive_skb_internal(), so readers need to use rcu_dereference_rtnl() - * to get the relevant information. For dev list reading, we again acquire - * rcu_read_lock() in rmnet_dellink() for netdev_master_upper_dev_get_rcu(). - * We also use unregister_netdevice_many() to free all rmnet devices in - * rmnet_force_unassociate_device() so we dont lose the rtnl_lock() and free in - * same context. - */ - -/* Local Definitions and Declarations */ - -static int rmnet_is_real_dev_registered(const struct net_device *real_dev) -{ - return rcu_access_pointer(real_dev->rx_handler) == rmnet_rx_handler; -} - -/* Needs rtnl lock */ -static struct rmnet_port* -rmnet_get_port_rtnl(const struct net_device *real_dev) -{ - return rtnl_dereference(real_dev->rx_handler_data); -} - -static int rmnet_unregister_real_device(struct net_device *real_dev, - struct rmnet_port *port) -{ - if (port->nr_rmnet_devs) - return -EINVAL; - - rmnet_map_cmd_exit(port); - rmnet_map_tx_aggregate_exit(port); - - rmnet_descriptor_deinit(port); - - kfree(port); - - netdev_rx_handler_unregister(real_dev); - - /* release reference on real_dev */ - dev_put(real_dev); - - netdev_dbg(real_dev, "Removed from rmnet\n"); - return 0; -} - -static int rmnet_register_real_device(struct net_device *real_dev) -{ - struct rmnet_port *port; - int rc, entry; - - ASSERT_RTNL(); - - if (rmnet_is_real_dev_registered(real_dev)) - return 0; - - port = kzalloc(sizeof(*port), GFP_ATOMIC); - if (!port) - return -ENOMEM; - - port->dev = real_dev; - rc = netdev_rx_handler_register(real_dev, rmnet_rx_handler, port); - if (rc) { - kfree(port); - return -EBUSY; - } - /* hold on to real dev for MAP data */ - dev_hold(real_dev); - - for (entry = 0; entry < RMNET_MAX_LOGICAL_EP; entry++) - INIT_HLIST_HEAD(&port->muxed_ep[entry]); - - rc = rmnet_descriptor_init(port); - if (rc) { - rmnet_descriptor_deinit(port); - return rc; - } - - rmnet_map_tx_aggregate_init(port); - rmnet_map_cmd_init(port); - - netdev_dbg(real_dev, "registered with rmnet\n"); - return 0; -} - -/* Needs either rcu_read_lock() or rtnl lock */ -static struct rmnet_port *rmnet_get_port(struct net_device *real_dev) -{ - if (rmnet_is_real_dev_registered(real_dev)) - return rcu_dereference_rtnl(real_dev->rx_handler_data); - else - return NULL; -} - -static struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id) -{ - struct rmnet_endpoint *ep; - - hlist_for_each_entry_rcu(ep, &port->muxed_ep[mux_id], hlnode) { - if (ep->mux_id == mux_id) - return ep; - } - - return NULL; -} diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_config.h b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_config.h deleted file mode 100644 index c74fcdf21..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_config.h +++ /dev/null @@ -1,174 +0,0 @@ -/* Copyright (c) 2013-2017, 2019 The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * RMNET Data configuration engine - * - */ - -#include <linux/skbuff.h> -#include <net/gro_cells.h> - -#ifndef _RMNET_CONFIG_H_ -#define _RMNET_CONFIG_H_ - -#define RMNET_MAX_LOGICAL_EP 255 -#define RMNET_MAX_VEID 4 - -struct rmnet_endpoint { - u8 mux_id; - struct net_device *egress_dev; - struct hlist_node hlnode; -}; - -struct rmnet_port_priv_stats { - u64 dl_hdr_last_qmap_vers; - u64 dl_hdr_last_ep_id; - u64 dl_hdr_last_trans_id; - u64 dl_hdr_last_seq; - u64 dl_hdr_last_bytes; - u64 dl_hdr_last_pkts; - u64 dl_hdr_last_flows; - u64 dl_hdr_count; - u64 dl_hdr_total_bytes; - u64 dl_hdr_total_pkts; - u64 dl_trl_last_seq; - u64 dl_trl_count; -}; - -struct rmnet_egress_agg_params { - u16 agg_size; - u16 agg_count; - u32 agg_time; -}; - -/* One instance of this structure is instantiated for each real_dev associated - * with rmnet. - */ -struct rmnet_port { - struct net_device *dev; - u32 data_format; - u8 nr_rmnet_devs; - u8 rmnet_mode; - struct hlist_head muxed_ep[RMNET_MAX_LOGICAL_EP]; - struct net_device *bridge_ep; - void *rmnet_perf; - - struct rmnet_egress_agg_params egress_agg_params; - - /* Protect aggregation related elements */ - spinlock_t agg_lock; - - struct sk_buff *agg_skb; - int agg_state; - u8 agg_count; - struct timespec agg_time; - struct timespec agg_last; - struct hrtimer hrtimer; - struct work_struct agg_wq; - - /* dl marker elements */ - struct list_head dl_list; - struct rmnet_port_priv_stats stats; - int dl_marker_flush; - - /* Descriptor pool */ - spinlock_t desc_pool_lock; - struct rmnet_frag_descriptor_pool *frag_desc_pool; - struct sk_buff *chain_head; - struct sk_buff *chain_tail; -}; - -extern struct rtnl_link_ops rmnet_link_ops; - -struct rmnet_vnd_stats { - u64 rx_pkts; - u64 rx_bytes; - u64 tx_pkts; - u64 tx_bytes; - u32 tx_drops; -}; - -struct rmnet_pcpu_stats { - struct rmnet_vnd_stats stats; - struct u64_stats_sync syncp; -}; - -struct rmnet_coal_close_stats { - u64 non_coal; - u64 ip_miss; - u64 trans_miss; - u64 hw_nl; - u64 hw_pkt; - u64 hw_byte; - u64 hw_time; - u64 hw_evict; - u64 coal; -}; - -struct rmnet_coal_stats { - u64 coal_rx; - u64 coal_pkts; - u64 coal_hdr_nlo_err; - u64 coal_hdr_pkt_err; - u64 coal_csum_err; - u64 coal_reconstruct; - u64 coal_ip_invalid; - u64 coal_trans_invalid; - struct rmnet_coal_close_stats close; - u64 coal_veid[RMNET_MAX_VEID]; -}; - -struct rmnet_priv_stats { - u64 csum_ok; - u64 csum_valid_unset; - u64 csum_validation_failed; - u64 csum_err_bad_buffer; - u64 csum_err_invalid_ip_version; - u64 csum_err_invalid_transport; - u64 csum_fragmented_pkt; - u64 csum_skipped; - u64 csum_sw; - u64 csum_hw; - struct rmnet_coal_stats coal; -}; - -struct rmnet_priv { - u8 mux_id; - struct net_device *real_dev; - struct rmnet_pcpu_stats __percpu *pcpu_stats; - struct gro_cells gro_cells; - struct rmnet_priv_stats stats; -}; - -enum rmnet_dl_marker_prio { - RMNET_PERF, - RMNET_SHS, -}; - -enum rmnet_trace_func { - RMNET_MODULE, - NW_STACK_MODULE, -}; - -enum rmnet_trace_evt { - RMNET_DLVR_SKB, - RMNET_RCV_FROM_PND, - RMNET_TX_UL_PKT, - NW_STACK_DEV_Q_XMIT, - NW_STACK_NAPI_GRO_FLUSH, - NW_STACK_RX, - NW_STACK_TX, -}; - -static int rmnet_is_real_dev_registered(const struct net_device *real_dev); -static struct rmnet_port *rmnet_get_port(struct net_device *real_dev); -static struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id); -#endif /* _RMNET_CONFIG_H_ */ diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_data.c b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_data.c deleted file mode 100644 index ad8953c30..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_data.c +++ /dev/null @@ -1,1150 +0,0 @@ -#if 0 - -#define RMNET_MAX_PACKET_SIZE 16384 -#define RMNET_DFLT_PACKET_SIZE 1500 -#define RMNET_NEEDED_HEADROOM 16 -#define RMNET_TX_QUEUE_LEN 1000 - -#define RMNET_MAX_LOGICAL_EP 255 -#define RMNET_MAP_DESC_HEADROOM 128 -#define RMNET_FRAG_DESCRIPTOR_POOL_SIZE 64 - -/* Pass the frame up the stack with no modifications to skb->dev */ -#define RMNET_EPMODE_NONE (0) -/* Replace skb->dev to a virtual rmnet device and pass up the stack */ -#define RMNET_EPMODE_VND (1) -/* Pass the frame directly to another device with dev_queue_xmit() */ -#define RMNET_EPMODE_BRIDGE (2) - -/* rmnet section */ - -#define RMNET_FLAGS_INGRESS_DEAGGREGATION (1U << 0) -#define RMNET_FLAGS_INGRESS_MAP_COMMANDS (1U << 1) -#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4 (1U << 2) -#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4 (1U << 3) -#define RMNET_FLAGS_INGRESS_COALESCE (1U << 4) -#define RMNET_FLAGS_INGRESS_MAP_CKSUMV5 (1U << 5) -#define RMNET_FLAGS_EGRESS_MAP_CKSUMV5 (1U << 6) - -enum rmnet_map_v5_header_type { - RMNET_MAP_HEADER_TYPE_UNKNOWN, - RMNET_MAP_HEADER_TYPE_COALESCING = 0x1, - RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD = 0x2, - RMNET_MAP_HEADER_TYPE_ENUM_LENGTH -}; - -/* Main QMAP header */ -struct rmnet_map_header { - u8 pad_len:6; - u8 next_hdr:1; - u8 cd_bit:1; - u8 mux_id; - __be16 pkt_len; -} __aligned(1); - -/* QMAP v5 headers */ -struct rmnet_map_v5_csum_header { - u8 next_hdr:1; - u8 header_type:7; - u8 hw_reserved:7; - u8 csum_valid_required:1; - __be16 reserved; -} __aligned(1); - -struct rmnet_map_v5_nl_pair { - __be16 pkt_len; - u8 csum_error_bitmap; - u8 num_packets; -} __aligned(1); - -/* NLO: Number-length object */ -#define RMNET_MAP_V5_MAX_NLOS (6) -#define RMNET_MAP_V5_MAX_PACKETS (48) - -struct rmnet_map_v5_coal_header { - u8 next_hdr:1; - u8 header_type:7; - u8 reserved1:4; - u8 num_nlos:3; - u8 csum_valid:1; - u8 close_type:4; - u8 close_value:4; - u8 reserved2:4; - u8 virtual_channel_id:4; - - struct rmnet_map_v5_nl_pair nl_pairs[RMNET_MAP_V5_MAX_NLOS]; -} __aligned(1); - -/* QMAP v4 headers */ -struct rmnet_map_dl_csum_trailer { - u8 reserved1; - u8 valid:1; - u8 reserved2:7; - u16 csum_start_offset; - u16 csum_length; - __be16 csum_value; -} __aligned(1); - -struct rmnet_frag_descriptor_pool { - struct list_head free_list; - u32 pool_size; -}; - -struct rmnet_frag_descriptor { - struct list_head list; - struct list_head sub_frags; - skb_frag_t frag; - u8 *hdr_ptr; - struct net_device *dev; - u32 hash; - __be32 tcp_seq; - __be16 ip_id; - u16 data_offset; - u16 gso_size; - u16 gso_segs; - u16 ip_len; - u16 trans_len; - u8 ip_proto; - u8 trans_proto; - u8 pkt_id; - u8 csum_valid:1, - hdrs_valid:1, - ip_id_set:1, - tcp_seq_set:1, - flush_shs:1, - reserved:3; -}; - -struct rmnet_endpoint { - u8 rmnet_mode; - u8 mux_id; - struct net_device *rmnet_dev; -}; - -/* One instance of this structure is instantiated for each real_dev associated - * with rmnet. - */ -struct rmnet_port { - struct net_device *dev; - u8 rmnet_mode; - u32 data_format; - u32 nr_rmnet_devs; - struct rmnet_endpoint muxed_ep[16]; - - /* Descriptor pool */ - spinlock_t desc_pool_lock; - struct rmnet_frag_descriptor_pool *frag_desc_pool; - struct sk_buff *chain_head; - struct sk_buff *chain_tail; -}; - -static struct sk_buff * add_qhdr_v5(struct sk_buff *skb, u8 mux_id) -{ - struct rmnet_map_header *map_header; - struct rmnet_map_v5_csum_header *ul_header; - u32 padding, map_datalen; - - map_datalen = skb->len; - padding = map_datalen%4; - if (padding) { - padding = 4 - padding; - if (skb_tailroom(skb) < padding) { - printk("skb_tailroom small!\n"); - padding = 0; - } - if (padding) - __skb_put(skb, padding); - } - - map_header = (struct rmnet_map_header *)skb_push(skb, (sizeof(struct rmnet_map_header) + sizeof(struct rmnet_map_v5_csum_header))); - - BUILD_BUG_ON((sizeof(struct rmnet_map_header) + sizeof(struct rmnet_map_v5_csum_header)) != 8); - - map_header->cd_bit = 0; - map_header->next_hdr = 1; - map_header->pad_len = padding; - map_header->mux_id = mux_id; - map_header->pkt_len = htons(map_datalen + padding); - - ul_header = (struct rmnet_map_v5_csum_header *)(map_header + 1); - memset(ul_header, 0, sizeof(*ul_header)); - ul_header->header_type = RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD; - - return skb; -} - -struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id) -{ - return &port->muxed_ep[0]; -} - -static void -rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_port *port) -{ - struct rmnet_nss_cb *nss_cb; - - //rmnet_vnd_rx_fixup(skb->dev, skb->len); - - /* Pass off the packet to NSS driver if we can */ - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (nss_cb) { - if (!port->chain_head) - port->chain_head = skb; - else - skb_shinfo(port->chain_tail)->frag_list = skb; - - port->chain_tail = skb; - return; - } - - skb_reset_transport_header(skb); - skb_reset_network_header(skb); - - skb->pkt_type = PACKET_HOST; - skb_set_mac_header(skb, 0); - - //if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) { - //} else { - //if (!rmnet_check_skb_can_gro(skb)) - // gro_cells_receive(&priv->gro_cells, skb); - //else - netif_receive_skb(skb); - //} -} - -static inline unsigned char *rmnet_map_data_ptr(struct sk_buff *skb) -{ - /* Nonlinear packets we receive are entirely within frag 0 */ - if (skb_is_nonlinear(skb) && skb->len == skb->data_len) - return skb_frag_address(skb_shinfo(skb)->frags); - - return skb->data; -} - -static inline void *rmnet_frag_data_ptr(struct rmnet_frag_descriptor *frag_desc) -{ - return skb_frag_address(&frag_desc->frag); -} - -static struct rmnet_frag_descriptor * -rmnet_get_frag_descriptor(struct rmnet_port *port) -{ - struct rmnet_frag_descriptor_pool *pool = port->frag_desc_pool; - struct rmnet_frag_descriptor *frag_desc; - - spin_lock(&port->desc_pool_lock); - if (!list_empty(&pool->free_list)) { - frag_desc = list_first_entry(&pool->free_list, - struct rmnet_frag_descriptor, - list); - list_del_init(&frag_desc->list); - } else { - frag_desc = kzalloc(sizeof(*frag_desc), GFP_ATOMIC); - if (!frag_desc) - goto out; - - INIT_LIST_HEAD(&frag_desc->list); - INIT_LIST_HEAD(&frag_desc->sub_frags); - pool->pool_size++; - } - -out: - spin_unlock(&port->desc_pool_lock); - return frag_desc; -} - -static void rmnet_recycle_frag_descriptor(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port) -{ - struct rmnet_frag_descriptor_pool *pool = port->frag_desc_pool; - struct page *page = skb_frag_page(&frag_desc->frag); - - list_del(&frag_desc->list); - if (page) - put_page(page); - - memset(frag_desc, 0, sizeof(*frag_desc)); - INIT_LIST_HEAD(&frag_desc->list); - INIT_LIST_HEAD(&frag_desc->sub_frags); - spin_lock(&port->desc_pool_lock); - list_add_tail(&frag_desc->list, &pool->free_list); - spin_unlock(&port->desc_pool_lock); -} - -static inline void rmnet_frag_fill(struct rmnet_frag_descriptor *frag_desc, - struct page *p, u32 page_offset, u32 len) -{ - get_page(p); - __skb_frag_set_page(&frag_desc->frag, p); - skb_frag_size_set(&frag_desc->frag, len); - frag_desc->frag.page_offset = page_offset; -} - -static inline void *rmnet_frag_pull(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port, - unsigned int size) -{ - if (size >= skb_frag_size(&frag_desc->frag)) { - pr_info("%s(): Pulling %u bytes from %u byte pkt. Dropping\n", - __func__, size, skb_frag_size(&frag_desc->frag)); - rmnet_recycle_frag_descriptor(frag_desc, port); - return NULL; - } - - frag_desc->frag.page_offset += size; - skb_frag_size_sub(&frag_desc->frag, size); - - return rmnet_frag_data_ptr(frag_desc); -} - -static inline void *rmnet_frag_trim(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port, - unsigned int size) -{ - if (!size) { - pr_info("%s(): Trimming %u byte pkt to 0. Dropping\n", - __func__, skb_frag_size(&frag_desc->frag)); - rmnet_recycle_frag_descriptor(frag_desc, port); - return NULL; - } - - if (size < skb_frag_size(&frag_desc->frag)) - skb_frag_size_set(&frag_desc->frag, size); - - return rmnet_frag_data_ptr(frag_desc); -} - -static inline u8 -rmnet_frag_get_next_hdr_type(struct rmnet_frag_descriptor *frag_desc) -{ - unsigned char *data = rmnet_frag_data_ptr(frag_desc); - - data += sizeof(struct rmnet_map_header); - return ((struct rmnet_map_v5_coal_header *)data)->header_type; -} - -static inline bool -rmnet_frag_get_csum_valid(struct rmnet_frag_descriptor *frag_desc) -{ - unsigned char *data = rmnet_frag_data_ptr(frag_desc); - - data += sizeof(struct rmnet_map_header); - return ((struct rmnet_map_v5_csum_header *)data)->csum_valid_required; -} - -static void rmnet_descriptor_add_frag(struct rmnet_port *port, struct list_head *list, - struct page *p, u32 page_offset, u32 len) -{ - struct rmnet_frag_descriptor *frag_desc; - - frag_desc = rmnet_get_frag_descriptor(port); - if (!frag_desc) - return; - - rmnet_frag_fill(frag_desc, p, page_offset, len); - list_add_tail(&frag_desc->list, list); -} - -static void rmnet_frag_deaggregate(skb_frag_t *frag, struct rmnet_port *port, - struct list_head *list) -{ - struct rmnet_map_header *maph; - u8 *data = skb_frag_address(frag); - u32 offset = 0; - u32 packet_len; - - while (offset < skb_frag_size(frag)) { - maph = (struct rmnet_map_header *)data; - packet_len = ntohs(maph->pkt_len); - - /* Some hardware can send us empty frames. Catch them */ - if (packet_len == 0) - return; - - packet_len += sizeof(*maph); - - if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) { - packet_len += sizeof(struct rmnet_map_dl_csum_trailer); - WARN_ON(1); - } else if (port->data_format & - (RMNET_FLAGS_INGRESS_MAP_CKSUMV5 | - RMNET_FLAGS_INGRESS_COALESCE) && !maph->cd_bit) { - u32 hsize = 0; - u8 type; - - type = ((struct rmnet_map_v5_coal_header *) - (data + sizeof(*maph)))->header_type; - switch (type) { - case RMNET_MAP_HEADER_TYPE_COALESCING: - hsize = sizeof(struct rmnet_map_v5_coal_header); - break; - case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD: - hsize = sizeof(struct rmnet_map_v5_csum_header); - break; - } - - packet_len += hsize; - } - else { - qmap_hex_dump(__func__, data, 64); - WARN_ON(1); - } - - if ((int)skb_frag_size(frag) - (int)packet_len < 0) - return; - - rmnet_descriptor_add_frag(port, list, skb_frag_page(frag), - frag->page_offset + offset, - packet_len); - - offset += packet_len; - data += packet_len; - } -} - - -#define RMNET_IP_VERSION_4 0x40 -#define RMNET_IP_VERSION_6 0x60 - -/* Helper Functions */ - -static void rmnet_set_skb_proto(struct sk_buff *skb) -{ - switch (rmnet_map_data_ptr(skb)[0] & 0xF0) { - case RMNET_IP_VERSION_4: - skb->protocol = htons(ETH_P_IP); - break; - case RMNET_IP_VERSION_6: - skb->protocol = htons(ETH_P_IPV6); - break; - default: - skb->protocol = htons(ETH_P_MAP); - WARN_ON(1); - break; - } -} - -/* Allocate and populate an skb to contain the packet represented by the - * frag descriptor. - */ -static struct sk_buff *rmnet_alloc_skb(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port) -{ - struct sk_buff *head_skb, *current_skb, *skb; - struct skb_shared_info *shinfo; - struct rmnet_frag_descriptor *sub_frag, *tmp; - - /* Use the exact sizes if we know them (i.e. RSB/RSC, rmnet_perf) */ - if (frag_desc->hdrs_valid) { - u16 hdr_len = frag_desc->ip_len + frag_desc->trans_len; - - head_skb = alloc_skb(hdr_len + RMNET_MAP_DESC_HEADROOM, - GFP_ATOMIC); - if (!head_skb) - return NULL; - - skb_reserve(head_skb, RMNET_MAP_DESC_HEADROOM); - skb_put_data(head_skb, frag_desc->hdr_ptr, hdr_len); - skb_reset_network_header(head_skb); - - if (frag_desc->trans_len) - skb_set_transport_header(head_skb, frag_desc->ip_len); - - /* Packets that have no data portion don't need any frags */ - if (hdr_len == skb_frag_size(&frag_desc->frag)) - goto skip_frags; - - /* If the headers we added are the start of the page, - * we don't want to add them twice - */ - if (frag_desc->hdr_ptr == rmnet_frag_data_ptr(frag_desc)) { - if (!rmnet_frag_pull(frag_desc, port, hdr_len)) { - kfree_skb(head_skb); - return NULL; - } - } - } else { - /* Allocate enough space to avoid penalties in the stack - * from __pskb_pull_tail() - */ - head_skb = alloc_skb(256 + RMNET_MAP_DESC_HEADROOM, - GFP_ATOMIC); - if (!head_skb) - return NULL; - - skb_reserve(head_skb, RMNET_MAP_DESC_HEADROOM); - } - - /* Add main fragment */ - get_page(skb_frag_page(&frag_desc->frag)); - skb_add_rx_frag(head_skb, 0, skb_frag_page(&frag_desc->frag), - frag_desc->frag.page_offset, - skb_frag_size(&frag_desc->frag), - skb_frag_size(&frag_desc->frag)); - - shinfo = skb_shinfo(head_skb); - current_skb = head_skb; - - /* Add in any frags from rmnet_perf */ - list_for_each_entry_safe(sub_frag, tmp, &frag_desc->sub_frags, list) { - skb_frag_t *frag; - u32 frag_size; - - frag = &sub_frag->frag; - frag_size = skb_frag_size(frag); - -add_frag: - if (shinfo->nr_frags < MAX_SKB_FRAGS) { - get_page(skb_frag_page(frag)); - skb_add_rx_frag(current_skb, shinfo->nr_frags, - skb_frag_page(frag), frag->page_offset, - frag_size, frag_size); - if (current_skb != head_skb) { - head_skb->len += frag_size; - head_skb->data_len += frag_size; - } - } else { - /* Alloc a new skb and try again */ - skb = alloc_skb(0, GFP_ATOMIC); - if (!skb) - break; - - if (current_skb == head_skb) - shinfo->frag_list = skb; - else - current_skb->next = skb; - - current_skb = skb; - shinfo = skb_shinfo(current_skb); - goto add_frag; - } - - rmnet_recycle_frag_descriptor(sub_frag, port); - } - -skip_frags: - head_skb->dev = frag_desc->dev; - rmnet_set_skb_proto(head_skb); - - /* Handle any header metadata that needs to be updated after RSB/RSC - * segmentation - */ - if (frag_desc->ip_id_set) { - struct iphdr *iph; - - iph = (struct iphdr *)rmnet_map_data_ptr(head_skb); - csum_replace2(&iph->check, iph->id, frag_desc->ip_id); - iph->id = frag_desc->ip_id; - } - - if (frag_desc->tcp_seq_set) { - struct tcphdr *th; - - th = (struct tcphdr *) - (rmnet_map_data_ptr(head_skb) + frag_desc->ip_len); - th->seq = frag_desc->tcp_seq; - } - - /* Handle csum offloading */ - if (frag_desc->csum_valid && frag_desc->hdrs_valid) { - /* Set the partial checksum information */ - //rmnet_frag_partial_csum(head_skb, frag_desc); - WARN_ON(1); - } else if (frag_desc->csum_valid) { - /* Non-RSB/RSC/perf packet. The current checksum is fine */ - head_skb->ip_summed = CHECKSUM_UNNECESSARY; - } else if (frag_desc->hdrs_valid && - (frag_desc->trans_proto == IPPROTO_TCP || - frag_desc->trans_proto == IPPROTO_UDP)) { - /* Unfortunately, we have to fake a bad checksum here, since - * the original bad value is lost by the hardware. The only - * reliable way to do it is to calculate the actual checksum - * and corrupt it. - */ - __sum16 *check; - __wsum csum; - unsigned int offset = skb_transport_offset(head_skb); - __sum16 pseudo; - - WARN_ON(1); - /* Calculate pseudo header and update header fields */ - if (frag_desc->ip_proto == 4) { - struct iphdr *iph = ip_hdr(head_skb); - __be16 tot_len = htons(head_skb->len); - - csum_replace2(&iph->check, iph->tot_len, tot_len); - iph->tot_len = tot_len; - pseudo = ~csum_tcpudp_magic(iph->saddr, iph->daddr, - head_skb->len - - frag_desc->ip_len, - frag_desc->trans_proto, 0); - } else { - struct ipv6hdr *ip6h = ipv6_hdr(head_skb); - - ip6h->payload_len = htons(head_skb->len - - sizeof(*ip6h)); - pseudo = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, - head_skb->len - - frag_desc->ip_len, - frag_desc->trans_proto, 0); - } - - if (frag_desc->trans_proto == IPPROTO_TCP) { - check = &tcp_hdr(head_skb)->check; - } else { - udp_hdr(head_skb)->len = htons(head_skb->len - - frag_desc->ip_len); - check = &udp_hdr(head_skb)->check; - } - - *check = pseudo; - csum = skb_checksum(head_skb, offset, head_skb->len - offset, - 0); - /* Add 1 to corrupt. This cannot produce a final value of 0 - * since csum_fold() can't return a value of 0xFFFF - */ - *check = csum16_add(csum_fold(csum), htons(1)); - head_skb->ip_summed = CHECKSUM_NONE; - } - - /* Handle any rmnet_perf metadata */ - if (frag_desc->hash) { - head_skb->hash = frag_desc->hash; - head_skb->sw_hash = 1; - } - - if (frag_desc->flush_shs) - head_skb->cb[0] = 1; - - /* Handle coalesced packets */ - //if (frag_desc->gso_segs > 1) - // rmnet_frag_gso_stamp(head_skb, frag_desc); - - return head_skb; -} - -/* Deliver the packets contained within a frag descriptor */ -static void rmnet_frag_deliver(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port) -{ - struct sk_buff *skb; - - skb = rmnet_alloc_skb(frag_desc, port); - if (skb) - rmnet_deliver_skb(skb, port); - rmnet_recycle_frag_descriptor(frag_desc, port); -} - -/* Process a QMAPv5 packet header */ -static int rmnet_frag_process_next_hdr_packet(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port, - struct list_head *list, - u16 len) -{ - int rc = 0; - - switch (rmnet_frag_get_next_hdr_type(frag_desc)) { - case RMNET_MAP_HEADER_TYPE_COALESCING: - rc = -1; - WARN_ON(1); - break; - case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD: - if (rmnet_frag_get_csum_valid(frag_desc)) { - frag_desc->csum_valid = true; - } else { - } - - if (!rmnet_frag_pull(frag_desc, port, - sizeof(struct rmnet_map_header) + - sizeof(struct rmnet_map_v5_csum_header))) { - rc = -EINVAL; - break; - } - - frag_desc->hdr_ptr = rmnet_frag_data_ptr(frag_desc); - - /* Remove padding only for csum offload packets. - * Coalesced packets should never have padding. - */ - if (!rmnet_frag_trim(frag_desc, port, len)) { - rc = -EINVAL; - break; - } - - list_del_init(&frag_desc->list); - list_add_tail(&frag_desc->list, list); - break; - default: - qmap_hex_dump(__func__, rmnet_frag_data_ptr(frag_desc), 64); - rc = -EINVAL; - break; - } - - return rc; -} - -static void -__rmnet_frag_ingress_handler(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port) -{ - struct rmnet_map_header *qmap; - struct rmnet_endpoint *ep; - struct rmnet_frag_descriptor *frag, *tmp; - LIST_HEAD(segs); - u16 len, pad; - u8 mux_id; - - qmap = (struct rmnet_map_header *)skb_frag_address(&frag_desc->frag); - mux_id = qmap->mux_id; - pad = qmap->pad_len; - len = ntohs(qmap->pkt_len) - pad; - - if (qmap->cd_bit) { - goto recycle; - } - - if (mux_id >= RMNET_MAX_LOGICAL_EP) - goto recycle; - - ep = rmnet_get_endpoint(port, mux_id); - if (!ep) - goto recycle; - - frag_desc->dev = ep->rmnet_dev; - - /* Handle QMAPv5 packet */ - if (qmap->next_hdr && - (port->data_format & (RMNET_FLAGS_INGRESS_COALESCE | - RMNET_FLAGS_INGRESS_MAP_CKSUMV5))) { - if (rmnet_frag_process_next_hdr_packet(frag_desc, port, &segs, - len)) - goto recycle; - } else { - /* We only have the main QMAP header to worry about */ - if (!rmnet_frag_pull(frag_desc, port, sizeof(*qmap))) - return; - - frag_desc->hdr_ptr = rmnet_frag_data_ptr(frag_desc); - - if (!rmnet_frag_trim(frag_desc, port, len)) - return; - - list_add_tail(&frag_desc->list, &segs); - } - - list_for_each_entry_safe(frag, tmp, &segs, list) { - list_del_init(&frag->list); - rmnet_frag_deliver(frag, port); - } - return; - -recycle: - rmnet_recycle_frag_descriptor(frag_desc, port); -} - -static void rmnet_frag_ingress_handler(struct sk_buff *skb, - struct rmnet_port *port) -{ - LIST_HEAD(desc_list); - int i = 0; - struct rmnet_nss_cb *nss_cb; - - /* Deaggregation and freeing of HW originating - * buffers is done within here - */ - while (skb) { - struct sk_buff *skb_frag; - - port->chain_head = NULL; - port->chain_tail = NULL; - - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - rmnet_frag_deaggregate(&skb_shinfo(skb)->frags[i], port, - &desc_list); - if (!list_empty(&desc_list)) { - struct rmnet_frag_descriptor *frag_desc, *tmp; - - list_for_each_entry_safe(frag_desc, tmp, - &desc_list, list) { - list_del_init(&frag_desc->list); - __rmnet_frag_ingress_handler(frag_desc, - port); - } - } - } - - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (nss_cb && port->chain_head) { - port->chain_head->cb[0] = 0; - netif_receive_skb(port->chain_head); - } - - skb_frag = skb_shinfo(skb)->frag_list; - skb_shinfo(skb)->frag_list = NULL; - consume_skb(skb); - skb = skb_frag; - } -} - -static void -rmnet_map_ingress_handler(struct sk_buff *skb, - struct rmnet_port *port) -{ - if (port->data_format & (RMNET_FLAGS_INGRESS_COALESCE | - RMNET_FLAGS_INGRESS_MAP_CKSUMV5)) { - if (skb_is_nonlinear(skb)) { - rmnet_frag_ingress_handler(skb, port); - return; - } - } - - WARN_ON(1); -} - -static rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb); -static int rmnet_is_real_dev_registered(const struct net_device *real_dev) -{ - return rcu_access_pointer(real_dev->rx_handler) == rmnet_rx_handler; -} - - -/* Needs either rcu_read_lock() or rtnl lock */ -struct rmnet_port *rmnet_get_port(struct net_device *real_dev) -{ - if (rmnet_is_real_dev_registered(real_dev)) - return rcu_dereference_rtnl(real_dev->rx_handler_data); - else - return NULL; -} - -static rx_handler_result_t rmnet_rx_priv_handler(struct sk_buff **pskb) -{ - struct sk_buff *skb = *pskb; - struct rmnet_nss_cb *nss_cb; - - if (!skb) - return RX_HANDLER_CONSUMED; - if (nss_debug) printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - - if (skb->pkt_type == PACKET_LOOPBACK) - return RX_HANDLER_PASS; - - /* Check this so that we dont loop around netif_receive_skb */ - if (skb->cb[0] == 1) { - skb->cb[0] = 0; - - skb->dev->stats.rx_packets++; - return RX_HANDLER_PASS; - } - - while (skb) { - struct sk_buff *skb_frag = skb_shinfo(skb)->frag_list; - - skb_shinfo(skb)->frag_list = NULL; - - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (nss_cb) - nss_cb->nss_tx(skb); - - skb = skb_frag; - } - - return RX_HANDLER_CONSUMED; -} - -/* Ingress / Egress Entry Points */ - -/* Processes packet as per ingress data format for receiving device. Logical - * endpoint is determined from packet inspection. Packet is then sent to the - * egress device listed in the logical endpoint configuration. - */ -static rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) -{ - struct sk_buff *skb = *pskb; - struct rmnet_port *port; - struct net_device *dev; - - if (!skb) - goto done; - - if (nss_debug) printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - - if (skb->pkt_type == PACKET_LOOPBACK) - return RX_HANDLER_PASS; - - if (skb->protocol != htons(ETH_P_MAP)) { - WARN_ON(1); - return RX_HANDLER_PASS; - } - - dev = skb->dev; - port = rmnet_get_port(dev); - - if (port == NULL) - return RX_HANDLER_PASS; - - port->chain_head = NULL; - port->chain_tail = NULL; - - switch (port->rmnet_mode) { - case RMNET_EPMODE_VND: - rmnet_map_ingress_handler(skb, port); - break; - case RMNET_EPMODE_BRIDGE: - //rmnet_bridge_handler(skb, port->bridge_ep); - break; - } - -done: - return RX_HANDLER_CONSUMED; -} - -static void rmnet_descriptor_deinit(struct rmnet_port *port) -{ - struct rmnet_frag_descriptor_pool *pool; - struct rmnet_frag_descriptor *frag_desc, *tmp; - - pool = port->frag_desc_pool; - - list_for_each_entry_safe(frag_desc, tmp, &pool->free_list, list) { - kfree(frag_desc); - pool->pool_size--; - } - - kfree(pool); -} - -static int rmnet_descriptor_init(struct rmnet_port *port) -{ - struct rmnet_frag_descriptor_pool *pool; - int i; - - spin_lock_init(&port->desc_pool_lock); - pool = kzalloc(sizeof(*pool), GFP_ATOMIC); - if (!pool) - return -ENOMEM; - - INIT_LIST_HEAD(&pool->free_list); - port->frag_desc_pool = pool; - - for (i = 0; i < RMNET_FRAG_DESCRIPTOR_POOL_SIZE; i++) { - struct rmnet_frag_descriptor *frag_desc; - - frag_desc = kzalloc(sizeof(*frag_desc), GFP_ATOMIC); - if (!frag_desc) - return -ENOMEM; - - INIT_LIST_HEAD(&frag_desc->list); - INIT_LIST_HEAD(&frag_desc->sub_frags); - list_add_tail(&frag_desc->list, &pool->free_list); - pool->pool_size++; - } - - return 0; -} - -struct rmnet_priv { - //struct rmnet_endpoint local_ep; - struct net_device *real_dev; - u8 mux_id; -}; - -static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct rmnet_priv *priv; - - if (nss_debug) printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - - priv = netdev_priv(dev); - if (priv->real_dev) { - add_qhdr_v5(skb, priv->mux_id); - skb->protocol = htons(ETH_P_MAP); - skb->dev = priv->real_dev; - dev_queue_xmit(skb); - dev->stats.tx_packets++; - //rmnet_egress_handler(skb); - } else { - //this_cpu_inc(priv->pcpu_stats->stats.tx_drops); - kfree_skb(skb); - } - return NETDEV_TX_OK; -} - -static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu) -{ - if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE) - return -EINVAL; - - rmnet_dev->mtu = new_mtu; - return 0; -} - -static const struct net_device_ops rmnet_vnd_ops = { - .ndo_start_xmit = rmnet_vnd_start_xmit, - .ndo_change_mtu = rmnet_vnd_change_mtu, - //.ndo_get_iflink = rmnet_vnd_get_iflink, - //.ndo_add_slave = rmnet_add_bridge, - //.ndo_del_slave = rmnet_del_bridge, - //.ndo_init = rmnet_vnd_init, - //.ndo_uninit = rmnet_vnd_uninit, - //.ndo_get_stats64 = rmnet_get_stats64, -}; - -static void rmnet_vnd_setup(struct net_device *rmnet_dev) -{ - rmnet_dev->netdev_ops = &rmnet_vnd_ops; - rmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE; - rmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM; - random_ether_addr(rmnet_dev->dev_addr); - rmnet_dev->tx_queue_len = RMNET_TX_QUEUE_LEN; - - /* Raw IP mode */ - rmnet_dev->header_ops = NULL; /* No header */ - rmnet_dev->type = ARPHRD_RAWIP; - rmnet_dev->hard_header_len = 0; - rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); - - //rmnet_dev->needs_free_netdev = true; - - rmnet_dev->hw_features = NETIF_F_RXCSUM; - rmnet_dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; - //rmnet_dev->hw_features |= NETIF_F_SG; - //rmnet_dev->hw_features |= NETIF_F_GRO_HW; -} -#else -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <linux/if_arp.h> -#include <linux/device.h> -#include <linux/errno.h> -#include <linux/etherdevice.h> - -static uint nss_debug = 0; -module_param( nss_debug, uint, S_IRUGO | S_IWUSR); - -/* rmnet section */ - -#define RMNET_FLAGS_INGRESS_DEAGGREGATION (1U << 0) -#define RMNET_FLAGS_INGRESS_MAP_COMMANDS (1U << 1) -#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4 (1U << 2) -#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4 (1U << 3) -#define RMNET_FLAGS_INGRESS_COALESCE (1U << 4) -#define RMNET_FLAGS_INGRESS_MAP_CKSUMV5 (1U << 5) -#define RMNET_FLAGS_EGRESS_MAP_CKSUMV5 (1U << 6) - -#ifdef CONFIG_ARCH_IPQ807x -#define CONFIG_QCA_NSS_DRV -#endif -#ifdef CONFIG_QCA_NSS_DRV -#include "rmnet_nss.c" -#else -#include "rmnet_nss.h" -#endif - -#include "rmnet_vnd.c" -#include "rmnet_map_command.c" -#include "rmnet_map_data.c" -#include "rmnet_descriptor.c" -#include "rmnet_config.c" -#include "rmnet_handlers.c" - -struct rmnet_nss_cb *rmnet_nss_callbacks __rcu __read_mostly; - -void rmnet_data_init(struct net_device *real_dev, u32 nr_rmnet_devs) -{ - struct rmnet_port *port; - struct rmnet_endpoint *ep; - struct net_device *rmnet_dev = NULL; - u32 nr = 0; - struct rmnet_nss_cb *nss_cb; - int rc = 0; - - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (!nss_cb) - { -#ifdef CONFIG_QCA_NSS_DRV - pr_err("%s(): initializing rmnet_nss\n", __func__); - RCU_INIT_POINTER(rmnet_nss_callbacks, &rmnet_nss); -#endif - } - - rtnl_lock(); - rc = rmnet_register_real_device(real_dev); - rtnl_unlock(); - - if (rc) { - pr_err("%s rmnet_register_real_device = %d\n", __func__, rc); - return; - } - - port = rmnet_get_port_rtnl(real_dev); - port->data_format = RMNET_FLAGS_INGRESS_DEAGGREGATION - | RMNET_FLAGS_INGRESS_MAP_CKSUMV5 | RMNET_FLAGS_EGRESS_MAP_CKSUMV5; - port->rmnet_mode = RMNET_EPMODE_VND; - - for (nr = 0; nr < nr_rmnet_devs; nr++) { - u8 mux_id = 0x81+nr; - - ep = kzalloc(sizeof(*ep), GFP_ATOMIC); - - rtnl_lock(); - rmnet_dev = alloc_netdev(sizeof(struct rmnet_priv), - "rmnet_data%d", NET_NAME_PREDICTABLE, - rmnet_vnd_setup); - - rmnet_vnd_newlink(mux_id, rmnet_dev, port, real_dev, ep); - netdev_rx_handler_register(rmnet_dev, rmnet_rx_priv_handler, NULL); - rtnl_unlock(); - - hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]); - } - - port->nr_rmnet_devs = nr_rmnet_devs; -} - -void rmnet_data_deinit(struct net_device *real_dev, u32 nr_rmnet_devs) -{ - struct rmnet_port *port; - u32 nr = 0; - struct rmnet_nss_cb *nss_cb; - - if (!real_dev || !rmnet_is_real_dev_registered(real_dev)) - return; - - port = rmnet_get_port_rtnl(real_dev); - - for (nr = 0; nr < nr_rmnet_devs; nr++) { - struct rmnet_endpoint *ep; - u8 mux_id = 0x81+nr; - - ep = rmnet_get_endpoint(port, mux_id); - if (ep) { - hlist_del_init_rcu(&ep->hlnode); - rmnet_vnd_dellink(mux_id, port, ep); - synchronize_rcu(); - kfree(ep); - } - } - - rmnet_unregister_real_device(real_dev, port); - - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (nss_cb) { -#ifdef CONFIG_QCA_NSS_DRV - struct hlist_node *tmp; - struct rmnet_nss_ctx *ctx; - int bkt; - - pr_err("%s(): exiting rmnet_nss\n", __func__); - RCU_INIT_POINTER(rmnet_nss_callbacks, NULL); - - /* Tear down all NSS contexts */ - hash_for_each_safe(rmnet_nss_ctx_hashtable, bkt, tmp, ctx, hnode) - rmnet_nss_free_ctx(ctx); -#endif - } -} -#endif diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_descriptor.c b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_descriptor.c deleted file mode 100644 index 75006d1cf..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_descriptor.c +++ /dev/null @@ -1,661 +0,0 @@ -/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * RMNET Packet Descriptor Framework - * - */ - -#include <linux/ip.h> -#include <linux/ipv6.h> -#include <net/ipv6.h> -#include <net/ip6_checksum.h> -#include "rmnet_config.h" -#include "rmnet_descriptor.h" -#include "rmnet_handlers.h" -#include "rmnet_private.h" -#include "rmnet_vnd.h" - -#define RMNET_FRAG_DESCRIPTOR_POOL_SIZE 64 -#define RMNET_DL_IND_HDR_SIZE (sizeof(struct rmnet_map_dl_ind_hdr) + \ - sizeof(struct rmnet_map_header) + \ - sizeof(struct rmnet_map_control_command_header)) -#define RMNET_DL_IND_TRL_SIZE (sizeof(struct rmnet_map_dl_ind_trl) + \ - sizeof(struct rmnet_map_header) + \ - sizeof(struct rmnet_map_control_command_header)) - -typedef void (*rmnet_perf_desc_hook_t)(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port); -typedef void (*rmnet_perf_chain_hook_t)(void); - -static struct rmnet_frag_descriptor * -rmnet_get_frag_descriptor(struct rmnet_port *port) -{ - struct rmnet_frag_descriptor_pool *pool = port->frag_desc_pool; - struct rmnet_frag_descriptor *frag_desc; - - spin_lock(&port->desc_pool_lock); - if (!list_empty(&pool->free_list)) { - frag_desc = list_first_entry(&pool->free_list, - struct rmnet_frag_descriptor, - list); - list_del_init(&frag_desc->list); - } else { - frag_desc = kzalloc(sizeof(*frag_desc), GFP_ATOMIC); - if (!frag_desc) - goto out; - - INIT_LIST_HEAD(&frag_desc->list); - INIT_LIST_HEAD(&frag_desc->sub_frags); - pool->pool_size++; - } - -out: - spin_unlock(&port->desc_pool_lock); - return frag_desc; -} - -static void rmnet_recycle_frag_descriptor(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port) -{ - struct rmnet_frag_descriptor_pool *pool = port->frag_desc_pool; - struct page *page = skb_frag_page(&frag_desc->frag); - - list_del(&frag_desc->list); - if (page) - put_page(page); - - memset(frag_desc, 0, sizeof(*frag_desc)); - INIT_LIST_HEAD(&frag_desc->list); - INIT_LIST_HEAD(&frag_desc->sub_frags); - spin_lock(&port->desc_pool_lock); - list_add_tail(&frag_desc->list, &pool->free_list); - spin_unlock(&port->desc_pool_lock); -} - -static void rmnet_descriptor_add_frag(struct rmnet_port *port, struct list_head *list, - struct page *p, u32 page_offset, u32 len) -{ - struct rmnet_frag_descriptor *frag_desc; - - frag_desc = rmnet_get_frag_descriptor(port); - if (!frag_desc) - return; - - rmnet_frag_fill(frag_desc, p, page_offset, len); - list_add_tail(&frag_desc->list, list); -} - -static u8 rmnet_frag_do_flow_control(struct rmnet_map_header *qmap, - struct rmnet_port *port, - int enable) -{ - struct rmnet_map_control_command *cmd; - struct rmnet_endpoint *ep; - struct net_device *vnd; - u16 ip_family; - u16 fc_seq; - u32 qos_id; - u8 mux_id; - int r; - - mux_id = qmap->mux_id; - cmd = (struct rmnet_map_control_command *) - ((char *)qmap + sizeof(*qmap)); - - if (mux_id >= RMNET_MAX_LOGICAL_EP) - return RX_HANDLER_CONSUMED; - - ep = rmnet_get_endpoint(port, mux_id); - if (!ep) - return RX_HANDLER_CONSUMED; - - vnd = ep->egress_dev; - - ip_family = cmd->flow_control.ip_family; - fc_seq = ntohs(cmd->flow_control.flow_control_seq_num); - qos_id = ntohl(cmd->flow_control.qos_id); - - /* Ignore the ip family and pass the sequence number for both v4 and v6 - * sequence. User space does not support creating dedicated flows for - * the 2 protocols - */ - r = rmnet_vnd_do_flow_control(vnd, enable); - if (r) - return RMNET_MAP_COMMAND_UNSUPPORTED; - else - return RMNET_MAP_COMMAND_ACK; -} - -static void rmnet_frag_send_ack(struct rmnet_map_header *qmap, - unsigned char type, - struct rmnet_port *port) -{ - struct rmnet_map_control_command *cmd; - struct net_device *dev = port->dev; - struct sk_buff *skb; - u16 alloc_len = ntohs(qmap->pkt_len) + sizeof(*qmap); - - skb = alloc_skb(alloc_len, GFP_ATOMIC); - if (!skb) - return; - - skb->protocol = htons(ETH_P_MAP); - skb->dev = dev; - - cmd = rmnet_map_get_cmd_start(skb); - cmd->cmd_type = type & 0x03; - - netif_tx_lock(dev); - dev->netdev_ops->ndo_start_xmit(skb, dev); - netif_tx_unlock(dev); -} - - -/* Process MAP command frame and send N/ACK message as appropriate. Message cmd - * name is decoded here and appropriate handler is called. - */ -static void rmnet_frag_command(struct rmnet_map_header *qmap, struct rmnet_port *port) -{ - struct rmnet_map_control_command *cmd; - unsigned char command_name; - unsigned char rc = 0; - - cmd = (struct rmnet_map_control_command *) - ((char *)qmap + sizeof(*qmap)); - command_name = cmd->command_name; - - switch (command_name) { - case RMNET_MAP_COMMAND_FLOW_ENABLE: - rc = rmnet_frag_do_flow_control(qmap, port, 1); - break; - - case RMNET_MAP_COMMAND_FLOW_DISABLE: - rc = rmnet_frag_do_flow_control(qmap, port, 0); - break; - - default: - rc = RMNET_MAP_COMMAND_UNSUPPORTED; - break; - } - if (rc == RMNET_MAP_COMMAND_ACK) - rmnet_frag_send_ack(qmap, rc, port); -} - -static void rmnet_frag_deaggregate(skb_frag_t *frag, struct rmnet_port *port, - struct list_head *list) -{ - struct rmnet_map_header *maph; - u8 *data = skb_frag_address(frag); - u32 offset = 0; - u32 packet_len; - - while (offset < skb_frag_size(frag)) { - maph = (struct rmnet_map_header *)data; - packet_len = ntohs(maph->pkt_len); - - /* Some hardware can send us empty frames. Catch them */ - if (packet_len == 0) - return; - - packet_len += sizeof(*maph); - - if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) { - packet_len += sizeof(struct rmnet_map_dl_csum_trailer); - WARN_ON(1); - } else if (port->data_format & - (RMNET_FLAGS_INGRESS_MAP_CKSUMV5 | - RMNET_FLAGS_INGRESS_COALESCE) && !maph->cd_bit) { - u32 hsize = 0; - u8 type; - - type = ((struct rmnet_map_v5_coal_header *) - (data + sizeof(*maph)))->header_type; - switch (type) { - case RMNET_MAP_HEADER_TYPE_COALESCING: - hsize = sizeof(struct rmnet_map_v5_coal_header); - break; - case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD: - hsize = sizeof(struct rmnet_map_v5_csum_header); - break; - } - - packet_len += hsize; - } - else { - //qmap_hex_dump(__func__, data, 64); - WARN_ON(1); - } - - if ((int)skb_frag_size(frag) - (int)packet_len < 0) - return; - - rmnet_descriptor_add_frag(port, list, skb_frag_page(frag), - frag->page_offset + offset, - packet_len); - - offset += packet_len; - data += packet_len; - } -} - -/* Allocate and populate an skb to contain the packet represented by the - * frag descriptor. - */ -static struct sk_buff *rmnet_alloc_skb(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port) -{ - struct sk_buff *head_skb, *current_skb, *skb; - struct skb_shared_info *shinfo; - struct rmnet_frag_descriptor *sub_frag, *tmp; - - /* Use the exact sizes if we know them (i.e. RSB/RSC, rmnet_perf) */ - if (frag_desc->hdrs_valid) { - u16 hdr_len = frag_desc->ip_len + frag_desc->trans_len; - - head_skb = alloc_skb(hdr_len + RMNET_MAP_DESC_HEADROOM, - GFP_ATOMIC); - if (!head_skb) - return NULL; - - skb_reserve(head_skb, RMNET_MAP_DESC_HEADROOM); - skb_put_data(head_skb, frag_desc->hdr_ptr, hdr_len); - skb_reset_network_header(head_skb); - - if (frag_desc->trans_len) - skb_set_transport_header(head_skb, frag_desc->ip_len); - - /* Packets that have no data portion don't need any frags */ - if (hdr_len == skb_frag_size(&frag_desc->frag)) - goto skip_frags; - - /* If the headers we added are the start of the page, - * we don't want to add them twice - */ - if (frag_desc->hdr_ptr == rmnet_frag_data_ptr(frag_desc)) { - if (!rmnet_frag_pull(frag_desc, port, hdr_len)) { - kfree_skb(head_skb); - return NULL; - } - } - } else { - /* Allocate enough space to avoid penalties in the stack - * from __pskb_pull_tail() - */ - head_skb = alloc_skb(256 + RMNET_MAP_DESC_HEADROOM, - GFP_ATOMIC); - if (!head_skb) - return NULL; - - skb_reserve(head_skb, RMNET_MAP_DESC_HEADROOM); - } - - /* Add main fragment */ - get_page(skb_frag_page(&frag_desc->frag)); - skb_add_rx_frag(head_skb, 0, skb_frag_page(&frag_desc->frag), - frag_desc->frag.page_offset, - skb_frag_size(&frag_desc->frag), - skb_frag_size(&frag_desc->frag)); - - shinfo = skb_shinfo(head_skb); - current_skb = head_skb; - - /* Add in any frags from rmnet_perf */ - list_for_each_entry_safe(sub_frag, tmp, &frag_desc->sub_frags, list) { - skb_frag_t *frag; - u32 frag_size; - - frag = &sub_frag->frag; - frag_size = skb_frag_size(frag); - -add_frag: - if (shinfo->nr_frags < MAX_SKB_FRAGS) { - get_page(skb_frag_page(frag)); - skb_add_rx_frag(current_skb, shinfo->nr_frags, - skb_frag_page(frag), frag->page_offset, - frag_size, frag_size); - if (current_skb != head_skb) { - head_skb->len += frag_size; - head_skb->data_len += frag_size; - } - } else { - /* Alloc a new skb and try again */ - skb = alloc_skb(0, GFP_ATOMIC); - if (!skb) - break; - - if (current_skb == head_skb) - shinfo->frag_list = skb; - else - current_skb->next = skb; - - current_skb = skb; - shinfo = skb_shinfo(current_skb); - goto add_frag; - } - - rmnet_recycle_frag_descriptor(sub_frag, port); - } - -skip_frags: - head_skb->dev = frag_desc->dev; - rmnet_set_skb_proto(head_skb); - - /* Handle any header metadata that needs to be updated after RSB/RSC - * segmentation - */ - if (frag_desc->ip_id_set) { - struct iphdr *iph; - - iph = (struct iphdr *)rmnet_map_data_ptr(head_skb); - csum_replace2(&iph->check, iph->id, frag_desc->ip_id); - iph->id = frag_desc->ip_id; - } - - if (frag_desc->tcp_seq_set) { - struct tcphdr *th; - - th = (struct tcphdr *) - (rmnet_map_data_ptr(head_skb) + frag_desc->ip_len); - th->seq = frag_desc->tcp_seq; - } - - /* Handle csum offloading */ - if (frag_desc->csum_valid && frag_desc->hdrs_valid) { - /* Set the partial checksum information */ - //rmnet_frag_partial_csum(head_skb, frag_desc); - WARN_ON(1); - } else if (frag_desc->csum_valid) { - /* Non-RSB/RSC/perf packet. The current checksum is fine */ - head_skb->ip_summed = CHECKSUM_UNNECESSARY; - } else if (frag_desc->hdrs_valid && - (frag_desc->trans_proto == IPPROTO_TCP || - frag_desc->trans_proto == IPPROTO_UDP)) { - /* Unfortunately, we have to fake a bad checksum here, since - * the original bad value is lost by the hardware. The only - * reliable way to do it is to calculate the actual checksum - * and corrupt it. - */ - __sum16 *check; - __wsum csum; - unsigned int offset = skb_transport_offset(head_skb); - __sum16 pseudo; - - WARN_ON(1); - /* Calculate pseudo header and update header fields */ - if (frag_desc->ip_proto == 4) { - struct iphdr *iph = ip_hdr(head_skb); - __be16 tot_len = htons(head_skb->len); - - csum_replace2(&iph->check, iph->tot_len, tot_len); - iph->tot_len = tot_len; - pseudo = ~csum_tcpudp_magic(iph->saddr, iph->daddr, - head_skb->len - - frag_desc->ip_len, - frag_desc->trans_proto, 0); - } else { - struct ipv6hdr *ip6h = ipv6_hdr(head_skb); - - ip6h->payload_len = htons(head_skb->len - - sizeof(*ip6h)); - pseudo = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, - head_skb->len - - frag_desc->ip_len, - frag_desc->trans_proto, 0); - } - - if (frag_desc->trans_proto == IPPROTO_TCP) { - check = &tcp_hdr(head_skb)->check; - } else { - udp_hdr(head_skb)->len = htons(head_skb->len - - frag_desc->ip_len); - check = &udp_hdr(head_skb)->check; - } - - *check = pseudo; - csum = skb_checksum(head_skb, offset, head_skb->len - offset, - 0); - /* Add 1 to corrupt. This cannot produce a final value of 0 - * since csum_fold() can't return a value of 0xFFFF - */ - *check = csum16_add(csum_fold(csum), htons(1)); - head_skb->ip_summed = CHECKSUM_NONE; - } - - /* Handle any rmnet_perf metadata */ - if (frag_desc->hash) { - head_skb->hash = frag_desc->hash; - head_skb->sw_hash = 1; - } - - if (frag_desc->flush_shs) - head_skb->cb[0] = 1; - - /* Handle coalesced packets */ - //if (frag_desc->gso_segs > 1) - // rmnet_frag_gso_stamp(head_skb, frag_desc); - - return head_skb; -} - -/* Deliver the packets contained within a frag descriptor */ -static void rmnet_frag_deliver(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port) -{ - struct sk_buff *skb; - - skb = rmnet_alloc_skb(frag_desc, port); - if (skb) - rmnet_deliver_skb(skb, port); - rmnet_recycle_frag_descriptor(frag_desc, port); -} - -/* Process a QMAPv5 packet header */ -static int rmnet_frag_process_next_hdr_packet(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port, - struct list_head *list, - u16 len) -{ - int rc = 0; - - switch (rmnet_frag_get_next_hdr_type(frag_desc)) { - case RMNET_MAP_HEADER_TYPE_COALESCING: - rc = -1; - WARN_ON(1); - break; - case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD: - if (rmnet_frag_get_csum_valid(frag_desc)) { - frag_desc->csum_valid = true; - } else { - } - - if (!rmnet_frag_pull(frag_desc, port, - sizeof(struct rmnet_map_header) + - sizeof(struct rmnet_map_v5_csum_header))) { - rc = -EINVAL; - break; - } - - frag_desc->hdr_ptr = rmnet_frag_data_ptr(frag_desc); - - /* Remove padding only for csum offload packets. - * Coalesced packets should never have padding. - */ - if (!rmnet_frag_trim(frag_desc, port, len)) { - rc = -EINVAL; - break; - } - - list_del_init(&frag_desc->list); - list_add_tail(&frag_desc->list, list); - break; - default: - //qmap_hex_dump(__func__, rmnet_frag_data_ptr(frag_desc), 64); - rc = -EINVAL; - break; - } - - return rc; -} - -static void -__rmnet_frag_ingress_handler(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port) -{ - struct rmnet_map_header *qmap; - struct rmnet_endpoint *ep; - struct rmnet_frag_descriptor *frag, *tmp; - LIST_HEAD(segs); - u16 len, pad; - u8 mux_id; - - qmap = (struct rmnet_map_header *)skb_frag_address(&frag_desc->frag); - mux_id = qmap->mux_id; - pad = qmap->pad_len; - len = ntohs(qmap->pkt_len) - pad; - - if (qmap->cd_bit) { - if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) { - //rmnet_frag_flow_command(qmap, port, len); - goto recycle; - } - - if (port->data_format & RMNET_FLAGS_INGRESS_MAP_COMMANDS) - rmnet_frag_command(qmap, port); - - goto recycle; - } - - if (mux_id >= RMNET_MAX_LOGICAL_EP) - goto recycle; - - ep = rmnet_get_endpoint(port, mux_id); - if (!ep) - goto recycle; - - frag_desc->dev = ep->egress_dev; - - /* Handle QMAPv5 packet */ - if (qmap->next_hdr && - (port->data_format & (RMNET_FLAGS_INGRESS_COALESCE | - RMNET_FLAGS_INGRESS_MAP_CKSUMV5))) { - if (rmnet_frag_process_next_hdr_packet(frag_desc, port, &segs, - len)) - goto recycle; - } else { - /* We only have the main QMAP header to worry about */ - if (!rmnet_frag_pull(frag_desc, port, sizeof(*qmap))) - return; - - frag_desc->hdr_ptr = rmnet_frag_data_ptr(frag_desc); - - if (!rmnet_frag_trim(frag_desc, port, len)) - return; - - list_add_tail(&frag_desc->list, &segs); - } - - list_for_each_entry_safe(frag, tmp, &segs, list) { - list_del_init(&frag->list); - rmnet_frag_deliver(frag, port); - } - return; - -recycle: - rmnet_recycle_frag_descriptor(frag_desc, port); -} - -static void rmnet_frag_ingress_handler(struct sk_buff *skb, - struct rmnet_port *port) -{ - LIST_HEAD(desc_list); - int i = 0; - struct rmnet_nss_cb *nss_cb; - - /* Deaggregation and freeing of HW originating - * buffers is done within here - */ - while (skb) { - struct sk_buff *skb_frag; - - port->chain_head = NULL; - port->chain_tail = NULL; - - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - rmnet_frag_deaggregate(&skb_shinfo(skb)->frags[i], port, - &desc_list); - if (!list_empty(&desc_list)) { - struct rmnet_frag_descriptor *frag_desc, *tmp; - - list_for_each_entry_safe(frag_desc, tmp, - &desc_list, list) { - list_del_init(&frag_desc->list); - __rmnet_frag_ingress_handler(frag_desc, - port); - } - } - } - - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (nss_cb && port->chain_head) { - port->chain_head->cb[0] = 0; - netif_receive_skb(port->chain_head); - } - - skb_frag = skb_shinfo(skb)->frag_list; - skb_shinfo(skb)->frag_list = NULL; - consume_skb(skb); - skb = skb_frag; - } -} - -void rmnet_descriptor_deinit(struct rmnet_port *port) -{ - struct rmnet_frag_descriptor_pool *pool; - struct rmnet_frag_descriptor *frag_desc, *tmp; - - pool = port->frag_desc_pool; - - list_for_each_entry_safe(frag_desc, tmp, &pool->free_list, list) { - kfree(frag_desc); - pool->pool_size--; - } - - kfree(pool); -} - -int rmnet_descriptor_init(struct rmnet_port *port) -{ - struct rmnet_frag_descriptor_pool *pool; - int i; - - spin_lock_init(&port->desc_pool_lock); - pool = kzalloc(sizeof(*pool), GFP_ATOMIC); - if (!pool) - return -ENOMEM; - - INIT_LIST_HEAD(&pool->free_list); - port->frag_desc_pool = pool; - - for (i = 0; i < RMNET_FRAG_DESCRIPTOR_POOL_SIZE; i++) { - struct rmnet_frag_descriptor *frag_desc; - - frag_desc = kzalloc(sizeof(*frag_desc), GFP_ATOMIC); - if (!frag_desc) - return -ENOMEM; - - INIT_LIST_HEAD(&frag_desc->list); - INIT_LIST_HEAD(&frag_desc->sub_frags); - list_add_tail(&frag_desc->list, &pool->free_list); - pool->pool_size++; - } - - return 0; -} diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_descriptor.h b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_descriptor.h deleted file mode 100644 index 962c663af..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_descriptor.h +++ /dev/null @@ -1,146 +0,0 @@ -/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * RMNET Packet Descriptor Framework - * - */ - -#ifndef _RMNET_DESCRIPTOR_H_ -#define _RMNET_DESCRIPTOR_H_ - -#include <linux/netdevice.h> -#include <linux/list.h> -#include <linux/skbuff.h> -#include "rmnet_config.h" -#include "rmnet_map.h" - -struct rmnet_frag_descriptor_pool { - struct list_head free_list; - u32 pool_size; -}; - -struct rmnet_frag_descriptor { - struct list_head list; - struct list_head sub_frags; - skb_frag_t frag; - u8 *hdr_ptr; - struct net_device *dev; - u32 hash; - __be32 tcp_seq; - __be16 ip_id; - u16 data_offset; - u16 gso_size; - u16 gso_segs; - u16 ip_len; - u16 trans_len; - u8 ip_proto; - u8 trans_proto; - u8 pkt_id; - u8 csum_valid:1, - hdrs_valid:1, - ip_id_set:1, - tcp_seq_set:1, - flush_shs:1, - reserved:3; -}; - -/* Descriptor management */ -static struct rmnet_frag_descriptor * -rmnet_get_frag_descriptor(struct rmnet_port *port); -static void rmnet_recycle_frag_descriptor(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port); -static void rmnet_descriptor_add_frag(struct rmnet_port *port, struct list_head *list, - struct page *p, u32 page_offset, u32 len); - -/* QMAP command packets */ - -/* Ingress data handlers */ -static void rmnet_frag_deaggregate(skb_frag_t *frag, struct rmnet_port *port, - struct list_head *list); -static void rmnet_frag_deliver(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port); -static int rmnet_frag_process_next_hdr_packet(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port, - struct list_head *list, - u16 len); -static void rmnet_frag_ingress_handler(struct sk_buff *skb, - struct rmnet_port *port); - -static int rmnet_descriptor_init(struct rmnet_port *port); -static void rmnet_descriptor_deinit(struct rmnet_port *port); - -static inline void *rmnet_frag_data_ptr(struct rmnet_frag_descriptor *frag_desc) -{ - return skb_frag_address(&frag_desc->frag); -} - -static inline void *rmnet_frag_pull(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port, - unsigned int size) -{ - if (size >= skb_frag_size(&frag_desc->frag)) { - pr_info("%s(): Pulling %u bytes from %u byte pkt. Dropping\n", - __func__, size, skb_frag_size(&frag_desc->frag)); - rmnet_recycle_frag_descriptor(frag_desc, port); - return NULL; - } - - frag_desc->frag.page_offset += size; - skb_frag_size_sub(&frag_desc->frag, size); - - return rmnet_frag_data_ptr(frag_desc); -} - -static inline void *rmnet_frag_trim(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port, - unsigned int size) -{ - if (!size) { - pr_info("%s(): Trimming %u byte pkt to 0. Dropping\n", - __func__, skb_frag_size(&frag_desc->frag)); - rmnet_recycle_frag_descriptor(frag_desc, port); - return NULL; - } - - if (size < skb_frag_size(&frag_desc->frag)) - skb_frag_size_set(&frag_desc->frag, size); - - return rmnet_frag_data_ptr(frag_desc); -} - -static inline void rmnet_frag_fill(struct rmnet_frag_descriptor *frag_desc, - struct page *p, u32 page_offset, u32 len) -{ - get_page(p); - __skb_frag_set_page(&frag_desc->frag, p); - skb_frag_size_set(&frag_desc->frag, len); - frag_desc->frag.page_offset = page_offset; -} - -static inline u8 -rmnet_frag_get_next_hdr_type(struct rmnet_frag_descriptor *frag_desc) -{ - unsigned char *data = rmnet_frag_data_ptr(frag_desc); - - data += sizeof(struct rmnet_map_header); - return ((struct rmnet_map_v5_coal_header *)data)->header_type; -} - -static inline bool -rmnet_frag_get_csum_valid(struct rmnet_frag_descriptor *frag_desc) -{ - unsigned char *data = rmnet_frag_data_ptr(frag_desc); - - data += sizeof(struct rmnet_map_header); - return ((struct rmnet_map_v5_csum_header *)data)->csum_valid_required; -} - -#endif /* _RMNET_DESCRIPTOR_H_ */ diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_handlers.c b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_handlers.c deleted file mode 100644 index 6f1ce9de8..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_handlers.c +++ /dev/null @@ -1,374 +0,0 @@ -/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * RMNET Data ingress/egress handler - * - */ - -#include <linux/netdevice.h> -#include <linux/netdev_features.h> -#include <linux/if_arp.h> -#include <linux/ip.h> -#include <linux/ipv6.h> -#include <net/sock.h> -#include <linux/tracepoint.h> -#include "rmnet_private.h" -#include "rmnet_config.h" -#include "rmnet_vnd.h" -#include "rmnet_map.h" -#include "rmnet_handlers.h" -#include "rmnet_descriptor.h" - -#define RMNET_IP_VERSION_4 0x40 -#define RMNET_IP_VERSION_6 0x60 - -/* Helper Functions */ - -static void rmnet_set_skb_proto(struct sk_buff *skb) -{ - switch (rmnet_map_data_ptr(skb)[0] & 0xF0) { - case RMNET_IP_VERSION_4: - skb->protocol = htons(ETH_P_IP); - break; - case RMNET_IP_VERSION_6: - skb->protocol = htons(ETH_P_IPV6); - break; - default: - skb->protocol = htons(ETH_P_MAP); - break; - } -} - -/* Generic handler */ - -static void -rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_port *port) -{ - struct rmnet_nss_cb *nss_cb; - - rmnet_vnd_rx_fixup(skb->dev, skb->len); - - /* Pass off the packet to NSS driver if we can */ - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (nss_cb) { - if (!port->chain_head) - port->chain_head = skb; - else - skb_shinfo(port->chain_tail)->frag_list = skb; - - port->chain_tail = skb; - return; - } - - skb_reset_transport_header(skb); - skb_reset_network_header(skb); - - skb->pkt_type = PACKET_HOST; - skb_set_mac_header(skb, 0); - - //if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) { - //} else { - //if (!rmnet_check_skb_can_gro(skb)) - // gro_cells_receive(&priv->gro_cells, skb); - //else - netif_receive_skb(skb); - //} -} - -/* Deliver a list of skbs after undoing coalescing */ -static void rmnet_deliver_skb_list(struct sk_buff_head *head, - struct rmnet_port *port) -{ - struct sk_buff *skb; - - while ((skb = __skb_dequeue(head))) { - rmnet_set_skb_proto(skb); - rmnet_deliver_skb(skb, port); - } -} - -/* MAP handler */ - -static void -_rmnet_map_ingress_handler(struct sk_buff *skb, - struct rmnet_port *port) -{ - struct rmnet_map_header *qmap; - struct rmnet_endpoint *ep; - struct sk_buff_head list; - u16 len, pad; - u8 mux_id; - - /* We don't need the spinlock since only we touch this */ - __skb_queue_head_init(&list); - - qmap = (struct rmnet_map_header *)rmnet_map_data_ptr(skb); - if (qmap->cd_bit) { - if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) { - //if (!rmnet_map_flow_command(skb, port, false)) - return; - } - - if (port->data_format & RMNET_FLAGS_INGRESS_MAP_COMMANDS) - return rmnet_map_command(skb, port); - - goto free_skb; - } - - mux_id = qmap->mux_id; - pad = qmap->pad_len; - len = ntohs(qmap->pkt_len) - pad; - - if (mux_id >= RMNET_MAX_LOGICAL_EP) - goto free_skb; - - ep = rmnet_get_endpoint(port, mux_id); - if (!ep) - goto free_skb; - - skb->dev = ep->egress_dev; - - /* Handle QMAPv5 packet */ - if (qmap->next_hdr && - (port->data_format & (RMNET_FLAGS_INGRESS_COALESCE | - RMNET_FLAGS_INGRESS_MAP_CKSUMV5))) { - if (rmnet_map_process_next_hdr_packet(skb, &list, len)) - goto free_skb; - } else { - /* We only have the main QMAP header to worry about */ - pskb_pull(skb, sizeof(*qmap)); - - rmnet_set_skb_proto(skb); - - if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) { - //if (!rmnet_map_checksum_downlink_packet(skb, len + pad)) - // skb->ip_summed = CHECKSUM_UNNECESSARY; - } - - pskb_trim(skb, len); - - /* Push the single packet onto the list */ - __skb_queue_tail(&list, skb); - } - - rmnet_deliver_skb_list(&list, port); - return; - -free_skb: - kfree_skb(skb); -} - -static void -rmnet_map_ingress_handler(struct sk_buff *skb, - struct rmnet_port *port) -{ - struct sk_buff *skbn; - - if (port->data_format & (RMNET_FLAGS_INGRESS_COALESCE | - RMNET_FLAGS_INGRESS_MAP_CKSUMV5)) { - if (skb_is_nonlinear(skb)) { - rmnet_frag_ingress_handler(skb, port); - return; - } - } - - /* Deaggregation and freeing of HW originating - * buffers is done within here - */ - while (skb) { - struct sk_buff *skb_frag = skb_shinfo(skb)->frag_list; - - skb_shinfo(skb)->frag_list = NULL; - while ((skbn = rmnet_map_deaggregate(skb, port)) != NULL) { - _rmnet_map_ingress_handler(skbn, port); - - if (skbn == skb) - goto next_skb; - } - - consume_skb(skb); -next_skb: - skb = skb_frag; - } -} - -static int rmnet_map_egress_handler(struct sk_buff *skb, - struct rmnet_port *port, u8 mux_id, - struct net_device *orig_dev) -{ - int required_headroom, additional_header_len, csum_type; - struct rmnet_map_header *map_header; - - additional_header_len = 0; - required_headroom = sizeof(struct rmnet_map_header); - csum_type = 0; - - if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4) { - additional_header_len = sizeof(struct rmnet_map_ul_csum_header); - csum_type = RMNET_FLAGS_EGRESS_MAP_CKSUMV4; - } else if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV5) { - additional_header_len = sizeof(struct rmnet_map_v5_csum_header); - csum_type = RMNET_FLAGS_EGRESS_MAP_CKSUMV5; - } - - required_headroom += additional_header_len; - - if (skb_headroom(skb) < required_headroom) { - if (pskb_expand_head(skb, required_headroom, 0, GFP_ATOMIC)) - return -ENOMEM; - } - - if (csum_type) - rmnet_map_checksum_uplink_packet(skb, orig_dev, csum_type); - - map_header = rmnet_map_add_map_header(skb, additional_header_len, 0, - port); - if (!map_header) - return -ENOMEM; - - map_header->mux_id = mux_id; - - if (port->data_format & RMNET_EGRESS_FORMAT_AGGREGATION) { - if (rmnet_map_tx_agg_skip(skb, required_headroom)) - goto done; - - rmnet_map_tx_aggregate(skb, port); - return -EINPROGRESS; - } - -done: - skb->protocol = htons(ETH_P_MAP); - return 0; -} - -static void -rmnet_bridge_handler(struct sk_buff *skb, struct net_device *bridge_dev) -{ - if (bridge_dev) { - skb->dev = bridge_dev; - dev_queue_xmit(skb); - } -} - -/* Ingress / Egress Entry Points */ - -/* Processes packet as per ingress data format for receiving device. Logical - * endpoint is determined from packet inspection. Packet is then sent to the - * egress device listed in the logical endpoint configuration. - */ -static rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) -{ - struct sk_buff *skb = *pskb; - struct rmnet_port *port; - struct net_device *dev; - - if (!skb) - goto done; - - if (skb->pkt_type == PACKET_LOOPBACK) - return RX_HANDLER_PASS; - - dev = skb->dev; - port = rmnet_get_port(dev); - - port->chain_head = NULL; - port->chain_tail = NULL; - - switch (port->rmnet_mode) { - case RMNET_EPMODE_VND: - rmnet_map_ingress_handler(skb, port); - break; - case RMNET_EPMODE_BRIDGE: - rmnet_bridge_handler(skb, port->bridge_ep); - break; - } - -done: - return RX_HANDLER_CONSUMED; -} - -static rx_handler_result_t rmnet_rx_priv_handler(struct sk_buff **pskb) -{ - struct sk_buff *skb = *pskb; - struct rmnet_nss_cb *nss_cb; - - if (!skb) - return RX_HANDLER_CONSUMED; - if (nss_debug) printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - - if (skb->pkt_type == PACKET_LOOPBACK) - return RX_HANDLER_PASS; - - /* Check this so that we dont loop around netif_receive_skb */ - if (skb->cb[0] == 1) { - skb->cb[0] = 0; - - skb->dev->stats.rx_packets++; - return RX_HANDLER_PASS; - } - - while (skb) { - struct sk_buff *skb_frag = skb_shinfo(skb)->frag_list; - - skb_shinfo(skb)->frag_list = NULL; - - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (nss_cb) - nss_cb->nss_tx(skb); - - skb = skb_frag; - } - - return RX_HANDLER_CONSUMED; -} - -/* Modifies packet as per logical endpoint configuration and egress data format - * for egress device configured in logical endpoint. Packet is then transmitted - * on the egress device. - */ -static void rmnet_egress_handler(struct sk_buff *skb) -{ - struct net_device *orig_dev; - struct rmnet_port *port; - struct rmnet_priv *priv; - u8 mux_id; - int err; - u32 skb_len; - - skb_orphan(skb); - - orig_dev = skb->dev; - priv = netdev_priv(orig_dev); - skb->dev = priv->real_dev; - mux_id = priv->mux_id; - - port = rmnet_get_port(skb->dev); - if (!port) - goto drop; - - skb_len = skb->len; - err = rmnet_map_egress_handler(skb, port, mux_id, orig_dev); - if (err == -ENOMEM) - goto drop; - else if (err == -EINPROGRESS) { - rmnet_vnd_tx_fixup(orig_dev, skb_len); - return; - } - - rmnet_vnd_tx_fixup(orig_dev, skb_len); - - dev_queue_xmit(skb); - return; - -drop: - this_cpu_inc(priv->pcpu_stats->stats.tx_drops); - kfree_skb(skb); -} diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_handlers.h b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_handlers.h deleted file mode 100644 index 29837baa7..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_handlers.h +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright (c) 2013, 2016-2017, 2019 - * The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * RMNET Data ingress/egress handler - * - */ - -#ifndef _RMNET_HANDLERS_H_ -#define _RMNET_HANDLERS_H_ - -#include "rmnet_config.h" - -enum rmnet_packet_context { - RMNET_NET_RX_CTX, - RMNET_WQ_CTX, -}; - -static void rmnet_egress_handler(struct sk_buff *skb); -static void rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_port *port); -static void rmnet_set_skb_proto(struct sk_buff *skb); -static rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb); -static rx_handler_result_t rmnet_rx_priv_handler(struct sk_buff **pskb); -#endif /* _RMNET_HANDLERS_H_ */ diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map.h b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map.h deleted file mode 100644 index ab4914933..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map.h +++ /dev/null @@ -1,272 +0,0 @@ -/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _RMNET_MAP_H_ -#define _RMNET_MAP_H_ - -#include <linux/skbuff.h> -#include "rmnet_config.h" - -struct rmnet_map_control_command { - u8 command_name; - u8 cmd_type:2; - u8 reserved:6; - u16 reserved2; - u32 transaction_id; - union { - struct { - u16 ip_family:2; - u16 reserved:14; - __be16 flow_control_seq_num; - __be32 qos_id; - } flow_control; - u8 data[0]; - }; -} __aligned(1); - -enum rmnet_map_commands { - RMNET_MAP_COMMAND_NONE, - RMNET_MAP_COMMAND_FLOW_DISABLE, - RMNET_MAP_COMMAND_FLOW_ENABLE, - RMNET_MAP_COMMAND_FLOW_START = 7, - RMNET_MAP_COMMAND_FLOW_END = 8, - /* These should always be the last 2 elements */ - RMNET_MAP_COMMAND_UNKNOWN, - RMNET_MAP_COMMAND_ENUM_LENGTH -}; - -enum rmnet_map_v5_header_type { - RMNET_MAP_HEADER_TYPE_UNKNOWN, - RMNET_MAP_HEADER_TYPE_COALESCING = 0x1, - RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD = 0x2, - RMNET_MAP_HEADER_TYPE_ENUM_LENGTH -}; - -enum rmnet_map_v5_close_type { - RMNET_MAP_COAL_CLOSE_NON_COAL, - RMNET_MAP_COAL_CLOSE_IP_MISS, - RMNET_MAP_COAL_CLOSE_TRANS_MISS, - RMNET_MAP_COAL_CLOSE_HW, - RMNET_MAP_COAL_CLOSE_COAL, -}; - -enum rmnet_map_v5_close_value { - RMNET_MAP_COAL_CLOSE_HW_NL, - RMNET_MAP_COAL_CLOSE_HW_PKT, - RMNET_MAP_COAL_CLOSE_HW_BYTE, - RMNET_MAP_COAL_CLOSE_HW_TIME, - RMNET_MAP_COAL_CLOSE_HW_EVICT, -}; - -/* Main QMAP header */ -struct rmnet_map_header { - u8 pad_len:6; - u8 next_hdr:1; - u8 cd_bit:1; - u8 mux_id; - __be16 pkt_len; -} __aligned(1); - -/* QMAP v5 headers */ -struct rmnet_map_v5_csum_header { - u8 next_hdr:1; - u8 header_type:7; - u8 hw_reserved:7; - u8 csum_valid_required:1; - __be16 reserved; -} __aligned(1); - -struct rmnet_map_v5_nl_pair { - __be16 pkt_len; - u8 csum_error_bitmap; - u8 num_packets; -} __aligned(1); - -/* NLO: Number-length object */ -#define RMNET_MAP_V5_MAX_NLOS (6) -#define RMNET_MAP_V5_MAX_PACKETS (48) - -struct rmnet_map_v5_coal_header { - u8 next_hdr:1; - u8 header_type:7; - u8 reserved1:4; - u8 num_nlos:3; - u8 csum_valid:1; - u8 close_type:4; - u8 close_value:4; - u8 reserved2:4; - u8 virtual_channel_id:4; - - struct rmnet_map_v5_nl_pair nl_pairs[RMNET_MAP_V5_MAX_NLOS]; -} __aligned(1); - -/* QMAP v4 headers */ -struct rmnet_map_dl_csum_trailer { - u8 reserved1; - u8 valid:1; - u8 reserved2:7; - u16 csum_start_offset; - u16 csum_length; - __be16 csum_value; -} __aligned(1); - -struct rmnet_map_ul_csum_header { - __be16 csum_start_offset; - u16 csum_insert_offset:14; - u16 udp_ind:1; - u16 csum_enabled:1; -} __aligned(1); - -struct rmnet_map_control_command_header { - u8 command_name; - u8 cmd_type:2; - u8 reserved:5; - u8 e:1; - u16 source_id:15; - u16 ext:1; - u32 transaction_id; -} __aligned(1); - -struct rmnet_map_flow_info_le { - __be32 mux_id; - __be32 flow_id; - __be32 bytes; - __be32 pkts; -} __aligned(1); - -struct rmnet_map_flow_info_be { - u32 mux_id; - u32 flow_id; - u32 bytes; - u32 pkts; -} __aligned(1); - -struct rmnet_map_dl_ind_hdr { - union { - struct { - u32 seq; - u32 bytes; - u32 pkts; - u32 flows; - struct rmnet_map_flow_info_le flow[0]; - } le __aligned(1); - struct { - __be32 seq; - __be32 bytes; - __be32 pkts; - __be32 flows; - struct rmnet_map_flow_info_be flow[0]; - } be __aligned(1); - } __aligned(1); -} __aligned(1); - -struct rmnet_map_dl_ind_trl { - union { - __be32 seq_be; - u32 seq_le; - } __aligned(1); -} __aligned(1); - -struct rmnet_map_dl_ind { - u8 priority; - union { - void (*dl_hdr_handler)(struct rmnet_map_dl_ind_hdr *); - void (*dl_hdr_handler_v2)(struct rmnet_map_dl_ind_hdr *, - struct - rmnet_map_control_command_header *); - } __aligned(1); - union { - void (*dl_trl_handler)(struct rmnet_map_dl_ind_trl *); - void (*dl_trl_handler_v2)(struct rmnet_map_dl_ind_trl *, - struct - rmnet_map_control_command_header *); - } __aligned(1); - struct list_head list; -}; - -#define RMNET_MAP_GET_MUX_ID(Y) (((struct rmnet_map_header *) \ - (Y)->data)->mux_id) -#define RMNET_MAP_GET_CD_BIT(Y) (((struct rmnet_map_header *) \ - (Y)->data)->cd_bit) -#define RMNET_MAP_GET_PAD(Y) (((struct rmnet_map_header *) \ - (Y)->data)->pad_len) -#define RMNET_MAP_GET_CMD_START(Y) ((struct rmnet_map_control_command *) \ - ((Y)->data + \ - sizeof(struct rmnet_map_header))) -#define RMNET_MAP_GET_LENGTH(Y) (ntohs(((struct rmnet_map_header *) \ - (Y)->data)->pkt_len)) - -#define RMNET_MAP_DEAGGR_SPACING 64 -#define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2) -#define RMNET_MAP_DESC_HEADROOM 128 - -#define RMNET_MAP_COMMAND_REQUEST 0 -#define RMNET_MAP_COMMAND_ACK 1 -#define RMNET_MAP_COMMAND_UNSUPPORTED 2 -#define RMNET_MAP_COMMAND_INVALID 3 - -#define RMNET_MAP_NO_PAD_BYTES 0 -#define RMNET_MAP_ADD_PAD_BYTES 1 - -static inline unsigned char *rmnet_map_data_ptr(struct sk_buff *skb) -{ - /* Nonlinear packets we receive are entirely within frag 0 */ - if (skb_is_nonlinear(skb) && skb->len == skb->data_len) - return skb_frag_address(skb_shinfo(skb)->frags); - - return skb->data; -} - -static inline struct rmnet_map_control_command * -rmnet_map_get_cmd_start(struct sk_buff *skb) -{ - unsigned char *data = rmnet_map_data_ptr(skb); - - data += sizeof(struct rmnet_map_header); - return (struct rmnet_map_control_command *)data; -} - -static inline u8 rmnet_map_get_next_hdr_type(struct sk_buff *skb) -{ - unsigned char *data = rmnet_map_data_ptr(skb); - - data += sizeof(struct rmnet_map_header); - return ((struct rmnet_map_v5_coal_header *)data)->header_type; -} - -static inline bool rmnet_map_get_csum_valid(struct sk_buff *skb) -{ - unsigned char *data = rmnet_map_data_ptr(skb); - - data += sizeof(struct rmnet_map_header); - return ((struct rmnet_map_v5_csum_header *)data)->csum_valid_required; -} - -static struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, - struct rmnet_port *port); -static struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, - int hdrlen, int pad, - struct rmnet_port *port); -static void rmnet_map_command(struct sk_buff *skb, struct rmnet_port *port); -static void rmnet_map_checksum_uplink_packet(struct sk_buff *skb, - struct net_device *orig_dev, - int csum_type); -static int rmnet_map_process_next_hdr_packet(struct sk_buff *skb, - struct sk_buff_head *list, - u16 len); -static int rmnet_map_tx_agg_skip(struct sk_buff *skb, int offset); -static void rmnet_map_tx_aggregate(struct sk_buff *skb, struct rmnet_port *port); -static void rmnet_map_tx_aggregate_init(struct rmnet_port *port); -static void rmnet_map_tx_aggregate_exit(struct rmnet_port *port); -static void rmnet_map_cmd_init(struct rmnet_port *port); -static void rmnet_map_cmd_exit(struct rmnet_port *port); -#endif /* _RMNET_MAP_H_ */ diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map_command.c b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map_command.c deleted file mode 100644 index 6c3318490..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map_command.c +++ /dev/null @@ -1,143 +0,0 @@ -/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/netdevice.h> -#include "rmnet_config.h" -#include "rmnet_map.h" -#include "rmnet_private.h" -#include "rmnet_vnd.h" - -#define RMNET_DL_IND_HDR_SIZE (sizeof(struct rmnet_map_dl_ind_hdr) + \ - sizeof(struct rmnet_map_header) + \ - sizeof(struct rmnet_map_control_command_header)) - -#define RMNET_MAP_CMD_SIZE (sizeof(struct rmnet_map_header) + \ - sizeof(struct rmnet_map_control_command_header)) - -#define RMNET_DL_IND_TRL_SIZE (sizeof(struct rmnet_map_dl_ind_trl) + \ - sizeof(struct rmnet_map_header) + \ - sizeof(struct rmnet_map_control_command_header)) - -static u8 rmnet_map_do_flow_control(struct sk_buff *skb, - struct rmnet_port *port, - int enable) -{ - struct rmnet_map_header *qmap; - struct rmnet_map_control_command *cmd; - struct rmnet_endpoint *ep; - struct net_device *vnd; - u16 ip_family; - u16 fc_seq; - u32 qos_id; - u8 mux_id; - int r; - - qmap = (struct rmnet_map_header *)rmnet_map_data_ptr(skb); - mux_id = qmap->mux_id; - cmd = rmnet_map_get_cmd_start(skb); - - if (mux_id >= RMNET_MAX_LOGICAL_EP) { - kfree_skb(skb); - return RX_HANDLER_CONSUMED; - } - - ep = rmnet_get_endpoint(port, mux_id); - if (!ep) { - kfree_skb(skb); - return RX_HANDLER_CONSUMED; - } - - vnd = ep->egress_dev; - - ip_family = cmd->flow_control.ip_family; - fc_seq = ntohs(cmd->flow_control.flow_control_seq_num); - qos_id = ntohl(cmd->flow_control.qos_id); - - /* Ignore the ip family and pass the sequence number for both v4 and v6 - * sequence. User space does not support creating dedicated flows for - * the 2 protocols - */ - r = rmnet_vnd_do_flow_control(vnd, enable); - if (r) { - kfree_skb(skb); - return RMNET_MAP_COMMAND_UNSUPPORTED; - } else { - return RMNET_MAP_COMMAND_ACK; - } -} - -static void rmnet_map_send_ack(struct sk_buff *skb, - unsigned char type, - struct rmnet_port *port) -{ - struct rmnet_map_control_command *cmd; - struct net_device *dev = skb->dev; - - if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) - pskb_trim(skb, - skb->len - sizeof(struct rmnet_map_dl_csum_trailer)); - - skb->protocol = htons(ETH_P_MAP); - - cmd = rmnet_map_get_cmd_start(skb); - cmd->cmd_type = type & 0x03; - - netif_tx_lock(dev); - dev->netdev_ops->ndo_start_xmit(skb, dev); - netif_tx_unlock(dev); -} - -/* Process MAP command frame and send N/ACK message as appropriate. Message cmd - * name is decoded here and appropriate handler is called. - */ -static void rmnet_map_command(struct sk_buff *skb, struct rmnet_port *port) -{ - struct rmnet_map_control_command *cmd; - unsigned char command_name; - unsigned char rc = 0; - - cmd = rmnet_map_get_cmd_start(skb); - command_name = cmd->command_name; - - switch (command_name) { - case RMNET_MAP_COMMAND_FLOW_ENABLE: - rc = rmnet_map_do_flow_control(skb, port, 1); - break; - - case RMNET_MAP_COMMAND_FLOW_DISABLE: - rc = rmnet_map_do_flow_control(skb, port, 0); - break; - - default: - rc = RMNET_MAP_COMMAND_UNSUPPORTED; - kfree_skb(skb); - break; - } - if (rc == RMNET_MAP_COMMAND_ACK) - rmnet_map_send_ack(skb, rc, port); -} - - -static void rmnet_map_cmd_exit(struct rmnet_port *port) -{ - struct rmnet_map_dl_ind *tmp, *idx; - - list_for_each_entry_safe(tmp, idx, &port->dl_list, list) - list_del_rcu(&tmp->list); -} - -static void rmnet_map_cmd_init(struct rmnet_port *port) -{ - INIT_LIST_HEAD(&port->dl_list); - - port->dl_marker_flush = -1; -} diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map_data.c b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map_data.c deleted file mode 100644 index 783412c69..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map_data.c +++ /dev/null @@ -1,682 +0,0 @@ -/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * RMNET Data MAP protocol - * - */ - -#include <linux/netdevice.h> -#include <linux/ip.h> -#include <linux/ipv6.h> -#include <net/ip6_checksum.h> -#include "rmnet_config.h" -#include "rmnet_map.h" -#include "rmnet_private.h" -#include "rmnet_handlers.h" - -#define RMNET_MAP_PKT_COPY_THRESHOLD 64 -#define RMNET_MAP_DEAGGR_SPACING 64 -#define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2) - -struct rmnet_map_coal_metadata { - void *ip_header; - void *trans_header; - u16 ip_len; - u16 trans_len; - u16 data_offset; - u16 data_len; - u8 ip_proto; - u8 trans_proto; - u8 pkt_id; - u8 pkt_count; -}; - -static __sum16 *rmnet_map_get_csum_field(unsigned char protocol, - const void *txporthdr) -{ - __sum16 *check = NULL; - - switch (protocol) { - case IPPROTO_TCP: - check = &(((struct tcphdr *)txporthdr)->check); - break; - - case IPPROTO_UDP: - check = &(((struct udphdr *)txporthdr)->check); - break; - - default: - check = NULL; - break; - } - - return check; -} - -static void rmnet_map_complement_ipv4_txporthdr_csum_field(void *iphdr) -{ - struct iphdr *ip4h = (struct iphdr *)iphdr; - void *txphdr; - u16 *csum; - - txphdr = iphdr + ip4h->ihl * 4; - - if (ip4h->protocol == IPPROTO_TCP || ip4h->protocol == IPPROTO_UDP) { - csum = (u16 *)rmnet_map_get_csum_field(ip4h->protocol, txphdr); - *csum = ~(*csum); - } -} - -static void -rmnet_map_ipv4_ul_csum_header(void *iphdr, - struct rmnet_map_ul_csum_header *ul_header, - struct sk_buff *skb) -{ - struct iphdr *ip4h = (struct iphdr *)iphdr; - __be16 *hdr = (__be16 *)ul_header, offset; - - offset = htons((__force u16)(skb_transport_header(skb) - - (unsigned char *)iphdr)); - ul_header->csum_start_offset = offset; - ul_header->csum_insert_offset = skb->csum_offset; - ul_header->csum_enabled = 1; - if (ip4h->protocol == IPPROTO_UDP) - ul_header->udp_ind = 1; - else - ul_header->udp_ind = 0; - - /* Changing remaining fields to network order */ - hdr++; - *hdr = htons((__force u16)*hdr); - - skb->ip_summed = CHECKSUM_NONE; - - rmnet_map_complement_ipv4_txporthdr_csum_field(iphdr); -} - -#if IS_ENABLED(CONFIG_IPV6) -static void rmnet_map_complement_ipv6_txporthdr_csum_field(void *ip6hdr) -{ - struct ipv6hdr *ip6h = (struct ipv6hdr *)ip6hdr; - void *txphdr; - u16 *csum; - - txphdr = ip6hdr + sizeof(struct ipv6hdr); - - if (ip6h->nexthdr == IPPROTO_TCP || ip6h->nexthdr == IPPROTO_UDP) { - csum = (u16 *)rmnet_map_get_csum_field(ip6h->nexthdr, txphdr); - *csum = ~(*csum); - } -} - -static void -rmnet_map_ipv6_ul_csum_header(void *ip6hdr, - struct rmnet_map_ul_csum_header *ul_header, - struct sk_buff *skb) -{ - struct ipv6hdr *ip6h = (struct ipv6hdr *)ip6hdr; - __be16 *hdr = (__be16 *)ul_header, offset; - - offset = htons((__force u16)(skb_transport_header(skb) - - (unsigned char *)ip6hdr)); - ul_header->csum_start_offset = offset; - ul_header->csum_insert_offset = skb->csum_offset; - ul_header->csum_enabled = 1; - - if (ip6h->nexthdr == IPPROTO_UDP) - ul_header->udp_ind = 1; - else - ul_header->udp_ind = 0; - - /* Changing remaining fields to network order */ - hdr++; - *hdr = htons((__force u16)*hdr); - - skb->ip_summed = CHECKSUM_NONE; - - rmnet_map_complement_ipv6_txporthdr_csum_field(ip6hdr); -} -#endif - -/* Adds MAP header to front of skb->data - * Padding is calculated and set appropriately in MAP header. Mux ID is - * initialized to 0. - */ -static struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, - int hdrlen, int pad, - struct rmnet_port *port) -{ - struct rmnet_map_header *map_header; - u32 padding, map_datalen; - u8 *padbytes; - - map_datalen = skb->len - hdrlen; - map_header = (struct rmnet_map_header *) - skb_push(skb, sizeof(struct rmnet_map_header)); - memset(map_header, 0, sizeof(struct rmnet_map_header)); - - /* Set next_hdr bit for csum offload packets */ - if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV5) - map_header->next_hdr = 1; - - if (pad == RMNET_MAP_NO_PAD_BYTES) { - map_header->pkt_len = htons(map_datalen); - return map_header; - } - - padding = ALIGN(map_datalen, 4) - map_datalen; - - if (padding == 0) - goto done; - - if (skb_tailroom(skb) < padding) - return NULL; - - padbytes = (u8 *)skb_put(skb, padding); - memset(padbytes, 0, padding); - -done: - map_header->pkt_len = htons(map_datalen + padding); - map_header->pad_len = padding & 0x3F; - - return map_header; -} - -/* Deaggregates a single packet - * A whole new buffer is allocated for each portion of an aggregated frame. - * Caller should keep calling deaggregate() on the source skb until 0 is - * returned, indicating that there are no more packets to deaggregate. Caller - * is responsible for freeing the original skb. - */ -static struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, - struct rmnet_port *port) -{ - struct rmnet_map_header *maph; - struct sk_buff *skbn; - unsigned char *data = rmnet_map_data_ptr(skb), *next_hdr = NULL; - u32 packet_len; - - if (skb->len == 0) - return NULL; - - maph = (struct rmnet_map_header *)data; - packet_len = ntohs(maph->pkt_len) + sizeof(struct rmnet_map_header); - - if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) - packet_len += sizeof(struct rmnet_map_dl_csum_trailer); - else if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) { - if (!maph->cd_bit) { - packet_len += sizeof(struct rmnet_map_v5_csum_header); - - /* Coalescing headers require MAPv5 */ - next_hdr = data + sizeof(*maph); - } - } - - if (((int)skb->len - (int)packet_len) < 0) - return NULL; - - /* Some hardware can send us empty frames. Catch them */ - if (ntohs(maph->pkt_len) == 0) - return NULL; - - if (next_hdr && - ((struct rmnet_map_v5_coal_header *)next_hdr)->header_type == - RMNET_MAP_HEADER_TYPE_COALESCING) - return skb; - - if (skb_is_nonlinear(skb)) { - skb_frag_t *frag0 = skb_shinfo(skb)->frags; - struct page *page = skb_frag_page(frag0); - - skbn = alloc_skb(RMNET_MAP_DEAGGR_HEADROOM, GFP_ATOMIC); - if (!skbn) - return NULL; - - skb_append_pagefrags(skbn, page, frag0->page_offset, - packet_len); - skbn->data_len += packet_len; - skbn->len += packet_len; - } else { - skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, - GFP_ATOMIC); - if (!skbn) - return NULL; - - skb_reserve(skbn, RMNET_MAP_DEAGGR_HEADROOM); - skb_put(skbn, packet_len); - memcpy(skbn->data, data, packet_len); - } - - pskb_pull(skb, packet_len); - - return skbn; -} - -static void rmnet_map_v4_checksum_uplink_packet(struct sk_buff *skb, - struct net_device *orig_dev) -{ - struct rmnet_priv *priv = netdev_priv(orig_dev); - struct rmnet_map_ul_csum_header *ul_header; - void *iphdr; - - ul_header = (struct rmnet_map_ul_csum_header *) - skb_push(skb, sizeof(struct rmnet_map_ul_csum_header)); - - if (unlikely(!(orig_dev->features & - (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)))) - goto sw_csum; - - if (skb->ip_summed == CHECKSUM_PARTIAL) { - iphdr = (char *)ul_header + - sizeof(struct rmnet_map_ul_csum_header); - - if (skb->protocol == htons(ETH_P_IP)) { - rmnet_map_ipv4_ul_csum_header(iphdr, ul_header, skb); - priv->stats.csum_hw++; - return; - } else if (skb->protocol == htons(ETH_P_IPV6)) { -#if IS_ENABLED(CONFIG_IPV6) - rmnet_map_ipv6_ul_csum_header(iphdr, ul_header, skb); - priv->stats.csum_hw++; - return; -#else - priv->stats.csum_err_invalid_ip_version++; - goto sw_csum; -#endif - } else { - priv->stats.csum_err_invalid_ip_version++; - } - } - -sw_csum: - ul_header->csum_start_offset = 0; - ul_header->csum_insert_offset = 0; - ul_header->csum_enabled = 0; - ul_header->udp_ind = 0; - - priv->stats.csum_sw++; -} - -static void rmnet_map_v5_checksum_uplink_packet(struct sk_buff *skb, - struct net_device *orig_dev) -{ - struct rmnet_priv *priv = netdev_priv(orig_dev); - struct rmnet_map_v5_csum_header *ul_header; - - ul_header = (struct rmnet_map_v5_csum_header *) - skb_push(skb, sizeof(*ul_header)); - memset(ul_header, 0, sizeof(*ul_header)); - ul_header->header_type = RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD; - - if (skb->ip_summed == CHECKSUM_PARTIAL) { - void *iph = (char *)ul_header + sizeof(*ul_header); - void *trans; - __sum16 *check; - u8 proto; - - if (skb->protocol == htons(ETH_P_IP)) { - u16 ip_len = ((struct iphdr *)iph)->ihl * 4; - - proto = ((struct iphdr *)iph)->protocol; - trans = iph + ip_len; - } else if (skb->protocol == htons(ETH_P_IPV6)) { - u16 ip_len = sizeof(struct ipv6hdr); - - proto = ((struct ipv6hdr *)iph)->nexthdr; - trans = iph + ip_len; - } else { - priv->stats.csum_err_invalid_ip_version++; - goto sw_csum; - } - - check = rmnet_map_get_csum_field(proto, trans); - if (check) { - *check = 0; - skb->ip_summed = CHECKSUM_NONE; - /* Ask for checksum offloading */ - ul_header->csum_valid_required = 1; - priv->stats.csum_hw++; - return; - } - } - -sw_csum: - priv->stats.csum_sw++; -} - - -/* Generates UL checksum meta info header for IPv4 and IPv6 over TCP and UDP - * packets that are supported for UL checksum offload. - */ -void rmnet_map_checksum_uplink_packet(struct sk_buff *skb, - struct net_device *orig_dev, - int csum_type) -{ - switch (csum_type) { - case RMNET_FLAGS_EGRESS_MAP_CKSUMV4: - rmnet_map_v4_checksum_uplink_packet(skb, orig_dev); - break; - case RMNET_FLAGS_EGRESS_MAP_CKSUMV5: - rmnet_map_v5_checksum_uplink_packet(skb, orig_dev); - break; - default: - break; - } -} - -static void rmnet_map_move_headers(struct sk_buff *skb) -{ - struct iphdr *iph; - u16 ip_len; - u16 trans_len = 0; - u8 proto; - - /* This only applies to non-linear SKBs */ - if (!skb_is_nonlinear(skb)) - return; - - iph = (struct iphdr *)rmnet_map_data_ptr(skb); - if (iph->version == 4) { - ip_len = iph->ihl * 4; - proto = iph->protocol; - if (iph->frag_off & htons(IP_OFFSET)) - /* No transport header information */ - goto pull; - } else if (iph->version == 6) { - struct ipv6hdr *ip6h = (struct ipv6hdr *)iph; - __be16 frag_off; - u8 nexthdr = ip6h->nexthdr; - - ip_len = ipv6_skip_exthdr(skb, sizeof(*ip6h), &nexthdr, - &frag_off); - if (ip_len < 0) - return; - - proto = nexthdr; - } else { - return; - } - - if (proto == IPPROTO_TCP) { - struct tcphdr *tp = (struct tcphdr *)((u8 *)iph + ip_len); - - trans_len = tp->doff * 4; - } else if (proto == IPPROTO_UDP) { - trans_len = sizeof(struct udphdr); - } else if (proto == NEXTHDR_FRAGMENT) { - /* Non-first fragments don't have the fragment length added by - * ipv6_skip_exthdr() and sho up as proto NEXTHDR_FRAGMENT, so - * we account for the length here. - */ - ip_len += sizeof(struct frag_hdr); - } - -pull: - __pskb_pull_tail(skb, ip_len + trans_len); - skb_reset_network_header(skb); - if (trans_len) - skb_set_transport_header(skb, ip_len); -} - - -/* Process a QMAPv5 packet header */ -static int rmnet_map_process_next_hdr_packet(struct sk_buff *skb, - struct sk_buff_head *list, - u16 len) -{ - struct rmnet_priv *priv = netdev_priv(skb->dev); - int rc = 0; - - switch (rmnet_map_get_next_hdr_type(skb)) { - case RMNET_MAP_HEADER_TYPE_COALESCING: - priv->stats.coal.coal_rx++; - break; - case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD: - if (rmnet_map_get_csum_valid(skb)) { - priv->stats.csum_ok++; - skb->ip_summed = CHECKSUM_UNNECESSARY; - } else { - priv->stats.csum_valid_unset++; - } - - /* Pull unnecessary headers and move the rest to the linear - * section of the skb. - */ - pskb_pull(skb, - (sizeof(struct rmnet_map_header) + - sizeof(struct rmnet_map_v5_csum_header))); - rmnet_map_move_headers(skb); - - /* Remove padding only for csum offload packets. - * Coalesced packets should never have padding. - */ - pskb_trim(skb, len); - __skb_queue_tail(list, skb); - break; - default: - rc = -EINVAL; - break; - } - - return rc; -} - -long rmnet_agg_time_limit __read_mostly = 1000000L; -long rmnet_agg_bypass_time __read_mostly = 10000000L; - -static int rmnet_map_tx_agg_skip(struct sk_buff *skb, int offset) -{ - u8 *packet_start = skb->data + offset; - int is_icmp = 0; - - if (skb->protocol == htons(ETH_P_IP)) { - struct iphdr *ip4h = (struct iphdr *)(packet_start); - - if (ip4h->protocol == IPPROTO_ICMP) - is_icmp = 1; - } else if (skb->protocol == htons(ETH_P_IPV6)) { - struct ipv6hdr *ip6h = (struct ipv6hdr *)(packet_start); - - if (ip6h->nexthdr == IPPROTO_ICMPV6) { - is_icmp = 1; - } else if (ip6h->nexthdr == NEXTHDR_FRAGMENT) { - struct frag_hdr *frag; - - frag = (struct frag_hdr *)(packet_start - + sizeof(struct ipv6hdr)); - if (frag->nexthdr == IPPROTO_ICMPV6) - is_icmp = 1; - } - } - - return is_icmp; -} - -static void rmnet_map_flush_tx_packet_work(struct work_struct *work) -{ - struct sk_buff *skb = NULL; - struct rmnet_port *port; - unsigned long flags; - - port = container_of(work, struct rmnet_port, agg_wq); - - spin_lock_irqsave(&port->agg_lock, flags); - if (likely(port->agg_state == -EINPROGRESS)) { - /* Buffer may have already been shipped out */ - if (likely(port->agg_skb)) { - skb = port->agg_skb; - port->agg_skb = NULL; - port->agg_count = 0; - memset(&port->agg_time, 0, sizeof(struct timespec)); - } - port->agg_state = 0; - } - - spin_unlock_irqrestore(&port->agg_lock, flags); - if (skb) - dev_queue_xmit(skb); -} - -static enum hrtimer_restart rmnet_map_flush_tx_packet_queue(struct hrtimer *t) -{ - struct rmnet_port *port; - - port = container_of(t, struct rmnet_port, hrtimer); - - schedule_work(&port->agg_wq); - return HRTIMER_NORESTART; -} - -static void rmnet_map_linearize_copy(struct sk_buff *dst, struct sk_buff *src) -{ - unsigned int linear = src->len - src->data_len, target = src->len; - unsigned char *src_buf; - struct sk_buff *skb; - - src_buf = src->data; - skb_put_data(dst, src_buf, linear); - target -= linear; - - skb = src; - - while (target) { - unsigned int i = 0, non_linear = 0; - - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - non_linear = skb_frag_size(&skb_shinfo(skb)->frags[i]); - src_buf = skb_frag_address(&skb_shinfo(skb)->frags[i]); - - skb_put_data(dst, src_buf, non_linear); - target -= non_linear; - } - - if (skb_shinfo(skb)->frag_list) { - skb = skb_shinfo(skb)->frag_list; - continue; - } - - if (skb->next) - skb = skb->next; - } -} - -static void rmnet_map_tx_aggregate(struct sk_buff *skb, struct rmnet_port *port) -{ - struct timespec diff, last; - int size, agg_count = 0; - struct sk_buff *agg_skb; - unsigned long flags; - -new_packet: - spin_lock_irqsave(&port->agg_lock, flags); - memcpy(&last, &port->agg_last, sizeof(struct timespec)); - getnstimeofday(&port->agg_last); - - if (!port->agg_skb) { - /* Check to see if we should agg first. If the traffic is very - * sparse, don't aggregate. We will need to tune this later - */ - diff = timespec_sub(port->agg_last, last); - size = port->egress_agg_params.agg_size - skb->len; - - if (diff.tv_sec > 0 || diff.tv_nsec > rmnet_agg_bypass_time || - size <= 0) { - spin_unlock_irqrestore(&port->agg_lock, flags); - skb->protocol = htons(ETH_P_MAP); - dev_queue_xmit(skb); - return; - } - - port->agg_skb = alloc_skb(port->egress_agg_params.agg_size, - GFP_ATOMIC); - if (!port->agg_skb) { - port->agg_skb = 0; - port->agg_count = 0; - memset(&port->agg_time, 0, sizeof(struct timespec)); - spin_unlock_irqrestore(&port->agg_lock, flags); - skb->protocol = htons(ETH_P_MAP); - dev_queue_xmit(skb); - return; - } - rmnet_map_linearize_copy(port->agg_skb, skb); - port->agg_skb->dev = skb->dev; - port->agg_skb->protocol = htons(ETH_P_MAP); - port->agg_count = 1; - getnstimeofday(&port->agg_time); - dev_kfree_skb_any(skb); - goto schedule; - } - diff = timespec_sub(port->agg_last, port->agg_time); - size = port->egress_agg_params.agg_size - port->agg_skb->len; - - if (skb->len > size || - port->agg_count >= port->egress_agg_params.agg_count || - diff.tv_sec > 0 || diff.tv_nsec > rmnet_agg_time_limit) { - agg_skb = port->agg_skb; - agg_count = port->agg_count; - port->agg_skb = 0; - port->agg_count = 0; - memset(&port->agg_time, 0, sizeof(struct timespec)); - port->agg_state = 0; - spin_unlock_irqrestore(&port->agg_lock, flags); - hrtimer_cancel(&port->hrtimer); - dev_queue_xmit(agg_skb); - goto new_packet; - } - - rmnet_map_linearize_copy(port->agg_skb, skb); - port->agg_count++; - dev_kfree_skb_any(skb); - -schedule: - if (port->agg_state != -EINPROGRESS) { - port->agg_state = -EINPROGRESS; - hrtimer_start(&port->hrtimer, - ns_to_ktime(port->egress_agg_params.agg_time), - HRTIMER_MODE_REL); - } - spin_unlock_irqrestore(&port->agg_lock, flags); -} - -static void rmnet_map_tx_aggregate_init(struct rmnet_port *port) -{ - hrtimer_init(&port->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - port->hrtimer.function = rmnet_map_flush_tx_packet_queue; - port->egress_agg_params.agg_size = 8192; - port->egress_agg_params.agg_count = 20; - port->egress_agg_params.agg_time = 3000000; - spin_lock_init(&port->agg_lock); - - INIT_WORK(&port->agg_wq, rmnet_map_flush_tx_packet_work); -} - -static void rmnet_map_tx_aggregate_exit(struct rmnet_port *port) -{ - unsigned long flags; - - hrtimer_cancel(&port->hrtimer); - cancel_work_sync(&port->agg_wq); - - spin_lock_irqsave(&port->agg_lock, flags); - if (port->agg_state == -EINPROGRESS) { - if (port->agg_skb) { - kfree_skb(port->agg_skb); - port->agg_skb = NULL; - port->agg_count = 0; - memset(&port->agg_time, 0, sizeof(struct timespec)); - } - - port->agg_state = 0; - } - - spin_unlock_irqrestore(&port->agg_lock, flags); -} diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_private.h b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_private.h deleted file mode 100644 index d384b7b9a..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_private.h +++ /dev/null @@ -1,34 +0,0 @@ -/* Copyright (c) 2013-2014, 2016-2019 The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _RMNET_PRIVATE_H_ -#define _RMNET_PRIVATE_H_ - -#define RMNET_MAX_PACKET_SIZE 16384 -#define RMNET_DFLT_PACKET_SIZE 1500 -#define RMNET_NEEDED_HEADROOM 16 -#define RMNET_TX_QUEUE_LEN 1000 - -/* Constants */ -#define RMNET_EGRESS_FORMAT_AGGREGATION BIT(31) -#define RMNET_INGRESS_FORMAT_DL_MARKER_V1 BIT(30) -#define RMNET_INGRESS_FORMAT_DL_MARKER_V2 BIT(29) - -#define RMNET_INGRESS_FORMAT_DL_MARKER (RMNET_INGRESS_FORMAT_DL_MARKER_V1 |\ -RMNET_INGRESS_FORMAT_DL_MARKER_V2) - -/* Replace skb->dev to a virtual rmnet device and pass up the stack */ -#define RMNET_EPMODE_VND (1) -/* Pass the frame directly to another device with dev_queue_xmit() */ -#define RMNET_EPMODE_BRIDGE (2) - -#endif /* _RMNET_PRIVATE_H_ */ diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_trace.h b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_trace.h deleted file mode 100644 index d453fc5d0..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_trace.h +++ /dev/null @@ -1,257 +0,0 @@ -/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM rmnet -#define TRACE_INCLUDE_FILE rmnet_trace - -#if !defined(_RMNET_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) -#define _RMNET_TRACE_H_ - -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <linux/tracepoint.h> - -/*****************************************************************************/ -/* Trace events for rmnet module */ -/*****************************************************************************/ -DECLARE_EVENT_CLASS - (rmnet_mod_template, - - TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2, - u64 ulong1, u64 ulong2, void *ptr1, void *ptr2), - - TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2), - - TP_STRUCT__entry( - __field(u8, func) - __field(u8, evt) - __field(u32, uint1) - __field(u32, uint2) - __field(u64, ulong1) - __field(u64, ulong2) - __field(void *, ptr1) - __field(void *, ptr2) - ), - - TP_fast_assign( - __entry->func = func; - __entry->evt = evt; - __entry->uint1 = uint1; - __entry->uint2 = uint2; - __entry->ulong1 = ulong1; - __entry->ulong2 = ulong2; - __entry->ptr1 = ptr1; - __entry->ptr2 = ptr2; - ), - -TP_printk("fun:%u ev:%u u1:%u u2:%u ul1:%llu ul2:%llu p1:0x%pK p2:0x%pK", - __entry->func, __entry->evt, - __entry->uint1, __entry->uint2, - __entry->ulong1, __entry->ulong2, - __entry->ptr1, __entry->ptr2) -) - -DEFINE_EVENT - (rmnet_mod_template, rmnet_low, - - TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2, - u64 ulong1, u64 ulong2, void *ptr1, void *ptr2), - - TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2) - -); - -DEFINE_EVENT - (rmnet_mod_template, rmnet_high, - - TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2, - u64 ulong1, u64 ulong2, void *ptr1, void *ptr2), - - TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2) - -); - -DEFINE_EVENT - (rmnet_mod_template, rmnet_err, - - TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2, - u64 ulong1, u64 ulong2, void *ptr1, void *ptr2), - - TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2) - -); - -/*****************************************************************************/ -/* Trace events for rmnet_perf module */ -/*****************************************************************************/ -DEFINE_EVENT - (rmnet_mod_template, rmnet_perf_low, - - TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2, - u64 ulong1, u64 ulong2, void *ptr1, void *ptr2), - - TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2) - -); - -DEFINE_EVENT - (rmnet_mod_template, rmnet_perf_high, - - TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2, - u64 ulong1, u64 ulong2, void *ptr1, void *ptr2), - - TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2) - -); - -DEFINE_EVENT - (rmnet_mod_template, rmnet_perf_err, - - TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2, - u64 ulong1, u64 ulong2, void *ptr1, void *ptr2), - - TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2) - -); - -/*****************************************************************************/ -/* Trace events for rmnet_shs module */ -/*****************************************************************************/ -DEFINE_EVENT - (rmnet_mod_template, rmnet_shs_low, - - TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2, - u64 ulong1, u64 ulong2, void *ptr1, void *ptr2), - - TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2) - -); - -DEFINE_EVENT - (rmnet_mod_template, rmnet_shs_high, - - TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2, - u64 ulong1, u64 ulong2, void *ptr1, void *ptr2), - - TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2) - -); - -DEFINE_EVENT - (rmnet_mod_template, rmnet_shs_err, - - TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2, - u64 ulong1, u64 ulong2, void *ptr1, void *ptr2), - - TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2) - -); - -DEFINE_EVENT - (rmnet_mod_template, rmnet_shs_wq_low, - - TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2, - u64 ulong1, u64 ulong2, void *ptr1, void *ptr2), - - TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2) - -); - -DEFINE_EVENT - (rmnet_mod_template, rmnet_shs_wq_high, - - TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2, - u64 ulong1, u64 ulong2, void *ptr1, void *ptr2), - - TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2) - -); - -DEFINE_EVENT - (rmnet_mod_template, rmnet_shs_wq_err, - - TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2, - u64 ulong1, u64 ulong2, void *ptr1, void *ptr2), - - TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2) - -); - -DECLARE_EVENT_CLASS - (rmnet_freq_template, - - TP_PROTO(u8 core, u32 newfreq), - - TP_ARGS(core, newfreq), - - TP_STRUCT__entry( - __field(u8, core) - __field(u32, newfreq) - ), - - TP_fast_assign( - __entry->core = core; - __entry->newfreq = newfreq; - - ), - -TP_printk("freq policy core:%u freq floor :%u", - __entry->core, __entry->newfreq) - -); - -DEFINE_EVENT - (rmnet_freq_template, rmnet_freq_boost, - - TP_PROTO(u8 core, u32 newfreq), - - TP_ARGS(core, newfreq) -); - -DEFINE_EVENT - (rmnet_freq_template, rmnet_freq_reset, - - TP_PROTO(u8 core, u32 newfreq), - - TP_ARGS(core, newfreq) -); - -TRACE_EVENT - (rmnet_freq_update, - - TP_PROTO(u8 core, u32 lowfreq, u32 highfreq), - - TP_ARGS(core, lowfreq, highfreq), - - TP_STRUCT__entry( - __field(u8, core) - __field(u32, lowfreq) - __field(u32, highfreq) - ), - - TP_fast_assign( - __entry->core = core; - __entry->lowfreq = lowfreq; - __entry->highfreq = highfreq; - - ), - -TP_printk("freq policy update core:%u policy freq floor :%u freq ceil :%u", - __entry->core, __entry->lowfreq, __entry->highfreq) -); -#endif /* _RMNET_TRACE_H_ */ - -/* This part must be outside protection */ -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH ../drivers/net/ethernet/qualcomm/rmnet -#include <trace/define_trace.h> diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_vnd.c b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_vnd.c deleted file mode 100644 index 4ef645daf..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_vnd.c +++ /dev/null @@ -1,382 +0,0 @@ -/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * RMNET Data virtual network driver - * - */ - -#include <linux/etherdevice.h> -#include <linux/if_arp.h> -#include <linux/ip.h> -#include <net/pkt_sched.h> -#include "rmnet_config.h" -#include "rmnet_handlers.h" -#include "rmnet_private.h" -#include "rmnet_map.h" -#include "rmnet_vnd.h" - -/* RX/TX Fixup */ - -static void rmnet_vnd_rx_fixup(struct net_device *dev, u32 skb_len) -{ - struct rmnet_priv *priv = netdev_priv(dev); - struct rmnet_pcpu_stats *pcpu_ptr; - - pcpu_ptr = this_cpu_ptr(priv->pcpu_stats); - - u64_stats_update_begin(&pcpu_ptr->syncp); - pcpu_ptr->stats.rx_pkts++; - pcpu_ptr->stats.rx_bytes += skb_len; - u64_stats_update_end(&pcpu_ptr->syncp); -} - -static void rmnet_vnd_tx_fixup(struct net_device *dev, u32 skb_len) -{ - struct rmnet_priv *priv = netdev_priv(dev); - struct rmnet_pcpu_stats *pcpu_ptr; - - pcpu_ptr = this_cpu_ptr(priv->pcpu_stats); - - u64_stats_update_begin(&pcpu_ptr->syncp); - pcpu_ptr->stats.tx_pkts++; - pcpu_ptr->stats.tx_bytes += skb_len; - u64_stats_update_end(&pcpu_ptr->syncp); -} - -/* Network Device Operations */ - -static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct rmnet_priv *priv; - - priv = netdev_priv(dev); - if (priv->real_dev) { - rmnet_egress_handler(skb); - } else { - this_cpu_inc(priv->pcpu_stats->stats.tx_drops); - kfree_skb(skb); - } - return NETDEV_TX_OK; -} - -static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu) -{ - if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE) - return -EINVAL; - - rmnet_dev->mtu = new_mtu; - return 0; -} - -static int rmnet_vnd_get_iflink(const struct net_device *dev) -{ - struct rmnet_priv *priv = netdev_priv(dev); - - return priv->real_dev->ifindex; -} - -static int rmnet_vnd_init(struct net_device *dev) -{ - struct rmnet_priv *priv = netdev_priv(dev); - int err; - - priv->pcpu_stats = alloc_percpu(struct rmnet_pcpu_stats); - if (!priv->pcpu_stats) - return -ENOMEM; - - err = gro_cells_init(&priv->gro_cells, dev); - if (err) { - free_percpu(priv->pcpu_stats); - return err; - } - - return 0; -} - -static void rmnet_vnd_uninit(struct net_device *dev) -{ - struct rmnet_priv *priv = netdev_priv(dev); - - gro_cells_destroy(&priv->gro_cells); - free_percpu(priv->pcpu_stats); -} - -static struct rtnl_link_stats64* rmnet_get_stats64(struct net_device *dev, - struct rtnl_link_stats64 *s) -{ - struct rmnet_priv *priv = netdev_priv(dev); - struct rmnet_vnd_stats total_stats; - struct rmnet_pcpu_stats *pcpu_ptr; - unsigned int cpu, start; - - memset(&total_stats, 0, sizeof(struct rmnet_vnd_stats)); - - for_each_possible_cpu(cpu) { - pcpu_ptr = per_cpu_ptr(priv->pcpu_stats, cpu); - - do { - start = u64_stats_fetch_begin_irq(&pcpu_ptr->syncp); - total_stats.rx_pkts += pcpu_ptr->stats.rx_pkts; - total_stats.rx_bytes += pcpu_ptr->stats.rx_bytes; - total_stats.tx_pkts += pcpu_ptr->stats.tx_pkts; - total_stats.tx_bytes += pcpu_ptr->stats.tx_bytes; - } while (u64_stats_fetch_retry_irq(&pcpu_ptr->syncp, start)); - - total_stats.tx_drops += pcpu_ptr->stats.tx_drops; - } - - s->rx_packets = total_stats.rx_pkts; - s->rx_bytes = total_stats.rx_bytes; - s->tx_packets = total_stats.tx_pkts; - s->tx_bytes = total_stats.tx_bytes; - s->tx_dropped = total_stats.tx_drops; - - return s; -} - -static const struct net_device_ops rmnet_vnd_ops = { - .ndo_start_xmit = rmnet_vnd_start_xmit, - .ndo_change_mtu = rmnet_vnd_change_mtu, - .ndo_get_iflink = rmnet_vnd_get_iflink, - //.ndo_add_slave = rmnet_add_bridge, - //.ndo_del_slave = rmnet_del_bridge, - .ndo_init = rmnet_vnd_init, - .ndo_uninit = rmnet_vnd_uninit, - .ndo_get_stats64 = rmnet_get_stats64, -}; - -static const char rmnet_gstrings_stats[][ETH_GSTRING_LEN] = { - "Checksum ok", - "Checksum valid bit not set", - "Checksum validation failed", - "Checksum error bad buffer", - "Checksum error bad ip version", - "Checksum error bad transport", - "Checksum skipped on ip fragment", - "Checksum skipped", - "Checksum computed in software", - "Checksum computed in hardware", - "Coalescing packets received", - "Coalesced packets", - "Coalescing header NLO errors", - "Coalescing header pcount errors", - "Coalescing checksum errors", - "Coalescing packet reconstructs", - "Coalescing IP version invalid", - "Coalescing L4 header invalid", - "Coalescing close Non-coalescable", - "Coalescing close L3 mismatch", - "Coalescing close L4 mismatch", - "Coalescing close HW NLO limit", - "Coalescing close HW packet limit", - "Coalescing close HW byte limit", - "Coalescing close HW time limit", - "Coalescing close HW eviction", - "Coalescing close Coalescable", - "Coalescing packets over VEID0", - "Coalescing packets over VEID1", - "Coalescing packets over VEID2", - "Coalescing packets over VEID3", -}; - -static const char rmnet_port_gstrings_stats[][ETH_GSTRING_LEN] = { - "MAP Cmd last version", - "MAP Cmd last ep id", - "MAP Cmd last transaction id", - "DL header last seen sequence", - "DL header last seen bytes", - "DL header last seen packets", - "DL header last seen flows", - "DL header pkts received", - "DL header total bytes received", - "DL header total pkts received", - "DL trailer last seen sequence", - "DL trailer pkts received", -}; - -static void rmnet_get_strings(struct net_device *dev, u32 stringset, u8 *buf) -{ - switch (stringset) { - case ETH_SS_STATS: - memcpy(buf, &rmnet_gstrings_stats, - sizeof(rmnet_gstrings_stats)); - memcpy(buf + sizeof(rmnet_gstrings_stats), - &rmnet_port_gstrings_stats, - sizeof(rmnet_port_gstrings_stats)); - break; - } -} - -static int rmnet_get_sset_count(struct net_device *dev, int sset) -{ - switch (sset) { - case ETH_SS_STATS: - return ARRAY_SIZE(rmnet_gstrings_stats) + - ARRAY_SIZE(rmnet_port_gstrings_stats); - default: - return -EOPNOTSUPP; - } -} - -static void rmnet_get_ethtool_stats(struct net_device *dev, - struct ethtool_stats *stats, u64 *data) -{ - struct rmnet_priv *priv = netdev_priv(dev); - struct rmnet_priv_stats *st = &priv->stats; - struct rmnet_port_priv_stats *stp; - struct rmnet_port *port; - - port = rmnet_get_port(priv->real_dev); - - if (!data || !port) - return; - - stp = &port->stats; - - memcpy(data, st, ARRAY_SIZE(rmnet_gstrings_stats) * sizeof(u64)); - memcpy(data + ARRAY_SIZE(rmnet_gstrings_stats), stp, - ARRAY_SIZE(rmnet_port_gstrings_stats) * sizeof(u64)); -} - -static int rmnet_stats_reset(struct net_device *dev) -{ - struct rmnet_priv *priv = netdev_priv(dev); - struct rmnet_port_priv_stats *stp; - struct rmnet_port *port; - - port = rmnet_get_port(priv->real_dev); - if (!port) - return -EINVAL; - - stp = &port->stats; - - memset(stp, 0, sizeof(*stp)); - return 0; -} - -static const struct ethtool_ops rmnet_ethtool_ops = { - .get_ethtool_stats = rmnet_get_ethtool_stats, - .get_strings = rmnet_get_strings, - .get_sset_count = rmnet_get_sset_count, - .nway_reset = rmnet_stats_reset, -}; - -/* Called by kernel whenever a new rmnet<n> device is created. Sets MTU, - * flags, ARP type, needed headroom, etc... - */ -void rmnet_vnd_setup(struct net_device *rmnet_dev) -{ - rmnet_dev->netdev_ops = &rmnet_vnd_ops; - rmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE; - rmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM; - random_ether_addr(rmnet_dev->dev_addr); - rmnet_dev->tx_queue_len = RMNET_TX_QUEUE_LEN; - - /* Raw IP mode */ - rmnet_dev->header_ops = NULL; /* No header */ - rmnet_dev->type = ARPHRD_RAWIP; - rmnet_dev->hard_header_len = 0; - rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); - - //rmnet_dev->needs_free_netdev = true; - - rmnet_dev->hw_features = NETIF_F_RXCSUM; - rmnet_dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; - //rmnet_dev->hw_features |= NETIF_F_SG; - //rmnet_dev->hw_features |= NETIF_F_GRO_HW; -} - -/* Exposed API */ - -static int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, - struct rmnet_port *port, - struct net_device *real_dev, - struct rmnet_endpoint *ep) -{ - struct rmnet_priv *priv = netdev_priv(rmnet_dev); - struct rmnet_nss_cb *nss_cb; - int rc; - - if (ep->egress_dev) - return -EINVAL; - - if (rmnet_get_endpoint(port, id)) - return -EBUSY; - - rmnet_dev->hw_features = NETIF_F_RXCSUM; - rmnet_dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; - rmnet_dev->hw_features |= NETIF_F_SG; - - priv->real_dev = real_dev; - - rc = register_netdevice(rmnet_dev); - if (!rc) { - ep->egress_dev = rmnet_dev; - ep->mux_id = id; - port->nr_rmnet_devs++; - - //rmnet_dev->rtnl_link_ops = &rmnet_link_ops; - - priv->mux_id = id; - - netdev_dbg(rmnet_dev, "rmnet dev created\n"); - } - - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (nss_cb) { - rc = nss_cb->nss_create(rmnet_dev); - if (rc) { - /* Log, but don't fail the device creation */ - netdev_err(rmnet_dev, "Device will not use NSS path: %d\n", rc); - rc = 0; - } else { - netdev_dbg(rmnet_dev, "NSS context created\n"); - } - } - - return rc; -} - -static int rmnet_vnd_dellink(u8 id, struct rmnet_port *port, - struct rmnet_endpoint *ep) -{ - struct rmnet_nss_cb *nss_cb; - - if (id >= RMNET_MAX_LOGICAL_EP || !ep->egress_dev) - return -EINVAL; - - if (ep->egress_dev) { - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (nss_cb) - nss_cb->nss_free(ep->egress_dev); - } - ep->egress_dev = NULL; - port->nr_rmnet_devs--; - - return 0; -} - -static int rmnet_vnd_do_flow_control(struct net_device *rmnet_dev, int enable) -{ - netdev_dbg(rmnet_dev, "Setting VND TX queue state to %d\n", enable); - /* Although we expect similar number of enable/disable - * commands, optimize for the disable. That is more - * latency sensitive than enable - */ - if (unlikely(enable)) - netif_wake_queue(rmnet_dev); - else - netif_stop_queue(rmnet_dev); - - return 0; -} diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_vnd.h b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_vnd.h deleted file mode 100644 index f8a65a953..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_vnd.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * RMNET Data Virtual Network Device APIs - * - */ - -#ifndef _RMNET_VND_H_ -#define _RMNET_VND_H_ - -static int rmnet_vnd_do_flow_control(struct net_device *dev, int enable); -static int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, - struct rmnet_port *port, - struct net_device *real_dev, - struct rmnet_endpoint *ep); -static int rmnet_vnd_dellink(u8 id, struct rmnet_port *port, - struct rmnet_endpoint *ep); -static void rmnet_vnd_rx_fixup(struct net_device *dev, u32 skb_len); -static void rmnet_vnd_tx_fixup(struct net_device *dev, u32 skb_len); -static void rmnet_vnd_setup(struct net_device *dev); -#endif /* _RMNET_VND_H_ */ diff --git a/package/wwan/quectel_MHI/src/devices/rmnet_handler.c b/package/wwan/quectel_MHI/src/devices/rmnet_handler.c deleted file mode 100644 index b10026292..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet_handler.c +++ /dev/null @@ -1,1129 +0,0 @@ -#if 0 - -#define RMNET_MAX_PACKET_SIZE 16384 -#define RMNET_DFLT_PACKET_SIZE 1500 -#define RMNET_NEEDED_HEADROOM 16 -#define RMNET_TX_QUEUE_LEN 1000 - -#define RMNET_MAX_LOGICAL_EP 255 -#define RMNET_MAP_DESC_HEADROOM 128 -#define RMNET_FRAG_DESCRIPTOR_POOL_SIZE 64 - -/* Pass the frame up the stack with no modifications to skb->dev */ -#define RMNET_EPMODE_NONE (0) -/* Replace skb->dev to a virtual rmnet device and pass up the stack */ -#define RMNET_EPMODE_VND (1) -/* Pass the frame directly to another device with dev_queue_xmit() */ -#define RMNET_EPMODE_BRIDGE (2) - -/* rmnet section */ - -#define RMNET_FLAGS_INGRESS_DEAGGREGATION (1U << 0) -#define RMNET_FLAGS_INGRESS_MAP_COMMANDS (1U << 1) -#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4 (1U << 2) -#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4 (1U << 3) -#define RMNET_FLAGS_INGRESS_COALESCE (1U << 4) -#define RMNET_FLAGS_INGRESS_MAP_CKSUMV5 (1U << 5) -#define RMNET_FLAGS_EGRESS_MAP_CKSUMV5 (1U << 6) - -enum rmnet_map_v5_header_type { - RMNET_MAP_HEADER_TYPE_UNKNOWN, - RMNET_MAP_HEADER_TYPE_COALESCING = 0x1, - RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD = 0x2, - RMNET_MAP_HEADER_TYPE_ENUM_LENGTH -}; - -/* Main QMAP header */ -struct rmnet_map_header { - u8 pad_len:6; - u8 next_hdr:1; - u8 cd_bit:1; - u8 mux_id; - __be16 pkt_len; -} __aligned(1); - -/* QMAP v5 headers */ -struct rmnet_map_v5_csum_header { - u8 next_hdr:1; - u8 header_type:7; - u8 hw_reserved:7; - u8 csum_valid_required:1; - __be16 reserved; -} __aligned(1); - -struct rmnet_map_v5_nl_pair { - __be16 pkt_len; - u8 csum_error_bitmap; - u8 num_packets; -} __aligned(1); - -/* NLO: Number-length object */ -#define RMNET_MAP_V5_MAX_NLOS (6) -#define RMNET_MAP_V5_MAX_PACKETS (48) - -struct rmnet_map_v5_coal_header { - u8 next_hdr:1; - u8 header_type:7; - u8 reserved1:4; - u8 num_nlos:3; - u8 csum_valid:1; - u8 close_type:4; - u8 close_value:4; - u8 reserved2:4; - u8 virtual_channel_id:4; - - struct rmnet_map_v5_nl_pair nl_pairs[RMNET_MAP_V5_MAX_NLOS]; -} __aligned(1); - -/* QMAP v4 headers */ -struct rmnet_map_dl_csum_trailer { - u8 reserved1; - u8 valid:1; - u8 reserved2:7; - u16 csum_start_offset; - u16 csum_length; - __be16 csum_value; -} __aligned(1); - -struct rmnet_frag_descriptor_pool { - struct list_head free_list; - u32 pool_size; -}; - -struct rmnet_frag_descriptor { - struct list_head list; - struct list_head sub_frags; - skb_frag_t frag; - u8 *hdr_ptr; - struct net_device *dev; - u32 hash; - __be32 tcp_seq; - __be16 ip_id; - u16 data_offset; - u16 gso_size; - u16 gso_segs; - u16 ip_len; - u16 trans_len; - u8 ip_proto; - u8 trans_proto; - u8 pkt_id; - u8 csum_valid:1, - hdrs_valid:1, - ip_id_set:1, - tcp_seq_set:1, - flush_shs:1, - reserved:3; -}; - -struct rmnet_endpoint { - u8 rmnet_mode; - u8 mux_id; - struct net_device *rmnet_dev; -}; - -/* One instance of this structure is instantiated for each real_dev associated - * with rmnet. - */ -struct rmnet_port { - struct net_device *dev; - u8 rmnet_mode; - u32 data_format; - u32 nr_rmnet_devs; - struct rmnet_endpoint muxed_ep[16]; - - /* Descriptor pool */ - spinlock_t desc_pool_lock; - struct rmnet_frag_descriptor_pool *frag_desc_pool; - struct sk_buff *chain_head; - struct sk_buff *chain_tail; -}; - -static struct sk_buff * add_qhdr_v5(struct sk_buff *skb, u8 mux_id) -{ - struct rmnet_map_header *map_header; - struct rmnet_map_v5_csum_header *ul_header; - u32 padding, map_datalen; - - map_datalen = skb->len; - padding = map_datalen%4; - if (padding) { - padding = 4 - padding; - if (skb_tailroom(skb) < padding) { - printk("skb_tailroom small!\n"); - padding = 0; - } - if (padding) - __skb_put(skb, padding); - } - - map_header = (struct rmnet_map_header *)skb_push(skb, (sizeof(struct rmnet_map_header) + sizeof(struct rmnet_map_v5_csum_header))); - - BUILD_BUG_ON((sizeof(struct rmnet_map_header) + sizeof(struct rmnet_map_v5_csum_header)) != 8); - - map_header->cd_bit = 0; - map_header->next_hdr = 1; - map_header->pad_len = padding; - map_header->mux_id = mux_id; - map_header->pkt_len = htons(map_datalen + padding); - - ul_header = (struct rmnet_map_v5_csum_header *)(map_header + 1); - memset(ul_header, 0, sizeof(*ul_header)); - ul_header->header_type = RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD; - - return skb; -} - -struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id) -{ - return &port->muxed_ep[0]; -} - -static void -rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_port *port) -{ - struct rmnet_nss_cb *nss_cb; - - //rmnet_vnd_rx_fixup(skb->dev, skb->len); - - /* Pass off the packet to NSS driver if we can */ - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (nss_cb) { - if (!port->chain_head) - port->chain_head = skb; - else - skb_shinfo(port->chain_tail)->frag_list = skb; - - port->chain_tail = skb; - return; - } - - skb_reset_transport_header(skb); - skb_reset_network_header(skb); - - skb->pkt_type = PACKET_HOST; - skb_set_mac_header(skb, 0); - - //if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) { - //} else { - //if (!rmnet_check_skb_can_gro(skb)) - // gro_cells_receive(&priv->gro_cells, skb); - //else - netif_receive_skb(skb); - //} -} - -static inline unsigned char *rmnet_map_data_ptr(struct sk_buff *skb) -{ - /* Nonlinear packets we receive are entirely within frag 0 */ - if (skb_is_nonlinear(skb) && skb->len == skb->data_len) - return skb_frag_address(skb_shinfo(skb)->frags); - - return skb->data; -} - -static inline void *rmnet_frag_data_ptr(struct rmnet_frag_descriptor *frag_desc) -{ - return skb_frag_address(&frag_desc->frag); -} - -static struct rmnet_frag_descriptor * -rmnet_get_frag_descriptor(struct rmnet_port *port) -{ - struct rmnet_frag_descriptor_pool *pool = port->frag_desc_pool; - struct rmnet_frag_descriptor *frag_desc; - - spin_lock(&port->desc_pool_lock); - if (!list_empty(&pool->free_list)) { - frag_desc = list_first_entry(&pool->free_list, - struct rmnet_frag_descriptor, - list); - list_del_init(&frag_desc->list); - } else { - frag_desc = kzalloc(sizeof(*frag_desc), GFP_ATOMIC); - if (!frag_desc) - goto out; - - INIT_LIST_HEAD(&frag_desc->list); - INIT_LIST_HEAD(&frag_desc->sub_frags); - pool->pool_size++; - } - -out: - spin_unlock(&port->desc_pool_lock); - return frag_desc; -} - -static void rmnet_recycle_frag_descriptor(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port) -{ - struct rmnet_frag_descriptor_pool *pool = port->frag_desc_pool; - struct page *page = skb_frag_page(&frag_desc->frag); - - list_del(&frag_desc->list); - if (page) - put_page(page); - - memset(frag_desc, 0, sizeof(*frag_desc)); - INIT_LIST_HEAD(&frag_desc->list); - INIT_LIST_HEAD(&frag_desc->sub_frags); - spin_lock(&port->desc_pool_lock); - list_add_tail(&frag_desc->list, &pool->free_list); - spin_unlock(&port->desc_pool_lock); -} - -static inline void rmnet_frag_fill(struct rmnet_frag_descriptor *frag_desc, - struct page *p, u32 page_offset, u32 len) -{ - get_page(p); - __skb_frag_set_page(&frag_desc->frag, p); - skb_frag_size_set(&frag_desc->frag, len); - frag_desc->frag.page_offset = page_offset; -} - -static inline void *rmnet_frag_pull(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port, - unsigned int size) -{ - if (size >= skb_frag_size(&frag_desc->frag)) { - pr_info("%s(): Pulling %u bytes from %u byte pkt. Dropping\n", - __func__, size, skb_frag_size(&frag_desc->frag)); - rmnet_recycle_frag_descriptor(frag_desc, port); - return NULL; - } - - frag_desc->frag.page_offset += size; - skb_frag_size_sub(&frag_desc->frag, size); - - return rmnet_frag_data_ptr(frag_desc); -} - -static inline void *rmnet_frag_trim(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port, - unsigned int size) -{ - if (!size) { - pr_info("%s(): Trimming %u byte pkt to 0. Dropping\n", - __func__, skb_frag_size(&frag_desc->frag)); - rmnet_recycle_frag_descriptor(frag_desc, port); - return NULL; - } - - if (size < skb_frag_size(&frag_desc->frag)) - skb_frag_size_set(&frag_desc->frag, size); - - return rmnet_frag_data_ptr(frag_desc); -} - -static inline u8 -rmnet_frag_get_next_hdr_type(struct rmnet_frag_descriptor *frag_desc) -{ - unsigned char *data = rmnet_frag_data_ptr(frag_desc); - - data += sizeof(struct rmnet_map_header); - return ((struct rmnet_map_v5_coal_header *)data)->header_type; -} - -static inline bool -rmnet_frag_get_csum_valid(struct rmnet_frag_descriptor *frag_desc) -{ - unsigned char *data = rmnet_frag_data_ptr(frag_desc); - - data += sizeof(struct rmnet_map_header); - return ((struct rmnet_map_v5_csum_header *)data)->csum_valid_required; -} - -static void rmnet_descriptor_add_frag(struct rmnet_port *port, struct list_head *list, - struct page *p, u32 page_offset, u32 len) -{ - struct rmnet_frag_descriptor *frag_desc; - - frag_desc = rmnet_get_frag_descriptor(port); - if (!frag_desc) - return; - - rmnet_frag_fill(frag_desc, p, page_offset, len); - list_add_tail(&frag_desc->list, list); -} - -static void rmnet_frag_deaggregate(skb_frag_t *frag, struct rmnet_port *port, - struct list_head *list) -{ - struct rmnet_map_header *maph; - u8 *data = skb_frag_address(frag); - u32 offset = 0; - u32 packet_len; - - while (offset < skb_frag_size(frag)) { - maph = (struct rmnet_map_header *)data; - packet_len = ntohs(maph->pkt_len); - - /* Some hardware can send us empty frames. Catch them */ - if (packet_len == 0) - return; - - packet_len += sizeof(*maph); - - if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) { - packet_len += sizeof(struct rmnet_map_dl_csum_trailer); - WARN_ON(1); - } else if (port->data_format & - (RMNET_FLAGS_INGRESS_MAP_CKSUMV5 | - RMNET_FLAGS_INGRESS_COALESCE) && !maph->cd_bit) { - u32 hsize = 0; - u8 type; - - type = ((struct rmnet_map_v5_coal_header *) - (data + sizeof(*maph)))->header_type; - switch (type) { - case RMNET_MAP_HEADER_TYPE_COALESCING: - hsize = sizeof(struct rmnet_map_v5_coal_header); - break; - case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD: - hsize = sizeof(struct rmnet_map_v5_csum_header); - break; - } - - packet_len += hsize; - } - else { - qmap_hex_dump(__func__, data, 64); - WARN_ON(1); - } - - if ((int)skb_frag_size(frag) - (int)packet_len < 0) - return; - - rmnet_descriptor_add_frag(port, list, skb_frag_page(frag), - frag->page_offset + offset, - packet_len); - - offset += packet_len; - data += packet_len; - } -} - - -#define RMNET_IP_VERSION_4 0x40 -#define RMNET_IP_VERSION_6 0x60 - -/* Helper Functions */ - -static void rmnet_set_skb_proto(struct sk_buff *skb) -{ - switch (rmnet_map_data_ptr(skb)[0] & 0xF0) { - case RMNET_IP_VERSION_4: - skb->protocol = htons(ETH_P_IP); - break; - case RMNET_IP_VERSION_6: - skb->protocol = htons(ETH_P_IPV6); - break; - default: - skb->protocol = htons(ETH_P_MAP); - WARN_ON(1); - break; - } -} - -/* Allocate and populate an skb to contain the packet represented by the - * frag descriptor. - */ -static struct sk_buff *rmnet_alloc_skb(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port) -{ - struct sk_buff *head_skb, *current_skb, *skb; - struct skb_shared_info *shinfo; - struct rmnet_frag_descriptor *sub_frag, *tmp; - - /* Use the exact sizes if we know them (i.e. RSB/RSC, rmnet_perf) */ - if (frag_desc->hdrs_valid) { - u16 hdr_len = frag_desc->ip_len + frag_desc->trans_len; - - head_skb = alloc_skb(hdr_len + RMNET_MAP_DESC_HEADROOM, - GFP_ATOMIC); - if (!head_skb) - return NULL; - - skb_reserve(head_skb, RMNET_MAP_DESC_HEADROOM); - skb_put_data(head_skb, frag_desc->hdr_ptr, hdr_len); - skb_reset_network_header(head_skb); - - if (frag_desc->trans_len) - skb_set_transport_header(head_skb, frag_desc->ip_len); - - /* Packets that have no data portion don't need any frags */ - if (hdr_len == skb_frag_size(&frag_desc->frag)) - goto skip_frags; - - /* If the headers we added are the start of the page, - * we don't want to add them twice - */ - if (frag_desc->hdr_ptr == rmnet_frag_data_ptr(frag_desc)) { - if (!rmnet_frag_pull(frag_desc, port, hdr_len)) { - kfree_skb(head_skb); - return NULL; - } - } - } else { - /* Allocate enough space to avoid penalties in the stack - * from __pskb_pull_tail() - */ - head_skb = alloc_skb(256 + RMNET_MAP_DESC_HEADROOM, - GFP_ATOMIC); - if (!head_skb) - return NULL; - - skb_reserve(head_skb, RMNET_MAP_DESC_HEADROOM); - } - - /* Add main fragment */ - get_page(skb_frag_page(&frag_desc->frag)); - skb_add_rx_frag(head_skb, 0, skb_frag_page(&frag_desc->frag), - frag_desc->frag.page_offset, - skb_frag_size(&frag_desc->frag), - skb_frag_size(&frag_desc->frag)); - - shinfo = skb_shinfo(head_skb); - current_skb = head_skb; - - /* Add in any frags from rmnet_perf */ - list_for_each_entry_safe(sub_frag, tmp, &frag_desc->sub_frags, list) { - skb_frag_t *frag; - u32 frag_size; - - frag = &sub_frag->frag; - frag_size = skb_frag_size(frag); - -add_frag: - if (shinfo->nr_frags < MAX_SKB_FRAGS) { - get_page(skb_frag_page(frag)); - skb_add_rx_frag(current_skb, shinfo->nr_frags, - skb_frag_page(frag), frag->page_offset, - frag_size, frag_size); - if (current_skb != head_skb) { - head_skb->len += frag_size; - head_skb->data_len += frag_size; - } - } else { - /* Alloc a new skb and try again */ - skb = alloc_skb(0, GFP_ATOMIC); - if (!skb) - break; - - if (current_skb == head_skb) - shinfo->frag_list = skb; - else - current_skb->next = skb; - - current_skb = skb; - shinfo = skb_shinfo(current_skb); - goto add_frag; - } - - rmnet_recycle_frag_descriptor(sub_frag, port); - } - -skip_frags: - head_skb->dev = frag_desc->dev; - rmnet_set_skb_proto(head_skb); - - /* Handle any header metadata that needs to be updated after RSB/RSC - * segmentation - */ - if (frag_desc->ip_id_set) { - struct iphdr *iph; - - iph = (struct iphdr *)rmnet_map_data_ptr(head_skb); - csum_replace2(&iph->check, iph->id, frag_desc->ip_id); - iph->id = frag_desc->ip_id; - } - - if (frag_desc->tcp_seq_set) { - struct tcphdr *th; - - th = (struct tcphdr *) - (rmnet_map_data_ptr(head_skb) + frag_desc->ip_len); - th->seq = frag_desc->tcp_seq; - } - - /* Handle csum offloading */ - if (frag_desc->csum_valid && frag_desc->hdrs_valid) { - /* Set the partial checksum information */ - //rmnet_frag_partial_csum(head_skb, frag_desc); - WARN_ON(1); - } else if (frag_desc->csum_valid) { - /* Non-RSB/RSC/perf packet. The current checksum is fine */ - head_skb->ip_summed = CHECKSUM_UNNECESSARY; - } else if (frag_desc->hdrs_valid && - (frag_desc->trans_proto == IPPROTO_TCP || - frag_desc->trans_proto == IPPROTO_UDP)) { - /* Unfortunately, we have to fake a bad checksum here, since - * the original bad value is lost by the hardware. The only - * reliable way to do it is to calculate the actual checksum - * and corrupt it. - */ - __sum16 *check; - __wsum csum; - unsigned int offset = skb_transport_offset(head_skb); - __sum16 pseudo; - - WARN_ON(1); - /* Calculate pseudo header and update header fields */ - if (frag_desc->ip_proto == 4) { - struct iphdr *iph = ip_hdr(head_skb); - __be16 tot_len = htons(head_skb->len); - - csum_replace2(&iph->check, iph->tot_len, tot_len); - iph->tot_len = tot_len; - pseudo = ~csum_tcpudp_magic(iph->saddr, iph->daddr, - head_skb->len - - frag_desc->ip_len, - frag_desc->trans_proto, 0); - } else { - struct ipv6hdr *ip6h = ipv6_hdr(head_skb); - - ip6h->payload_len = htons(head_skb->len - - sizeof(*ip6h)); - pseudo = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, - head_skb->len - - frag_desc->ip_len, - frag_desc->trans_proto, 0); - } - - if (frag_desc->trans_proto == IPPROTO_TCP) { - check = &tcp_hdr(head_skb)->check; - } else { - udp_hdr(head_skb)->len = htons(head_skb->len - - frag_desc->ip_len); - check = &udp_hdr(head_skb)->check; - } - - *check = pseudo; - csum = skb_checksum(head_skb, offset, head_skb->len - offset, - 0); - /* Add 1 to corrupt. This cannot produce a final value of 0 - * since csum_fold() can't return a value of 0xFFFF - */ - *check = csum16_add(csum_fold(csum), htons(1)); - head_skb->ip_summed = CHECKSUM_NONE; - } - - /* Handle any rmnet_perf metadata */ - if (frag_desc->hash) { - head_skb->hash = frag_desc->hash; - head_skb->sw_hash = 1; - } - - if (frag_desc->flush_shs) - head_skb->cb[0] = 1; - - /* Handle coalesced packets */ - //if (frag_desc->gso_segs > 1) - // rmnet_frag_gso_stamp(head_skb, frag_desc); - - return head_skb; -} - -/* Deliver the packets contained within a frag descriptor */ -static void rmnet_frag_deliver(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port) -{ - struct sk_buff *skb; - - skb = rmnet_alloc_skb(frag_desc, port); - if (skb) - rmnet_deliver_skb(skb, port); - rmnet_recycle_frag_descriptor(frag_desc, port); -} - -/* Process a QMAPv5 packet header */ -static int rmnet_frag_process_next_hdr_packet(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port, - struct list_head *list, - u16 len) -{ - int rc = 0; - - switch (rmnet_frag_get_next_hdr_type(frag_desc)) { - case RMNET_MAP_HEADER_TYPE_COALESCING: - rc = -1; - WARN_ON(1); - break; - case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD: - if (rmnet_frag_get_csum_valid(frag_desc)) { - frag_desc->csum_valid = true; - } else { - } - - if (!rmnet_frag_pull(frag_desc, port, - sizeof(struct rmnet_map_header) + - sizeof(struct rmnet_map_v5_csum_header))) { - rc = -EINVAL; - break; - } - - frag_desc->hdr_ptr = rmnet_frag_data_ptr(frag_desc); - - /* Remove padding only for csum offload packets. - * Coalesced packets should never have padding. - */ - if (!rmnet_frag_trim(frag_desc, port, len)) { - rc = -EINVAL; - break; - } - - list_del_init(&frag_desc->list); - list_add_tail(&frag_desc->list, list); - break; - default: - qmap_hex_dump(__func__, rmnet_frag_data_ptr(frag_desc), 64); - rc = -EINVAL; - break; - } - - return rc; -} - -static void -__rmnet_frag_ingress_handler(struct rmnet_frag_descriptor *frag_desc, - struct rmnet_port *port) -{ - struct rmnet_map_header *qmap; - struct rmnet_endpoint *ep; - struct rmnet_frag_descriptor *frag, *tmp; - LIST_HEAD(segs); - u16 len, pad; - u8 mux_id; - - qmap = (struct rmnet_map_header *)skb_frag_address(&frag_desc->frag); - mux_id = qmap->mux_id; - pad = qmap->pad_len; - len = ntohs(qmap->pkt_len) - pad; - - if (qmap->cd_bit) { - goto recycle; - } - - if (mux_id >= RMNET_MAX_LOGICAL_EP) - goto recycle; - - ep = rmnet_get_endpoint(port, mux_id); - if (!ep) - goto recycle; - - frag_desc->dev = ep->rmnet_dev; - - /* Handle QMAPv5 packet */ - if (qmap->next_hdr && - (port->data_format & (RMNET_FLAGS_INGRESS_COALESCE | - RMNET_FLAGS_INGRESS_MAP_CKSUMV5))) { - if (rmnet_frag_process_next_hdr_packet(frag_desc, port, &segs, - len)) - goto recycle; - } else { - /* We only have the main QMAP header to worry about */ - if (!rmnet_frag_pull(frag_desc, port, sizeof(*qmap))) - return; - - frag_desc->hdr_ptr = rmnet_frag_data_ptr(frag_desc); - - if (!rmnet_frag_trim(frag_desc, port, len)) - return; - - list_add_tail(&frag_desc->list, &segs); - } - - list_for_each_entry_safe(frag, tmp, &segs, list) { - list_del_init(&frag->list); - rmnet_frag_deliver(frag, port); - } - return; - -recycle: - rmnet_recycle_frag_descriptor(frag_desc, port); -} - -static void rmnet_frag_ingress_handler(struct sk_buff *skb, - struct rmnet_port *port) -{ - LIST_HEAD(desc_list); - int i = 0; - struct rmnet_nss_cb *nss_cb; - - /* Deaggregation and freeing of HW originating - * buffers is done within here - */ - while (skb) { - struct sk_buff *skb_frag; - - port->chain_head = NULL; - port->chain_tail = NULL; - - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - rmnet_frag_deaggregate(&skb_shinfo(skb)->frags[i], port, - &desc_list); - if (!list_empty(&desc_list)) { - struct rmnet_frag_descriptor *frag_desc, *tmp; - - list_for_each_entry_safe(frag_desc, tmp, - &desc_list, list) { - list_del_init(&frag_desc->list); - __rmnet_frag_ingress_handler(frag_desc, - port); - } - } - } - - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (nss_cb && port->chain_head) { - port->chain_head->cb[0] = 0; - netif_receive_skb(port->chain_head); - } - - skb_frag = skb_shinfo(skb)->frag_list; - skb_shinfo(skb)->frag_list = NULL; - consume_skb(skb); - skb = skb_frag; - } -} - -static void -rmnet_map_ingress_handler(struct sk_buff *skb, - struct rmnet_port *port) -{ - if (port->data_format & (RMNET_FLAGS_INGRESS_COALESCE | - RMNET_FLAGS_INGRESS_MAP_CKSUMV5)) { - if (skb_is_nonlinear(skb)) { - rmnet_frag_ingress_handler(skb, port); - return; - } - } - - WARN_ON(1); -} - -static rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb); -static int rmnet_is_real_dev_registered(const struct net_device *real_dev) -{ - return rcu_access_pointer(real_dev->rx_handler) == rmnet_rx_handler; -} - - -/* Needs either rcu_read_lock() or rtnl lock */ -struct rmnet_port *rmnet_get_port(struct net_device *real_dev) -{ - if (rmnet_is_real_dev_registered(real_dev)) - return rcu_dereference_rtnl(real_dev->rx_handler_data); - else - return NULL; -} - -static rx_handler_result_t rmnet_rx_priv_handler(struct sk_buff **pskb) -{ - struct sk_buff *skb = *pskb; - struct rmnet_nss_cb *nss_cb; - - if (!skb) - return RX_HANDLER_CONSUMED; - if (nss_debug) printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - - if (skb->pkt_type == PACKET_LOOPBACK) - return RX_HANDLER_PASS; - - /* Check this so that we dont loop around netif_receive_skb */ - if (skb->cb[0] == 1) { - skb->cb[0] = 0; - - skb->dev->stats.rx_packets++; - return RX_HANDLER_PASS; - } - - while (skb) { - struct sk_buff *skb_frag = skb_shinfo(skb)->frag_list; - - skb_shinfo(skb)->frag_list = NULL; - - nss_cb = rcu_dereference(rmnet_nss_callbacks); - if (nss_cb) - nss_cb->nss_tx(skb); - - skb = skb_frag; - } - - return RX_HANDLER_CONSUMED; -} - -/* Ingress / Egress Entry Points */ - -/* Processes packet as per ingress data format for receiving device. Logical - * endpoint is determined from packet inspection. Packet is then sent to the - * egress device listed in the logical endpoint configuration. - */ -static rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) -{ - struct sk_buff *skb = *pskb; - struct rmnet_port *port; - struct net_device *dev; - - if (!skb) - goto done; - - if (nss_debug) printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - - if (skb->pkt_type == PACKET_LOOPBACK) - return RX_HANDLER_PASS; - - if (skb->protocol != htons(ETH_P_MAP)) { - WARN_ON(1); - return RX_HANDLER_PASS; - } - - dev = skb->dev; - port = rmnet_get_port(dev); - - if (port == NULL) - return RX_HANDLER_PASS; - - port->chain_head = NULL; - port->chain_tail = NULL; - - switch (port->rmnet_mode) { - case RMNET_EPMODE_VND: - rmnet_map_ingress_handler(skb, port); - break; - case RMNET_EPMODE_BRIDGE: - //rmnet_bridge_handler(skb, port->bridge_ep); - break; - } - -done: - return RX_HANDLER_CONSUMED; -} - -static void rmnet_descriptor_deinit(struct rmnet_port *port) -{ - struct rmnet_frag_descriptor_pool *pool; - struct rmnet_frag_descriptor *frag_desc, *tmp; - - pool = port->frag_desc_pool; - - list_for_each_entry_safe(frag_desc, tmp, &pool->free_list, list) { - kfree(frag_desc); - pool->pool_size--; - } - - kfree(pool); -} - -static int rmnet_descriptor_init(struct rmnet_port *port) -{ - struct rmnet_frag_descriptor_pool *pool; - int i; - - spin_lock_init(&port->desc_pool_lock); - pool = kzalloc(sizeof(*pool), GFP_ATOMIC); - if (!pool) - return -ENOMEM; - - INIT_LIST_HEAD(&pool->free_list); - port->frag_desc_pool = pool; - - for (i = 0; i < RMNET_FRAG_DESCRIPTOR_POOL_SIZE; i++) { - struct rmnet_frag_descriptor *frag_desc; - - frag_desc = kzalloc(sizeof(*frag_desc), GFP_ATOMIC); - if (!frag_desc) - return -ENOMEM; - - INIT_LIST_HEAD(&frag_desc->list); - INIT_LIST_HEAD(&frag_desc->sub_frags); - list_add_tail(&frag_desc->list, &pool->free_list); - pool->pool_size++; - } - - return 0; -} - -struct rmnet_priv { - //struct rmnet_endpoint local_ep; - struct net_device *real_dev; - u8 mux_id; -}; - -static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct rmnet_priv *priv; - - if (nss_debug) printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len); - - priv = netdev_priv(dev); - if (priv->real_dev) { - add_qhdr_v5(skb, priv->mux_id); - skb->protocol = htons(ETH_P_MAP); - skb->dev = priv->real_dev; - dev_queue_xmit(skb); - dev->stats.tx_packets++; - //rmnet_egress_handler(skb); - } else { - //this_cpu_inc(priv->pcpu_stats->stats.tx_drops); - kfree_skb(skb); - } - return NETDEV_TX_OK; -} - -static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu) -{ - if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE) - return -EINVAL; - - rmnet_dev->mtu = new_mtu; - return 0; -} - -static const struct net_device_ops rmnet_vnd_ops = { - .ndo_start_xmit = rmnet_vnd_start_xmit, - .ndo_change_mtu = rmnet_vnd_change_mtu, - //.ndo_get_iflink = rmnet_vnd_get_iflink, - //.ndo_add_slave = rmnet_add_bridge, - //.ndo_del_slave = rmnet_del_bridge, - //.ndo_init = rmnet_vnd_init, - //.ndo_uninit = rmnet_vnd_uninit, - //.ndo_get_stats64 = rmnet_get_stats64, -}; - -static void rmnet_vnd_setup(struct net_device *rmnet_dev) -{ - rmnet_dev->netdev_ops = &rmnet_vnd_ops; - rmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE; - rmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM; - random_ether_addr(rmnet_dev->dev_addr); - rmnet_dev->tx_queue_len = RMNET_TX_QUEUE_LEN; - - /* Raw IP mode */ - rmnet_dev->header_ops = NULL; /* No header */ - rmnet_dev->type = ARPHRD_RAWIP; - rmnet_dev->hard_header_len = 0; - rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); - - //rmnet_dev->needs_free_netdev = true; - - rmnet_dev->hw_features = NETIF_F_RXCSUM; - rmnet_dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; - //rmnet_dev->hw_features |= NETIF_F_SG; - //rmnet_dev->hw_features |= NETIF_F_GRO_HW; -} -#else -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <linux/if_arp.h> -#include <linux/device.h> -#include <linux/errno.h> -#include <linux/etherdevice.h> - -static uint nss_debug = 0; -module_param( nss_debug, uint, S_IRUGO | S_IWUSR); - -/* rmnet section */ - -#define RMNET_FLAGS_INGRESS_DEAGGREGATION (1U << 0) -#define RMNET_FLAGS_INGRESS_MAP_COMMANDS (1U << 1) -#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4 (1U << 2) -#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4 (1U << 3) -#define RMNET_FLAGS_INGRESS_COALESCE (1U << 4) -#define RMNET_FLAGS_INGRESS_MAP_CKSUMV5 (1U << 5) -#define RMNET_FLAGS_EGRESS_MAP_CKSUMV5 (1U << 6) - -#ifdef CONFIG_ARCH_IPQ807x -#define CONFIG_QCA_NSS_DRV -#endif -#ifdef CONFIG_QCA_NSS_DRV -#include "rmnet/rmnet_nss.c" -#else -#include "rmnet_nss.h" -#endif - -#include "rmnet/rmnet_vnd.c" -#include "rmnet/rmnet_map_command.c" -#include "rmnet/rmnet_map_data.c" -#include "rmnet/rmnet_descriptor.c" -#include "rmnet/rmnet_config.c" -#include "rmnet/rmnet_handlers.c" - -struct rmnet_nss_cb *rmnet_nss_callbacks __rcu __read_mostly; - -void rmnet_init(struct net_device *real_dev, u32 nr_rmnet_devs) -{ - struct rmnet_port *port; - struct rmnet_endpoint *ep; - struct net_device *rmnet_dev = NULL; - u32 nr = 0; - struct rmnet_nss_cb *nss_cb = rcu_dereference(rmnet_nss_callbacks); - - if (!nss_cb) { -#ifdef CONFIG_QCA_NSS_DRV - rmnet_nss_init(); -#endif - } - - rmnet_register_real_device(real_dev); - - port = rmnet_get_port_rtnl(real_dev); - - port->data_format = RMNET_FLAGS_INGRESS_DEAGGREGATION - | RMNET_FLAGS_INGRESS_MAP_CKSUMV5 | RMNET_FLAGS_EGRESS_MAP_CKSUMV5; - port->rmnet_mode = RMNET_EPMODE_VND; - - for (nr = 0; nr < nr_rmnet_devs; nr++) { - u8 mux_id = 0x81+nr; - - rmnet_dev = alloc_netdev(sizeof(struct rmnet_priv), - "rmnet_data%d", NET_NAME_PREDICTABLE, - rmnet_vnd_setup); - - ep = kzalloc(sizeof(*ep), GFP_ATOMIC); - - rmnet_vnd_newlink(mux_id, rmnet_dev, port, real_dev, ep); - netdev_rx_handler_register(rmnet_dev, rmnet_rx_priv_handler, NULL); - hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]); - } - - port->nr_rmnet_devs = nr_rmnet_devs; -} - -void rmnet_deinit(struct net_device *real_dev, u32 nr_rmnet_devs) -{ - struct rmnet_port *port; - u32 nr = 0; - struct rmnet_nss_cb *nss_cb = rcu_dereference(rmnet_nss_callbacks); - - port = rmnet_get_port_rtnl(real_dev); - - if (!real_dev || !rmnet_is_real_dev_registered(real_dev)) - return; - - port = rmnet_get_port_rtnl(real_dev); - - for (nr = 0; nr < nr_rmnet_devs; nr++) { - struct rmnet_endpoint *ep; - u8 mux_id = 0x81+nr; - - ep = rmnet_get_endpoint(port, mux_id); - if (ep) { - hlist_del_init_rcu(&ep->hlnode); - rmnet_vnd_dellink(mux_id, port, ep); - synchronize_rcu(); - kfree(ep); - } - } - - rmnet_unregister_real_device(real_dev, port); - - if (nss_cb) { -#ifdef CONFIG_QCA_NSS_DRV - rmnet_nss_exit(); -#endif - } -} -#endif diff --git a/package/wwan/quectel_MHI/src/devices/rmnet_nss.c b/package/wwan/quectel_MHI/src/devices/rmnet_nss.c deleted file mode 100644 index e6e841468..000000000 --- a/package/wwan/quectel_MHI/src/devices/rmnet_nss.c +++ /dev/null @@ -1,424 +0,0 @@ -/* Copyright (c) 2019, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/hashtable.h> -#include <linux/if_ether.h> -#include <linux/ip.h> -#include <qca-nss-drv/nss_api_if.h> - -#include <linux/rmnet_nss.h> - -#define RMNET_NSS_HASH_BITS 8 -#define hash_add_ptr(table, node, key) \ - hlist_add_head(node, &table[hash_ptr(key, HASH_BITS(table))]) - -static DEFINE_HASHTABLE(rmnet_nss_ctx_hashtable, RMNET_NSS_HASH_BITS); - -struct rmnet_nss_ctx { - struct hlist_node hnode; - struct net_device *rmnet_dev; - struct nss_rmnet_rx_handle *nss_ctx; -}; - -enum __rmnet_nss_stat { - RMNET_NSS_RX_ETH, - RMNET_NSS_RX_FAIL, - RMNET_NSS_RX_NON_ETH, - RMNET_NSS_RX_BUSY, - RMNET_NSS_TX_NO_CTX, - RMNET_NSS_TX_SUCCESS, - RMNET_NSS_TX_FAIL, - RMNET_NSS_TX_NONLINEAR, - RMNET_NSS_TX_BAD_IP, - RMNET_NSS_EXCEPTIONS, - RMNET_NSS_EX_BAD_HDR, - RMNET_NSS_EX_BAD_IP, - RMNET_NSS_EX_SUCCESS, - RMNET_NSS_TX_BAD_FRAGS, - RMNET_NSS_TX_LINEARIZE_FAILS, - RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS, - RMNET_NSS_TX_BUSY_LOOP, - RMNET_NSS_NUM_STATS, -}; - -static unsigned long rmnet_nss_stats[RMNET_NSS_NUM_STATS]; - -#define RMNET_NSS_STAT(name, counter, desc) \ - module_param_named(name, rmnet_nss_stats[counter], ulong, 0444); \ - MODULE_PARM_DESC(name, desc) - -RMNET_NSS_STAT(rmnet_nss_rx_ethernet, RMNET_NSS_RX_ETH, - "Number of Ethernet headers successfully removed"); -RMNET_NSS_STAT(rmnet_nss_rx_fail, RMNET_NSS_RX_FAIL, - "Number of Ethernet headers that could not be removed"); -RMNET_NSS_STAT(rmnet_nss_rx_non_ethernet, RMNET_NSS_RX_NON_ETH, - "Number of non-Ethernet packets received"); -RMNET_NSS_STAT(rmnet_nss_rx_busy, RMNET_NSS_RX_BUSY, - "Number of packets dropped decause rmnet_data device was busy"); -RMNET_NSS_STAT(rmnet_nss_tx_slow, RMNET_NSS_TX_NO_CTX, - "Number of packets sent over non-NSS-accelerated rmnet device"); -RMNET_NSS_STAT(rmnet_nss_tx_fast, RMNET_NSS_TX_SUCCESS, - "Number of packets sent over NSS-accelerated rmnet device"); -RMNET_NSS_STAT(rmnet_nss_tx_fail, RMNET_NSS_TX_FAIL, - "Number of packets that NSS could not transmit"); -RMNET_NSS_STAT(rmnet_nss_tx_nonlinear, RMNET_NSS_TX_NONLINEAR, - "Number of non linear sent over NSS-accelerated rmnet device"); -RMNET_NSS_STAT(rmnet_nss_tx_invalid_ip, RMNET_NSS_TX_BAD_IP, - "Number of ingress packets with invalid IP headers"); -RMNET_NSS_STAT(rmnet_nss_tx_invalid_frags, RMNET_NSS_TX_BAD_FRAGS, - "Number of ingress packets with invalid frag format"); -RMNET_NSS_STAT(rmnet_nss_tx_linearize_fail, RMNET_NSS_TX_LINEARIZE_FAILS, - "Number of ingress packets where linearize in tx fails"); -RMNET_NSS_STAT(rmnet_nss_tx_exceptions, RMNET_NSS_EXCEPTIONS, - "Number of times our DL exception handler was invoked"); -RMNET_NSS_STAT(rmnet_nss_exception_non_ethernet, RMNET_NSS_EX_BAD_HDR, - "Number of non-Ethernet exception packets"); -RMNET_NSS_STAT(rmnet_nss_exception_invalid_ip, RMNET_NSS_EX_BAD_IP, - "Number of exception packets with invalid IP headers"); -RMNET_NSS_STAT(rmnet_nss_exception_success, RMNET_NSS_EX_SUCCESS, - "Number of exception packets handled successfully"); -RMNET_NSS_STAT(rmnet_nss_tx_non_zero_headlen_frags, RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS, - "Number of packets with non zero headlen"); -RMNET_NSS_STAT(rmnet_nss_tx_busy_loop, RMNET_NSS_TX_BUSY_LOOP, - "Number of times tx packets busy looped"); - -static void rmnet_nss_inc_stat(enum __rmnet_nss_stat stat) -{ - if (stat >= 0 && stat < RMNET_NSS_NUM_STATS) - rmnet_nss_stats[stat]++; -} - -static struct rmnet_nss_ctx *rmnet_nss_find_ctx(struct net_device *dev) -{ - struct rmnet_nss_ctx *ctx; - struct hlist_head *bucket; - u32 hash; - - hash = hash_ptr(dev, HASH_BITS(rmnet_nss_ctx_hashtable)); - bucket = &rmnet_nss_ctx_hashtable[hash]; - hlist_for_each_entry(ctx, bucket, hnode) { - if (ctx->rmnet_dev == dev) - return ctx; - } - - return NULL; -} - -static void rmnet_nss_free_ctx(struct rmnet_nss_ctx *ctx) -{ - if (ctx) { - hash_del(&ctx->hnode); - nss_rmnet_rx_xmit_callback_unregister(ctx->nss_ctx); - nss_rmnet_rx_destroy_sync(ctx->nss_ctx); - kfree(ctx); - } -} - -/* Pull off an ethernet header, if possible */ -static int rmnet_nss_ethhdr_pull(struct sk_buff *skb) -{ - if (!skb->protocol || skb->protocol == htons(ETH_P_802_3)) { - void *ret = skb_pull(skb, sizeof(struct ethhdr)); - - rmnet_nss_inc_stat((ret) ? RMNET_NSS_RX_ETH : - RMNET_NSS_RX_FAIL); - return !ret; - } - - rmnet_nss_inc_stat(RMNET_NSS_RX_NON_ETH); - return -1; -} - -/* Copy headers to linear section for non linear packets */ -static int rmnet_nss_adjust_header(struct sk_buff *skb) -{ - struct iphdr *iph; - skb_frag_t *frag; - int bytes = 0; - u8 transport; - - if (skb_shinfo(skb)->nr_frags != 1) { - rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_FRAGS); - return -EINVAL; - } - - if (skb_headlen(skb)) { - rmnet_nss_inc_stat(RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS); - return 0; - } - - frag = &skb_shinfo(skb)->frags[0]; - - iph = (struct iphdr *)(skb_frag_address(frag)); - - if (iph->version == 4) { - bytes = iph->ihl*4; - transport = iph->protocol; - } else if (iph->version == 6) { - struct ipv6hdr *ip6h = (struct ipv6hdr *)iph; - - bytes = sizeof(struct ipv6hdr); - /* Dont have to account for extension headers yet */ - transport = ip6h->nexthdr; - } else { - rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_IP); - return -EINVAL; - } - - if (transport == IPPROTO_TCP) { - struct tcphdr *th; - - th = (struct tcphdr *)((u8 *)iph + bytes); - bytes += th->doff * 4; - } else if (transport == IPPROTO_UDP) { - bytes += sizeof(struct udphdr); - } else { - /* cant do anything else here unfortunately so linearize */ - if (skb_linearize(skb)) { - rmnet_nss_inc_stat(RMNET_NSS_TX_LINEARIZE_FAILS); - return -EINVAL; - } else { - return 0; - } - } - - if (bytes > skb_frag_size(frag)) { - rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_FRAGS); - return -EINVAL; - } - - skb_push(skb, bytes); - memcpy(skb->data, iph, bytes); - - /* subtract to account for skb_push */ - skb->len -= bytes; - - frag->page_offset += bytes; - skb_frag_size_sub(frag, bytes); - - /* subtract to account for skb_frag_size_sub */ - skb->data_len -= bytes; - - return 0; -} - -/* Main downlink handler - * Looks up NSS contex associated with the device. If the context is found, - * we add a dummy ethernet header with the approriate protocol field set, - * the pass the packet off to NSS for hardware acceleration. - */ -int rmnet_nss_tx(struct sk_buff *skb) -{ - struct ethhdr *eth; - struct rmnet_nss_ctx *ctx; - struct net_device *dev = skb->dev; - nss_tx_status_t rc; - unsigned int len; - u8 version; - - if (skb_is_nonlinear(skb)) { - if (rmnet_nss_adjust_header(skb)) - goto fail; - else - rmnet_nss_inc_stat(RMNET_NSS_TX_NONLINEAR); - } - - version = ((struct iphdr *)skb->data)->version; - - ctx = rmnet_nss_find_ctx(dev); - if (!ctx) { - rmnet_nss_inc_stat(RMNET_NSS_TX_NO_CTX); - return -EINVAL; - } - - eth = (struct ethhdr *)skb_push(skb, sizeof(*eth)); - memset(ð->h_dest, 0, ETH_ALEN * 2); - if (version == 4) { - eth->h_proto = htons(ETH_P_IP); - } else if (version == 6) { - eth->h_proto = htons(ETH_P_IPV6); - } else { - rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_IP); - goto fail; - } - - skb->protocol = htons(ETH_P_802_3); - /* Get length including ethhdr */ - len = skb->len; - -transmit: - rc = nss_rmnet_rx_tx_buf(ctx->nss_ctx, skb); - if (rc == NSS_TX_SUCCESS) { - /* Increment rmnet_data device stats. - * Don't call rmnet_data_vnd_rx_fixup() to do this, as - * there's no guarantee the skb pointer is still valid. - */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += len; - rmnet_nss_inc_stat(RMNET_NSS_TX_SUCCESS); - return 0; - } else if (rc == NSS_TX_FAILURE_QUEUE) { - rmnet_nss_inc_stat(RMNET_NSS_TX_BUSY_LOOP); - goto transmit; - } - -fail: - rmnet_nss_inc_stat(RMNET_NSS_TX_FAIL); - kfree_skb(skb); - return 1; -} - -/* Called by NSS in the DL exception case. - * Since the packet cannot be sent over the accelerated path, we need to - * handle it. Remove the ethernet header and pass it onward to the stack - * if possible. - */ -void rmnet_nss_receive(struct net_device *dev, struct sk_buff *skb, - struct napi_struct *napi) -{ - rmnet_nss_inc_stat(RMNET_NSS_EXCEPTIONS); - - if (!skb) - return; - - if (rmnet_nss_ethhdr_pull(skb)) { - rmnet_nss_inc_stat(RMNET_NSS_EX_BAD_HDR); - goto drop; - } - - /* reset header pointers */ - skb_reset_transport_header(skb); - skb_reset_network_header(skb); - skb_reset_mac_header(skb); - - /* reset packet type */ - skb->pkt_type = PACKET_HOST; - - skb->dev = dev; - - /* reset protocol type */ - switch (skb->data[0] & 0xF0) { - case 0x40: - skb->protocol = htons(ETH_P_IP); - break; - case 0x60: - skb->protocol = htons(ETH_P_IPV6); - break; - default: - rmnet_nss_inc_stat(RMNET_NSS_EX_BAD_IP); - goto drop; - } - - rmnet_nss_inc_stat(RMNET_NSS_EX_SUCCESS); - - /* Set this so that we dont loop around netif_receive_skb */ - - skb->cb[0] = 1; - - netif_receive_skb(skb); - return; - -drop: - kfree_skb(skb); -} - -/* Called by NSS in the UL acceleration case. - * We are guaranteed to have an ethernet packet here from the NSS hardware, - * We need to pull the header off and invoke our ndo_start_xmit function - * to handle transmitting the packet to the network stack. - */ -void rmnet_nss_xmit(struct net_device *dev, struct sk_buff *skb) -{ - netdev_tx_t ret; - - skb_pull(skb, sizeof(struct ethhdr)); - rmnet_nss_inc_stat(RMNET_NSS_RX_ETH); - - /* NSS takes care of shaping, so bypassing Qdiscs like this is OK */ - ret = dev->netdev_ops->ndo_start_xmit(skb, dev); - if (unlikely(ret == NETDEV_TX_BUSY)) { - dev_kfree_skb_any(skb); - rmnet_nss_inc_stat(RMNET_NSS_RX_BUSY); - } -} - -/* Create and register an NSS context for an rmnet_data device */ -int rmnet_nss_create_vnd(struct net_device *dev) -{ - struct rmnet_nss_ctx *ctx; - - ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC); - if (!ctx) - return -ENOMEM; - - ctx->rmnet_dev = dev; - ctx->nss_ctx = nss_rmnet_rx_create_sync_nexthop(dev, NSS_N2H_INTERFACE, - NSS_C2C_TX_INTERFACE); - if (!ctx->nss_ctx) { - kfree(ctx); - return -1; - } - - nss_rmnet_rx_register(ctx->nss_ctx, rmnet_nss_receive, dev); - nss_rmnet_rx_xmit_callback_register(ctx->nss_ctx, rmnet_nss_xmit); - hash_add_ptr(rmnet_nss_ctx_hashtable, &ctx->hnode, dev); - return 0; -} - -/* Unregister and destroy the NSS context for an rmnet_data device */ -int rmnet_nss_free_vnd(struct net_device *dev) -{ - struct rmnet_nss_ctx *ctx; - - ctx = rmnet_nss_find_ctx(dev); - rmnet_nss_free_ctx(ctx); - - return 0; -} - -static const struct rmnet_nss_cb rmnet_nss = { - .nss_create = rmnet_nss_create_vnd, - .nss_free = rmnet_nss_free_vnd, - .nss_tx = rmnet_nss_tx, -}; - -int __init rmnet_nss_init(void) -{ - pr_err("%s(): initializing rmnet_nss\n", __func__); - RCU_INIT_POINTER(rmnet_nss_callbacks, &rmnet_nss); - return 0; -} - -void __exit rmnet_nss_exit(void) -{ - struct hlist_node *tmp; - struct rmnet_nss_ctx *ctx; - int bkt; - - pr_err("%s(): exiting rmnet_nss\n", __func__); - RCU_INIT_POINTER(rmnet_nss_callbacks, NULL); - - /* Tear down all NSS contexts */ - hash_for_each_safe(rmnet_nss_ctx_hashtable, bkt, tmp, ctx, hnode) - rmnet_nss_free_ctx(ctx); -} - -#if 0 -MODULE_LICENSE("GPL v2"); -module_init(rmnet_nss_init); -module_exit(rmnet_nss_exit); -#endif