update wwan
This commit is contained in:
parent
dfb5b8d66f
commit
b1d23cd4cb
@ -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
|
||||
|
||||
|
@ -1 +1,20 @@
|
||||
# luci-app-hypermodem
|
||||
# luci-app-hypermodem
|
||||
|
||||
# 目录
|
||||
|
||||
[一、说明](#一说明)
|
||||
|
||||
# 一、说明
|
||||
|
||||
原项目地址:https://github.com/momokind/luci-app-hypermodem
|
||||
|
||||
插件功能
|
||||
|
||||
- 支持USB和PCIe两种通信方式的通信模组
|
||||
|
||||
- 支持IPv6
|
||||
|
||||
- 支持高通和紫光展锐两个平台的通信模组
|
||||
|
||||
- 支持常见厂商的通信模组(例如:移远,广和通等)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 密码"
|
@ -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()
|
||||
|
0
package/wwan/luci-app-hypermodem/root/etc/uci-defaults/luci-hypermodem
Normal file → Executable file
0
package/wwan/luci-app-hypermodem/root/etc/uci-defaults/luci-hypermodem
Normal file → Executable file
@ -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
|
||||
|
@ -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 |
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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%>
|
||||
|
@ -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>
|
||||
|
@ -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 "自检"
|
||||
|
||||
|
@ -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 "自检"
|
||||
|
||||
|
@ -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'
|
||||
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'
|
@ -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
|
||||
|
||||
#获取不到拨号模式
|
||||
|
@ -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"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
|
||||
|
928
package/wwan/luci-app-modem/root/usr/share/modem/huawei.sh
Executable file
928
package/wwan/luci-app-modem/root/usr/share/modem/huawei.sh
Executable file
@ -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
|
||||
}
|
@ -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})
|
||||
|
94
package/wwan/luci-app-modem/root/usr/share/modem/modem_band.sh
Executable file
94
package/wwan/luci-app-modem/root/usr/share/modem/modem_band.sh
Executable file
@ -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"
|
@ -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"
|
||||
|
||||
#调试开关
|
||||
|
@ -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
|
||||
|
||||
|
0
package/wwan/luci-app-modem/root/usr/share/modem/modem_init.sh
Normal file → Executable file
0
package/wwan/luci-app-modem/root/usr/share/modem/modem_init.sh
Normal file → Executable file
@ -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
|
||||
|
||||
|
@ -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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ m_modem_presets()
|
||||
"fibocom") fibocom_presets ;;
|
||||
"meig") meig_presets ;;
|
||||
"simcom") simcom_presets ;;
|
||||
"huawei") huawei_presets ;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
674
package/wwan/luci-app-sms-tool/LICENSE
Normal file
674
package/wwan/luci-app-sms-tool/LICENSE
Normal file
@ -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>.
|
32
package/wwan/luci-app-sms-tool/Makefile
Normal file
32
package/wwan/luci-app-sms-tool/Makefile
Normal file
@ -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
|
Binary file not shown.
After Width: | Height: | Size: 9.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
242
package/wwan/luci-app-sms-tool/luasrc/controller/modem/sms.lua
Normal file
242
package/wwan/luci-app-sms-tool/luasrc/controller/modem/sms.lua
Normal file
@ -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
|
@ -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
|
136
package/wwan/luci-app-sms-tool/luasrc/view/modem/atcommands.htm
Normal file
136
package/wwan/luci-app-sms-tool/luasrc/view/modem/atcommands.htm
Normal file
@ -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%>
|
||||
|
285
package/wwan/luci-app-sms-tool/luasrc/view/modem/readsms.htm
Normal file
285
package/wwan/luci-app-sms-tool/luasrc/view/modem/readsms.htm
Normal file
@ -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%>
|
237
package/wwan/luci-app-sms-tool/luasrc/view/modem/sendsms.htm
Normal file
237
package/wwan/luci-app-sms-tool/luasrc/view/modem/sendsms.htm
Normal file
@ -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%>
|
265
package/wwan/luci-app-sms-tool/luasrc/view/modem/ussd.htm
Normal file
265
package/wwan/luci-app-sms-tool/luasrc/view/modem/ussd.htm
Normal file
@ -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%>
|
288
package/wwan/luci-app-sms-tool/po/pl/sms-tool.po
Normal file
288
package/wwan/luci-app-sms-tool/po/pl/sms-tool.po
Normal file
@ -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"
|
||||
|
246
package/wwan/luci-app-sms-tool/po/zh-cn/sms-tool.po
Normal file
246
package/wwan/luci-app-sms-tool/po/zh-cn/sms-tool.po
Normal file
@ -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 是多任务处理,请选择“否”."
|
||||
|
28
package/wwan/luci-app-sms-tool/root/etc/config/atcmds.user
Normal file
28
package/wwan/luci-app-sms-tool/root/etc/config/atcmds.user
Normal file
@ -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"
|
@ -0,0 +1 @@
|
||||
other user;8613188888888
|
14
package/wwan/luci-app-sms-tool/root/etc/config/sms_tool
Normal file
14
package/wwan/luci-app-sms-tool/root/etc/config/sms_tool
Normal file
@ -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'
|
26
package/wwan/luci-app-sms-tool/root/etc/init.d/smsled
Executable file
26
package/wwan/luci-app-sms-tool/root/etc/init.d/smsled
Executable file
@ -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
|
||||
}
|
23
package/wwan/luci-app-sms-tool/root/etc/uci-defaults/set_sms_ports.sh
Executable file
23
package/wwan/luci-app-sms-tool/root/etc/uci-defaults/set_sms_ports.sh
Executable file
@ -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
|
7
package/wwan/luci-app-sms-tool/root/etc/uci-defaults/start-smsled
Executable file
7
package/wwan/luci-app-sms-tool/root/etc/uci-defaults/start-smsled
Executable file
@ -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
|
21
package/wwan/luci-app-sms-tool/root/sbin/cronsync.sh
Executable file
21
package/wwan/luci-app-sms-tool/root/sbin/cronsync.sh
Executable file
@ -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
|
24
package/wwan/luci-app-sms-tool/root/sbin/set_sms_ports.sh
Executable file
24
package/wwan/luci-app-sms-tool/root/sbin/set_sms_ports.sh
Executable file
@ -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
|
21
package/wwan/luci-app-sms-tool/root/sbin/smsled-init.sh
Executable file
21
package/wwan/luci-app-sms-tool/root/sbin/smsled-init.sh
Executable file
@ -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
|
||||
|
39
package/wwan/luci-app-sms-tool/root/sbin/smsled.sh
Executable file
39
package/wwan/luci-app-sms-tool/root/sbin/smsled.sh
Executable file
@ -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
|
@ -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" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
34
package/wwan/meig-cm/Makefile
Normal file
34
package/wwan/meig-cm/Makefile
Normal file
@ -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))
|
229
package/wwan/meig-cm/src/GobiNetCM.c
Normal file
229
package/wwan/meig-cm/src/GobiNetCM.c
Normal file
@ -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,
|
||||
};
|
377
package/wwan/meig-cm/src/MPQCTL.h
Normal file
377
package/wwan/meig-cm/src/MPQCTL.h
Normal file
@ -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
|
301
package/wwan/meig-cm/src/MPQMI.h
Normal file
301
package/wwan/meig-cm/src/MPQMI.h
Normal file
@ -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
|
426
package/wwan/meig-cm/src/MPQMUX.c
Normal file
426
package/wwan/meig-cm/src/MPQMUX.c
Normal file
@ -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);
|
||||
}
|
3322
package/wwan/meig-cm/src/MPQMUX.h
Normal file
3322
package/wwan/meig-cm/src/MPQMUX.h
Normal file
File diff suppressed because it is too large
Load Diff
25
package/wwan/meig-cm/src/Makefile
Normal file
25
package/wwan/meig-cm/src/Makefile
Normal file
@ -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
|
1988
package/wwan/meig-cm/src/QMIThread.c
Normal file
1988
package/wwan/meig-cm/src/QMIThread.c
Normal file
File diff suppressed because it is too large
Load Diff
228
package/wwan/meig-cm/src/QMIThread.h
Normal file
228
package/wwan/meig-cm/src/QMIThread.h
Normal file
@ -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
|
371
package/wwan/meig-cm/src/QmiWwanCM.c
Normal file
371
package/wwan/meig-cm/src/QmiWwanCM.c
Normal file
@ -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,
|
||||
};
|
61
package/wwan/meig-cm/src/ReleaseNote.txt
Normal file
61
package/wwan/meig-cm/src/ReleaseNote.txt
Normal file
@ -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
|
||||
|
5
package/wwan/meig-cm/src/build.sh
Normal file
5
package/wwan/meig-cm/src/build.sh
Normal file
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
#export PATH=/home/zhaopengfei/ruiming/aarch64-himix200-linux/bin:$PATH
|
||||
make clean
|
||||
make CROSS_COMPILE=arm-hisiv500-linux-
|
||||
|
63
package/wwan/meig-cm/src/default.script
Normal file
63
package/wwan/meig-cm/src/default.script
Normal file
@ -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
|
231
package/wwan/meig-cm/src/device.c
Normal file
231
package/wwan/meig-cm/src/device.c
Normal file
@ -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;
|
||||
}
|
90
package/wwan/meig-cm/src/dhcpclient.c
Normal file
90
package/wwan/meig-cm/src/dhcpclient.c
Normal file
@ -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
|
796
package/wwan/meig-cm/src/main.c
Normal file
796
package/wwan/meig-cm/src/main.c
Normal file
@ -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;
|
||||
}
|
1765
package/wwan/meig-cm/src/mbim-cm.c
Normal file
1765
package/wwan/meig-cm/src/mbim-cm.c
Normal file
File diff suppressed because it is too large
Load Diff
BIN
package/wwan/meig-cm/src/meig-cm
Normal file
BIN
package/wwan/meig-cm/src/meig-cm
Normal file
Binary file not shown.
BIN
package/wwan/meig-cm/src/meig-qmi-proxy
Normal file
BIN
package/wwan/meig-cm/src/meig-qmi-proxy
Normal file
Binary file not shown.
1277
package/wwan/meig-cm/src/meig-qmi-proxy.c
Normal file
1277
package/wwan/meig-cm/src/meig-qmi-proxy.c
Normal file
File diff suppressed because it is too large
Load Diff
494
package/wwan/meig-cm/src/ql-ifconfig.c
Normal file
494
package/wwan/meig-cm/src/ql-ifconfig.c
Normal file
@ -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
|
||||
|
||||
|
264
package/wwan/meig-cm/src/qmap_bridge_mode.c
Normal file
264
package/wwan/meig-cm/src/qmap_bridge_mode.c
Normal file
@ -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;
|
||||
}
|
306
package/wwan/meig-cm/src/udhcpc.c
Normal file
306
package/wwan/meig-cm/src/udhcpc.c
Normal file
@ -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);
|
||||
}
|
208
package/wwan/meig-cm/src/util.c
Normal file
208
package/wwan/meig-cm/src/util.c
Normal file
@ -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]);
|
||||
}
|
55
package/wwan/meig-cm/src/util.h
Normal file
55
package/wwan/meig-cm/src/util.h
Normal file
@ -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
|
@ -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
|
||||
|
@ -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:
|
File diff suppressed because it is too large
Load Diff
@ -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_ */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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_ */
|
||||
|
@ -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_ */
|
||||
|
@ -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;
|
||||
|
@ -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.
|
@ -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
|
@ -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;
|
||||
}
|
@ -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_ */
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
@ -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_ */
|
@ -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);
|
||||
}
|
@ -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_ */
|
@ -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_ */
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
@ -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_ */
|
@ -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>
|
@ -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;
|
||||
}
|
@ -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_ */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user