update wwan

This commit is contained in:
DHDAXCW 2025-03-14 20:44:12 +08:00
parent dfb5b8d66f
commit b1d23cd4cb
102 changed files with 19830 additions and 8077 deletions

View File

@ -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

View File

@ -1 +1,20 @@
# luci-app-hypermodem
# luci-app-hypermodem
# 目录
[一、说明](#一说明)
# 一、说明
原项目地址https://github.com/momokind/luci-app-hypermodem
插件功能
- 支持USB和PCIe两种通信方式的通信模组
- 支持IPv6
- 支持高通和紫光展锐两个平台的通信模组
- 支持常见厂商的通信模组(例如:移远,广和通等)

View File

@ -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

View File

@ -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 密码"

View File

@ -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()

View File

View 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

View File

@ -26,6 +26,7 @@
| 厂家名称 | 模组名称 | 平台 | 数据传输模式 | 端口模式 |
| -------- | -------------------------------------------------- | -------- | ------------ | ---------------------------- |
| 华为 | MH5000-31 | 华为 | USB | ECMNCM |
| 移远通信 | RG200U-CNDONGLE版 | 紫光展锐 | USB | ECMMBIMRNDISNCM |
| 移远通信 | RM500U-CN | 紫光展锐 | USB | ECMMBIMRNDISNCM |
| 移远通信 | RM500U-EA | 紫光展锐 | USB | ECMMBIMRNDISNCM |

View File

@ -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)

View File

@ -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> &nbsp;';
//频段偏好
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> &nbsp;';
}
}
//设置当前频段偏好(全未启用则为空)
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> &nbsp;';
// }
// }
//设置第一次获取数据标志
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> &nbsp;
@ -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>&nbsp;
<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>&nbsp;
<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>&nbsp;
<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>&nbsp;
<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%>

View File

@ -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>

View File

@ -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 "自检"

View File

@ -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 "自检"

View File

@ -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拨号模式LinuxDebug > 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'

View File

@ -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
#获取不到拨号模式

View File

@ -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拨号模式LinuxDebug > 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"}
]
}
}
}

View File

@ -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"
}

View 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:频段类型2G3G4G5G
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 SlotSIM卡卡槽
# 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 StatusSIM状态
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 NumberSIM卡号码手机号
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')
#RSSI4G信号强度指示
# rssi_num=$(echo $response | awk -F',' '{print $1}')
# rssi=$(huawei_get_rssi $rssi_num)
#BER4G信道误码率
# 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
}

View File

@ -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:频段类型2G3G4G5G
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')
#获取2G3G频段信息
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 SlotSIM卡卡槽
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})

View 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"

View File

@ -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"
#调试开关

View File

@ -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

View File

View 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

View File

@ -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"]
}
}
}
}

View File

@ -83,6 +83,7 @@ m_modem_presets()
"fibocom") fibocom_presets ;;
"meig") meig_presets ;;
"simcom") simcom_presets ;;
"huawei") huawei_presets ;;
esac
}

View File

@ -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

View 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>.

View 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

View 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

View File

@ -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

View 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%>

View 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()" />&nbsp;&nbsp;&nbsp;<input type="button" id="delabtn" class="btn cbi-button cbi-button-neutral" value="<%:删除消息(s)%>" onclick="delete_new()" />
</div>
</div>
<%+footer%>

View 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%>

View 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%>

View 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"

View 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 是多任务处理,请选择“否”."

View 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"

View File

@ -0,0 +1 @@
other user;8613188888888

View 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'

View 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
}

View 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

View 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

View 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

View 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

View 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

View 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

View File

@ -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" ]
}
}
}

View 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))

View 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,
};

View 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

View 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

View 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);
}

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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,
};

View File

@ -0,0 +1,61 @@
Release Notes
[WCDMA&LTE_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&LTE_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&LTE_QConnectManager_Linux&Android_V1.1.45]
Date: 2018/09/13
enhancement:
1. support EG12 PCIE interface
[WCDMA&LTE_QConnectManager_Linux&Android_V1.1.44]
Date: 2018/09/10
enhancement:
1. support setup IPV4&IPV6 data call.
[WCDMA&LTE_QConnectManager_Linux&Android_V1.1.43]
[WCDMA&LTE_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&LTE_QConnectManager_Linux&Android_V1.1.41]
Date: 2018/05/24
enhancement:
1. fix a cdma data call error
[WCDMA&LTE_QConnectManager_Linux&Android_V1.1.40]
Date: 2018/05/12
enhancement:
1. support GobiNet's QMAP fucntion and bridge mode.
'Meig_WCDMA&LTE_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

View 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-

View 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

View 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;
}

View 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

View 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;
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View 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

View 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;
}

View 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);
}

View 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]);
}

View 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

View File

@ -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

View File

@ -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

View File

@ -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_ */

View File

@ -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

View File

@ -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

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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_ */

View File

@ -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>

View File

@ -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;
}

View File

@ -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