diff --git a/package/wwan/luci-app-hypermodem/Makefile b/package/wwan/luci-app-hypermodem/Makefile
index fbb799164..98dc04411 100644
--- a/package/wwan/luci-app-hypermodem/Makefile
+++ b/package/wwan/luci-app-hypermodem/Makefile
@@ -8,7 +8,18 @@
 include $(TOPDIR)/rules.mk
 
 LUCI_TITLE:=Hyper Modem Server
-LUCI_DEPENDS:=+luci-compat
+LUCI_DEPENDS:=+luci-compat +kmod-usb-net  +kmod-usb-net-cdc-ether +kmod-usb-acm \
+		+kmod-usb-net-qmi-wwan  +kmod-usb-net-rndis +kmod-usb-serial-qualcomm \
+		+kmod-usb-net-sierrawireless +kmod-usb-ohci +kmod-usb-serial \
+		+kmod-usb-serial-option +kmod-usb-wdm \
+		+kmod-usb2 +kmod-usb3 \
+		+kmod-usb-net-cdc-mbim \
+		+usbutils \
+		+luci-proto-qmi \
+		+pciutils \
+		+kmod-pcie_mhi \
+		+quectel-CM-5G \
+		+grep \
 
 include $(TOPDIR)/feeds/luci/luci.mk
 
diff --git a/package/wwan/luci-app-hypermodem/README.md b/package/wwan/luci-app-hypermodem/README.md
index d77455362..e6fe7bbd7 100644
--- a/package/wwan/luci-app-hypermodem/README.md
+++ b/package/wwan/luci-app-hypermodem/README.md
@@ -1 +1,20 @@
-# luci-app-hypermodem
\ No newline at end of file
+# luci-app-hypermodem
+
+# 目录
+
+[一、说明](#一说明)
+
+# 一、说明
+
+原项目地址:https://github.com/momokind/luci-app-hypermodem
+
+插件功能
+
+- 支持USB和PCIe两种通信方式的通信模组
+
+- 支持IPv6
+
+- 支持高通和紫光展锐两个平台的通信模组
+
+- 支持常见厂商的通信模组(例如:移远,广和通等)
+
diff --git a/package/wwan/luci-app-hypermodem/luasrc/model/cbi/hypermodem.lua b/package/wwan/luci-app-hypermodem/luasrc/model/cbi/hypermodem.lua
index 9f94f781a..51e5459e7 100644
--- a/package/wwan/luci-app-hypermodem/luasrc/model/cbi/hypermodem.lua
+++ b/package/wwan/luci-app-hypermodem/luasrc/model/cbi/hypermodem.lua
@@ -5,7 +5,7 @@ mp = Map("hypermodem")
 mp.title = translate("Hyper Modem Server")
 mp.description = translate("Modem Server For OpenWrt")
 
-s = mp:section(TypedSection, "service", "Base Setting")
+s = mp:section(TypedSection, "service", translate("Base Setting"))
 s.anonymous = true
 
 enabled = s:option(Flag, "enabled", translate("Enable"))
@@ -16,6 +16,11 @@ ipv6 = s:option(Flag, "ipv6", translate("Enable IPv6"))
 ipv6.default = 1
 ipv6.rmempty = false
 
+network_bridge = s:option(Flag, "network_bridge", translate("Enable Network bridge"))
+network_bridge.description = translate("After checking, enable network interface bridge.")
+network_bridge.default = 0
+network_bridge.rmempty = false
+
 device = s:option(Value, "device", translate("Modem device"))
 device.rmempty = false
 
@@ -29,20 +34,34 @@ if device_suggestions then
 end
 
 apn = s:option(Value, "apn", translate("APN"))
+apn.default = ""
 apn.rmempty = true
+apn:value("", translate("Auto Choose"))
+apn:value("cmnet", translate("China Mobile"))
+apn:value("3gnet", translate("China Unicom"))
+apn:value("ctnet", translate("China Telecom"))
+apn:value("cbnet", translate("China Broadcast"))
+apn:value("5gscuiot", translate("Skytone"))
 
-username = s:option(Value, "username", translate("PAP/CHAP username"))
-username.rmempty = true
-
-password = s:option(Value, "password", translate("PAP/CHAP password"))
-password.rmempty = true
-
-auth = s:option(Value, "auth", translate("Authentication Type"))
-auth.rmempty = true
-auth:value("", translate("-- Please choose --"))
-auth:value("both", "PAP/CHAP (both)")
+auth = s:option(ListValue, "auth", translate("Authentication Type"))
+auth.default = "none"
+auth.rmempty = false
+auth:value("none", translate("NONE"))
+auth:value("both", translate("PAP/CHAP (both)"))
 auth:value("pap", "PAP")
 auth:value("chap", "CHAP")
-auth:value("none", "NONE")
+
+username = s:option(Value, "username", translate("PAP/CHAP Username"))
+username.rmempty = true
+username:depends("auth", "both")
+username:depends("auth", "pap")
+username:depends("auth", "chap")
+
+password = s:option(Value, "password", translate("PAP/CHAP Password"))
+password.rmempty = true
+password.password = true
+password:depends("auth", "both")
+password:depends("auth", "pap")
+password:depends("auth", "chap")
 
 return mp
diff --git a/package/wwan/luci-app-hypermodem/po/zh-cn/hypermodem.po b/package/wwan/luci-app-hypermodem/po/zh-cn/hypermodem.po
index 2434a4187..383e59532 100644
--- a/package/wwan/luci-app-hypermodem/po/zh-cn/hypermodem.po
+++ b/package/wwan/luci-app-hypermodem/po/zh-cn/hypermodem.po
@@ -22,3 +22,42 @@ msgstr "OpenWrt移动网络拨号服务"
 
 msgid "Enable IPv6"
 msgstr "启用IPv6协商"
+
+msgid "Enable Network bridge"
+msgstr "启用网络桥架"
+
+msgid "After checking, enable network interface bridge."
+msgstr "勾选后,启用网络接口桥接。"
+
+msgid "APN"
+msgstr "接入点"
+
+msgid "China Mobile"
+msgstr "中国移动"
+
+msgid "China Unicom"
+msgstr "中国联通"
+
+msgid "China Telecom"
+msgstr "中国电信"
+
+msgid "China Broadcast"
+msgstr "中国广电"
+
+msgid "Skytone"
+msgstr "天际通"
+
+msgid "Authentication Type"
+msgstr "认证类型"
+
+msgid "PAP/CHAP (both)"
+msgstr "PAP/CHAP (均使用)"
+
+msgid "NONE"
+msgstr "无"
+
+msgid "PAP/CHAP Username"
+msgstr "PAP/CHAP 用户名"
+
+msgid "PAP/CHAP Password"
+msgstr "PAP/CHAP 密码"
\ No newline at end of file
diff --git a/package/wwan/luci-app-hypermodem/root/etc/init.d/hypermodem b/package/wwan/luci-app-hypermodem/root/etc/init.d/hypermodem
index 4c4e2080a..b44da1838 100755
--- a/package/wwan/luci-app-hypermodem/root/etc/init.d/hypermodem
+++ b/package/wwan/luci-app-hypermodem/root/etc/init.d/hypermodem
@@ -5,43 +5,98 @@ START=94
 STOP=13
 USE_PROCD=1
 
-pre_set()
+#设置防火墙
+# $1:网络接口名称
+set_firewall()
 {
-	[ "$(uci get network.wan.ifname)" != "$1" ] && {
-		uci set network.wwan='interface'
-		uci set network.wwan.ifname="$1"
-		uci set network.wwan.proto='dhcp'
-		if [ "$ipv6" = 1 ]; then
-			uci set network.wwan6='interface'
-			uci set network.wwan6.ifname="$1"
-			uci set network.wwan6.proto='dhcpv6'
-			uci set network.wwan6.extendprefix='1'
-		fi
-		uci commit network
+	local interface_name="$1"
 
-		num=`uci show firewall |grep "name='wan'" |wc -l`
-		wwan_num=`uci get firewall.@zone[$num].network |grep -w wwan |wc -l`
-		wwan6_num=`uci get firewall.@zone[$num].network |grep -w wwan6 |wc -l`
-		if [ "$wwan_num" = "0" ]; then
-			uci add_list firewall.@zone[$num].network='wwan'
-		fi
-		if [ "$ipv6" = 1 ]; then
-			if [ "$wwan6_num" = "0" ]; then
-				uci add_list firewall.@zone[$num].network='wwan6'
-			fi
-		fi
-		uci commit firewall
+	local num=$(uci show firewall | grep "name='wan'" | wc -l)
+	local wwan_num=$(uci -q get firewall.@zone[$num].network | grep -w "${interface_name}" | wc -l)
+	if [ "$wwan_num" = "0" ]; then
+		uci add_list firewall.@zone[$num].network="${interface_name}"
+	fi
+	uci commit firewall
+}
 
-		ifup wwan
-		if [ "$ipv6" = 1 ]; then
-			ifup wwan6
-		fi
+#设置IPv4网络接口
+# $1:网络接口名称
+# $2:网络接口
+set_ipv4_interface()
+{
+	local interface_name="$1"
+	local network_interface="$2"
+
+	#添加或修改网络配置
+	uci set network.${interface_name}='interface'
+	uci set network.${interface_name}.proto='dhcp'
+	uci set network.${interface_name}.device="${network_interface}"
+	uci set network.${interface_name}.ifname="${network_interface}"
+	uci commit network
+
+	#加入WAN防火墙
+	set_firewall "${interface_name}"
+
+	#启动网络接口
+	ifup "${interface_name}"
+}
+
+#设置IPv6网络接口
+# $1:网络接口名称
+# $2:网络接口
+set_ipv6_interface()
+{
+	local interface_name="$1"
+	local network_interface="$2"
+
+	#添加或修改网络配置
+	uci set network.${interface_name}='interface'
+	uci set network.${interface_name}.proto='dhcpv6'
+	uci set network.${interface_name}.extendprefix='1'
+	uci set network.${interface_name}.device="${network_interface}"
+	uci set network.${interface_name}.ifname="${network_interface}"
+	uci commit network
+
+	#加入WAN防火墙
+	set_firewall "${interface_name}"
+	
+	#启动网络接口
+	ifup "${interface_name}"
+}
+
+#设置IPV4和IPv6网络接口
+# $1:IPV4网络接口名称
+# $2:IPv6网络接口名称
+# $3:网络接口
+set_ipv4v6_interface()
+{
+	local ipv4_interface_name="$1"
+	local ipv6_interface_name="$2"
+	local network_interface="$3"
+
+	#设置IPV4网络接口
+	set_ipv4_interface "${ipv4_interface_name}" "${network_interface}"
+	#设置IPV6网络接口(别名)
+	set_ipv6_interface "${ipv6_interface_name}" "@${ipv4_interface_name}"
+}
+
+#设置网络接口
+# $2:网络接口
+set_interface()
+{
+	local network_interface="$1"
+
+	local pdp_type
+	[ "$ipv6" = "1" ] && {
+		pdp_type="ipv4v6"
 	}
 
-	[ "$(uci get network.wan6.ifname)" == "$1" ] && {
-		uci set network.wan6.extendprefix='1'
-		uci commit network
-	}
+	case $pdp_type in
+		"ipv4") set_ipv4_interface "wwan_5g" "${network_interface}" ;;
+		"ipv6") set_ipv6_interface "wwan6_5g" "${network_interface}" ;;
+		"ipv4v6") set_ipv4v6_interface "wwan_5g" "wwan6_5g" "${network_interface}" ;;
+		*) set_ipv4v6_interface "wwan_5g" "wwan6_5g" "${network_interface}" ;;
+	esac
 }
 
 run_dial()
@@ -55,6 +110,7 @@ run_dial()
 		local password
 		local auth
 		local ipv6
+		local network_bridge
 		local device
 
 		config_get apn $1 apn
@@ -62,48 +118,56 @@ run_dial()
 		config_get password $1 password
 		config_get auth $1 auth
 		config_get ipv6 $1 ipv6
+		config_get network_bridge $1 network_bridge
 		config_get device $1 device
 
-		devname="$(basename "$device")"
-		devicepath="$(find /sys/class/ -name $devname)"
-		devpath="$(readlink -f $devicepath/device/)"
-		ifname="$( ls "$devpath"/net )"
+		devname="$(basename "${device}")"
+		devicepath="$(find /sys/class/ -name ${devname})"
+		devpath="$(readlink -f ${devicepath}/device/)"
+		network="$( ls "${devpath}"/net )"
 
+		#拨号配置
 		procd_open_instance
 		procd_set_param command quectel-CM
 		if [ "$ipv6" = 1 ]; then
-			procd_append_param command -4 -6
+			procd_append_param command "-4" "-6"
+		fi
+		if [ "$network_bridge" = 1 ]; then
+			procd_append_param command "-b"
 		fi
 		if [ "$apn" != "" ];then
-			procd_append_param command -s $apn
+			procd_append_param command "-s" "$apn"
 		fi
-		if [ "$user" != "" ]; then
-			procd_append_param command $user
+		if [ "$username" != "" ]; then
+			procd_append_param command "$username"
 		fi
 		if [ "$password" != "" ]; then
-			procd_append_param command $password
+			procd_append_param command "$password"
 		fi
 		if [ "$auth" != "" ]; then
-			procd_append_param command $auth
+			procd_append_param command "$auth"
 		fi
 		if [ "$device" != "" ]; then
-			procd_append_param command -i $ifname
+			procd_append_param command -i "$network"
 		fi
 		procd_set_param respawn
 		procd_close_instance
 
+		#设置网络接口
+		local network_interface
 		if [ -d /sys/class/net/rmnet_mhi0 ]; then
-			pre_set rmnet_mhi0.1
+			network_interface="rmnet_mhi0.1"
 		elif [ -d /sys/class/net/wwan0_1 ]; then
-			pre_set wwan0_1
+			network_interface="wwan0_1"
 		elif [ -d /sys/class/net/wwan0.1 ]; then
-			pre_set wwan0.1
+			network_interface="wwan0.1"
 		elif [ -d /sys/class/net/wwan0 ]; then
-			pre_set wwan0
+			network_interface="wwan0"
 		fi
+		set_interface "${network_interface}"
 	fi
 
-	sleep 3
+	sleep 15
 }
 
 service_triggers()
diff --git a/package/wwan/luci-app-hypermodem/root/etc/uci-defaults/luci-hypermodem b/package/wwan/luci-app-hypermodem/root/etc/uci-defaults/luci-hypermodem
old mode 100644
new mode 100755
diff --git a/package/wwan/luci-app-modem/Makefile b/package/wwan/luci-app-modem/Makefile
index 16a8cc292..3b026664c 100644
--- a/package/wwan/luci-app-modem/Makefile
+++ b/package/wwan/luci-app-modem/Makefile
@@ -24,8 +24,10 @@ LUCI_DEPENDS:=+luci-compat \
 		+kmod-pcie_mhi \
 		+pciutils \
 		+quectel-CM-5G \
+		+modemmanager \
+		+luci-proto-modemmanager \
 		+sms-tool \
-		+jq
+		+bc +jq
 
 define Package/luci-app-modem/conffiles
 /etc/config/modem
diff --git a/package/wwan/luci-app-modem/README.md b/package/wwan/luci-app-modem/README.md
index 52ba5004e..e75c283c4 100644
--- a/package/wwan/luci-app-modem/README.md
+++ b/package/wwan/luci-app-modem/README.md
@@ -26,6 +26,7 @@
 
 | 厂家名称 | 模组名称                                           | 平台     | 数据传输模式 | 端口模式                     |
 | -------- | -------------------------------------------------- | -------- | ------------ | ---------------------------- |
+| 华为     | MH5000-31                                          | 华为     | USB          | ECM,NCM                     |
 | 移远通信 | RG200U-CN(DONGLE版)                              | 紫光展锐 | USB          | ECM,MBIM,RNDIS,NCM        |
 | 移远通信 | RM500U-CN                                          | 紫光展锐 | USB          | ECM,MBIM,RNDIS,NCM        |
 | 移远通信 | RM500U-EA                                          | 紫光展锐 | USB          | ECM,MBIM,RNDIS,NCM        |
diff --git a/package/wwan/luci-app-modem/luasrc/controller/modem.lua b/package/wwan/luci-app-modem/luasrc/controller/modem.lua
index c7dcb06c2..bd712688d 100644
--- a/package/wwan/luci-app-modem/luasrc/controller/modem.lua
+++ b/package/wwan/luci-app-modem/luasrc/controller/modem.lua
@@ -34,6 +34,7 @@ function index()
 	entry({"admin", "network", "modem", "set_mode"}, call("setMode"), nil).leaf = true
 	entry({"admin", "network", "modem", "get_network_prefer_info"}, call("getNetworkPreferInfo"), nil).leaf = true
 	entry({"admin", "network", "modem", "set_network_prefer"}, call("setNetworkPrefer"), nil).leaf = true
+	entry({"admin", "network", "modem", "set_band_prefer"}, call("setBandPrefer"), nil).leaf = true
 	entry({"admin", "network", "modem", "get_self_test_info"}, call("getSelfTestInfo"), nil).leaf = true
 	entry({"admin", "network", "modem", "get_quick_commands"}, call("getQuickCommands"), nil).leaf = true
 	entry({"admin", "network", "modem", "send_at_command"}, call("sendATCommand"), nil).leaf = true
@@ -587,14 +588,24 @@ end
 function getNetworkPreferInfo()
 	local at_port = http.formvalue("port")
 	
-	--获取制造商
-	local manufacturer=getManufacturer(at_port)
+	--获取制造商,数据接口,模组名称
+	local manufacturer
+	local data_interface
+	local name
+	uci:foreach("modem", "modem-device", function (modem_device)
+		if at_port == modem_device["at_port"] then
+			manufacturer=modem_device["manufacturer"]
+			data_interface=modem_device["data_interface"]
+			name=modem_device["name"]
+			return true --跳出循环
+		end
+	end)
 
 	--获取值
 	local network_prefer_info
 	if manufacturer~="unknown" then
 		--获取模组网络偏好
-		local command="source "..script_path..manufacturer..".sh && "..manufacturer.."_get_network_prefer "..at_port
+		local command="source "..script_path..manufacturer..".sh && "..manufacturer.."_get_network_prefer "..at_port.." "..data_interface.." "..name
 		local result=shell(command)
 		network_prefer_info=json.parse(result)
 	end
@@ -615,8 +626,18 @@ function setNetworkPrefer()
     local at_port = http.formvalue("port")
 	local network_prefer_config = json.stringify(http.formvalue("prefer_config"))
 
-	--获取制造商
-	local manufacturer=getManufacturer(at_port)
+	--获取制造商,数据接口,模组名称
+	local manufacturer
+	local data_interface
+	local name
+	uci:foreach("modem", "modem-device", function (modem_device)
+		if at_port == modem_device["at_port"] then
+			manufacturer=modem_device["manufacturer"]
+			data_interface=modem_device["data_interface"]
+			name=modem_device["name"]
+			return true --跳出循环
+		end
+	end)
 
 	--设置模组网络偏好
 	local command="source "..script_path..manufacturer..".sh && "..manufacturer.."_set_network_prefer "..at_port.." "..network_prefer_config
@@ -624,11 +645,48 @@ function setNetworkPrefer()
 
 	--获取设置好后的模组网络偏好
 	local network_prefer={}
-	if at_port and manufacturer and manufacturer~="unknown" then
-		local command="source "..script_path..manufacturer..".sh && "..manufacturer.."_get_network_prefer "..at_port
-		local result=shell(command)
-		network_prefer=json.parse(result)
-	end
+	-- if at_port and manufacturer and manufacturer~="unknown" then
+	-- 	local command="source "..script_path..manufacturer..".sh && "..manufacturer.."_get_network_prefer "..at_port.." "..data_interface.." "..name
+	-- 	local result=shell(command)
+	-- 	network_prefer=json.parse(result)
+	-- end
+
+	-- 写入Web界面
+	luci.http.prepare_content("application/json")
+	luci.http.write_json(network_prefer)
+end
+
+--[[
+@Description 设置频段偏好
+]]
+function setBandPrefer()
+    local at_port = http.formvalue("port")
+	local network_prefer_config = json.stringify(http.formvalue("prefer_config"))
+
+	--获取制造商,数据接口,模组名称
+	local manufacturer
+	local data_interface
+	local name
+	uci:foreach("modem", "modem-device", function (modem_device)
+		if at_port == modem_device["at_port"] then
+			manufacturer=modem_device["manufacturer"]
+			data_interface=modem_device["data_interface"]
+			name=modem_device["name"]
+			return true --跳出循环
+		end
+	end)
+
+	--设置模组网络偏好
+	local command="source "..script_path..manufacturer..".sh && "..manufacturer.."_set_band_prefer "..at_port.." "..network_prefer_config
+	shell(command)
+
+	--获取设置好后的模组网络偏好
+	local network_prefer={}
+	-- if at_port and manufacturer and manufacturer~="unknown" then
+	-- 	local command="source "..script_path..manufacturer..".sh && "..manufacturer.."_get_network_prefer "..at_port.." "..data_interface.." "..name
+	-- 	local result=shell(command)
+	-- 	network_prefer=json.parse(result)
+	-- end
 
 	-- 写入Web界面
 	luci.http.prepare_content("application/json")
@@ -894,6 +952,7 @@ function getPluginInfo()
 	--制造商私有驱动
 	usb_driver_info["qmi_wwan_q.ko"]="Not loaded"
 	usb_driver_info["qmi_wwan_f.ko"]="Not loaded"
+	usb_driver_info["qmi_wwan_m.ko"]="Not loaded"
 	usb_driver_info["meig_cdc_driver.ko"]="Not loaded"
 	setModelStatus(usb_driver_info)
 
diff --git a/package/wwan/luci-app-modem/luasrc/view/modem/modem_debug.htm b/package/wwan/luci-app-modem/luasrc/view/modem/modem_debug.htm
index ebb911965..e92b87ba5 100644
--- a/package/wwan/luci-app-modem/luasrc/view/modem/modem_debug.htm
+++ b/package/wwan/luci-app-modem/luasrc/view/modem/modem_debug.htm
@@ -46,6 +46,39 @@
 				copy_to_input();
 		});
 
+		// 设置网络偏好事件
+		set_network_prefer_event();
+	}
+
+	// 设置频段偏好事件
+	function set_band_prefer_event(network_type)
+	{
+		//获取网络偏好选项元素
+		var prefer_option_auto = document.getElementById(network_type+'_prefer_option_auto');
+		var prefer_option_custom = document.getElementById(network_type+'_prefer_option_custom');
+		//网络偏好选项为自动时触发
+		prefer_option_auto.addEventListener('change', function() {
+			if (prefer_option_auto.checked)
+			{
+				//禁用偏好复选框
+				disabled_prefer_custom_config(network_type+"_",true);
+				//全选偏好复选框
+				all_choose_prefer_custom_config(network_type+"_",true);
+			}
+		});
+		//网络偏好选项为自定义时触发
+		prefer_option_custom.addEventListener('change', function() {
+			if (prefer_option_custom.checked)
+			{
+				//禁用偏好复选框
+				disabled_prefer_custom_config(network_type+"_",false);
+			}
+		});
+	}
+
+	// 设置网络偏好事件
+	function set_network_prefer_event()
+	{
 		//获取网络偏好选项元素
 		var prefer_option_auto = document.getElementById('prefer_option_auto');
 		var prefer_option_custom = document.getElementById('prefer_option_custom');
@@ -54,9 +87,9 @@
 			if (prefer_option_auto.checked)
 			{
 				//禁用偏好复选框
-				disabled_prefer_custom_config(true);
+				disabled_prefer_custom_config("",true);
 				//全选偏好复选框
-				all_choose_prefer_custom_config(true);
+				all_choose_prefer_custom_config("",true);
 			}
 		});
 		//网络偏好选项为自定义时触发
@@ -64,9 +97,15 @@
 			if (prefer_option_custom.checked)
 			{
 				//禁用偏好复选框
-				disabled_prefer_custom_config(false);
+				disabled_prefer_custom_config("",false);
 			}
 		});
+
+		//设置频段偏好事件
+		set_band_prefer_event("5g");
+		set_band_prefer_event("4g");
+		set_band_prefer_event("3g");
+		set_band_prefer_event("2g");
 	}
 
 	//设置标签菜单事件
@@ -412,9 +451,9 @@
 	}
 
 	// 全选网络偏好复选框
-	function all_choose_prefer_custom_config(status)
+	function all_choose_prefer_custom_config(id_prefix,status)
 	{
-		var checkboxes=document.getElementById('prefer_custom_config').querySelectorAll('input[type="checkbox"]');
+		var checkboxes=document.getElementById(id_prefix+'prefer_custom_config').querySelectorAll('input[type="checkbox"]');
 		for(checkbox of checkboxes)
 		{
 			//设置网络偏好复选框状态
@@ -423,9 +462,9 @@
 	}
 
 	// 禁用网络偏好复选框
-	function disabled_prefer_custom_config(status)
+	function disabled_prefer_custom_config(id_prefix,status)
 	{
-		var checkboxes=document.getElementById('prefer_custom_config').querySelectorAll('input[type="checkbox"]');
+		var checkboxes=document.getElementById(id_prefix+'prefer_custom_config').querySelectorAll('input[type="checkbox"]');
 		for(checkbox of checkboxes)
 		{
 			//禁用
@@ -433,6 +472,25 @@
 		}
 	}
 
+	// 禁用网络偏好功能
+	function disabled_network_prefer_function(id_prefix,status)
+	{
+		//偏好选项
+		var prefer_option_auto = document.getElementById('prefer_option_auto'); 
+		prefer_option_auto.disabled=status;
+		document.getElementById('prefer_option_custom').disabled=status;
+		
+		//网络偏好为自动则不启用
+		if (!prefer_option_auto.checked)
+		{
+			//偏好复选框
+			disabled_prefer_custom_config("",status);
+		}
+
+		//偏好按钮
+		document.getElementById(id_prefix+'_prefer_button').disabled=status;
+	}
+
 	// 禁用功能
 	function disabled_function(function_name,status)
 	{
@@ -454,20 +512,10 @@
 		//网络偏好
 		else if (function_name=="network_prefer")
 		{
-			//偏好选项
-			document.getElementById('prefer_option_auto').disabled=status;
-			document.getElementById('prefer_option_custom').disabled=status;
-			
-			//网络偏好为自动则不启用
-			var prefer_option_auto = document.getElementById('prefer_option_auto');
-			if (!prefer_option_auto.checked)
-			{
-				//偏好复选框
-				disabled_prefer_custom_config(status);
-			}
-
-			//偏好按钮
-			document.getElementById('network_prefer_button').disabled=status;
+			// 网络类型
+			disabled_network_prefer_function("network",status);
+			// 频段
+			disabled_network_prefer_function("band",status);
 		}
 	}
 
@@ -549,29 +597,141 @@
 		);
 	}
 
-	// 获取当前网络视图
-	function get_current_prefer_view(network_prefer)
+	// 设置网络偏好视图
+	function set_network_prefer_view(network_prefer)
 	{
-		var current_prefer_view="";
+		var current_prefer_view=""; //当前网络偏好
+		var prefer_check_view=""; //网络偏好复选框
+		var network_type_count=0; //统计已启用的网络类型
 
-		//自动状态判断(全部选中为自动)
-		if (network_prefer["3G"]&&network_prefer["4G"]&&network_prefer["5G"])
+		for(var keys of network_prefer)
 		{
-			//更新当前偏好
-			current_prefer_view="AUTO";
-		}
-		else
-		{
-			//更新当前偏好
-			for(key in network_prefer)
+			for(var key in keys)
 			{
-				if (network_prefer[key]) {
+				var band_info_display="none"; //频段偏好显示
+				var is_check=""; //是否选中(启用)
+				var network_type=key.toLowerCase(); //转小写
+				
+				//已启动的网络类型
+				if (keys[key]["enable"])
+				{
+					//设置偏好视图
 					current_prefer_view+=key+" ";
+					if (keys[key]["band"].length!=0) {
+						band_info_display="block";
+					}
+					is_check="checked";
+					network_type_count++;
+				}
+				//设置偏好复选框视图
+				prefer_check_view+='<span class="cbi-checkbox"><input id="prefer_config_'+network_type+'" type="checkbox" class="cbi-input-checkbox" value="'+network_type+'" '+is_check+'><span>'+key+'</span></span> &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%>
diff --git a/package/wwan/luci-app-modem/luasrc/view/modem/plugin_info.htm b/package/wwan/luci-app-modem/luasrc/view/modem/plugin_info.htm
index aa966862d..3268d68f6 100644
--- a/package/wwan/luci-app-modem/luasrc/view/modem/plugin_info.htm
+++ b/package/wwan/luci-app-modem/luasrc/view/modem/plugin_info.htm
@@ -257,6 +257,12 @@
 						<td class="td cbi-value-field" data-title="<%:Status%>" id="qmi_wwan_f_status">-</td>
 					</tr>
 					<tr class="tr cbi-section-table-row cbi-rowstyle-1">
+						<td class="td cbi-value-field" data-title="<%:Driver Type%>"><%:Private (Meig)%></td>
+						<td class="td cbi-value-field" data-title="<%:Mode%>"><%:QMI%></td>
+						<td class="td cbi-value-field" data-title="<%:Kernel Model%>" id="qmi_wwan_m_kernel_model_name">qmi_wwan_m.ko</td>
+						<td class="td cbi-value-field" data-title="<%:Status%>" id="qmi_wwan_m_status">-</td>
+					</tr>
+					<tr class="tr cbi-section-table-row cbi-rowstyle-2">
 						<td class="td cbi-value-field" data-title="<%:Driver Type%>"><%:Private (Meig)%></td>
 						<td class="td cbi-value-field" data-title="<%:Mode%>"><%:NCM%></td>
 						<td class="td cbi-value-field" data-title="<%:Kernel Model%>" id="meig_cdc_driver_kernel_model_name">meig_cdc_driver.ko</td>
diff --git a/package/wwan/luci-app-modem/po/zh-cn/modem.po b/package/wwan/luci-app-modem/po/zh-cn/modem.po
index f8e70878f..55145ca03 100644
--- a/package/wwan/luci-app-modem/po/zh-cn/modem.po
+++ b/package/wwan/luci-app-modem/po/zh-cn/modem.po
@@ -103,6 +103,24 @@ msgstr "选择一个模组进行调试"
 msgid "Network Preferences"
 msgstr "网络偏好"
 
+msgid "Apply Network Prefer"
+msgstr "应用网络偏好"
+
+msgid "Apply Band Prefer"
+msgstr "应用频段偏好"
+
+msgid "2G Band Preferences"
+msgstr "2G 频段偏好"
+
+msgid "3G Band Preferences"
+msgstr "3G 频段偏好"
+
+msgid "4G Band Preferences"
+msgstr "4G 频段偏好"
+
+msgid "5G Band Preferences"
+msgstr "5G 频段偏好"
+
 msgid "Self Test"
 msgstr "自检"
 
diff --git a/package/wwan/luci-app-modem/po/zh_Hans/modem.po b/package/wwan/luci-app-modem/po/zh_Hans/modem.po
index f8e70878f..55145ca03 100644
--- a/package/wwan/luci-app-modem/po/zh_Hans/modem.po
+++ b/package/wwan/luci-app-modem/po/zh_Hans/modem.po
@@ -103,6 +103,24 @@ msgstr "选择一个模组进行调试"
 msgid "Network Preferences"
 msgstr "网络偏好"
 
+msgid "Apply Network Prefer"
+msgstr "应用网络偏好"
+
+msgid "Apply Band Prefer"
+msgstr "应用频段偏好"
+
+msgid "2G Band Preferences"
+msgstr "2G 频段偏好"
+
+msgid "3G Band Preferences"
+msgstr "3G 频段偏好"
+
+msgid "4G Band Preferences"
+msgstr "4G 频段偏好"
+
+msgid "5G Band Preferences"
+msgstr "5G 频段偏好"
+
 msgid "Self Test"
 msgstr "自检"
 
diff --git a/package/wwan/luci-app-modem/root/etc/config/custom_at_commands b/package/wwan/luci-app-modem/root/etc/config/custom_at_commands
index 86121064d..9b0194382 100644
--- a/package/wwan/luci-app-modem/root/etc/config/custom_at_commands
+++ b/package/wwan/luci-app-modem/root/etc/config/custom_at_commands
@@ -276,8 +276,8 @@ config custom-commands
 	option command 'AT^SYSINFOEX'
 
 config custom-commands
-	option description '查询载波聚合小区信息 > AT^CELLINFO=3'
-	option command 'AT^CELLINFO=3'
+	option description '查询载波聚合小区信息 > AT^CELLINFO=1'
+	option command 'AT^CELLINFO=1'
 
 config custom-commands
 	option description '查询当前拨号模式 > AT+SER?'
@@ -329,4 +329,64 @@ config custom-commands
 
 config custom-commands
 	option description '重启模组 > AT+RESET'
-	option command 'AT+RESET'
\ No newline at end of file
+	option command 'AT+RESET'
+
+config custom-commands
+	option description '****************华为****************'
+	option command 'ATI'
+
+config custom-commands
+	option description '获取SIM卡卡槽状态 > AT^SIMSLOT?'
+	option command 'AT^SIMSLOT?'
+
+config custom-commands
+	option description '设置当前使用的为卡1 > AT^SIMSWITCH=1'
+	option command 'AT^SIMSWITCH=1'
+
+config custom-commands
+	option description '设置当前使用的为卡2 > AT^SIMSWITCH=0'
+	option command 'AT^SIMSWITCH=0'
+
+config custom-commands
+	option description '查询网络信息 > AT^SYSINFOEX'
+	option command 'AT^SYSINFOEX'
+
+config custom-commands
+	option description '查询当前拨号模式 > AT^SETMODE?'
+	option command 'AT^SETMODE?'
+
+config custom-commands
+	option description 'ECM拨号模式(Linux) > AT^SETMODE=0'
+	option command 'AT^SETMODE=0'
+
+config custom-commands
+	option description 'NCM拨号模式(Windows) > AT^SETMODE=1'
+	option command 'AT^SETMODE=1'
+
+config custom-commands
+	option description 'ECM拨号模式(Linux,Debug) > AT^SETMODE=2'
+	option command 'AT^SETMODE=2'
+
+config custom-commands
+	option description 'NCM拨号模式(Windows,Debug) > AT^SETMODE=3'
+	option command 'AT^SETMODE=3'
+
+config custom-commands
+	option description '锁4G > AT^SYSCFGEX="03",40000000,1,2,7FFFFFFFFFFFFFFF,,'
+	option command 'AT^SYSCFGEX="03",40000000,1,2,7FFFFFFFFFFFFFFF,,'
+
+config custom-commands
+	option description '锁5G > AT^SYSCFGEX="08",40000000,1,2,7FFFFFFFFFFFFFFF,,'
+	option command 'AT^SYSCFGEX="08",40000000,1,2,7FFFFFFFFFFFFFFF,,'
+
+config custom-commands
+	option description '恢复自动搜索网络 > AT^SYSCFGEX="00",3FFFFFFF,1,2,7FFFFFFFFFFFFFFF,,'
+	option command 'AT^SYSCFGEX="00",3FFFFFFF,1,2,7FFFFFFFFFFFFFFF,,'
+
+config custom-commands
+	option description '获取模组温度 > AT^CHIPTEMP?'
+	option command 'AT^CHIPTEMP?'
+
+config custom-commands
+	option description '重启模组 > AT^RESET'
+	option command 'AT^RESET'
\ No newline at end of file
diff --git a/package/wwan/luci-app-modem/root/etc/init.d/modem b/package/wwan/luci-app-modem/root/etc/init.d/modem
index edf82a966..3edd09397 100755
--- a/package/wwan/luci-app-modem/root/etc/init.d/modem
+++ b/package/wwan/luci-app-modem/root/etc/init.d/modem
@@ -445,6 +445,8 @@ stop_ecm()
 		fi
 	elif [ "$manufacturer" = "meig" ]; then
 		at_command="AT^NDISDUP=${define_connect},0"
+	elif [ "$manufacturer" = "huawei" ]; then
+		at_command="AT^NDISDUP=${define_connect},0"
 	else
 		at_command='ATI'
 	fi
@@ -633,13 +635,12 @@ dial()
 	[ -z "${modem_no}" ] && return 0
 	
 	#获取模组的拨号模式
-	local time=0
+	local time=20
 	local mode
-	while [ $time -lt 10 ]; do
+	for i in $(seq 1 ${time}); do
 		mode=$(get_mode ${modem_no})
 		[ -n "$mode" ] && [ "$mode" != "unknown" ] && break
 		sleep 5s
-		time=$((time+1))
     done
 
 	#获取不到拨号模式
diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/at_commands.json b/package/wwan/luci-app-modem/root/usr/share/modem/at_commands.json
index 03ba7e7f0..20b709ad5 100644
--- a/package/wwan/luci-app-modem/root/usr/share/modem/at_commands.json
+++ b/package/wwan/luci-app-modem/root/usr/share/modem/at_commands.json
@@ -144,7 +144,7 @@
 				{"设置当前使用的为卡1 > AT^SIMSLOT=1":"AT^SIMSLOT=1"},
 				{"设置当前使用的为卡2 > AT^SIMSLOT=2":"AT^SIMSLOT=2"},
 				{"查询网络信息 > AT^SYSINFOEX":"AT^SYSINFOEX"},
-				{"查询载波聚合小区信息 > AT^CELLINFO=3":"AT^CELLINFO=3"},
+				{"查询载波聚合小区信息 > AT^CELLINFO=1":"AT^CELLINFO=1"},
 				{"查询当前拨号模式 > AT+SER?":"AT+SER?"},
 				{"QMI/GobiNet拨号模式 > AT+SER=1,1":"AT+SER=1,1"},
 				{"ECM拨号模式 > AT+SER=2,1":"AT+SER=2,1"},
@@ -159,6 +159,24 @@
 				{"获取模组温度 > AT+TEMP":"AT+TEMP"},
 				{"重启模组 > AT+RESET":"AT+RESET"}
 			]
+		},
+		"huawei":{
+			"hisilicon":[
+				{"获取SIM卡卡槽状态 > AT^SIMSLOT?":"AT^SIMSLOT?"},
+				{"设置当前使用的为卡1 > AT^SIMSWITCH=1":"AT^SIMSWITCH=1"},
+				{"设置当前使用的为卡2 > AT^SIMSWITCH=0":"AT^SIMSWITCH=0"},
+				{"查询网络信息 > AT^SYSINFOEX":"AT^SYSINFOEX"},
+				{"查询当前拨号模式 > AT^SETMODE?":"AT^SETMODE?"},
+				{"ECM拨号模式(Linux) > AT^SETMODE=0":"AT^SETMODE=0"},
+				{"NCM拨号模式(Windows) > AT^SETMODE=1":"AT^SETMODE=1"},
+				{"ECM拨号模式(Linux,Debug) > AT^SETMODE=2":"AT^SETMODE=2"},
+				{"NCM拨号模式(Windows,Debug) > AT^SETMODE=3":"AT^SETMODE=3"},
+				{"锁4G > AT^SYSCFGEX=\"03\",40000000,1,2,7FFFFFFFFFFFFFFF,,":"AT^SYSCFGEX=\"03\",40000000,1,2,7FFFFFFFFFFFFFFF,,"},
+				{"锁5G > AT^SYSCFGEX=\"08\",40000000,1,2,7FFFFFFFFFFFFFFF,,":"AT^SYSCFGEX=\"08\",40000000,1,2,7FFFFFFFFFFFFFFF,,"},
+				{"恢复自动搜索网络 > AT^SYSCFGEX=\"00\",3FFFFFFF,1,2,7FFFFFFFFFFFFFFF,,":"AT^SYSCFGEX=\"00\",3FFFFFFF,1,2,7FFFFFFFFFFFFFFF,,"},
+				{"获取模组温度 > AT^CHIPTEMP?":"AT^CHIPTEMP?"},
+				{"重启模组 > AT^RESET":"AT^RESET"}
+			]
 		}
 	}
 }
\ No newline at end of file
diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/fibocom.sh b/package/wwan/luci-app-modem/root/usr/share/modem/fibocom.sh
index 57e678bd8..4008eb02d 100755
--- a/package/wwan/luci-app-modem/root/usr/share/modem/fibocom.sh
+++ b/package/wwan/luci-app-modem/root/usr/share/modem/fibocom.sh
@@ -43,7 +43,7 @@ fibocom_get_dns()
 
     #获取DNS地址
     at_command="AT+GTDNS=${define_connect}"
-    local response=$(at ${at_port} ${at_command} | grep "+GTDNS: " | grep -E '[0-9]+.[0-9]+.[0-9]+.[0-9]+' | sed -n '1p')
+    local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+GTDNS: " | grep -E '[0-9]+.[0-9]+.[0-9]+.[0-9]+' | sed -n '1p')
 
     local ipv4_dns1=$(echo "${response}" | awk -F'"' '{print $2}' | awk -F',' '{print $1}')
     [ -z "$ipv4_dns1" ] && {
@@ -203,29 +203,32 @@ fibocom_set_mode()
 
 #获取网络偏好
 # $1:AT串口
+# $2:数据接口
+# $3:模组名称
 fibocom_get_network_prefer()
 {
     local at_port="$1"
-    at_command="AT+GTACT?"
-    local network_prefer_num=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "+GTACT:" | awk -F',' '{print $1}' | sed 's/+GTACT: //g')
+    local data_interface="$2"
+    local modem_name="$3"
     
+    at_command="AT+GTACT?"
+    local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+GTACT:" | sed 's/+GTACT: //g')
+    local network_type_num=$(echo "$response" | awk -F',' '{print $1}')
+
+    #获取网络类型
+    # local network_prefer_2g="0";
     local network_prefer_3g="0";
     local network_prefer_4g="0";
     local network_prefer_5g="0";
 
     #匹配不同的网络类型
-    case "$network_prefer_num" in
+    case "$network_type_num" in
         "1") network_prefer_3g="1" ;;
         "2") network_prefer_4g="1" ;;
         "4")
             network_prefer_3g="1"
             network_prefer_4g="1"
         ;;
-        "10")
-            network_prefer_3g="1"
-            network_prefer_4g="1"
-            network_prefer_5g="1"
-        ;;
         "14") network_prefer_5g="1" ;;
         "16")
             network_prefer_3g="1"
@@ -235,26 +238,37 @@ fibocom_get_network_prefer()
             network_prefer_4g="1"
             network_prefer_5g="1"
         ;;
-        "20")
-            network_prefer_3g="1"
-            network_prefer_4g="1"
-            network_prefer_5g="1"
-        ;;
-        *)
+        "10"|"20"|*)
             network_prefer_3g="1"
             network_prefer_4g="1"
             network_prefer_5g="1"
         ;;
     esac
 
+    #获取频段信息
+    # local band_2g_info="[]"
+    local band_3g_info="[]"
+    local band_4g_info="[]"
+    local band_5g_info="[]"
+
+    #生成网络偏好
     local network_prefer="{
-        \"network_prefer\":{
-            \"3G\":$network_prefer_3g,
-            \"4G\":$network_prefer_4g,
-            \"5G\":$network_prefer_5g
-        }
+        \"network_prefer\":[
+            {\"3G\":{
+                \"enable\":$network_prefer_3g,
+                \"band\":$band_3g_info
+            }},
+            {\"4G\":{
+                \"enable\":$network_prefer_4g,
+                \"band\":$band_4g_info
+            }},
+            {\"5G\":{
+                \"enable\":$network_prefer_5g,
+                \"band\":$band_5g_info
+            }}
+        ]
     }"
-    echo "$network_prefer"
+    echo "${network_prefer}"
 }
 
 #设置网络偏好
@@ -265,41 +279,41 @@ fibocom_set_network_prefer()
     local at_port="$1"
     local network_prefer="$2"
 
-    #获取网络偏好数字
-    local network_prefer_num
+    #获取网络偏好配置
+    local network_prefer_config
 
     #获取选中的数量
     local count=$(echo "$network_prefer" | grep -o "1" | wc -l)
-    #获取每个偏好的值
-    local network_prefer_3g=$(echo "$network_prefer" | jq -r '.["3G"]')
-    local network_prefer_4g=$(echo "$network_prefer" | jq -r '.["4G"]')
-    local network_prefer_5g=$(echo "$network_prefer" | jq -r '.["5G"]')
+    #获取启用的网络偏好
+    local enable_5g=$(echo "$network_prefer" | jq -r '.["5G"].enable')
+    local enable_4g=$(echo "$network_prefer" | jq -r '.["4G"].enable')
+    local enable_3g=$(echo "$network_prefer" | jq -r '.["3G"].enable')
 
     case "$count" in
         "1")
-            if [ "$network_prefer_3g" = "1" ]; then
-                network_prefer_num="1"
-            elif [ "$network_prefer_4g" = "1" ]; then
-                network_prefer_num="2"
-            elif [ "$network_prefer_5g" = "1" ]; then
-                network_prefer_num="14"
+            if [ "$enable_3g" = "1" ]; then
+                network_prefer_config="1"
+            elif [ "$enable_4g" = "1" ]; then
+                network_prefer_config="2"
+            elif [ "$enable_5g" = "1" ]; then
+                network_prefer_config="14"
             fi
         ;;
         "2")
-            if [ "$network_prefer_3g" = "1" ] && [ "$network_prefer_4g" = "1" ]; then
-                network_prefer_num="4"
-            elif [ "$network_prefer_3g" = "1" ] && [ "$network_prefer_5g" = "1" ]; then
-                network_prefer_num="16"
-            elif [ "$network_prefer_4g" = "1" ] && [ "$network_prefer_5g" = "1" ]; then
-                network_prefer_num="17"
+            if [ "$enable_3g" = "1" ] && [ "$enable_4g" = "1" ]; then
+                network_prefer_config="4"
+            elif [ "$enable_3g" = "1" ] && [ "$enable_5g" = "1" ]; then
+                network_prefer_config="16"
+            elif [ "$enable_4g" = "1" ] && [ "$enable_5g" = "1" ]; then
+                network_prefer_config="17"
             fi
         ;;
-        "3") network_prefer_num="20" ;;
-        *) network_prefer_num="10" ;;
+        "3") network_prefer_config="20" ;;
+        *) network_prefer_config="10" ;;
     esac
-
+echo "$network_prefer_config" >> /root/a
     #设置模组
-    at_command="AT+GTACT=$network_prefer_num"
+    at_command="AT+GTACT=${network_prefer_config}"
     sh ${SCRIPT_DIR}/modem_at.sh $at_port "$at_command"
 }
 
diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/huawei.sh b/package/wwan/luci-app-modem/root/usr/share/modem/huawei.sh
new file mode 100755
index 000000000..e3d95ba1e
--- /dev/null
+++ b/package/wwan/luci-app-modem/root/usr/share/modem/huawei.sh
@@ -0,0 +1,928 @@
+#!/bin/sh
+# Copyright (C) 2023 Siriling <siriling@qq.com>
+
+#脚本目录
+SCRIPT_DIR="/usr/share/modem"
+
+#预设
+huawei_presets()
+{
+    #关闭模组主动上报
+	at_command='AT^CURC=0'
+	sh "${SCRIPT_DIR}/modem_at.sh" "$at_port" "$at_command"
+
+    #开启5G NA NSA接入
+	at_command='AT^C5GOPTION=1,3,3'
+	sh "${SCRIPT_DIR}/modem_at.sh" "$at_port" "$at_command"
+}
+
+#获取DNS
+# $1:AT串口
+# $2:连接定义
+huawei_get_dns()
+{
+    local at_port="$1"
+    local define_connect="$2"
+
+    [ -z "$define_connect" ] && {
+        define_connect="1"
+    }
+
+    local public_dns1_ipv4="223.5.5.5"
+    local public_dns2_ipv4="119.29.29.29"
+    local public_dns1_ipv6="2400:3200::1" #下一代互联网北京研究中心:240C::6666,阿里:2400:3200::1,腾讯:2402:4e00::
+    local public_dns2_ipv6="2402:4e00::"
+
+    #获取DNS地址(IPv4)
+    at_command="AT^DHCP=${define_connect}"
+    local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "\^DHCP:" | sed -n '1p')
+
+    local ipv4_dns1=$(echo "${response}" | awk -F',' '{print $5}')
+    if [ -z "$ipv4_dns1" ]; then
+        ipv4_dns1="${public_dns1_ipv4}"
+    else
+        #按字节(byte)将十六进制拆分并转换为对应的十进制表示
+        ipv4_dns1=$(echo "$ipv4_dns1" | awk '{
+            for (i = length; i >= 1; i -= 2) {
+                printf "%d.", "0x" substr($0, i-1, 2)
+            }
+        }')
+        ipv4_dns1="${ipv4_dns1%?}"
+    fi
+
+    local ipv4_dns2=$(echo "${response}" | awk -F',' '{print $6}')
+    if [ -z "$ipv4_dns2" ]; then
+        ipv4_dns2="${public_dns1_ipv4}"
+    else
+        #按字节(byte)将十六进制拆分并转换为对应的十进制表示
+        ipv4_dns2=$(echo "$ipv4_dns2" | awk '{
+            for (i = length; i >= 1; i -= 2) {
+                printf "%d.", "0x" substr($0, i-1, 2)
+            }
+        }')
+        ipv4_dns2="${ipv4_dns2%?}"
+    fi
+
+    #获取DNS地址(IPv6)
+    at_command="AT^DHCPV6=${define_connect}"
+    local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "\^DHCPV6:" | sed -n '1p')
+
+    local ipv6_dns1=$(echo "${response}" | awk -F',' '{print $5}')
+    [ -z "$ipv6_dns1" ] && {
+        ipv6_dns1="${public_dns1_ipv6}"
+    }
+
+    local ipv6_dns2=$(echo "${response}" | awk -F',' '{print $6}')
+    [ -z "$ipv6_dns2" ] && {
+        ipv6_dns2="${public_dns2_ipv6}"
+    }
+
+    dns="{
+        \"dns\":{
+            \"ipv4_dns1\":\"$ipv4_dns1\",
+            \"ipv4_dns2\":\"$ipv4_dns2\",
+            \"ipv6_dns1\":\"$ipv6_dns1\",
+            \"ipv6_dns2\":\"$ipv6_dns2\"
+        }
+    }"
+
+    echo "$dns"
+}
+
+#获取拨号模式
+# $1:AT串口
+# $2:平台
+huawei_get_mode()
+{
+    local at_port="$1"
+    local platform="$2"
+
+    at_command="AT^SETMODE?"
+    local mode_num=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "\^SETMODE:" | awk -F': ' '{print $2}' | sed 's/\r//g')
+
+    if [ -z "$mode_num" ]; then
+        echo "unknown"
+        return
+    fi
+
+    #获取芯片平台
+	if [ -z "$platform" ]; then
+		local modem_number=$(uci -q get modem.@global[0].modem_number)
+        for i in $(seq 0 $((modem_number-1))); do
+            local at_port_tmp=$(uci -q get modem.modem$i.at_port)
+            if [ "$at_port" = "$at_port_tmp" ]; then
+                platform=$(uci -q get modem.modem$i.platform)
+                break
+            fi
+        done
+	fi
+
+    local mode
+    case "$platform" in
+        "hisilicon")
+            case "$mode_num" in
+                "0"|"2") mode="ecm" ;;
+                "1"|"3"|"4"|"5") mode="ncm" ;;
+                "6") mode="rndis" ;;
+                "7") mode="mbim" ;;
+                "8") mode="ppp" ;;
+                *) mode="$mode_num" ;;
+            esac
+        ;;
+        *)
+            mode="$mode_num"
+        ;;
+    esac
+    echo "${mode}"
+}
+
+#设置拨号模式
+# $1:AT串口
+# $2:拨号模式配置
+huawei_set_mode()
+{
+    local at_port="$1"
+    local mode_config="$2"
+
+    #获取芯片平台
+    local platform
+    local modem_number=$(uci -q get modem.@global[0].modem_number)
+    for i in $(seq 0 $((modem_number-1))); do
+        local at_port_tmp=$(uci -q get modem.modem$i.at_port)
+        if [ "$at_port" = "$at_port_tmp" ]; then
+            platform=$(uci -q get modem.modem$i.platform)
+            break
+        fi
+    done
+
+    #获取拨号模式配置
+    local mode_num
+    case "$platform" in
+        "hisilicon")
+            case "$mode_config" in
+                "ecm") mode_num="0" ;;
+                "ncm") mode_num="4" ;;
+                *) mode_num="0" ;;
+            esac
+        ;;
+        *)
+            mode_num="0"
+        ;;
+    esac
+
+    #设置模组
+    at_command="AT^SETMODE=${mode_num}"
+    sh ${SCRIPT_DIR}/modem_at.sh ${at_port} "${at_command}"
+}
+
+#获取位
+# $1:频段名称
+huawei_get_bit()
+{
+    local band_name="$1"
+
+    local bit
+    case "$band_name" in
+        "DCS_1800") bit="8" ;;
+        "E-GSM_900"|"E_GSM_900") bit="9" ;;
+        "P-GSM_900"|"P_GSM_900") bit="10" ;;
+        "GSM_450") bit="17" ;;
+        "GSM_480") bit="18" ;;
+        "GSM_750") bit="19" ;;
+        "GSM_850") bit="20" ;;
+        "R-GSM_900"|"R_GSM_900") bit="21" ;;
+        "PCS_1900") bit="22" ;;
+    esac
+
+    echo "${bit}"
+}
+
+#获取频段信息
+# $1:频段二进制数
+# $2:支持的频段
+# $3:频段类型(2G,3G,4G,5G)
+huawei_get_band_info()
+{
+    local band_bin="$1"
+    local support_band="$2"
+    local band_type="$3"
+
+    local band_info=""
+    local support_band=$(echo "$support_band" | sed 's/,/ /g')
+    if [ "$band_type" = "2G" ]; then
+
+        for band in $support_band; do
+            #获取bit位
+            local bit=$(huawei_get_bit ${band})
+            #获取值
+            local enable="${band_bin: $((-bit)):1}"
+            [ -z "$enable" ] && enable="0"
+            #设置频段信息
+            # band_info=$(echo ${band_info} | jq '. += [{"'$band'":'$enable'}]')
+            band_info="${band_info},{\"$band\":$enable}"
+        done
+    else
+        #频段频段起始,前缀位置
+        local start_bit
+        local band_prefix
+        case "$band_type" in
+            "3G")
+                start_bit="23"
+                band_prefix="WCDMA_B"
+            ;;
+            "4G")
+                start_bit="1"
+                band_prefix="LTE_BC"
+            ;;
+            "5G")
+                start_bit="1"
+                band_prefix="NR5G_N"
+            ;;
+        esac
+
+        for band in $support_band; do
+            #获取值(从start_bit位开始)
+            local enable="${band_bin: $((-band-start_bit+1)):1}"
+            [ -z "$enable" ] && enable="0"
+            #设置频段信息
+            # band_info=$(echo ${band_info} | jq '. += [{'$band_prefix$band':'$enable'}]')
+            band_info="${band_info},{\"$band_prefix$band\":$enable}"
+        done
+    fi
+    #去掉第一个,
+    band_info="["${band_info/,/}"]"
+    # band_info="[${band_info%?}]"
+
+    echo "${band_info}"
+}
+
+#获取网络偏好
+# $1:AT串口
+# $2:数据接口
+# $3:模组名称
+huawei_get_network_prefer()
+{
+    local at_port="$1"
+    local data_interface="$2"
+    local modem_name="$3"
+
+    at_command="AT^SYSCFGEX?"
+    local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "\^SYSCFGEX:" | sed 's/\^SYSCFGEX://g')
+    local network_type_num=$(echo "$response" | awk -F'"' '{print $2}')
+
+    #获取网络类型
+    local network_prefer_2g="0";
+    local network_prefer_3g="0";
+    local network_prefer_4g="0";
+    local network_prefer_5g="0";
+
+    #匹配不同的网络类型
+    local auto=$(echo "${network_type_num}" | grep "00")
+    if [ -n "$auto" ]; then
+        network_prefer_2g="1"
+        network_prefer_3g="1"
+        network_prefer_4g="1"
+        network_prefer_5g="1"
+    else
+        local gsm=$(echo "${network_type_num}" | grep "01")
+        local wcdma=$(echo "${network_type_num}" | grep "02")
+        local lte=$(echo "${network_type_num}" | grep "03")
+        local nr=$(echo "${network_type_num}" | grep "08")
+        if [ -n "$gsm" ]; then
+            network_prefer_2g="1"
+        fi 
+        if [ -n "$wcdma" ]; then
+            network_prefer_3g="1"
+        fi
+        if [ -n "$lte" ]; then
+            network_prefer_4g="1"
+        fi
+        if [ -n "$nr" ]; then
+            network_prefer_5g="1"
+        fi
+    fi
+
+	#获取模组信息
+    local modem_info=$(jq '.modem_support.'$data_interface'."'$modem_name'"' ${SCRIPT_DIR}/modem_support.json)
+
+    #获取模组支持的频段
+    local support_2g_band=$(echo "$modem_info" | jq -r '.band_2g')
+    local support_3g_band=$(echo "$modem_info" | jq -r '.band_3g')
+    local support_4g_band=$(echo "$modem_info" | jq -r '.band_4g')
+    local support_5g_band=$(echo "$modem_info" | jq -r '.band_5g')
+
+    #获取频段信息
+    local band_hex_2g_3g=$(echo "$response" | awk -F',' '{print $2}')
+    #十六进制转二进制
+    local bin_2g_3g=$(echo "obase=2; ibase=16; $band_hex_2g_3g" | bc)
+    local band_2g_info=$(huawei_get_band_info "${bin_2g_3g}" "${support_2g_band}" "2G")
+    local band_3g_info=$(huawei_get_band_info "${bin_2g_3g}" "${support_3g_band}" "3G")
+
+    local band_hex_4g_5g=$(echo "$response" | awk -F',' '{print $5}' | sed 's/\r//g')
+    #十六进制转二进制
+    local bin_4g_5g=$(echo "obase=2; ibase=16; $band_hex_4g_5g" | bc)
+    local band_4g_info=$(huawei_get_band_info "${bin_4g_5g}" "${support_4g_band}" "4G")
+    local band_5g_info=$(huawei_get_band_info "${bin_4g_5g}" "${support_5g_band}" "5G")
+
+    #生成网络偏好
+    local network_prefer="{
+        \"network_prefer\":[
+            {\"2G\":{
+                \"enable\":$network_prefer_2g,
+                \"band\":$band_2g_info
+            }},
+            {\"3G\":{
+                \"enable\":$network_prefer_3g,
+                \"band\":$band_3g_info
+            }},
+            {\"4G\":{
+                \"enable\":$network_prefer_4g,
+                \"band\":$band_4g_info
+            }},
+            {\"5G\":{
+                \"enable\":$network_prefer_5g,
+                \"band\":$band_5g_info
+            }}
+        ]
+    }"
+    echo "${network_prefer}"
+}
+
+#设置网络偏好
+# $1:AT串口
+# $2:网络偏好配置
+huawei_set_network_prefer()
+{
+    local at_port="$1"
+    local network_prefer="$2"
+
+    #获取启用的网络偏好
+    local enable_5g=$(echo "$network_prefer" | jq -r '.["5G"].enable')
+    local enable_4g=$(echo "$network_prefer" | jq -r '.["4G"].enable')
+    local enable_3g=$(echo "$network_prefer" | jq -r '.["3G"].enable')
+    local enable_2g=$(echo "$network_prefer" | jq -r '.["2G"].enable')
+
+    #获取网络偏好配置
+    local network_prefer_config
+    [ "$enable_5g" = "1" ] && network_prefer_config="${network_prefer_config}08"
+    [ "$enable_4g" = "1" ] && network_prefer_config="${network_prefer_config}03"
+    [ "$enable_3g" = "1" ] && network_prefer_config="${network_prefer_config}02"
+    [ "$enable_2g" = "1" ] && network_prefer_config="${network_prefer_config}01"
+
+    [ -z "$network_prefer_config" ] && network_prefer_config="99"
+
+    #设置模组
+    at_command='AT^SYSCFGEX="'${network_prefer_config}'",40000000,1,2,40000000,,'
+    sh ${SCRIPT_DIR}/modem_at.sh "${at_port}" "${at_command}"
+}
+
+#设置频段
+# $1:AT串口
+# $2:频段偏好配置
+huawei_set_band_prefer()
+{
+    local at_port="$1"
+    local network_prefer="$2"
+
+    #获取选中的数量
+    local count=$(echo "$network_prefer" | grep -o "1" | wc -l)
+    #获取每个偏好的值
+    local network_prefer_5g=$(echo "$network_prefer" | jq -r '.["5G"]')
+    local network_prefer_4g=$(echo "$network_prefer" | jq -r '.["4G"]')
+    local network_prefer_3g=$(echo "$network_prefer" | jq -r '.["3G"]')
+    local network_prefer_2g=$(echo "$network_prefer" | jq -r '.["2G"]')
+
+    #获取启用的网络偏好
+    local enable_5g=$(echo "$network_prefer_5g" | jq -r '.enable')
+    local enable_4g=$(echo "$network_prefer_4g" | jq -r '.enable')
+    local enable_3g=$(echo "$network_prefer_3g" | jq -r '.enable')
+    local enable_2g=$(echo "$network_prefer_2g" | jq -r '.enable')
+
+    #获取网络偏好配置和频段偏好配置
+    local network_prefer_config
+    local band_hex_2g_3g=0
+    local band_hex_4g_5g=0
+
+    [ "$enable_5g" = "1" ] && {
+        network_prefer_config="${network_prefer_config}08"
+        local band_tmp=$(echo "$network_prefer_5g" | jq -r '.band[]')
+        
+        local i=0
+        local bands=$(echo "$band_tmp" | jq -r 'to_entries | .[] | .key')
+        #遍历band的值
+        for band in $bands; do
+            local value=$(echo "$network_prefer_5g" | jq -r '.band'"[$i].$band")
+            [ "$value" = "1" ] && {
+                #获取bit位
+                local bit=$(echo "$band" | sed 's/NR5G_N//g')
+                #获取值
+                local result=$(echo "obase=16; ibase=10; 2^($bit-1)" | bc)
+                band_hex_4g_5g=$(echo "obase=16; ibase=16; $band_hex_4g_5g + $result" | bc)
+            }
+            i=$((i+1))
+        done
+    }
+
+    [ "$enable_4g" = "1" ] && {
+        network_prefer_config="${network_prefer_config}03"
+        local band_tmp=$(echo "$network_prefer_4g" | jq -r '.band[]')
+
+        local i=0
+        local bands=$(echo "$band_tmp" | jq -r 'to_entries | .[] | .key')
+        #遍历band的值
+        for band in $bands; do
+            local value=$(echo "$network_prefer_4g" | jq -r '.band'"[$i].$band")
+            [ "$value" = "1" ] && {
+                #获取bit位
+                local bit=$(echo "$band" | sed 's/LTE_BC//g')
+                #获取值
+                local result=$(echo "obase=16; ibase=10; 2^($bit-1)" | bc)
+                band_hex_4g_5g=$(echo "obase=16; ibase=16; $band_hex_4g_5g + $result" | bc)
+            }
+            i=$((i+1))
+        done
+    }
+
+    [ "$enable_3g" = "1" ] && {
+        network_prefer_config="${network_prefer_config}02"
+        local band_tmp=$(echo "$network_prefer_3g" | jq -r '.band[]')
+
+        local i=0
+        local bands=$(echo "$band_tmp" | jq -r 'to_entries | .[] | .key')
+        #遍历band的值
+        for band in $bands; do
+            local value=$(echo "$network_prefer_3g" | jq -r '.band'"[$i].$band")
+            [ "$value" = "1" ] && {
+                #获取bit位
+                local bit=$(echo "$band" | sed 's/WCDMA_B//g')
+                #获取值
+                local result=$(echo "obase=16; ibase=10; 2^($bit+22-1)" | bc)
+                band_hex_2g_3g=$(echo "obase=16; ibase=16; $band_hex_2g_3g + $result" | bc)
+            }
+            i=$((i+1))
+        done
+    }
+
+    [ "$enable_2g" = "1" ] && {
+        network_prefer_config="${network_prefer_config}01"
+        local band_tmp=$(echo "$network_prefer_2g" | jq -r '.band[]')
+
+        local i=0
+        local bands=$(echo "$band_tmp" | jq -r 'to_entries | .[] | .key')
+        #遍历band的值
+        for band in $bands; do
+            # band_format=$(echo "$band" | sed 's/-/_/g')
+            local value=$(echo "$network_prefer_2g" | jq -r '.band'"[$i].$band")
+            [ "$value" = "1" ] && {
+                #获取bit位
+                local bit=$(huawei_get_bit ${band})
+                #获取值
+                local result=$(echo "obase=16; ibase=10; 2^($bit-1)" | bc)
+                band_hex_2g_3g=$(echo "obase=16; ibase=16; $band_hex_2g_3g + $result" | bc)
+            }
+            i=$((i+1))
+        done
+    }
+
+    [ -z "$network_prefer_config" ] && network_prefer_config="99"
+
+    #设置模组
+    at_command='AT^SYSCFGEX="'${network_prefer_config}'",'"${band_hex_2g_3g},1,2,${band_hex_4g_5g},,"
+    sh ${SCRIPT_DIR}/modem_at.sh "${at_port}" "${at_command}"
+}
+
+#获取电压
+# $1:AT串口
+huawei_get_voltage()
+{
+    local at_port="$1"
+    
+    # #Voltage(电压)
+    # at_command="AT+ADCREAD=0"
+	# local voltage=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "+ADCREAD:" | awk -F' ' '{print $2}' | sed 's/\r//g')
+    # voltage=$(awk "BEGIN{ printf \"%.2f\", $voltage / 1000000 }" | sed 's/\.*0*$//')
+    # echo "${voltage}"
+}
+
+#获取温度
+# $1:AT串口
+huawei_get_temperature()
+{
+    local at_port="$1"
+    
+    #Temperature(温度)
+    at_command="AT^CHIPTEMP?"
+	response=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "\^CHIPTEMP" | awk -F',' '{print $6}' | sed 's/\r//g')
+
+    local temperature
+	if [ -n "$response" ]; then
+        response=$(awk "BEGIN{ printf \"%.2f\", $response / 10 }" | sed 's/\.*0*$//')
+		temperature="${response}$(printf "\xc2\xb0")C"
+    else
+        temperature="NaN $(printf "\xc2\xb0")C"
+	fi
+
+    echo "${temperature}"
+}
+
+#获取连接状态
+# $1:AT串口
+# $2:连接定义
+huawei_get_connect_status()
+{
+    local at_port="$1"
+    local define_connect="$2"
+
+    #默认值为1
+    [ -z "$define_connect" ] && {
+        define_connect="1"
+    }
+
+    at_command="AT+CGPADDR=${define_connect}"
+    local ipv4=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+CGPADDR: " | awk -F'"' '{print $2}')
+    local not_ip="0.0.0.0"
+
+    #设置连接状态
+    local connect_status
+    if [ -z "$ipv4" ] || [[ "$ipv4" = *"$not_ip"* ]]; then
+        connect_status="disconnect"
+    else
+        connect_status="connect"
+    fi
+
+    echo "${connect_status}"
+}
+
+#基本信息
+huawei_base_info()
+{
+    debug "Huawei base info"
+
+    #Name(名称)
+    at_command="AT+CGMM"
+    name=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "+CGMM: " | awk -F': ' '{print $2}' | sed 's/\r//g')
+    #Manufacturer(制造商)
+    at_command="AT+CGMI"
+    manufacturer=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | sed -n '2p' | sed 's/\r//g')
+    #Revision(固件版本)
+    at_command="AT+CGMR"
+    revision=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | sed -n '2p' | sed 's/\r//g')
+
+    #Mode(拨号模式)
+    mode=$(huawei_get_mode ${at_port} ${platform} | tr 'a-z' 'A-Z')
+
+    #Temperature(温度)
+    temperature=$(huawei_get_temperature ${at_port})
+}
+
+#获取SIM卡状态
+# $1:SIM卡状态标志
+huawei_get_sim_status()
+{
+    local sim_status
+    case $1 in
+        "") sim_status="miss" ;;
+        *"ERROR"*) sim_status="miss" ;;
+        *"READY"*) sim_status="ready" ;;
+        *"SIM PIN"*) sim_status="MT is waiting SIM PIN to be given" ;;
+        *"SIM PUK"*) sim_status="MT is waiting SIM PUK to be given" ;;
+        *"PH-FSIM PIN"*) sim_status="MT is waiting phone-to-SIM card password to be given" ;;
+        *"PH-FSIM PIN"*) sim_status="MT is waiting phone-to-very first SIM card password to be given" ;;
+        *"PH-FSIM PUK"*) sim_status="MT is waiting phone-to-very first SIM card unblocking password to be given" ;;
+        *"SIM PIN2"*) sim_status="MT is waiting SIM PIN2 to be given" ;;
+        *"SIM PUK2"*) sim_status="MT is waiting SIM PUK2 to be given" ;;
+        *"PH-NET PIN"*) sim_status="MT is waiting network personalization password to be given" ;;
+        *"PH-NET PUK"*) sim_status="MT is waiting network personalization unblocking password to be given" ;;
+        *"PH-NETSUB PIN"*) sim_status="MT is waiting network subset personalization password to be given" ;;
+        *"PH-NETSUB PUK"*) sim_status="MT is waiting network subset personalization unblocking password to be given" ;;
+        *"PH-SP PIN"*) sim_status="MT is waiting service provider personalization password to be given" ;;
+        *"PH-SP PUK"*) sim_status="MT is waiting service provider personalization unblocking password to be given" ;;
+        *"PH-CORP PIN"*) sim_status="MT is waiting corporate personalization password to be given" ;;
+        *"PH-CORP PUK"*) sim_status="MT is waiting corporate personalization unblocking password to be given" ;;
+        *) sim_status="unknown" ;;
+    esac
+    echo "${sim_status}"
+}
+
+#SIM卡信息
+huawei_sim_info()
+{
+    debug "Huawei sim info"
+    
+    #SIM Slot(SIM卡卡槽)
+    # at_command="AT^SIMSLOT?"
+	# response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "\^SIMSLOT:" | awk -F': ' '{print $2}' | awk -F',' '{print $2}')
+
+    # if [ "$response" != "0" ]; then
+    #     sim_slot="1"
+    # else
+    #     sim_slot="2"
+    # fi
+    sim_slot="1"
+
+    #IMEI(国际移动设备识别码)
+    at_command="AT+CGSN"
+	imei=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | sed -n '2p' | sed 's/\r//g')
+
+    #SIM Status(SIM状态)
+    at_command="AT+CPIN?"
+	sim_status_flag=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+CPIN: ")
+    sim_status=$(huawei_get_sim_status "$sim_status_flag")
+
+    if [ "$sim_status" != "ready" ]; then
+        return
+    fi
+
+    #ISP(互联网服务提供商)
+    at_command="AT+COPS?"
+    isp=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+COPS" | awk -F'"' '{print $2}')
+    # if [ "$isp" = "CHN-CMCC" ] || [ "$isp" = "CMCC" ]|| [ "$isp" = "46000" ]; then
+    #     isp="中国移动"
+    # elif [ "$isp" = "CHN-UNICOM" ] || [ "$isp" = "UNICOM" ] || [ "$isp" = "46001" ]; then
+    #     isp="中国联通"
+    # elif [ "$isp" = "CHN-CT" ] || [ "$isp" = "CT" ] || [ "$isp" = "46011" ]; then
+    #     isp="中国电信"
+    # fi
+
+    #SIM Number(SIM卡号码,手机号)
+    at_command="AT+CNUM"
+	sim_number=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+CNUM: " | awk -F'"' '{print $2}')
+    [ -z "$sim_number" ] && {
+        sim_number=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+CNUM: " | awk -F'"' '{print $4}')
+    }
+	
+    #IMSI(国际移动用户识别码)
+    at_command="AT+CIMI"
+	imsi=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | sed -n '2p' | sed 's/\r//g')
+
+    #ICCID(集成电路卡识别码)
+    at_command="AT+ICCID"
+	iccid=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep -o "+ICCID:[ ]*[-0-9]\+" | grep -o "[-0-9]\{1,4\}")
+}
+
+#获取网络类型
+# $1:网络类型数字
+huawei_get_rat()
+{
+    local rat
+    case $1 in
+		"0"|"1"|"3"|"8") rat="GSM" ;;
+		"2"|"4"|"5"|"6"|"9"|"10") rat="WCDMA" ;;
+        "7") rat="LTE" ;;
+        "11"|"12") rat="NR" ;;
+	esac
+    echo "${rat}"
+}
+
+#获取信号强度指示(4G)
+# $1:信号强度指示数字
+huawei_get_rssi()
+{
+    local rssi
+    case $1 in
+		"99") rssi="unknown" ;;
+		* )  rssi=$((2 * $1 - 113)) ;;
+	esac
+    echo "$rssi"
+}
+
+#网络信息
+huawei_network_info()
+{
+    debug "Huawei network info"
+
+    #Connect Status(连接状态)
+    connect_status=$(huawei_get_connect_status ${at_port} ${define_connect})
+    if [ "$connect_status" != "connect" ]; then
+        return
+    fi
+
+    #Network Type(网络类型)
+    at_command="AT^SYSINFOEX"
+    network_type=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "\^SYSINFOEX:" | awk -F'"' '{print $4}')
+
+    [ -z "$network_type" ] && {
+        at_command='AT+COPS?'
+        local rat_num=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+COPS:" | awk -F',' '{print $4}' | sed 's/\r//g')
+        network_type=$(huawei_get_rat ${rat_num})
+    }
+
+    #设置网络类型为5G时,信号强度指示用RSRP代替
+    # at_command="AT+GTCSQNREN=1"
+    # sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command
+
+    #CSQ(信号强度)
+    at_command="AT+CSQ"
+    response=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "+CSQ:" | sed 's/+CSQ: //g' | sed 's/\r//g')
+
+    #RSSI(4G信号强度指示)
+    # rssi_num=$(echo $response | awk -F',' '{print $1}')
+    # rssi=$(huawei_get_rssi $rssi_num)
+    #BER(4G信道误码率)
+    # ber=$(echo $response | awk -F',' '{print $2}')
+
+    # #PER(信号强度)
+    # if [ -n "$csq" ]; then
+    #     per=$(($csq * 100/31))"%"
+    # fi
+
+    #AMBR(最大比特率)
+    at_command="AT^DHCP?"
+    response=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "\^DHCP:" | sed 's/\^DHCP: //g' | sed 's/\r//g')
+    ambr_ul_tmp=$(echo "$response" | awk -F',' '{print $8}')
+    ambr_dl_tmp=$(echo "$response" | awk -F',' '{print $7}')
+
+    [ -z "$ambr_ul_tmp" ] && {
+        at_command="AT^DHCPV6?"
+        response=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "\^DHCPV6:" | sed 's/\^DHCPV6: //g' | sed 's/\r//g')
+        ambr_ul_tmp=$(echo "$response" | awk -F',' '{print $8}')
+        ambr_dl_tmp=$(echo "$response" | awk -F',' '{print $7}')
+    }
+
+    #AMBR UL(上行签约速率,单位,Mbps)
+    ambr_ul=$(awk "BEGIN{ printf \"%.2f\", $ambr_ul_tmp / 1000000 }" | sed 's/\.*0*$//')
+    #AMBR DL(下行签约速率,单位,Mbps)
+    ambr_dl=$(awk "BEGIN{ printf \"%.2f\", $ambr_dl_tmp / 1000000 }" | sed 's/\.*0*$//')
+
+    # #速率统计
+    # at_command='AT^DSFLOWQRY'
+    # response=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "\^DSFLOWRPT:" | sed 's/\^DSFLOWRPT: //g' | sed 's/\r//g')
+
+    # #当前上传速率(单位,Byte/s)
+    # tx_rate=$(echo $response | awk -F',' '{print $1}')
+
+    # #当前下载速率(单位,Byte/s)
+    # rx_rate=$(echo $response | awk -F',' '{print $2}')
+}
+
+#获取NR子载波间隔
+# $1:NR子载波间隔数字
+huawei_get_scs()
+{
+    local scs
+	case $1 in
+		"0") scs="15" ;;
+		"1") scs="30" ;;
+        "2") scs="60" ;;
+        "3") scs="120" ;;
+        "4") scs="240" ;;
+        *) scs=$(awk "BEGIN{ print 2^$1 * 15 }") ;;
+	esac
+    echo "$scs"
+}
+
+#获取频段
+# $1:网络类型
+# $2:频段数字
+huawei_get_band()
+{
+    local band
+    case $1 in
+        "GSM")
+            case $2 in
+                "0") band="850" ;;
+                "1") band="900" ;;
+                "2") band="1800" ;;
+                "3") band="1900" ;;
+            esac
+        ;;
+		"WCDMA") band="$2" ;;
+		"LTE") band="$(($2-100))" ;;
+        "NR") band="$2" band="${band#*50}" ;;
+	esac
+    echo "$band"
+}
+
+#小区信息
+huawei_cell_info()
+{
+    debug "Huawei cell info"
+
+    at_command="AT^MONSC"
+    response=$(sh ${SCRIPT_DIR}/modem_at.sh $at_port $at_command | grep "\^MONSC:" | sed 's/\^MONSC: //')
+    
+    local rat=$(echo "$response" | awk -F',' '{print $1}')
+
+    case $rat in
+        "NR")
+            network_mode="NR5G-SA Mode"
+            nr_mcc=$(echo "$response" | awk -F',' '{print $2}')
+            nr_mnc=$(echo "$response" | awk -F',' '{print $3}')
+            nr_arfcn=$(echo "$response" | awk -F',' '{print $4}')
+            nr_scs_num=$(echo "$response" | awk -F',' '{print $5}')
+            nr_scs=$(tdtech_get_scs ${nr_scs_num})
+            nr_cell_id_hex=$(echo "$response" | awk -F',' '{print $6}')
+            nr_cell_id=$(echo "ibase=16; $nr_cell_id_hex" | bc)
+            nr_physical_cell_id_hex=$(echo "$response" | awk -F',' '{print $7}')
+            nr_physical_cell_id=$(echo "ibase=16; $nr_physical_cell_id_hex" | bc)
+            nr_tac=$(echo "$response" | awk -F',' '{print $8}')
+            nr_rsrp=$(echo "$response" | awk -F',' '{print $9}')
+            nr_rsrq=$(echo "$response" | awk -F',' '{print $10}')
+            nr_sinr=$(echo "$response" | awk -F',' '{print $11}' | sed 's/\r//g')
+        ;;
+        "LTE-NR")
+            network_mode="EN-DC Mode"
+            #LTE
+            endc_lte_mcc=$(echo "$response" | awk -F',' '{print $2}')
+            endc_lte_mnc=$(echo "$response" | awk -F',' '{print $3}')
+            endc_lte_earfcn=$(echo "$response" | awk -F',' '{print $4}')
+            endc_lte_cell_id_hex=$(echo "$response" | awk -F',' '{print $5}')
+            endc_lte_cell_id=$(echo "ibase=16; $endc_lte_cell_id_hex" | bc)
+            endc_lte_physical_cell_id_hex=$(echo "$response" | awk -F',' '{print $6}')
+            endc_lte_physical_cell_id=$(echo "ibase=16; $endc_lte_physical_cell_id_hex" | bc)
+            endc_lte_tac=$(echo "$response" | awk -F',' '{print $7}')
+            endc_lte_rsrp=$(echo "$response" | awk -F',' '{print $8}')
+            endc_lte_rsrq=$(echo "$response" | awk -F',' '{print $9}')
+            endc_lte_rxlev=$(echo "$response" | awk -F',' '{print $10}' | sed 's/\r//g')
+            #NR5G-NSA
+            endc_nr_mcc=$(echo "$response" | awk -F',' '{print $2}')
+            endc_nr_mnc=$(echo "$response" | awk -F',' '{print $3}')
+            endc_nr_arfcn=$(echo "$response" | awk -F',' '{print $4}')
+            endc_nr_scs_num=$(echo "$response" | awk -F',' '{print $5}')
+            endc_nr_scs=$(tdtech_get_scs ${nr_scs_num})
+            endc_nr_cell_id_hex=$(echo "$response" | awk -F',' '{print $6}')
+            endc_nr_cell_id=$(echo "ibase=16; $endc_nr_cell_id_hex" | bc)
+            endc_nr_physical_cell_id_hex=$(echo "$response" | awk -F',' '{print $7}')
+            endc_nr_physical_cell_id=$(echo "ibase=16; $endc_nr_physical_cell_id_hex" | bc)
+            endc_nr_tac=$(echo "$response" | awk -F',' '{print $8}')
+            endc_nr_rsrp=$(echo "$response" | awk -F',' '{print $9}')
+            endc_nr_rsrq=$(echo "$response" | awk -F',' '{print $10}')
+            endc_nr_sinr=$(echo "$response" | awk -F',' '{print $11}' | sed 's/\r//g')
+        ;;
+        "LTE"|"eMTC"|"NB-IoT")
+            network_mode="LTE Mode"
+            lte_mcc=$(echo "$response" | awk -F',' '{print $2}')
+            lte_mnc=$(echo "$response" | awk -F',' '{print $3}')
+            lte_earfcn=$(echo "$response" | awk -F',' '{print $4}')
+            lte_cell_id_hex=$(echo "$response" | awk -F',' '{print $5}')
+            lte_cell_id=$(echo "ibase=16; $lte_cell_id_hex" | bc)
+            lte_physical_cell_id_hex=$(echo "$response" | awk -F',' '{print $6}')
+            lte_physical_cell_id=$(echo "ibase=16; $lte_physical_cell_id_hex" | bc)
+            lte_tac=$(echo "$response" | awk -F',' '{print $7}')
+            lte_rsrp=$(echo "$response" | awk -F',' '{print $8}')
+            lte_rsrq=$(echo "$response" | awk -F',' '{print $9}')
+            lte_rxlev=$(echo "$response" | awk -F',' '{print $10}' | sed 's/\r//g')
+        ;;
+        "WCDMA"|"TD-SCDMA"|"UMTS")
+            network_mode="WCDMA Mode"
+            wcdma_mcc=$(echo "$response" | awk -F',' '{print $2}')
+            wcdma_mnc=$(echo "$response" | awk -F',' '{print $3}')
+            wcdma_arfcn=$(echo "$response" | awk -F',' '{print $4}')
+            wcdma_psc=$(echo "$response" | awk -F',' '{print $5}')
+            wcdma_cell_id_hex=$(echo "$response" | awk -F',' '{print $6}')
+            wcdma_cell_id=$(echo "ibase=16; $wcdma_cell_id_hex" | bc)
+            wcdma_lac=$(echo "$response" | awk -F',' '{print $7}')
+            wcdma_rscp=$(echo "$response" | awk -F',' '{print $8}')
+            wcdma_rxlev=$(echo "$response" | awk -F',' '{print $9}')
+            wcdma_ecn0=$(echo "$response" | awk -F',' '{print $10}')
+            wcdma_drx=$(echo "$response" | awk -F',' '{print $11}')
+            wcdma_ura=$(echo "$response" | awk -F',' '{print $12}' | sed 's/\r//g')
+        ;;
+        "GSM")
+            network_mode="GSM Mode"
+            gsm_mcc=$(echo "$response" | awk -F',' '{print $2}')
+            gsm_mnc=$(echo "$response" | awk -F',' '{print $3}')
+            gsm_band_num=$(echo "$response" | awk -F',' '{print $4}')
+            gsm_band=$(tdtech_get_band "GSM" ${gsm_band_num})
+            gsm_arfcn=$(echo "$response" | awk -F',' '{print $5}')
+            gsm_bsic=$(echo "$response" | awk -F',' '{print $6}')
+            gsm_cell_id_hex=$(echo "$response" | awk -F',' '{print $7}')
+            gsm_cell_id=$(echo "ibase=16; $gsm_cell_id_hex" | bc)
+            gsm_lac=$(echo "$response" | awk -F',' '{print $8}')
+            gsm_rxlev=$(echo "$response" | awk -F',' '{print $9}')
+            gsm_rx_quality=$(echo "$response" | awk -F',' '{print $10}')
+            gsm_ta=$(echo "$response" | awk -F',' '{print $11}' | sed 's/\r//g')
+        ;;
+    esac
+}
+
+#获取华为模组信息
+# $1:AT串口
+# $2:平台
+# $3:连接定义
+get_huawei_info()
+{
+    debug "get huawei info"
+    #设置AT串口
+    at_port="$1"
+    platform="$2"
+    define_connect="$3"
+
+    #基本信息
+    huawei_base_info
+
+	#SIM卡信息
+    huawei_sim_info
+    if [ "$sim_status" != "ready" ]; then
+        return
+    fi
+
+    #网络信息
+    huawei_network_info
+    if [ "$connect_status" != "connect" ]; then
+        return
+    fi
+
+    #小区信息
+    huawei_cell_info
+}
\ No newline at end of file
diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/meig.sh b/package/wwan/luci-app-modem/root/usr/share/modem/meig.sh
index f832eb546..adb73cbd2 100755
--- a/package/wwan/luci-app-modem/root/usr/share/modem/meig.sh
+++ b/package/wwan/luci-app-modem/root/usr/share/modem/meig.sh
@@ -35,7 +35,7 @@ meig_get_dns()
 
     #获取DNS地址
     at_command="AT+CGCONTRDP=${define_connect}"
-    local response=$(at ${at_port} ${at_command} | grep "+CGCONTRDP: " | grep -E '[0-9]+.[0-9]+.[0-9]+.[0-9]+' | sed -n '1p')
+    local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+CGCONTRDP: " | grep -E '[0-9]+.[0-9]+.[0-9]+.[0-9]+' | sed -n '1p')
 
     local ipv4_dns1=$(echo "${response}" | awk -F',' '{print $7}' | awk -F' ' '{print $1}')
     [ -z "$ipv4_dns1" ] && {
@@ -161,28 +161,122 @@ meig_set_mode()
     sh ${SCRIPT_DIR}/modem_at.sh ${at_port} "${at_command}"
 }
 
+#获取位
+# $1:频段名称
+meig_get_bit()
+{
+    local band_name="$1"
+
+    local bit
+    case "$band_name" in
+        "DCS_1800") bit="8" ;;
+        "E-GSM_900") bit="9" ;;
+        "P-GSM_900") bit="10" ;;
+        "GSM_450") bit="17" ;;
+        "GSM_480") bit="18" ;;
+        "GSM_750") bit="19" ;;
+        "GSM_850") bit="20" ;;
+        "R-GSM_900") bit="21" ;;
+        "PCS_1900") bit="22" ;;
+    esac
+
+    echo "${bit}"
+}
+
+#获取频段信息
+# $1:频段二进制数
+# $2:支持的频段
+# $3:频段类型(2G,3G,4G,5G)
+meig_get_band_info()
+{
+    local band_bin="$1"
+    local support_band="$2"
+    local band_type="$3"
+
+    local band_info=""
+    local support_band=$(echo "$support_band" | sed 's/,/ /g')
+    if [ "$band_type" = "2G" ]; then
+
+        for band in $support_band; do
+            #获取bit位
+            local bit=$(meig_get_bit ${band})
+            #获取值
+            local enable="${band_bin: $((-bit)):1}"
+            [ -z "$enable" ] && enable="0"
+            #设置频段信息
+            # band_info=$(echo ${band_info} | jq '. += [{"'$band'":'$enable'}]')
+            band_info="${band_info},{\"$band\":$enable}"
+        done
+    else
+        #频段频段起始,前缀位置
+        local start_bit
+        local band_prefix
+        case "$band_type" in
+            "3G")
+                start_bit="23"
+                band_prefix="WCDMA_B"
+            ;;
+            "4G")
+                start_bit="1"
+                band_prefix="LTE_BC"
+            ;;
+            "5G")
+                start_bit="1"
+                band_prefix="NR5G_N"
+            ;;
+        esac
+
+        for band in $support_band; do
+            #获取值(从start_bit位开始)
+            local enable="${band_bin: $((-band-start_bit+1)):1}"
+            [ -z "$enable" ] && enable="0"
+            #设置频段信息
+            # band_info=$(echo ${band_info} | jq '. += [{'$band_prefix$band':'$enable'}]')
+            band_info="${band_info},{\"$band_prefix$band\":$enable}"
+        done
+    fi
+    #去掉第一个,
+    band_info="["${band_info/,/}"]"
+    # band_info="[${band_info%?}]"
+
+    echo "${band_info}"
+}
+
 #获取网络偏好
 # $1:AT串口
+# $2:数据接口
+# $3:模组名称
 meig_get_network_prefer()
 {
     local at_port="$1"
+    local data_interface="$2"
+    local modem_name="$3"
+
     at_command="AT^SYSCFGEX?"
-    local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "\^SYSCFGEX:" | awk -F'"' '{print $2}')
-    
+    local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "\^SYSCFGEX:" | sed 's/\^SYSCFGEX://g')
+    local network_type_num=$(echo "$response" | awk -F'"' '{print $2}')
+
+    #获取网络类型
+    local network_prefer_2g="0";
     local network_prefer_3g="0";
     local network_prefer_4g="0";
     local network_prefer_5g="0";
 
     #匹配不同的网络类型
-    local auto=$(echo "${response}" | grep "00")
+    local auto=$(echo "${network_type_num}" | grep "00")
     if [ -n "$auto" ]; then
+        network_prefer_2g="1"
         network_prefer_3g="1"
         network_prefer_4g="1"
         network_prefer_5g="1"
     else
-        local wcdma=$(echo "${response}" | grep "02")
-        local lte=$(echo "${response}" | grep "03")
-        local nr=$(echo "${response}" | grep "04")
+        local gsm=$(echo "${network_type_num}" | grep "01")
+        local wcdma=$(echo "${network_type_num}" | grep "02")
+        local lte=$(echo "${network_type_num}" | grep "03")
+        local nr=$(echo "${network_type_num}" | grep "04")
+        if [ -n "$gsm" ]; then
+            network_prefer_2g="1"
+        fi 
         if [ -n "$wcdma" ]; then
             network_prefer_3g="1"
         fi
@@ -194,14 +288,58 @@ meig_get_network_prefer()
         fi
     fi
 
+	#获取模组信息
+    local modem_info=$(jq '.modem_support.'$data_interface'."'$modem_name'"' ${SCRIPT_DIR}/modem_support.json)
+
+    #获取模组支持的频段
+    local support_2g_band=$(echo "$modem_info" | jq -r '.band_2g')
+    local support_3g_band=$(echo "$modem_info" | jq -r '.band_3g')
+    local support_4g_band=$(echo "$modem_info" | jq -r '.band_4g')
+    local support_5g_band=$(echo "$modem_info" | jq -r '.band_5g')
+
+    #获取2G,3G频段信息
+    local band_hex_2g_3g=$(echo "$response" | awk -F',' '{print $2}')
+    #十六进制转二进制
+    local bin_2g_3g=$(echo "obase=2; ibase=16; $band_hex_2g_3g" | bc)
+    local band_2g_info=$(meig_get_band_info "${bin_2g_3g}" "${support_2g_band}" "2G")
+    local band_3g_info=$(meig_get_band_info "${bin_2g_3g}" "${support_3g_band}" "3G")
+
+    #获取4G频段信息
+    local band_hex_4g_1=$(echo "$response" | awk -F',' '{print $5}' | sed 's/\r//g')
+    local band_hex_4g_2=$(echo "$response" | awk -F',' '{print $7}' | sed 's/\r//g')
+    #十六进制转二进制
+    local bin_4g=$(echo "obase=2; ibase=16; $band_hex_4g_1 + $band_hex_4g_2" | bc)
+    local band_4g_info=$(meig_get_band_info "${bin_4g}" "${support_4g_band}" "4G")
+    
+    #获取5G频段信息
+    local band_hex_5g_1=$(echo "$response" | awk -F',' '{print $8}' | sed 's/\r//g')
+    local band_hex_5g_2=$(echo "$response" | awk -F',' '{print $9}' | sed 's/\r//g')
+    #十六进制转二进制
+    local bin_5g=$(echo "obase=2; ibase=16; $band_hex_5g_1 + $band_hex_5g_2" | bc)
+    local band_5g_info=$(meig_get_band_info "${bin_5g}" "${support_5g_band}" "5G")
+
+    #生成网络偏好
     local network_prefer="{
-        \"network_prefer\":{
-            \"3G\":$network_prefer_3g,
-            \"4G\":$network_prefer_4g,
-            \"5G\":$network_prefer_5g
-        }
+        \"network_prefer\":[
+            {\"2G\":{
+                \"enable\":$network_prefer_2g,
+                \"band\":$band_2g_info
+            }},
+            {\"3G\":{
+                \"enable\":$network_prefer_3g,
+                \"band\":$band_3g_info
+            }},
+            {\"4G\":{
+                \"enable\":$network_prefer_4g,
+                \"band\":$band_4g_info
+            }},
+            {\"5G\":{
+                \"enable\":$network_prefer_5g,
+                \"band\":$band_5g_info
+            }}
+        ]
     }"
-    echo "$network_prefer"
+    echo "${network_prefer}"
 }
 
 #设置网络偏好
@@ -215,35 +353,18 @@ meig_set_network_prefer()
     #获取网络偏好配置
     local network_prefer_config
 
-    #获取选中的数量
-    local count=$(echo "$network_prefer" | grep -o "1" | wc -l)
-    #获取每个偏好的值
-    local network_prefer_3g=$(echo "$network_prefer" | jq -r '.["3G"]')
-    local network_prefer_4g=$(echo "$network_prefer" | jq -r '.["4G"]')
-    local network_prefer_5g=$(echo "$network_prefer" | jq -r '.["5G"]')
+    #获取启用的网络偏好
+    local enable_5g=$(echo "$network_prefer" | jq -r '.["5G"].enable')
+    local enable_4g=$(echo "$network_prefer" | jq -r '.["4G"].enable')
+    local enable_3g=$(echo "$network_prefer" | jq -r '.["3G"].enable')
 
-    case "$count" in
-        "1")
-            if [ "$network_prefer_3g" = "1" ]; then
-                network_prefer_config="02"
-            elif [ "$network_prefer_4g" = "1" ]; then
-                network_prefer_config="03"
-            elif [ "$network_prefer_5g" = "1" ]; then
-                network_prefer_config="04"
-            fi
-        ;;
-        "2")
-            if [ "$network_prefer_3g" = "1" ] && [ "$network_prefer_4g" = "1" ]; then
-                network_prefer_config="0302"
-            elif [ "$network_prefer_3g" = "1" ] && [ "$network_prefer_5g" = "1" ]; then
-                network_prefer_config="0402"
-            elif [ "$network_prefer_4g" = "1" ] && [ "$network_prefer_5g" = "1" ]; then
-                network_prefer_config="0403"
-            fi
-        ;;
-        "3") network_prefer_config="00" ;;
-        *) network_prefer_config="00" ;;
-    esac
+    #获取网络偏好配置
+    local network_prefer_config
+    [ "$enable_5g" = "1" ] && network_prefer_config="${network_prefer_config}04"
+    [ "$enable_4g" = "1" ] && network_prefer_config="${network_prefer_config}03"
+    [ "$enable_3g" = "1" ] && network_prefer_config="${network_prefer_config}02"
+
+    [ -z "$network_prefer_config" ] && network_prefer_config="00"
 
     #设置模组
     at_command='AT^SYSCFGEX="'${network_prefer_config}'",all,0,2,all,all,all,all,1'
@@ -314,7 +435,7 @@ meig_get_connect_status()
 #基本信息
 meig_base_info()
 {
-    debug "meig base info"
+    debug "Meig base info"
 
     #Name(名称)
     at_command="AT+CGMM"
@@ -365,7 +486,7 @@ meig_get_sim_status()
 #SIM卡信息
 meig_sim_info()
 {
-    debug "meig sim info"
+    debug "Meig sim info"
     
     #SIM Slot(SIM卡卡槽)
     at_command="AT^SIMSLOT?"
@@ -517,7 +638,7 @@ meig_get_lte_ambr()
 #网络信息
 meig_network_info()
 {
-    debug "meig network info"
+    debug "Meig network info"
 
     #Connect Status(连接状态)
     connect_status=$(meig_get_connect_status ${at_port} ${define_connect})
diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/modem_band.sh b/package/wwan/luci-app-modem/root/usr/share/modem/modem_band.sh
new file mode 100755
index 000000000..7972b7fcd
--- /dev/null
+++ b/package/wwan/luci-app-modem/root/usr/share/modem/modem_band.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+# Copyright (C) 2023 Siriling <siriling@qq.com>
+
+#脚本目录
+SCRIPT_DIR="/usr/share/modem"
+source "${SCRIPT_DIR}/modem_debug.sh"
+
+#初始化模组频段
+init_modem_band()
+{
+	#2G
+	DCS_1800="-"
+	E-GSM_900="-"
+	P-GSM_900="-"
+	GSM_450="-"
+	GSM_480="-"
+	GSM_750="-"
+	GSM_850="-"
+	R-GSM_900="-"
+	PCS_1900="-"
+
+	#3G
+
+	#4G
+	B1="-"
+	B2="-"
+	B3="-"
+	B4="-"
+	B5="-"
+	B6="-"
+	B7="-"
+	B8="-"
+	B9="-"
+	B10="-"
+	B11="-"
+	B12="-"
+	B13="-"
+	B14="-"
+	B17="-"
+	B18="-"
+	B19="-"
+	B20="-"
+	B21="-"
+	B25="-"
+	B26="-"
+	B28="-"
+	B29="-"
+	B30="-"
+	B32="-"
+	B34="-"
+	B38="-"
+	B39="-"
+	B40="-"
+	B41="-"
+	B42="-"
+	B66="-"
+	B71="-"
+
+	#5G
+	N1="-"
+	N2="-"
+	N3="-"
+	N5="-"
+	N7="-"
+	N8="-"
+	N12="-"
+	N20="-"
+	N25="-"
+	N28="-"
+	N38="-"
+	N40="-"
+	N41="-"
+	N48="-"
+	N66="-"
+	N71="-"
+	N77="-"
+	N78="-"
+	N79="-"
+}
+
+#获取模组数据信息
+# $1:AT串口
+# $2:制造商
+# $3:平台
+# $4:连接定义
+modem_band()
+{
+	#初始化模组频段
+    debug "初始化模组频段"
+    init_modem_band
+    debug "初始化模组频段完成"
+}
+
+modem_band "$1" "$2" "$3" "$4"
\ No newline at end of file
diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/modem_debug.sh b/package/wwan/luci-app-modem/root/usr/share/modem/modem_debug.sh
index 44b6b4664..eec9cf42d 100755
--- a/package/wwan/luci-app-modem/root/usr/share/modem/modem_debug.sh
+++ b/package/wwan/luci-app-modem/root/usr/share/modem/modem_debug.sh
@@ -7,6 +7,7 @@ SCRIPT_DIR="/usr/share/modem"
 source "${SCRIPT_DIR}/quectel.sh"
 source "${SCRIPT_DIR}/fibocom.sh"
 source "${SCRIPT_DIR}/meig.sh"
+source "${SCRIPT_DIR}/huawei.sh"
 # source "${SCRIPT_DIR}/simcom.sh"
 
 #调试开关
diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/modem_info.sh b/package/wwan/luci-app-modem/root/usr/share/modem/modem_info.sh
index 0028d1173..d27fc4f39 100755
--- a/package/wwan/luci-app-modem/root/usr/share/modem/modem_info.sh
+++ b/package/wwan/luci-app-modem/root/usr/share/modem/modem_info.sh
@@ -371,6 +371,7 @@ get_modem_info()
 		"fibocom") get_fibocom_info "${at_port}" "${platform}" "${define_connect}" ;;
 		"meig") get_meig_info "${at_port}" "${platform}" "${define_connect}" ;;
 		"simcom") get_simcom_info "${at_port}" "${platform}" "${define_connect}" ;;
+		"huawei") get_huawei_info "${at_port}" "${platform}" "${define_connect}" ;;
 		*) debug "未适配该模组" ;;
 	esac
 
diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/modem_init.sh b/package/wwan/luci-app-modem/root/usr/share/modem/modem_init.sh
old mode 100644
new mode 100755
diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/modem_network_task.sh b/package/wwan/luci-app-modem/root/usr/share/modem/modem_network_task.sh
index e9ebafe04..17b401eda 100755
--- a/package/wwan/luci-app-modem/root/usr/share/modem/modem_network_task.sh
+++ b/package/wwan/luci-app-modem/root/usr/share/modem/modem_network_task.sh
@@ -24,13 +24,13 @@ reset_network_interface()
     local interface_name_ipv6="wwan6_5g_${modem_no}"
 
     #获取IPv4地址
-    at_command="AT+CGPADDR=${define_connect}"
-    local ipv4=$(at ${at_port} ${at_command} | grep "+CGPADDR: " | awk -F',' '{print $2}' | sed 's/"//g')
+    local at_command="AT+CGPADDR=${define_connect}"
+    local ipv4=$(at ${at_port} ${at_command} | grep "+CGPADDR: " | sed -n '1p' | awk -F',' '{print $2}' | sed 's/"//g')
     #输出日志
     # echo "[$(date +"%Y-%m-%d %H:%M:%S")] Get Modem new IPv4 address : ${ipv4}" >> "${MODEM_RUNDIR}/modem${modem_no}_dial.cache"
 
     #获取DNS地址
-    dns=$(fibocom_get_dns ${at_port} ${define_connect})
+    local dns=$(fibocom_get_dns ${at_port} ${define_connect})
     local ipv4_dns1=$(echo "${dns}" | jq -r '.dns.ipv4_dns1')
     local ipv4_dns2=$(echo "${dns}" | jq -r '.dns.ipv4_dns2')
     #输出日志
@@ -117,13 +117,17 @@ ecm_dial()
     # at "${at_port}" "${at_command}"
 
     #拨号
-    at_command
+    local at_command
     if [ "$manufacturer" = "quectel" ]; then
         at_command="AT+QNETDEVCTL=${define_connect},3,1"
     elif [ "$manufacturer" = "fibocom" ]; then
         at_command="AT+GTRNDIS=1,${define_connect}"
     elif [ "$manufacturer" = "meig" ]; then
         at_command="AT^NDISDUP=${define_connect},1"
+    elif [ "$manufacturer" = "huawei" ]; then
+        at_command="AT^NDISDUP=${define_connect},1"
+    elif [ "$manufacturer" = "tdtech" ]; then
+        at_command="AT^NDISDUP=${define_connect},1"
     else
         at_command='ATI'
     fi
@@ -150,10 +154,10 @@ rndis_dial()
     local define_connect="$4"
     local modem_no="$5"
 
-    #手动设置IP(广和通FM350-GL)
+    #手动拨号(广和通FM350-GL)
     if [ "$manufacturer" = "fibocom" ] && [ "$platform" = "mediatek" ]; then
 
-        at_command="AT+CGACT=1,${define_connect}"
+        local at_command="AT+CGACT=1,${define_connect}"
         #打印日志
         dial_log "${at_command}" "${MODEM_RUNDIR}/modem${modem_no}_dial.cache"
         #激活并拨号
@@ -202,6 +206,15 @@ modem_network_task()
     local interface_name="wwan_5g_${modem_no}"
     local interface_name_ipv6="wwan6_5g_${modem_no}"
 
+    #AT串口未获取到重新获取(解决模组还在识别中,就已经开始拨号的问题)
+    while [ -z "$manufacturer" ] || [ "$manufacturer" = "unknown" ]; do
+        at_port=$(uci -q get modem.modem${modem_no}.at_port)
+        manufacturer=$(uci -q get modem.modem${modem_no}.manufacturer)
+        platform=$(uci -q get modem.modem${modem_no}.platform)
+        define_connect=$(uci -q get modem.modem${modem_no}.define_connect)
+        sleep 1s
+    done
+
     #重载配置(解决AT命令发不出去的问题)
     # service modem reload
 
@@ -228,7 +241,7 @@ modem_network_task()
 
         #网络连接检查
         local at_command="AT+CGPADDR=${define_connect}"
-        local ipv4=$(at ${at_port} ${at_command} | grep "+CGPADDR: " | awk -F',' '{print $2}' | sed 's/"//g')
+        local ipv4=$(at ${at_port} ${at_command} | grep "+CGPADDR: " | sed -n '1p' | awk -F',' '{print $2}' | sed 's/"//g')
 
         if [ -z "$ipv4" ]; then
 
diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/modem_support.json b/package/wwan/luci-app-modem/root/usr/share/modem/modem_support.json
index 0df94657e..aaa85f3d0 100644
--- a/package/wwan/luci-app-modem/root/usr/share/modem/modem_support.json
+++ b/package/wwan/luci-app-modem/root/usr/share/modem/modem_support.json
@@ -55,7 +55,8 @@
                 "platform":"qualcomm",
                 "data_interface":"usb",
                 "define_connect":"1",
-                "modes":["qmi","gobinet","ecm","mbim","rndis","ncm"]
+                "modes":["qmi","gobinet","ecm","mbim","rndis","ncm"],
+                "band_5g":"1,2,3,5,7,8,12,20,25,28,38,40,41,48,66,71,77,78,79,257,258,260,261"
             },
             "rm502q-ae":{
                 "manufacturer_id":"2c7c",
@@ -152,6 +153,18 @@
                 "data_interface":"usb",
                 "define_connect":"1",
                 "modes":["qmi","gobinet","ecm","rndis","ncm"]
+            },
+            "mh5000-31":{
+                "manufacturer_id":"12d1",
+                "manufacturer":"huawei",
+                "platform":"hisilicon",
+                "data_interface":"usb",
+                "define_connect":"1",
+                "modes":["ecm","ncm"],
+                "band_2g":"DCS_1800,E-GSM_900,P-GSM_900,GSM_850,R-GSM_900,PCS_1900",
+                "band_3g":"1,2,5,6,8,9,19",
+                "band_4g":"1,2,3,4,5,6,7,8,9,10,11,12,13,14,17,18,19,20,21,25,26,28,33,34,35,36,37,38,39,40,41,42,43",
+                "band_5g":"1,2,3,5,7,8,12,20,25,28,38,40,41,78,79"
             }
         },
         "pcie":{
@@ -258,6 +271,12 @@
                     "vendor_id":["2dee"],
                     "product_id":["4d57","4d58","4d59"]
                 }
+            },
+            "huawei":{
+                "hisilicon":{
+                    "vendor_id":["12d1"],
+                    "product_id":["15c3"]
+                }
             }
         }
     }
diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/modem_util.sh b/package/wwan/luci-app-modem/root/usr/share/modem/modem_util.sh
index 9e5455203..4b08eacf2 100755
--- a/package/wwan/luci-app-modem/root/usr/share/modem/modem_util.sh
+++ b/package/wwan/luci-app-modem/root/usr/share/modem/modem_util.sh
@@ -83,6 +83,7 @@ m_modem_presets()
 		"fibocom") fibocom_presets ;;
 		"meig") meig_presets ;;
 		"simcom") simcom_presets ;;
+		"huawei") huawei_presets ;;
 	esac
 }
 
diff --git a/package/wwan/luci-app-modem/root/usr/share/modem/quectel.sh b/package/wwan/luci-app-modem/root/usr/share/modem/quectel.sh
index fa54fdd86..d937dae5e 100755
--- a/package/wwan/luci-app-modem/root/usr/share/modem/quectel.sh
+++ b/package/wwan/luci-app-modem/root/usr/share/modem/quectel.sh
@@ -30,7 +30,7 @@ quectel_get_dns()
 
     #获取DNS地址
     at_command="AT+GTDNS=${define_connect}"
-    local response=$(at ${at_port} ${at_command} | grep "+GTDNS: ")
+    local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+GTDNS: ")
 
     local ipv4_dns1=$(echo "${response}" | awk -F'"' '{print $2}' | awk -F',' '{print $1}')
     [ -z "$ipv4_dns1" ] && {
@@ -174,12 +174,20 @@ quectel_set_mode()
 
 #获取网络偏好
 # $1:AT串口
+# $2:数据接口
+# $3:模组名称
 quectel_get_network_prefer()
 {
     local at_port="$1"
+    local data_interface="$2"
+    local modem_name="$3"
+
     at_command='AT+QNWPREFCFG="mode_pref"'
-    local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+QNWPREFCFG:" | awk -F',' '{print $2}' | sed 's/\r//g')
-    
+    local response=$(sh ${SCRIPT_DIR}/modem_at.sh ${at_port} ${at_command} | grep "+QNWPREFCFG:" | sed 's/\r//g')
+    local network_type_num=$(echo "$response" | awk -F',' '{print $2}')
+
+    #获取网络类型
+    # local network_prefer_2g="0";
     local network_prefer_3g="0";
     local network_prefer_4g="0";
     local network_prefer_5g="0";
@@ -205,14 +213,30 @@ quectel_get_network_prefer()
         fi
     fi
 
+    #获取频段信息
+    # local band_2g_info="[]"
+    local band_3g_info="[]"
+    local band_4g_info="[]"
+    local band_5g_info="[]"
+
+    #生成网络偏好
     local network_prefer="{
-        \"network_prefer\":{
-            \"3G\":$network_prefer_3g,
-            \"4G\":$network_prefer_4g,
-            \"5G\":$network_prefer_5g
-        }
+        \"network_prefer\":[
+            {\"3G\":{
+                \"enable\":$network_prefer_3g,
+                \"band\":$band_3g_info
+            }},
+            {\"4G\":{
+                \"enable\":$network_prefer_4g,
+                \"band\":$band_4g_info
+            }},
+            {\"5G\":{
+                \"enable\":$network_prefer_5g,
+                \"band\":$band_5g_info
+            }}
+        ]
     }"
-    echo "$network_prefer"
+    echo "${network_prefer}"
 }
 
 #设置网络偏好
@@ -223,32 +247,32 @@ quectel_set_network_prefer()
     local at_port="$1"
     local network_prefer="$2"
 
-    #获取网络偏好配置
+     #获取网络偏好配置
     local network_prefer_config
 
     #获取选中的数量
     local count=$(echo "$network_prefer" | grep -o "1" | wc -l)
-    #获取每个偏好的值
-    local network_prefer_3g=$(echo "$network_prefer" | jq -r '.["3G"]')
-    local network_prefer_4g=$(echo "$network_prefer" | jq -r '.["4G"]')
-    local network_prefer_5g=$(echo "$network_prefer" | jq -r '.["5G"]')
+    #获取启用的网络偏好
+    local enable_5g=$(echo "$network_prefer" | jq -r '.["5G"].enable')
+    local enable_4g=$(echo "$network_prefer" | jq -r '.["4G"].enable')
+    local enable_3g=$(echo "$network_prefer" | jq -r '.["3G"].enable')
 
     case "$count" in
         "1")
-            if [ "$network_prefer_3g" = "1" ]; then
+            if [ "$enable_3g" = "1" ]; then
                 network_prefer_config="WCDMA"
-            elif [ "$network_prefer_4g" = "1" ]; then
+            elif [ "$enable_4g" = "1" ]; then
                 network_prefer_config="LTE"
-            elif [ "$network_prefer_5g" = "1" ]; then
+            elif [ "$enable_5g" = "1" ]; then
                 network_prefer_config="NR5G"
             fi
         ;;
         "2")
-            if [ "$network_prefer_3g" = "1" ] && [ "$network_prefer_4g" = "1" ]; then
+            if [ "$enable_3g" = "1" ] && [ "$enable_4g" = "1" ]; then
                 network_prefer_config="WCDMA:LTE"
-            elif [ "$network_prefer_3g" = "1" ] && [ "$network_prefer_5g" = "1" ]; then
+            elif [ "$enable_3g" = "1" ] && [ "$enable_5g" = "1" ]; then
                 network_prefer_config="WCDMA:NR5G"
-            elif [ "$network_prefer_4g" = "1" ] && [ "$network_prefer_5g" = "1" ]; then
+            elif [ "$enable_4g" = "1" ] && [ "$enable_5g" = "1" ]; then
                 network_prefer_config="LTE:NR5G"
             fi
         ;;
@@ -934,7 +958,7 @@ quectel_cell_info()
     esac
 }
 
-# SIMCOM获取基站信息
+# Quectel获取基站信息
 Quectel_Cellinfo()
 {
     # return
diff --git a/package/wwan/luci-app-sms-tool/LICENSE b/package/wwan/luci-app-sms-tool/LICENSE
new file mode 100644
index 000000000..f288702d2
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/LICENSE
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/package/wwan/luci-app-sms-tool/Makefile b/package/wwan/luci-app-sms-tool/Makefile
new file mode 100644
index 000000000..db9d2a20c
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/Makefile
@@ -0,0 +1,32 @@
+# Copyright (C) 2023 Siriling <siriling@qq.com>
+# This is free software, licensed under the GNU General Public License v3.
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=luci-app-sms-tool
+LUCI_TITLE:=LuCI Support for sms_tool
+LUCI_PKGARCH:=all
+PKG_VERSION:=1.0.0
+PKG_LICENSE:=GPLv3
+PKG_LINCESE_FILES:=LICENSE
+PKF_MAINTAINER:=siriling <siriling@qq.com>
+LUCI_DEPENDS:=+sms-tool +luci-compat +kmod-usb-serial +kmod-usb-serial-option
+
+define Package/luci-app-sms-tool/postinst
+#!/bin/sh
+rm -rf /tmp/luci-indexcache
+rm -rf /tmp/luci-modulecache/
+/sbin/set_sms_ports.sh
+exit 0
+endef
+
+define Package/$(PKG_NAME)/config
+# shown in make menuconfig <Help>
+help
+	$(LUCI_TITLE)
+	Version: $(PKG_VERSION)
+endef
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/package/wwan/luci-app-sms-tool/htdocs/luci-static/resources/icons/delsms.png b/package/wwan/luci-app-sms-tool/htdocs/luci-static/resources/icons/delsms.png
new file mode 100644
index 000000000..b036e813f
Binary files /dev/null and b/package/wwan/luci-app-sms-tool/htdocs/luci-static/resources/icons/delsms.png differ
diff --git a/package/wwan/luci-app-sms-tool/htdocs/luci-static/resources/icons/delsms2.png b/package/wwan/luci-app-sms-tool/htdocs/luci-static/resources/icons/delsms2.png
new file mode 100644
index 000000000..eeb13c5e6
Binary files /dev/null and b/package/wwan/luci-app-sms-tool/htdocs/luci-static/resources/icons/delsms2.png differ
diff --git a/package/wwan/luci-app-sms-tool/luasrc/controller/modem/sms.lua b/package/wwan/luci-app-sms-tool/luasrc/controller/modem/sms.lua
new file mode 100644
index 000000000..f807e9e72
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/luasrc/controller/modem/sms.lua
@@ -0,0 +1,242 @@
+	local util = require "luci.util"
+	local fs = require "nixio.fs"
+	local sys = require "luci.sys"
+	local http = require "luci.http"
+	local dispatcher = require "luci.dispatcher"
+	local http = require "luci.http"
+	local sys = require "luci.sys"
+	local uci = require "luci.model.uci".cursor()
+
+module("luci.controller.modem.sms", package.seeall)
+
+function index()
+	entry({"admin", "modem"}, firstchild(), "Modem", 30).dependent=false
+	entry({"admin", "modem", "sms"}, alias("admin", "modem", "sms", "readsms"), translate("短信"), 20)
+	entry({"admin", "modem", "sms", "readsms"},template("modem/readsms"),translate("收到的信息"), 10)
+ 	entry({"admin", "modem", "sms", "sendsms"},template("modem/sendsms"),translate("发送消息"), 20)
+ 	entry({"admin", "modem", "sms", "ussd"},template("modem/ussd"),translate("USSD 代码"), 30)
+	entry({"admin", "modem", "sms", "atcommands"},template("modem/atcommands"),translate("AT 命令"), 40)
+	entry({"admin", "modem", "sms", "smsconfig"},cbi("modem/smsconfig"),translate("配置"), 50)
+	entry({"admin", "modem", "sms", "delete_one"}, call("delete_sms", smsindex), nil).leaf = true
+	entry({"admin", "modem", "sms", "delete_all"}, call("delete_all_sms"), nil).leaf = true
+	entry({"admin", "modem", "sms", "run_ussd"}, call("ussd"), nil).leaf = true
+	entry({"admin", "modem", "sms", "run_at"}, call("at"), nil).leaf = true
+	entry({"admin", "modem", "sms", "run_sms"}, call("sms"), nil).leaf = true
+	entry({"admin", "modem", "sms", "readsim"}, call("slots"), nil).leaf = true
+	entry({"admin", "modem", "sms", "countsms"}, call("count_sms"), nil).leaf = true
+	entry({"admin", "modem", "sms", "user_ussd"}, call("userussd"), nil).leaf = true
+	entry({"admin", "modem", "sms", "user_atc"}, call("useratc"), nil).leaf = true
+	entry({"admin", "modem", "sms", "user_phonebook"}, call("userphb"), nil).leaf = true
+end
+
+
+function delete_sms(smsindex)
+local devv = tostring(uci:get("sms_tool", "general", "readport"))
+local s = smsindex
+for d in s:gmatch("%d+") do 
+	os.execute("sms_tool -d " .. devv .. " delete " .. d .. "")
+end
+end
+
+function delete_all_sms()
+	local devv = tostring(uci:get("sms_tool", "general", "readport"))
+	os.execute("sms_tool -d " .. devv .. " delete all")
+end
+
+function get_ussd()
+    local cursor = luci.model.uci.cursor()
+    if cursor:get("sms_tool", "general", "ussd") == "1" then
+        return " -R"
+    else
+        return ""
+    end
+end
+
+
+function get_pdu()
+    local cursor = luci.model.uci.cursor()
+    if cursor:get("sms_tool", "general", "pdu") == "1" then
+        return " -r"
+    else
+        return ""
+    end
+end
+
+
+function ussd()
+    local devv = tostring(uci:get("sms_tool", "general", "ussdport"))
+
+	local ussd = get_ussd()
+	local pdu = get_pdu()
+
+    local ussd_code = http.formvalue("code")
+    if ussd_code then
+	    local odpall = io.popen("sms_tool -d " .. devv .. ussd .. pdu .. " ussd " .. ussd_code .." 2>&1")
+	    local odp =  odpall:read("*a")
+	    odpall:close()
+        http.write(tostring(odp))
+    else
+        http.write_json(http.formvalue())
+    end
+end
+
+
+function at()
+    local devv = tostring(uci:get("sms_tool", "general", "atport"))
+
+    local at_code = http.formvalue("code")
+    if at_code then
+	    local odpall = io.popen("sms_tool -d " .. devv .. " at "  ..at_code:gsub("[$]", "\\\$"):gsub("\"", "\\\"").." 2>&1")
+	    local odp =  odpall:read("*a")
+	    odpall:close()
+        http.write(tostring(odp))
+    else
+        http.write_json(http.formvalue())
+    end
+end
+
+
+function sms()
+    local devv = tostring(uci:get("sms_tool", "general", "sendport"))
+    local sms_code = http.formvalue("scode")
+
+    nr = (string.sub(sms_code, 1, 20))
+    msgall = string.sub(sms_code, 21)
+    msg = string.gsub(msgall, "\n", " ")
+
+    if sms_code then
+	    local odpall = io.popen("sms_tool -d " .. devv .. " send " .. nr .." '".. msg .."'")
+	    local odp =  odpall:read("*a")
+	    odpall:close()
+        http.write(tostring(odp))
+    else
+        http.write_json(http.formvalue())
+    end
+
+end
+
+function slots()
+	local sim = { }
+	local devv = tostring(uci:get("sms_tool", "general", "readport"))
+	local led = tostring(uci:get("sms_tool", "general", "smsled"))
+	local dsled = tostring(uci:get("sms_tool", "general", "ledtype"))
+	local ln = tostring(uci:get("sms_tool", "general", "lednotify"))
+
+	local smsmem = tostring(uci:get("sms_tool", "general", "storage"))
+
+	local statusb = luci.util.exec("sms_tool -s" .. smsmem .. " -d ".. devv .. " status")
+	local usex = string.sub (statusb, 23, 27)
+	local max = statusb:match('[^: ]+$')
+	sim["use"] = string.match(usex, '%d+')
+	local smscount = string.match(usex, '%d+')
+	if ln == "1" then
+      		luci.sys.call("echo " .. smscount .. " > /etc/config/sms_count")
+		if dsled == "S" then
+		luci.util.exec("/etc/init.d/led restart")
+		end
+		if dsled == "D" then
+		luci.sys.call("echo 0 > '/sys/class/leds/" .. led .. "/brightness'")
+		end
+ 	end
+	sim["all"] = string.match(max, '%d+')
+	luci.http.prepare_content("application/json")
+	luci.http.write_json(sim)
+end
+
+
+function count_sms()
+    os.execute("sleep 3")
+    local cursor = luci.model.uci.cursor()
+    if cursor:get("sms_tool", "general", "lednotify") == "1" then
+        local devv = tostring(uci:get("sms_tool", "general", "readport"))
+
+	 local smsmem = tostring(uci:get("sms_tool", "general", "storage"))
+
+        local statusb = luci.util.exec("sms_tool -s" .. smsmem .. " -d ".. devv .. " status")
+        local smsnum = string.sub (statusb, 23, 27)
+        local smscount = string.match(smsnum, '%d+')
+        os.execute("echo " .. smscount .. " > /etc/config/sms_count")
+    end
+end
+
+
+function uussd(rv)
+	local c = nixio.fs.access("/etc/config/ussd.user") and
+		io.popen("cat /etc/config/ussd.user")
+
+	if c then
+		for l in c:lines() do
+			local i = l
+			if i then
+				rv[#rv + 1] = {
+					usd = i
+				}
+			end
+		end
+		c:close()
+	end
+end
+
+
+
+function userussd()
+	local usd = { }
+	uussd(usd)
+	luci.http.prepare_content("application/json")
+	luci.http.write_json(usd)
+end
+
+
+function uat(rv)
+	local c = nixio.fs.access("/etc/config/atcmds.user") and
+		io.popen("cat /etc/config/atcmds.user")
+
+	if c then
+		for l in c:lines() do
+			local i = l
+			if i then
+				rv[#rv + 1] = {
+					atu = i
+				}
+			end
+		end
+		c:close()
+	end
+end
+
+
+
+function useratc()
+	local atu = { }
+	uat(atu)
+	luci.http.prepare_content("application/json")
+	luci.http.write_json(atu)
+end
+
+
+
+function uphb(rv)
+	local c = nixio.fs.access("/etc/config/phonebook.user") and
+		io.popen("cat /etc/config/phonebook.user")
+
+	if c then
+		for l in c:lines() do
+			local i = l
+			if i then
+				rv[#rv + 1] = {
+					phb = i
+				}
+			end
+		end
+		c:close()
+	end
+end
+
+
+
+function userphb()
+	local phb = { }
+	uphb(phb)
+	luci.http.prepare_content("application/json")
+	luci.http.write_json(phb)
+end
diff --git a/package/wwan/luci-app-sms-tool/luasrc/model/cbi/modem/smsconfig.lua b/package/wwan/luci-app-sms-tool/luasrc/model/cbi/modem/smsconfig.lua
new file mode 100644
index 000000000..dc756e5c4
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/luasrc/model/cbi/modem/smsconfig.lua
@@ -0,0 +1,226 @@
+local util = require "luci.util"
+local fs = require "nixio.fs"
+local sys = require "luci.sys"
+local http = require "luci.http"
+local dispatcher = require "luci.dispatcher"
+local http = require "luci.http"
+local sys = require "luci.sys"
+local uci = require "luci.model.uci".cursor()
+
+local USSD_FILE_PATH = "/etc/config/ussd.user"
+local PHB_FILE_PATH = "/etc/config/phonebook.user"
+local SMSC_FILE_PATH = "/etc/config/smscommands.user"
+local AT_FILE_PATH = "/etc/config/atcmds.user"
+
+local led = tostring(uci:get("sms_tool", "general", "smsled"))
+local dsled = tostring(uci:get("sms_tool", "general", "ledtype"))
+local ledtime = tostring(uci:get("sms_tool", "general", "checktime"))
+
+local m
+local s
+local dev1, dev2, dev3, dev4, leds
+local try_devices1 = nixio.fs.glob("/dev/tty[A-Z][A-Z]*")
+local try_devices2 = nixio.fs.glob("/dev/tty[A-Z][A-Z]*")
+local try_devices3 = nixio.fs.glob("/dev/tty[A-Z][A-Z]*")
+local try_devices4 = nixio.fs.glob("/dev/tty[A-Z][A-Z]*")
+local try_leds = nixio.fs.glob("/sys/class/leds/*")
+
+
+local devv = tostring(uci:get("sms_tool", "general", "readport"))
+
+local smsmem = tostring(uci:get("sms_tool", "general", "storage"))
+
+local statusb = luci.util.exec("sms_tool -s".. smsmem .. " -d ".. devv .. " status")
+
+local smsnum = string.sub (statusb, 23, 27)
+
+local smscount = string.match(smsnum, '%d+')
+
+m = Map("sms_tool", translate("配置短信工具"),
+	translate("sms_tool和gui应用程序的配置面板。"))
+
+s = m:section(NamedSection, 'general' , "sms_tool" , "" .. translate(""))
+s.anonymous = true
+s:tab("sms", translate("SMS 设置"))
+s:tab("ussd", translate("USSD 代码设置"))
+s:tab("at", translate("AT 命令设置"))
+s:tab("info", translate("通知设置"))
+
+this_tab = "sms"
+
+dev1 = s:taboption(this_tab, Value, "readport", translate("短信读取端口"))
+if try_devices1 then
+local node
+for node in try_devices1 do
+dev1:value(node, node)
+end
+end
+
+mem = s:taboption(this_tab, ListValue, "storage", translate("信息存储区"), translate("信息存储在一个特定的位置(例如,在SIM卡或调制解调器内存),但根据设备的类型,其他区域也可能是可用的。"))
+mem.default = "SM"
+mem:value("SM", translate("SIM 卡"))
+mem:value("ME", translate("调制解调器内存"))
+mem.rmempty = true
+
+local msm = s:taboption(this_tab, Flag, "mergesms", translate("合并分割的信息"), translate("勾选这个选项会使阅读信息更容易,但会导致显示和接收的信息数量不一致。"))
+msm.rmempty = false
+
+dev2 = s:taboption(this_tab, Value, "sendport", translate("短信发送端口"))
+if try_devices2 then
+local node
+for node in try_devices2 do
+dev2:value(node, node)
+end
+end
+
+local t = s:taboption(this_tab, Value, "pnumber", translate("前缀号码"), translate("电话号码的前面应该有国家的前缀(波兰是48,没有'+')。如果号码是5个、4个或3个字符,它将被视为 '短',不应该在前面加上国家前缀。"))
+t.rmempty = true
+t.default = 48
+
+local f = s:taboption(this_tab, Flag, "prefix", translate("为电话号码添加前缀"), translate("自动添加电话号码字段的前缀。"))
+f.rmempty = false
+
+
+local i = s:taboption(this_tab, Flag, "information", translate("号码和前缀的解释"), translate("在发送短信的标签中,显示前缀的解释和正确的电话号码。"))
+i.rmempty = false
+
+local ta = s:taboption(this_tab, TextValue, "user_phonebook", translate("用户电话簿"), translate("每一行必须有以下格式。'联系人姓名;电话号码'。保存到文件'/etc/config/phonebook.user'。"))
+ta.rows = 7
+ta.rmempty = false
+
+function ta.cfgvalue(self, section)
+    return fs.readfile(PHB_FILE_PATH)
+end
+
+function ta.write(self, section, value)
+    		value = value:gsub("\r\n", "\n")
+    		fs.writefile(PHB_FILE_PATH, value)
+end
+
+this_taba = "ussd"
+
+dev3 = s:taboption(this_taba, Value, "ussdport", translate("USSD发送端口"))
+if try_devices3 then
+local node
+for node in try_devices3 do
+dev3:value(node, node)
+end
+end
+
+local u = s:taboption(this_taba, Flag, "ussd", translate("以纯文本发送USSD代码"), translate("以纯文本发送USSD代码。命令没有被编码到PDU中。"))
+u.rmempty = false
+
+local p = s:taboption(this_taba, Flag, "pdu", translate("接收没有PDU解码的信息"), translate("接收并显示消息,而不将其解码为PDU。"))
+p.rmempty = false
+
+local tb = s:taboption(this_taba, TextValue, "user_ussd", translate("用户USSD代码"), translate("每一行必须有以下格式。'代码名称;代码'。保存到文件'/etc/config/ussd.user'。"))
+tb.rows = 7
+tb.rmempty = true
+
+function tb.cfgvalue(self, section)
+    return fs.readfile(USSD_FILE_PATH)
+end
+
+function tb.write(self, section, value)
+    		value = value:gsub("\r\n", "\n")
+    		fs.writefile(USSD_FILE_PATH, value)
+end
+
+this_tabc = "at"
+
+dev4 = s:taboption(this_tabc, Value, "atport", translate("AT命令的发送端口"))
+if try_devices4 then
+local node
+for node in try_devices4 do
+dev4:value(node, node)
+end
+end
+
+local tat = s:taboption(this_tabc, TextValue, "user_at", translate("用户AT命令"), translate("每一行必须有以下格式。'AT命令名称;AT命令'。保存到文件'/etc/config/atcmds.user'。"))
+tat.rows = 20
+tat.rmempty = true
+
+function tat.cfgvalue(self, section)
+    return fs.readfile(AT_FILE_PATH)
+end
+
+function tat.write(self, section, value)
+    		value = value:gsub("\r\n", "\n")
+    		fs.writefile(AT_FILE_PATH, value)
+end
+
+this_tabb = "info"
+
+local uw = s:taboption(this_tabb, Flag, "lednotify", translate("通知新消息"), translate("LED通知有新的信息。在激活这个功能之前,请配置并保存短信阅读端口,检查短信收件箱的时间,并选择通知LED。"))
+uw.rmempty = false
+
+function uw.write(self, section, value)
+if devv ~= nil or devv ~= '' then
+if ( smscount ~= nil and led ~= nil ) then
+    if value == '1' then
+
+       luci.sys.call("echo " .. smscount .. " > /etc/config/sms_count")
+	luci.sys.call("uci set sms_tool.general.lednotify=" .. 1 .. ";/etc/init.d/smsled enable;/etc/init.d/smsled start")
+	luci.sys.call("/sbin/cronsync.sh")
+
+    elseif value == '0' then
+       luci.sys.call("uci set sms_tool.general.lednotify=" .. 0 .. ";/etc/init.d/smsled stop;/etc/init.d/smsled disable")
+	    if dsled == 'D' then
+		luci.sys.call("echo 0 > '/sys/class/leds/" .. led .. "/brightness'")
+	    end
+	luci.sys.call("/sbin/cronsync.sh")
+
+    end
+return Flag.write(self, section ,value)
+  end
+end
+end
+
+local time = s:taboption(this_tabb, Value, "checktime", translate("每(几)分钟检查一次收件箱"), translate("指定你想在多少分钟内检查你的收件箱。"))
+time.rmempty = false
+time.maxlength = 2
+time.default = 5
+
+function time.validate(self, value)
+	if ( tonumber(value) < 60 and tonumber(value) > 0 ) then
+	return value
+	end
+end
+
+sync = s:taboption(this_tabb, ListValue, "prestart", translate("每隔一段时间重新启动收件箱检查程序"), translate("该过程将在选定的时间间隔内重新启动。这将消除检查收件箱的延迟。"))
+sync.default = "6"
+sync:value("4", translate("4h"))
+sync:value("6", translate("6h"))
+sync:value("8", translate("8h"))
+sync:value("12", translate("12h"))
+sync.rmempty = true
+
+
+leds = s:taboption(this_tabb, Value, "smsled", translate("通知LED"), translate("选择通知LED。"))
+if try_leds then
+local node
+local status
+for node in try_leds do
+local status = node
+local all = string.sub (status, 17)
+leds:value(all, all)
+end
+end
+
+oled = s:taboption(this_tabb, ListValue, "ledtype", translate("该二极管只专门用于这些通知"), translate("如果路由器只有一个LED,或者LED是多任务的,就选'No'。"))
+oled.default = "D"
+oled:value("S", translate("No"))
+oled:value("D", translate("Yes"))
+oled.rmempty = true
+
+local timeon = s:taboption(this_tabb, Value, "ledtimeon", translate("每(几)秒打开LED灯"), translate("指定LED应该亮多长时间。"))
+timeon.rmempty = false
+timeon.maxlength = 3
+timeon.default = 1
+
+local timeoff = s:taboption(this_tabb, Value, "ledtimeoff", translate("每(几)秒关闭LED灯"), translate("指定LED应该关闭多长时间。"))
+timeoff.rmempty = false
+timeoff.maxlength = 3
+timeoff.default = 5
+
+return m
diff --git a/package/wwan/luci-app-sms-tool/luasrc/view/modem/atcommands.htm b/package/wwan/luci-app-sms-tool/luasrc/view/modem/atcommands.htm
new file mode 100644
index 000000000..3510d8b87
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/luasrc/view/modem/atcommands.htm
@@ -0,0 +1,136 @@
+<%+header%>
+
+<!--
+-- Copyright 2020-2022 Rafał Wabik (IceG) - From eko.one.pl forum
+-- Licensed to the GNU General Public License v3.0.
+-->
+
+	<h2><%:AT 命令%></h2>
+	<div class="cbi-map-descr"><%:通过sms_tool处理AT命令的Web用户界面。关于sms-tool的更多信息,请见%> <a href="https://eko.one.pl/?p=openwrt-sms_tool" target="_blank"><%:eko.one.pl 论坛%></a>.</div>
+	<p></p>
+	<h4><%:向调制解调器发送命令%></h4>
+	<div class="table" width="100%">
+
+		<div class="tr">
+			<div class="td left"><%:用户AT命令%>:</div>
+
+			<div class="td left";">
+				<select name="ussd" id="pl" onclick="copyFunction()">
+
+ 				</select>
+			</div>
+			<div class="td left""></div>
+			</div>
+
+		<div class="tr">
+			<div class="td left" ><%:要发送的命令%>:</div>
+			<div class="td left" ><input type="text" id="code"></div>
+		</div>
+
+	</div>
+
+	<div class="table" width="100%">
+		<div class="td left";"><%:回复%>:
+		<p>
+		<pre id="odp" style="visibility: hidden;"></pre></div>
+
+		<div class="tr cbi-rowstyle-2">
+		<div class="td right"><input type="button" style="margin-right: 5%"; id="sendcmd" class="btn cbi-button cbi-button-neutral" value="<%:发送命令%>" /></div>
+		</div>
+
+	</div>
+
+<script type="text/javascript">
+
+window.onload = function readUSER() {
+
+
+			XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "user_atc")%>', null,
+			function(x, json)
+			{
+				select = document.getElementById('pl');
+
+				var count = Object.keys(json).length;
+
+
+					for(var d=0;d<=count;d++)
+						{
+    			    				var opt = document.createElement('option');
+							var s = json[d].atu;
+							var fields = s.split(/;/);
+							var name = fields[0];
+							var code = fields[1];
+							opt.text = name;
+							opt.value = code.trim();
+    					    		opt.innerHTML = name;
+    							select.appendChild(opt);
+						}
+
+			}
+			);
+
+
+
+}
+
+function copyFunction() {
+
+  var node = document.getElementById('odp');
+  node.style.visibility = 'hidden';
+
+  var x = document.getElementById("pl").value;
+  document.getElementById("code").value = x;
+  document.getElementById("odp").innerHTML = "";
+}
+
+
+function postcmd(cmd) {
+    (new XHR()).post("<%=luci.dispatcher.build_url("admin", "modem", "sms", "run_at")%>", {"code":cmd}, function(x) {
+        console.log(x.response)
+        console.log(x)
+
+        var aStr = x.response;
+	 var myre = /^[\s\t]*(\r\n|\n|\r)/gm;
+	 var bStr = aStr.replace(myre,"");
+        document.getElementById("odp").innerHTML = bStr;
+	 var el = document.getElementsByName("odp")[0];
+	 el.value.replace(/(\r\n|\n|\r)/gm, "");
+
+
+    });
+    return false;
+}
+
+
+document.addEventListener('DOMContentLoaded', function (ev) {var button = document.getElementById("sendcmd");
+        button.addEventListener("click", function () {
+
+
+		var s = document.getElementById("code").value;
+		if ( s.length == 0 )
+		{
+  			document.getElementById("odp").innerHTML = "";
+			alert("<%:Please enter a AT Command%>");
+			return false;
+		}
+
+        var cmd = document.getElementById("code");
+        postcmd(cmd.value);
+        cmd.value = "";
+
+	    var node = document.getElementById('odp');
+    	if (node.style.visibility=='visible') {
+        node.style.visibility = 'hidden';
+    	}
+    	else
+        node.style.visibility = 'visible'
+
+        return true;
+        });
+    }, true);
+
+
+</script>
+
+<%+footer%>
+
diff --git a/package/wwan/luci-app-sms-tool/luasrc/view/modem/readsms.htm b/package/wwan/luci-app-sms-tool/luasrc/view/modem/readsms.htm
new file mode 100644
index 000000000..dc8245e2d
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/luasrc/view/modem/readsms.htm
@@ -0,0 +1,285 @@
+<%
+	local util = require "luci.util"
+	local fs = require "nixio.fs"
+	local sys = require "luci.sys"
+	local http = require "luci.http"
+	local dispatcher = require "luci.dispatcher"
+	local uci = require "luci.model.uci".cursor()
+
+	local devv = tostring(uci:get("sms_tool", "general", "readport"))
+	local smsmem = tostring(uci:get("sms_tool", "general", "storage"))
+	local sms = tostring(luci.sys.exec("sms_tool -s" .. smsmem .. " -d " .. devv .. " -f '%Y-%m-%d %H:%M' -j recv 2>/dev/null"))
+	local smsmer = tostring(uci:get("sms_tool", "general", "mergesms"))
+	local smscuta = string.sub (sms, 8)
+	local smscut = smscuta:sub(1, #smscuta - 2)
+	local statusb = luci.util.exec("sms_tool -s" .. smsmem .. " -d ".. devv .. " status")
+	local all = statusb:match('[^: ]+$')
+	local location = ""
+	local l = string.sub (statusb, 15, 16)
+	if l == "SM" then
+      		location = translate("SIM card")
+ 	end
+	if l == "ME" then
+      		location = translate("Modem memory")
+ 	end
+-%>
+	
+<%+header%>
+
+<!--
+-- Copyright 2020-2022 Rafał Wabik (IceG) - From eko.one.pl forum
+-- Licensed to the GNU General Public License v3.0.
+-->
+
+<style>
+
+table {
+  border-collapse: collapse;
+  width: 100%;
+}
+
+th, td {
+  border-bottom: 1px solid var(--border-color-medium);
+  font-size: 12px;
+  padding: 10px;
+  text-align: justify;
+  display: table-cell;
+  vertical-align: top;
+}
+
+td input[type="checkbox"] {
+    float: left;
+    margin: 0 auto;
+    width: 40%;
+}
+
+tr:nth-child(odd) {background-color: var(--background-color-medium)}
+
+</style>
+
+<script type="text/javascript">//<![CDATA[
+
+	window.onload = function readSMS() {
+
+			XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "readsim")%>', null,
+			function(x, sim)
+			{
+			var vn = parseInt(sim.use) || 0;
+			var mn = parseInt(sim.all) || 100;
+			var pc = Math.floor((100 / mn) * vn);
+			document.getElementById('usse').innerHTML=sim.use  + " / " + sim.all + " " + '('+ pc + '%)';
+			}
+			);
+
+			var msgm = <%=smsmer%>;
+			if ( msgm == "0" ) {
+
+			var array = <%=smscut%>;
+			var sortedData = array .sort((function (a, b) { return new Date(b.timestamp) - new Date(a.timestamp) }));
+			var table = document.getElementById("smsTable");
+			var data = JSON.stringify(sortedData);
+			var json = JSON.parse(data);
+
+			var x = <%=all%>;
+        		for (var d = 0; d < json.length; d++) {
+
+            					row = table.insertRow(-1);
+  						var cell1 = row.insertCell(0);
+  						var cell2 = row.insertCell(0);
+  						var cell3 = row.insertCell(0);
+  						var cell4 = row.insertCell(0);
+						cell4.innerHTML = "<input type='checkbox' name='smsn' id="+"btn"+json[d].index+" /><img src='<%=resource%>/icons/delsms.png'>"
+ 				 		cell3.innerHTML = json[d].sender;
+  						cell2.innerHTML = json[d].timestamp;
+    						cell1.innerHTML = json[d].content;	
+				}
+			}
+
+			if ( msgm == "1" ) {
+
+			var array = <%=smscut%>;
+			var sortedData = array .sort((function (a, b) { return new Date(b.timestamp) - new Date(a.timestamp) }));
+			var table = document.getElementById("smsTable");
+
+			var MergeMySMS = sortedData;
+    			result = [];
+
+			MergeMySMS.forEach(function (o) {
+    			if (!this[o.sender]) {
+    			if(o.part > 0){
+        		this[o.sender] = { index: o.index, sender: o.sender, timestamp: o.timestamp, part: o.part, total: o.total, content: o.content, contentparts: [] };
+        		this[o.sender].contentparts[o.part] = o.content;
+        		}else{
+        		this[o.sender] = { index: o.index, sender: o.sender, timestamp: o.timestamp, part: o.part, total: o.total, content: o.content};
+        		}
+        		result.push(this[o.sender]);
+        		return;
+    			}
+			if (this[o.sender].total == o.total && this[o.sender].timestamp == o.timestamp && this[o.sender].sender == o.sender && this[o.sender].part > 0) {
+    			this[o.sender].index += '-' + o.index;
+            		this[o.sender].contentparts[o.part] = o.content;}
+			else {
+			this[o.sender] = { index: o.index, sender: o.sender, timestamp: o.timestamp, part: o.part, total: o.total, content: o.content };
+        		result.push(this[o.sender]);
+        		return;
+			}
+			}, Object.create(null));
+			result.forEach(function(o) {
+                		if(o.contentparts){
+                    		o.contentparts.shift();
+                    		o.content = o.contentparts.join('');
+                		}
+			});
+
+			var data = JSON.stringify(result);
+			var json = JSON.parse(data);
+
+			var x = <%=all%>;
+        		for (var d = 0; d < json.length; d++) {
+
+            					row = table.insertRow(-1);
+  						var cell1 = row.insertCell(0);
+  						var cell2 = row.insertCell(0);
+  						var cell3 = row.insertCell(0);
+  						var cell4 = row.insertCell(0);
+						cell4.innerHTML = "<input type='checkbox' name='smsn' id="+"btn"+json[d].index+" /><img src='<%=resource%>/icons/delsms.png'>"
+ 				 		cell3.innerHTML = json[d].sender;
+  						cell2.innerHTML = json[d].timestamp;
+    						cell1.innerHTML = json[d].content;
+				}
+			}
+	}
+
+
+function refreshSMS() {
+window.location.reload();
+}
+
+function toggle(source) {
+    var checkboxes = document.querySelectorAll('input[type="checkbox"]');
+    for (var i = 0; i < checkboxes.length; i++) {
+        if (checkboxes[i] != source)
+            checkboxes[i].checked = source.checked;
+    }
+}
+
+
+function delete_new()
+{
+if (document.querySelectorAll('input[name="smsn"]:checked').length === document.querySelectorAll('input[name="smsn"]').length) {
+
+            	if (confirm("<%:Delete all the messages?%>")) {
+		var rowCount = smsTable.rows.length;
+		document.getElementById('usse').innerHTML = "0"  + " / " + <%=all%>;
+		for (var i = rowCount - 1; i > 0; i--) {
+            		smsTable.deleteRow(i);}
+
+		XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "delete_all")%>/' , null,
+
+			function()
+			{
+			document.getElementById('usse').innerHTML = "0"  + " / " + <%=all%> + " " + '(0%)';
+			}
+		);
+
+		XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "countsms")%>/' , null,
+
+			function()
+			{
+			}
+		);
+		return false;
+		}
+
+
+        } else {
+
+		if (document.querySelectorAll('input[name="smsn"]:checked').length == 0){
+    		alert('<%:Please select the message(s) to be deleted%>');   
+		} else {
+
+                var items = document.getElementsByName("smsn");
+                var selectedItems = "";
+                for (var i = 0; i < items.length; i++) {
+                    if (items[i].type == "checkbox" && items[i].checked == true) selectedItems += items[i].id + "\n";
+                }
+		  smsindex = selectedItems.replace(/\s/g, '');
+
+	if (confirm("<%:Delete selected message(s)?%>")) {
+
+		XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "delete_one")%>/' + smsindex, null,
+
+			function()
+			{
+       			var table = document.getElementById("smsTable");
+  			var index = 1;
+  			while (index < table.rows.length) {
+
+    			var input = table.rows[index].cells[0].children[0];
+      			if (input && input.checked) {
+      			table.deleteRow(index);
+    			}
+    			else {
+      			index++;
+  			}
+        		}
+
+		XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "readsim")%>', null,
+			function(x, sim)
+			{
+			var vn = parseInt(sim.use) || 0;
+			var mn = parseInt(sim.all) || 100;
+			var pc = Math.floor((100 / mn) * vn);
+			document.getElementById('usse').innerHTML=sim.use  + " / " + sim.all + " " + '('+ pc + '%)';
+
+			}
+			);
+			}
+
+		);
+
+		XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "countsms")%>/' , null,
+
+			function()
+			{
+			}
+		);
+		return false;
+		}
+        }
+	}
+}
+
+//]]></script>
+
+<h2><%:短信%></h2>
+	<div class="cbi-map-descr"><%:通过sms_tool接收信息的Web用户界面。关于短信工具的更多信息,请见%> <a href="https://eko.one.pl/?p=openwrt-sms_tool" target="_blank"><%:eko.one.pl 论坛%></a>。</div>
+	<p></p>
+	<h4><%:接收的信息%></h4>
+	<div class="table" width="100%">
+	<div class="tr"><div class="td left" width="33%"><%:信息存储在%>:</div><div class="td left"><%=location%></div></div>
+	<div class="tr"><div class="td left" width="33%"><%:信息 (收件箱 / 最大值)%>:</div><div class="td left" id="usse"></div></div>
+	</div>
+
+	<table id="smsTable" width="100%" class="table">
+  	<tr class="tr cbi-rowstyle-2">
+
+    	<td class="td left" width="7%"><input type='checkbox' id='allcheck' onclick='toggle(this);'/></td>
+    	<td class="td left" width="11%"><strong><%:来自%></strong></td>
+    	<td class="td left" width="15%"><strong><%:收到%></strong></td>
+    	<td class="td center" width="66%" ><strong><%:信息%></strong></td>
+
+ 	</tr>
+	</table>
+
+	<div class="table" width="100%" >
+    	<div class="tr cbi-rowstyle-2">
+	
+	<div class="td right" style="width:100%;">
+	<div style="float: left;"></div>
+	<input type="button" id="delbtn" class="btn cbi-button cbi-button-neutral" value="<%:刷新消息%>" onclick="refreshSMS()" />&nbsp;&nbsp;&nbsp;<input type="button" id="delabtn" class="btn cbi-button cbi-button-neutral" value="<%:删除消息(s)%>" onclick="delete_new()" />
+
+	</div>
+	</div>
+
+<%+footer%>
diff --git a/package/wwan/luci-app-sms-tool/luasrc/view/modem/sendsms.htm b/package/wwan/luci-app-sms-tool/luasrc/view/modem/sendsms.htm
new file mode 100644
index 000000000..f9747c6a5
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/luasrc/view/modem/sendsms.htm
@@ -0,0 +1,237 @@
+<%
+	local util = require "luci.util"
+	local sys = require "luci.sys"
+	local uci = require "luci.model.uci".cursor()
+
+	local pon = tostring(uci:get("sms_tool", "general", "prefix"))
+	local pnumber = tostring(uci:get("sms_tool", "general", "pnumber"))
+	local info = tostring(uci:get("sms_tool", "general", "information"))
+
+-%>
+
+<%+header%>
+
+<!--
+-- Copyright 2020-2022 Rafał Wabik (IceG) - From eko.one.pl forum
+-- Licensed to the GNU General Public License v3.0.
+-->
+
+<style>
+
+textarea {
+   height:120px;
+   max-height:120px;
+   min-height:120px;
+   min-width:100%;
+   resize:vertical;
+   }
+
+}
+
+</style>
+
+<script type="text/javascript">//<![CDATA[
+
+
+window.onload = function prefiksSMS() {
+
+		var phk = document.getElementById("numer").value;
+		var on = <%=pon%>;
+
+		var msg = <%=info%>;
+
+		var pfixnum = document.getElementById("numer");
+		if ( on == "1" ) {
+		var phn2 = <%=pnumber%>;
+		pfixnum.value = phn2;
+		}
+
+		if ( msg == "1" ) {
+		alert("<%:电话号码的前面应该有国家的前缀(波兰是48,没有+)。如果号码是5个、4个或3个字符,它将被视为短号码,而不应在前面加上国家前缀。(The phone number should be preceded by the country prefix (for Poland it is 48, without +). If the number is 5, 4 or 3 characters, it is treated as.. short and should not be preceded by a country prefix.)%>");
+		}
+
+
+
+XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "user_phonebook")%>', null,
+			function(x, json)
+			{
+				select = document.getElementById('pb');
+				var opt = document.createElement('option');
+				opt.text = phn2;
+				opt.value = phn2;
+    			opt.innerHTML = ">";
+    			select.appendChild(opt);
+    			    	
+				var countpb = Object.keys(json).length;
+
+
+					for(var d=0;d<=countpb;d++)
+						{
+							var opt = document.createElement('option');
+							var s = json[d].phb;
+							var fields = s.split(/;/);
+							var name = fields[0];
+							var number = fields[1];
+							opt.text = name;
+							opt.value = number.trim();
+    							opt.innerHTML = name;
+    							select.appendChild(opt);
+						}
+
+			}
+			);
+
+
+
+
+}
+
+
+function copyFunction2() {
+
+ 	var x = document.getElementById("pb").value;
+     	document.getElementById("numer").value = x;
+     	document.getElementById("odp").innerHTML = "";
+
+
+}
+
+
+function count_replace(obj) {
+
+        document.getElementById('counter').innerHTML = (160 - document.getElementById('smstxt').value.length);
+
+	obj.value = obj.value.replace(/ą/g, 'a').replace(/Ą/g, 'A');
+        obj.value = obj.value.replace(/ć/g, 'c').replace(/Ć/g, 'C')
+        obj.value = obj.value.replace(/ę/g, 'e').replace(/Ę/g, 'E')
+        obj.value = obj.value.replace(/ł/g, 'l').replace(/Ł/g, 'L')
+        obj.value = obj.value.replace(/ń/g, 'n').replace(/Ń/g, 'N')
+        obj.value = obj.value.replace(/ó/g, 'o').replace(/Ó/g, 'O')
+        obj.value = obj.value.replace(/ś/g, 's').replace(/Ś/g, 'S')
+        obj.value = obj.value.replace(/ż/g, 'z').replace(/Ż/g, 'Z')
+        obj.value = obj.value.replace(/ź/g, 'z').replace(/Ź/g, 'Z');
+	document.getElementById("odp").innerHTML = "";
+
+}
+
+function codsp() {
+
+		document.getElementById("odp").innerHTML = "";
+
+}
+
+
+
+function postcmd(cmd) {
+    (new XHR()).post("<%=luci.dispatcher.build_url("admin", "modem", "sms", "run_sms")%>", {"scode":cmd}, function(x) {
+        console.log(x.response)
+        console.log(x)
+	 	var cut = x.response;
+		cut = cut.substr(0, 20);
+	if ( cut == "sms sent sucessfully" ) {
+        document.getElementById("odp").innerHTML = "<%:SMS sent sucessfully%>"};
+
+    });
+    return false;
+}
+
+
+document.addEventListener('DOMContentLoaded', function (ev) {var button = document.getElementById("sendsms");
+        button.addEventListener("click", function () {
+
+
+		var ph = document.getElementById("numer").value;
+		if ( ph.length < 2 || ph.length == 0 )
+		{
+  			document.getElementById("odp").innerHTML = "";
+			alert("<%:Please enter phone number%>");
+			return false;
+		}
+
+		ph = ph.trim();
+
+		if ( isNaN(ph) )
+		{
+			alert("<%:Please correct phone number%>");
+			return false;
+		}
+
+		var num = ph.concat("                    ");
+		num = num.substr(0, 20);
+
+		var tm = document.getElementById("smstxt").value;
+		if ( tm.length == 0 )
+		{
+			alert("<%:Please enter a message text%>");
+			return false;
+		}
+		num = num.concat(tm);
+        	postcmd(num);
+	 	    var delsms = document.getElementById("smstxt");
+		    var delnum = document.getElementById("numer");
+			document.getElementById('counter').innerHTML = "160";
+		    delsms.value = "";
+		    delnum.value = "";
+
+		var on = <%=pon%>;
+		var pfixnum = document.getElementById("numer");
+		if ( on == "1" ) {
+		var phn2 = <%=pnumber%>;
+		pfixnum.value = phn2;
+		}
+
+        	return true;
+        });
+    }, true);
+
+
+//]]></script>
+
+
+	<h2><%:短信%></h2>
+	<div class="cbi-map-descr"><%:通过sms_tool发送信息的Web用户界面。关于短信工具的更多信息,请见%> <a href="https://eko.one.pl/?p=openwrt-sms_tool" target="_blank"><%:eko.one.pl 论坛%></a>.</div>
+	<p></p>
+	
+	<h4><%:发送短信%></h4>
+
+	<div class="table">
+
+		<div class="tr">
+			<div class="td left" ><%:用户电话簿%>:</div>
+			<div class="td left" >
+
+				<select name="ussd" id="pb" onchange="copyFunction2()">
+
+ 				</select>
+</div>
+			<div class="td left" ></div>
+		</div>
+
+		<div class="tr">
+			<div class="td left" ><%:发送至%>:</div>
+			<div class="td left" ><input type="text" id="numer" onKeyUp="codsp()" placeholder="<%:PHONE_NUMBER%>" required minlength="3" maxlength="24" size="11"></div>
+			<div class="td left" ></div>
+		</div>
+		<div class="tr">
+			<div class="td left" ><%:消息正文%>:</div>
+			<div class="td left" ><textarea id="smstxt" onKeyUp="count_replace(this)" required></textarea><div id="counter">160</div></div>
+			<div class="td left" ></div>
+		</div>
+			
+		<div class="tr">
+			<div class="td left" ><%:状态%>:</div><div class="td left" id="odp"></div>
+
+		</div>
+
+	</div>
+	<div class="table">
+
+		<div class="tr cbi-rowstyle-2">
+		<div class="td right"><input type="button" style="margin-right: 5%"; id="sendsms" class="btn cbi-button cbi-button-neutral" value="<%:发送短信%>" /></div>
+		</div>
+
+	</div>
+
+
+
+<%+footer%>
diff --git a/package/wwan/luci-app-sms-tool/luasrc/view/modem/ussd.htm b/package/wwan/luci-app-sms-tool/luasrc/view/modem/ussd.htm
new file mode 100644
index 000000000..38be1c3c4
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/luasrc/view/modem/ussd.htm
@@ -0,0 +1,265 @@
+<%
+	local util = require "luci.util"
+	local fs = require "nixio.fs"
+	local sys = require "luci.sys"
+	local http = require "luci.http"
+	local dispatcher = require "luci.dispatcher"
+	local uci = require "luci.model.uci".cursor()
+
+-%>
+
+
+<%+header%>
+
+<!--
+-- Copyright 2020-2022 Rafał Wabik (IceG) - From eko.one.pl forum
+-- Licensed to the GNU General Public License v3.0.
+-->
+
+	<h2><%:USSD 代码%></h2>
+	<div class="cbi-map-descr"><%:通过sms_tool处理USSD代码的Web用户界面。关于短信工具的更多信息,请见%> <a href="https://eko.one.pl/?p=openwrt-sms_tool" target="_blank"><%:eko.one.pl forum%></a>.</div>
+	<p></p>
+	<h4><%:发送 USSD 代码%></h4>
+	<div class="table" width="100%">
+
+		<div class="tr">
+			<div class="td left" width="25%"><%:用户 USSD 代码%>:</div>
+
+			<div class="td left" >
+				<select name="ussd" id="pl" onclick="copyFunction()">
+
+ 				</select>
+			</div>
+			<div class="td left" ></div>
+			</div>
+
+		<div class="tr">
+			<div class="td left" ><%:要发送的代码%>:</div>
+			<div class="td left" ><input type="text" id="code" required size="20"></div>
+
+		</div>
+		<div class="tr">
+			<div class="td left" ><%:回复%>:</div><div class="td left" ><pre id="odp" style="visibility: hidden;"></pre></div>
+
+		</div>
+	</div>
+
+	<div class="table" width="100%">
+		<div class="tr cbi-rowstyle-2">
+		<div class="td right"><input type="button" style="margin-right: 5%"; id="sendussd" class="btn cbi-button cbi-button-neutral" value="<%:发送代码%>" /></div>
+		</div>
+
+	</div>
+
+<script type="text/javascript">//<![CDATA[
+
+
+window.onload = function readUSER() {
+
+
+			XHR.get('<%=luci.dispatcher.build_url("admin", "modem", "sms", "user_ussd")%>', null,
+			function(x, json)
+			{
+				select = document.getElementById('pl');
+
+				var count = Object.keys(json).length;
+
+
+					for(var d=0;d<=count;d++)
+						{
+    			    				var opt = document.createElement('option');
+							var s = json[d].usd;
+							var fields = s.split(/;/);
+							var name = fields[0];
+							var code = fields[1];
+							opt.text = name;
+							opt.value = code.trim();
+    					    		opt.innerHTML = name;
+    							select.appendChild(opt);
+						}
+
+			}
+			);
+
+
+
+}
+
+function copyFunction() {
+  var node = document.getElementById('odp');
+  node.style.visibility = 'hidden';
+  
+  var x = document.getElementById("pl").value;
+  document.getElementById("code").value = x;
+  document.getElementById("odp").innerHTML = "";
+}
+
+function postcmd(cmd) {
+    (new XHR()).post("<%=luci.dispatcher.build_url("admin", "modem", "sms", "run_ussd")%>", {"code":cmd}, function(x) {
+        console.log(x.response)
+        console.log(x)
+	 	var cut = x.response;
+		if ( true == cut.includes("error: 0") ) {
+        		document.getElementById("odp").innerHTML = "<%:Phone/Modem failure.%>"};
+		if ( true == cut.includes("error: 1") ) {
+        		document.getElementById("odp").innerHTML = "<%:No connection to phone.%>"};
+		if ( true == cut.includes("error: 2") ) {
+        		document.getElementById("odp").innerHTML = "<%:Phone/Modem adapter link reserved.%>"};
+		if ( true == cut.includes("error: 3") ) {
+        		document.getElementById("odp").innerHTML = "<%:Operation not allowed.%>"};
+		if ( true == cut.includes("error: 4") ) {
+        		document.getElementById("odp").innerHTML = "<%:Operation not supported.%>"};
+		if ( true == cut.includes("error: 5") ) {
+        		document.getElementById("odp").innerHTML = "<%:PH_SIM PIN required.%>"};
+		if ( true == cut.includes("error: 6") ) {
+        		document.getElementById("odp").innerHTML = "<%:PH_FSIM PIN required.%>"};
+		if ( true == cut.includes("error: 7") ) {
+        		document.getElementById("odp").innerHTML = "<%:PH_FSIM PUK required.%>"};
+		if ( true == cut.includes("error: 10") ) {
+        		document.getElementById("odp").innerHTML = "<%:SIM not inserted.%>"};
+		if ( true == cut.includes("error: 11") ) {
+        		document.getElementById("odp").innerHTML = "<%:SIM PIN required.%>"};
+		if ( true == cut.includes("error: 12") ) {
+        		document.getElementById("odp").innerHTML = "<%:SIM PUK required.%>"};
+		if ( true == cut.includes("error: 13") ) {
+        		document.getElementById("odp").innerHTML = "<%:SIM failure.%>"};
+		if ( true == cut.includes("error: 14") ) {
+        		document.getElementById("odp").innerHTML = "<%:SIM busy.%>"};
+		if ( true == cut.includes("error: 15") ) {
+        		document.getElementById("odp").innerHTML = "<%:SIM wrong.%>"};
+		if ( true == cut.includes("error: 16") ) {
+        		document.getElementById("odp").innerHTML = "<%:Incorrect password.%>"};
+		if ( true == cut.includes("error: 17") ) {
+        		document.getElementById("odp").innerHTML = "<%:SIM PIN2 required.%>"};
+		if ( true == cut.includes("error: 18") ) {
+        		document.getElementById("odp").innerHTML = "<%:SIM PUK2 required.%>"};
+		if ( true == cut.includes("error: 20") ) {
+        		document.getElementById("odp").innerHTML = "<%:Memory full.%>"};
+		if ( true == cut.includes("error: 21") ) {
+        		document.getElementById("odp").innerHTML = "<%:Invalid index.%>"};
+		if ( true == cut.includes("error: 22") ) {
+        		document.getElementById("odp").innerHTML = "<%:Not found.%>"};
+		if ( true == cut.includes("error: 23") ) {
+        		document.getElementById("odp").innerHTML = "<%:Memory failure.%>"};
+		if ( true == cut.includes("error: 24") ) {
+        		document.getElementById("odp").innerHTML = "<%:Text string too long.%>"};
+		if ( true == cut.includes("error: 25") ) {
+        		document.getElementById("odp").innerHTML = "<%:Invalid characters in text string.%>"};
+		if ( true == cut.includes("error: 26") ) {
+        		document.getElementById("odp").innerHTML = "<%:Dial string too long.%>"};
+		if ( true == cut.includes("error: 27") ) {
+        		document.getElementById("odp").innerHTML = "<%:Invalid characters in dial string.%>"};
+		if ( true == cut.includes("error: 30") ) {
+        		document.getElementById("odp").innerHTML = "<%:No network service.%>"};
+		if ( true == cut.includes("error: 31") ) {
+        		document.getElementById("odp").innerHTML = "<%:Network timeout.%>"};
+		if ( true == cut.includes("error: 32") ) {
+        		document.getElementById("odp").innerHTML = "<%:Network not allowed, emergency calls only.%>"};
+		if ( true == cut.includes("error: 40") ) {
+        		document.getElementById("odp").innerHTML = "<%:Network personalization PIN required.%>"};
+		if ( true == cut.includes("error: 41") ) {
+        		document.getElementById("odp").innerHTML = "<%:Network personalization PUK required.%>"};
+		if ( true == cut.includes("error: 42") ) {
+        		document.getElementById("odp").innerHTML = "<%:Network subset personalization PIN required.%>"};
+		if ( true == cut.includes("error: 43") ) {
+        		document.getElementById("odp").innerHTML = "<%:Network subset personalization PUK required.%>"};
+		if ( true == cut.includes("error: 44") ) {
+        		document.getElementById("odp").innerHTML = "<%:Service provider personalization PIN required.%>"};
+		if ( true == cut.includes("error: 45") ) {
+        		document.getElementById("odp").innerHTML = "<%:Service provider personalization PUK required.%>"};
+		if ( true == cut.includes("error: 46") ) {
+        		document.getElementById("odp").innerHTML = "<%:Corporate personalization PIN required.%>"};
+		if ( true == cut.includes("error: 47") ) {
+        		document.getElementById("odp").innerHTML = "<%:Corporate personalization PUK required.%>"};
+		if ( true == cut.includes("error: 48") ) {
+        		document.getElementById("odp").innerHTML = "<%:PH-SIM PUK required.%>"};
+		if ( true == cut.includes("error: 100") ) {
+        		document.getElementById("odp").innerHTML = "<%:Unknown error.%>"};
+		if ( true == cut.includes("error: 103") ) {
+        		document.getElementById("odp").innerHTML = "<%:Illegal MS.%>"};
+		if ( true == cut.includes("error: 106") ) {
+        		document.getElementById("odp").innerHTML = "<%:Illegal ME.%>"};
+		if ( true == cut.includes("error: 107") ) {
+        		document.getElementById("odp").innerHTML = "<%:GPRS services not allowed.%>"};
+		if ( true == cut.includes("error: 111") ) {
+        		document.getElementById("odp").innerHTML = "<%:PLMN not allowed.%>"};
+		if ( true == cut.includes("error: 112") ) {
+        		document.getElementById("odp").innerHTML = "<%:Location area not allowed.%>"};
+		if ( true == cut.includes("error: 113") ) {
+        		document.getElementById("odp").innerHTML = "<%:Roaming not allowed in this location area.%>"};
+		if ( true == cut.includes("error: 126") ) {
+        		document.getElementById("odp").innerHTML = "<%:Operation temporary not allowed.%>"};
+		if ( true == cut.includes("error: 132") ) {
+        		document.getElementById("odp").innerHTML = "<%:Service operation not supported.%>"};
+		if ( true == cut.includes("error: 133") ) {
+        		document.getElementById("odp").innerHTML = "<%:Requested service option not subscribed.%>"};
+		if ( true == cut.includes("error: 134") ) {
+        		document.getElementById("odp").innerHTML = "<%:Service option temporary out of order.%>"};
+		if ( true == cut.includes("error: 148") ) {
+        		document.getElementById("odp").innerHTML = "<%:Unspecified GPRS error.%>"};
+		if ( true == cut.includes("error: 149") ) {
+        		document.getElementById("odp").innerHTML = "<%:PDP authentication failure.%>"};
+		if ( true == cut.includes("error: 150") ) {
+        		document.getElementById("odp").innerHTML = "<%:Invalid mobile class.%>"};
+		if ( true == cut.includes("error: 256") ) {
+        		document.getElementById("odp").innerHTML = "<%:Operation temporarily not allowed.%>"};
+		if ( true == cut.includes("error: 257") ) {
+        		document.getElementById("odp").innerHTML = "<%:Call barred.%>"};
+		if ( true == cut.includes("error: 258") ) {
+        		document.getElementById("odp").innerHTML = "<%:Phone/Modem is busy.%>"};
+		if ( true == cut.includes("error: 259") ) {
+        		document.getElementById("odp").innerHTML = "<%:User abort.%>"};
+		if ( true == cut.includes("error: 260") ) {
+        		document.getElementById("odp").innerHTML = "<%:Invalid dial string.%>"};
+		if ( true == cut.includes("error: 261") ) {
+        		document.getElementById("odp").innerHTML = "<%:SS not executed.%>"};
+		if ( true == cut.includes("error: 262") ) {
+        		document.getElementById("odp").innerHTML = "<%:SIM Blocked.%>"};
+		if ( true == cut.includes("error: 263") ) {
+        		document.getElementById("odp").innerHTML = "<%:Invalid block.%>"};
+		if ( true == cut.includes("error: 527") ) {
+        		document.getElementById("odp").innerHTML = "<%:Please wait, and retry your selection later (Specific Modem Sierra).%>"};
+		if ( true == cut.includes("error: 528") ) {
+        		document.getElementById("odp").innerHTML = "<%:Location update failure – emergency calls only (Specific Modem Sierra).%>"};
+		if ( true == cut.includes("error: 529") ) {
+        		document.getElementById("odp").innerHTML = "<%:Selection failure – emergency calls only (Specific Modem Sierra).%>"};
+		if ( true == cut.includes("error: 772") ) {
+        		document.getElementById("odp").innerHTML = "<%:SIM powered down.%>"};
+		if ( true != cut.includes("error: ") ) {
+        		document.getElementById("odp").innerHTML = x.response};
+
+    });
+    return false;
+}
+
+document.addEventListener('DOMContentLoaded', function (ev) {var button = document.getElementById("sendussd");
+        button.addEventListener("click", function () {
+
+
+		var s = document.getElementById("code").value;
+		if ( s.length == 0 )
+		{
+  			document.getElementById("odp").innerHTML = "";
+			alert("<%:Please enter a USSD code%>");
+			return false;
+		}
+
+        var cmd = document.getElementById("code");
+        postcmd(cmd.value);
+        cmd.value = "";
+
+        var node = document.getElementById('odp');
+    	if (node.style.visibility=='visible') {
+        node.style.visibility = 'hidden';
+    	}
+    	else
+        node.style.visibility = 'visible'
+
+        return true;
+        });
+    }, true);
+
+
+//]]></script>
+
+<%+footer%>
diff --git a/package/wwan/luci-app-sms-tool/po/pl/sms-tool.po b/package/wwan/luci-app-sms-tool/po/pl/sms-tool.po
new file mode 100644
index 000000000..2ef0778b5
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/po/pl/sms-tool.po
@@ -0,0 +1,288 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+"Language: pl\n"
+"Last-Translator: Rafał Wabik (IceG) - From eko.one.pl forum\n"
+
+msgid "SMS Messages"
+msgstr "Wiadomości SMS"
+
+msgid "Received Messages"
+msgstr "Odebrane wiadomości"
+
+msgid "Send Messages"
+msgstr "Wysyłanie wiadomości"
+
+msgid "USSD"
+msgstr "USSD"
+
+msgid "From"
+msgstr "Nadawca"
+
+msgid "Received"
+msgstr "Odebrano"
+
+msgid "Message"
+msgstr "Wiadomość"
+
+msgid "Configuration"
+msgstr "Ustawienia"
+
+msgid "Sample codes"
+msgstr "Przykładowe kody"
+
+msgid "Sending USSD codes"
+msgstr "Wysyłanie kodów USSD"
+
+msgid "Please enter phone number"
+msgstr "Proszę podać numer telefonu"
+
+msgid "Please correct phone number"
+msgstr "Proszę poprawić numer telefonu"
+
+msgid "Please enter a message text"
+msgstr "Proszę podać treść wiadomości"
+
+msgid "Please select the message(s) to be deleted"
+msgstr "Proszę wybrać wiadomość(-ci) do usunięcia"
+
+msgid "Reply"
+msgstr "Odpowiedź"
+
+msgid "SMS sent sucessfully"
+msgstr "SMS został wysłany"
+
+msgid "Please enter a USSD code"
+msgstr "Proszę podać kod do wysłania"
+
+msgid "Delete selected message(s)?"
+msgstr "Usunąć wybraną(-e) wiadomość(-ci)?"
+
+msgid "Delete all the messages?"
+msgstr "Usunąć wszystkie wiadomości?"
+
+msgid "Configuration sms-tool"
+msgstr "Konfiguracja sms-tool"
+
+msgid "Configuration panel for sms_tool and gui application."
+msgstr "Panel ustawień dla aplikacji sms_tool oraz dla interfejsu użytkownika."
+
+msgid "Device"
+msgstr "Urządzenie"
+
+msgid "Web UI for handling messages via sms_tool."
+msgstr "Interfejs użytkownika dla sms_tool do obsługi wiadomości SMS."
+
+msgid "Web UI for handling USSD codes via sms_tool."
+msgstr "Interfejs użytkownika dla sms_tool do obsługi kodów USSD."
+
+msgid "SIM card"
+msgstr "Karta SIM"
+
+msgid "Modem memory"
+msgstr "Pamięć modemu"
+
+msgid "Messages store in"
+msgstr "Miejsce przechowywania wiadomości"
+
+msgid "Messages (Inbox / Maximum)"
+msgstr "Wiadomości (Odebrane / Maksymalnie)"
+
+msgid "Delete Selected"
+msgstr "Usuń wybraną"
+
+msgid "Delete message(s)"
+msgstr "Usuń wiadomość(-ci)"
+
+msgid "Delete All"
+msgstr "Usuń wszystkie"
+
+msgid "USSD Codes"
+msgstr "Kody USSD"
+
+msgid "Send to"
+msgstr "Wyślij do"
+
+msgid "Message text"
+msgstr "Treść wiadomości"
+
+msgid "Send Message"
+msgstr "Wyślij SMS"
+
+msgid "Code to send"
+msgstr "Kod do wysłania"
+
+msgid "Send Code"
+msgstr "Wyślij kod"
+
+msgid "SMS Settings"
+msgstr "Ustawienia dla wiadomości SMS"
+
+msgid "USSD Codes Settings"
+msgstr "Ustawienia dla kodów USSD"
+
+msgid "SMS Reading Port"
+msgstr "Port do odczytu SMS"
+
+msgid "SMS Sending Port"
+msgstr "Port do wysyłania SMS"
+
+msgid "USSD Sending Port"
+msgstr "Port do wysyłania kodów USSD"
+
+msgid "Add Prefix to Phone Number"
+msgstr "Dodaj prefiks do numeru telefonu"
+
+msgid "Automatically add prefix to the phone number field."
+msgstr "Automatycznie uzupełnij pole numeru telefonu o prefiks."
+
+msgid "Prefix Number"
+msgstr "Numer prefiks"
+
+msgid "PHONE_NUMBER"
+msgstr "48NumerTelefonu"
+
+msgid "Refresh SMS"
+msgstr "Odśwież"
+
+msgid "Sending USSD Code in plain text"
+msgstr "Wysyłaj kod USSD jawnym tekstem"
+
+msgid "Receive message without PDU decoding"
+msgstr "Odbierz wiadomość bez dekodowania jej jako PDU"
+
+msgid "Send the USSD code in plain text. Command is not being coded to the PDU."
+msgstr "Wysyła kod USSD jawnym tekstem. Polecenie nie jest dekodowane na PDU."
+
+msgid "Receive and display the message without decoding it as a PDU."
+msgstr "Odebrana wiadomość wyświetlana jest bez dekodowania jej jako PDU."
+
+msgid "Explanation of number and prefix"
+msgstr "Wyjaśnienie dot. numeru i prefiksu"
+
+msgid "In the tab for sending SMSes, show an explanation of the prefix and the correct phone number."
+msgstr "W zakładce umożliwiającej wysyłanie SMS-ów pokaż wyjaśnienie dotyczące prefiksu i poprawnego numeru telefonu."
+
+msgid "The phone number should be preceded by the country prefix (for Poland it is 48, without '+'). If the number is 5, 4 or 3 characters, it is treated as 'short' and should not be preceded by a country prefix."
+msgstr "Numer telefonu należy poprzedzić prefiksem kraju (dla Polski jest to 48, bez znaku '+'). Jeżeli numer jest 5, 4 lub 3 znakowy to jest on traktowany jako 'krótki' i nie należy go poprzedzać prefiksem kraju."
+
+msgid "The phone number should be preceded by the country prefix (for Poland it is 48, without +). If the number is 5, 4 or 3 characters, it is treated as.. short and should not be preceded by a country prefix."
+msgstr "Numer telefonu należy poprzedzić prefiksem kraju (dla Polski jest to 48, bez znaku +). Jeżeli numer jest 5, 4 lub 3 znakowy to jest on traktowany jako krótki i nie należy go poprzedzać prefiksem kraju."
+
+msgid "Each line must have the following format: 'Contact name;Phone number'. Save to file '/etc/config/phonebook.user'."
+msgstr "Każda linijka powinna mieć następujący format: 'Nazwa kontaktu;Numer telefonu'. Dla wygody kontakty użytkownika zapisywane są w pliku '/etc/config/phonebook.user'."
+
+msgid "User Phonebook"
+msgstr "Kontakty użytkownika"
+
+msgid "User USSD Codes"
+msgstr "Kody USSD użytkownika"
+
+msgid "Each line must have the following format: 'Code name;Code'. Save to file '/etc/config/ussd.user'."
+msgstr "Każda linijka powinna mieć następujący format: 'Etykieta kodu;Kod'. Dla wygody kody użytkownika zapisywane są w pliku '/etc/config/ussd.user'."
+
+msgid "Notification Settings"
+msgstr "Ustawienia powiadomień"
+
+msgid "The LED informs about a new message. Before activating this function, please config and save the SMS reading port, time to check SMS inbox and select the notification LED."
+msgstr "Powiadomienie diodą o nowej wiadomości. Przed uruchomieniem tej funkcji proszę ustawić port odczytu wiadomości, czas sprawdzania skrzynki odbiorczej oraz wybrać diodę powiadomień."
+
+msgid "Check inbox every minute(s)"
+msgstr "Sprawdzaj skrzynkę odbiorczą co minut(-y)"
+
+msgid "Specify how many minutes you want your inbox to be checked."
+msgstr "Podaj co ile minut ma być sprawdzana skrzynka odbiorcza w poszukiwaniu nowych wiadomości."
+
+msgid "Notification LED"
+msgstr "Dioda powiadomień"
+
+msgid "Select the notification LED."
+msgstr "Wybierz diodę powiadomień."
+
+msgid "Notify new messages"
+msgstr "Powiadomienie o nowych wiadomościach"
+
+msgid "Turn on the LED for seconds(s)"
+msgstr "Włącz diodę na sekund(-y)"
+
+msgid "Turn off the LED for seconds(s)"
+msgstr "Wyłącz diodę na sekund(-y)"
+
+msgid "Specify for how long the LED should be on."
+msgstr "Podaj przez jaki czas dioda ma być włączona."
+
+msgid "Specify for how long the LED should be off."
+msgstr "Podaj przez jaki czas dioda ma być wyłączona."
+
+msgid "Merge split messages"
+msgstr "Połącz podzielone wiadomości"
+
+msgid "Checking this option will make it easier to read the messages, but it will cause a discrepancy in the number of messages shown and received."
+msgstr "Podzielone wiadomości zostaną złączone. Zaznaczenie tej opcji ułatwi czytanie SMS-ów, ale spowoduje niezgodność w ilości pokazanych, odebranych wiadomości."
+
+msgid "Message storage area"
+msgstr "Miejsce przechowywania wiadomości"
+
+msgid "Messages are stored in a specific location (for example, on the SIM card or modem memory), but other areas may also be available depending on the type of device."
+msgstr "Wiadomości przechowywane są w określonym miejscu (np. na karcie SIM lub pamięci modemu), ale w zależności od typu urządzenia mogą być dostępne także inne obszary."
+
+msgid "AT Commands"
+msgstr "Polecenia AT"
+
+msgid "Web UI for handling AT commands via sms_tool."
+msgstr "Interfejs użytkownika dla sms_tool do obsługi poleceń AT."
+
+msgid "Sending commands to modem"
+msgstr "Wysyłanie poleceń do modemu"
+
+msgid "User AT Commands"
+msgstr "Polecenia AT użytkownika"
+
+msgid "Command to send"
+msgstr "Polecenie do wysłania"
+
+msgid "Please enter a AT Command"
+msgstr "Proszę podać polecenie AT do wysłania"
+
+msgid "AT Commands Sending Port"
+msgstr "Port do wysyłania poleceń AT"
+
+msgid "Send Command"
+msgstr "Wyślij polecenie"
+
+msgid "AT Commands Settings"
+msgstr "Ustawienia dla poleceń AT"
+
+msgid "Each line must have the following format: 'AT Command name;AT Command'. Save to file '/etc/config/atcmds.user'."
+msgstr "Każda linijka powinna mieć następujący format: 'Etykieta polecenia;polecenie AT'. Dla wygody polecenia użytkownika zapisywane są w pliku '/etc/config/atcmds.user'."
+
+msgid "Restart the inbox checking process every"
+msgstr "Uruchom proces ponownie po"
+
+msgid "The process will restart at the selected time interval. This will eliminate the delay in checking your inbox."
+msgstr "Proces zostanie uruchomiony ponownie po wybranym przez użytkownika czasie. Pozwoli to wyeliminować opóźnienie w sprawdzaniu skrzynki odbiorczej."
+
+msgid "4h"
+msgstr "4 godz."
+
+msgid "6h"
+msgstr "6 godz."
+
+msgid "8h"
+msgstr "8 godz."
+
+msgid "12h"
+msgstr "12 godz."
+
+msgid "The diode is dedicated only to these notifications"
+msgstr "Dioda jest dedykowana tylko tym powiadomieniom"
+
+msgid "Select 'No' in case the router has only one LED or if the LED is multi-tasking."
+msgstr "Wybierz 'Nie' w przypadku, kiedy router ma tylko jedną diodę lub gdy dioda obsługuje wiele zadań."
+
+msgid "No"
+msgstr "Nie"
+
+msgid "Yes"
+msgstr "Tak"
+
diff --git a/package/wwan/luci-app-sms-tool/po/zh-cn/sms-tool.po b/package/wwan/luci-app-sms-tool/po/zh-cn/sms-tool.po
new file mode 100644
index 000000000..4417c9cbe
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/po/zh-cn/sms-tool.po
@@ -0,0 +1,246 @@
+msgid "Modem"
+msgstr "调制解调器"
+
+msgid "SMS Messages"
+msgstr "SMS 信息"
+
+msgid "Received Messages"
+msgstr "接收信息"
+
+msgid "Send Messages"
+msgstr "发送信息"
+
+msgid "From"
+msgstr "发件人"
+
+msgid "Received"
+msgstr "接收时间"
+
+msgid "Message"
+msgstr "信息"
+
+msgid "Configuration"
+msgstr "参数配置"
+
+msgid "Sample codes"
+msgstr "代码样本"
+
+msgid "Please enter phone number"
+msgstr "请输入手机号"
+
+msgid "Please correct phone number"
+msgstr "请更正手机号"
+
+msgid "Please enter a message text"
+msgstr "请输入信息内容"
+
+msgid "Please select the message(s) to be deleted"
+msgstr "请选择要删除的信息"
+
+msgid "Reply"
+msgstr "回复"
+
+msgid "SMS sent sucessfully"
+msgstr "信息发送成功"
+
+msgid "Delete selected message(s)?"
+msgstr "删除选择的信息?"
+
+msgid "Delete all the messages?"
+msgstr "删除所有信息?"
+
+msgid "Configuration sms-tool"
+msgstr "配置 sms-tool"
+
+msgid "Configuration panel for sms_tool and gui application."
+msgstr "sms_tool 和 gui 应用程序的配置面板."
+
+msgid "Device"
+msgstr "设备"
+
+msgid "Web UI for handling messages via sms_tool."
+msgstr "用于通过 sms_tool 处理信息的 Web UI."
+
+msgid "Web UI for sending messages via sms_tool."
+msgstr "用于通过 sms_tool 发送信息的 Web UI."
+
+msgid "Web UI for receiveling messages via sms_tool."
+msgstr "用于通过 sms_tool 接收信息的 Web UI."
+
+msgid "SIM card"
+msgstr "SIM卡"
+
+msgid "Modem memory"
+msgstr "Modem内存"
+
+msgid "Messages store in"
+msgstr "信息存储在"
+
+msgid "Messages (Inbox / Maximum)"
+msgstr "信息(收件箱/最大)"
+
+msgid "Delete Selected"
+msgstr "删除所选"
+
+msgid "Delete message(s)"
+msgstr "删除信息"
+
+msgid "Delete All"
+msgstr "删除所有"
+
+msgid "Send to"
+msgstr "发送到"
+
+msgid "Message text"
+msgstr "信息文本"
+
+msgid "Send Message"
+msgstr "发送信息"
+
+msgid "Code to send"
+msgstr "代码发送"
+
+msgid "Send Code"
+msgstr "发送代码"
+
+msgid "SMS Settings"
+msgstr "SMS 设置"
+
+msgid "SMS Reading Port"
+msgstr "SMS 读取端口"
+
+msgid "SMS Sending Port"
+msgstr "SMS 发送端口"
+
+msgid "Add Prefix to Phone Number"
+msgstr "为手机号码添加前缀"
+
+msgid "Automatically add prefix to the phone number field."
+msgstr "自动为手机号码字段添加前缀."
+
+msgid "Prefix Number"
+msgstr "前缀编号"
+
+msgid "PHONE_NUMBER"
+msgstr "手机号码"
+
+msgid "Refresh SMS"
+msgstr "刷新信息"
+
+msgid "Explanation of number and prefix"
+msgstr "数字和前缀的解释"
+
+msgid "In the tab for sending SMSes, show an explanation of the prefix and the correct phone number."
+msgstr "在发送短信的选项卡中,显示前缀说明和正确的电话号码."
+
+msgid "The phone number should be preceded by the country prefix (for Poland it is 48, without '+'). If the number is 5, 4 or 3 characters, it is treated as 'short' and should not be preceded by a country prefix."
+msgstr "电话号码前面应有国家/地区前缀(中国为 86,不带“+”), 号码是 5、4 或 3 个字符的短号除外."
+
+msgid "The phone number should be preceded by the country prefix (for Poland it is 48, without +). If the number is 5, 4 or 3 characters, it is treated as.. short and should not be preceded by a country prefix."
+msgstr "电话号码前面应有国家/地区前缀(中国为 86,不带“+”), 号码是 5、4 或 3 个字符的短号除外."
+
+msgid "Each line must have the following format: 'Contact name;Phone number'. Save to file '/etc/config/phonebook.user'."
+msgstr "每行必须具有以下格式:“联系人姓名;电话号码”。 保存到文件“/etc/config/phonebook.user”."
+
+msgid "User Phonebook"
+msgstr "电话簿"
+
+msgid "Notification Settings"
+msgstr "通知设置"
+
+msgid "The LED informs about a new message. Before activating this function, please config and save the SMS reading port, time to check SMS inbox and select the notification LED."
+msgstr "LED 新消息通知。 激活此功能前,请配置并保存短信阅读端口,及时查看短信收件箱并选择通知LED."
+
+msgid "Check inbox every minute(s)"
+msgstr "每分钟检查收件箱"
+
+msgid "Specify how many minutes you want your inbox to be checked."
+msgstr "指定要检查收件箱的分钟数."
+
+msgid "Notification LED"
+msgstr "通知LED"
+
+msgid "Select the notification LED."
+msgstr "选择通知LED."
+
+msgid "Notify new messages"
+msgstr "通知新消息"
+
+msgid "Turn on the LED for seconds(s)"
+msgstr "打开LED秒数"
+
+msgid "Turn off the LED for seconds(s)"
+msgstr "关闭LED秒数"
+
+msgid "Specify for how long the LED should be on."
+msgstr "指定 LED 应亮多长时间."
+
+msgid "Specify for how long the LED should be off."
+msgstr "指定 LED 应关闭多长时间."
+
+msgid "Merge split messages"
+msgstr "合并分割的消息"
+
+msgid "Checking this option will make it easier to read the messages, but it will cause a discrepancy in the number of messages shown and received."
+msgstr "选中此选项将使阅读消息更容易,但会导致显示和接收的消息数量出现差异."
+
+msgid "Message storage area"
+msgstr "消息存储区"
+
+msgid "Messages are stored in a specific location (for example, on the SIM card or modem memory), but other areas may also be available depending on the type of device."
+msgstr "消息存储在特定位置(例如,SIM 卡或调制解调器内存中),但其他区域也可能可用,具体取决于设备类型."
+
+msgid "AT Commands"
+msgstr "AT 命令"
+
+msgid "Web UI for handling AT commands via sms_tool."
+msgstr "用于通过 sms_tool 处理 AT 命令的 Web UI."
+
+msgid "Sending commands to modem"
+msgstr "向调制解调器发送命令"
+
+msgid "User AT Commands"
+msgstr "用户 AT 命令"
+
+msgid "Command to send"
+msgstr "命令发送"
+
+msgid "Please enter a AT Command"
+msgstr "请输入 AT 命令"
+
+msgid "AT Commands Sending Port"
+msgstr "AT命令发送端口"
+
+msgid "Send Command"
+msgstr "发送命令"
+
+msgid "AT Commands Settings"
+msgstr "AT命令设置"
+
+msgid "Each line must have the following format: 'AT Command name;AT Command'. Save to file '/etc/config/atcmds.user'."
+msgstr "每行必须具有以下格式:'AT Command name;AT Command'。 保存到文件“/etc/config/atcmds.user”."
+
+msgid "Restart the inbox checking process every"
+msgstr "每次重新启动收件箱检查过程"
+
+msgid "The process will restart at the selected time interval. This will eliminate the delay in checking your inbox."
+msgstr "该过程将在选定的时间间隔重新开始。 这将消除检查收件箱的延迟."
+
+msgid "4h"
+msgstr "4小时."
+
+msgid "6h"
+msgstr "6小时."
+
+msgid "8h"
+msgstr "8小时."
+
+msgid "12h"
+msgstr "12小时."
+
+msgid "The diode is dedicated only to these notifications"
+msgstr "二极管仅用于这些通知"
+
+msgid "Select 'No' in case the router has only one LED or if the LED is multi-tasking."
+msgstr "如果路由器只有一个 LED 或者 LED 是多任务处理,请选择“否”."
+
diff --git a/package/wwan/luci-app-sms-tool/root/etc/config/atcmds.user b/package/wwan/luci-app-sms-tool/root/etc/config/atcmds.user
new file mode 100644
index 000000000..44bafb90e
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/root/etc/config/atcmds.user
@@ -0,0 +1,28 @@
+------查询------;AT
+查看IMEI;AT+CGSN
+固件版本信息;AT+GMR
+信号强度;AT+CSQ
+正在使用的网络信息;AT+QNWINFO
+查询限速;AT+C5GQOSRDP=1
+模块温度;AT+QTEMP
+查询运营商名称;AT+QSPN
+查询小区信息;AT+QENG="servingcell"
+-----3/4/5G网络配置-----;AT
+查询当前网络搜索模式;AT+QNWPREFCFG="mode_pref"
+!切换仅3G;AT+QNWPREFCFG="mode_pref",WCDMA
+切换仅4G;AT+QNWPREFCFG="mode_pref",LTE
+切换仅5G;AT+QNWPREFCFG="mode_pref",NR5G
+切换5,4G;AT+QNWPREFCFG="mode_pref",NR5G:LTE
+切换AUTO;AT+QNWPREFCFG="mode_pref",AUTO
+-----锁频段-----;AT
+查询支持的所有频段;AT+QNWPREFCFG=?
+查询当前配置的5G频段;AT+QNWPREFCFG="nr5g_band"
+查询当前配置的4G频段;AT+QNWPREFCFG="lte_band"
+!5G锁频段(78);AT+QNWPREFCFG="nr5g_band",78
+5G默认频段;AT+QNWPREFCFG="nr5g_band",1:28:41:77:78:79
+!4G锁频段(1);AT+QNWPREFCFG="lte_band",1
+4G默认频段;AT+QNWPREFCFG="lte_band",1:2:3:5:7:8:20:28:34:38:39:40:41
+-----MORE-----;AT
+查看产品型号;ATI
+挂断现有语音;ATH
+!更改IMEI,需重启;AT+EGMR=1,7,"868227050701486"
\ No newline at end of file
diff --git a/package/wwan/luci-app-sms-tool/root/etc/config/phonebook.user b/package/wwan/luci-app-sms-tool/root/etc/config/phonebook.user
new file mode 100644
index 000000000..74c98ab4d
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/root/etc/config/phonebook.user
@@ -0,0 +1 @@
+other user;8613188888888
diff --git a/package/wwan/luci-app-sms-tool/root/etc/config/sms_tool b/package/wwan/luci-app-sms-tool/root/etc/config/sms_tool
new file mode 100644
index 000000000..7bff8f6d2
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/root/etc/config/sms_tool
@@ -0,0 +1,14 @@
+
+config sms_tool 'general'
+	option pnumber '86'
+	option prefix '1'
+	option ledtimeon '1'
+	option ledtimeoff '5'
+	option lednotify '0'
+	option checktime '10'
+	option mergesms '0'
+	option information '0'
+	option pdu '0'
+    	option storage 'SM'
+    	option prestart '6'
+    	option ledtype 'D'
diff --git a/package/wwan/luci-app-sms-tool/root/etc/init.d/smsled b/package/wwan/luci-app-sms-tool/root/etc/init.d/smsled
new file mode 100755
index 000000000..2442b145e
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/root/etc/init.d/smsled
@@ -0,0 +1,26 @@
+#!/bin/sh /etc/rc.common
+# Copyright 2020 Rafał Wabik (IceG) - From eko.one.pl forum
+# Licensed to the GNU General Public License v3.0.
+
+USE_PROCD=1
+START=99
+STOP=01
+
+start_service() {
+    procd_open_instance
+    procd_set_param command /bin/sh "/sbin/smsled-init.sh"
+    procd_close_instance
+}
+
+stop_service() {
+	for KILLPID in `ps | grep 'smsled' | awk ' { print $1;}'`; do 
+  	(kill -9 $KILLPID >/dev/null 2>&1 )&
+	done
+	sleep 1
+	return 0
+}
+
+restart_service() {
+    stop_service
+    start_service
+}
diff --git a/package/wwan/luci-app-sms-tool/root/etc/uci-defaults/set_sms_ports.sh b/package/wwan/luci-app-sms-tool/root/etc/uci-defaults/set_sms_ports.sh
new file mode 100755
index 000000000..fbc77d705
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/root/etc/uci-defaults/set_sms_ports.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+# Copyright 2020-2021 Rafał Wabik (IceG) - From eko.one.pl forum
+# Licensed to the GNU General Public License v3.0.
+
+work=false
+for port in /dev/ttyUSB*
+do
+    [[ -e $port ]] || continue
+    gcom -d $port info &> /tmp/testusb
+    testUSB=`cat /tmp/testusb | grep "Error\|Can't"`
+    if [ -z "$testUSB" ]; then 
+        work=$port
+        break
+    fi
+done
+rm -rf /tmp/testusb
+
+if [ $work != false ]; then
+uci set sms_tool.@sms_tool[0].readport=$work
+uci set sms_tool.@sms_tool[0].sendport=$work
+uci set sms_tool.@sms_tool[0].atport=$work
+uci commit sms_tool
+fi
diff --git a/package/wwan/luci-app-sms-tool/root/etc/uci-defaults/start-smsled b/package/wwan/luci-app-sms-tool/root/etc/uci-defaults/start-smsled
new file mode 100755
index 000000000..95ca388b7
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/root/etc/uci-defaults/start-smsled
@@ -0,0 +1,7 @@
+#!/bin/sh
+# Copyright 2020 Rafał Wabik (IceG) - From eko.one.pl forum
+# Licensed to the GNU General Public License v3.0.
+
+/etc/init.d/smsled disable
+
+exit 0
diff --git a/package/wwan/luci-app-sms-tool/root/sbin/cronsync.sh b/package/wwan/luci-app-sms-tool/root/sbin/cronsync.sh
new file mode 100755
index 000000000..39f7899c3
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/root/sbin/cronsync.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+[ -e /etc/crontabs/root ] || touch /etc/crontabs/root
+
+SLED=$(uci -q get sms_tool.general.lednotify)
+if [ "x$SLED" != "x1" ]; then
+	if grep -q "smsled" /etc/crontabs/root; then
+		grep -v "/init.d/smsled" /etc/crontabs/root > /tmp/new_cron
+		mv /tmp/new_cron /etc/crontabs/root
+		/etc/init.d/cron restart
+	fi
+	exit 0
+fi
+
+if ! grep -q "smsled" /etc/crontabs/root; then
+PTR=$(uci -q get sms_tool.general.prestart)
+	echo "1 */$PTR * * * /etc/init.d/smsled enable" >> /etc/crontabs/root
+	/etc/init.d/cron restart
+fi
+
+exit 0
diff --git a/package/wwan/luci-app-sms-tool/root/sbin/set_sms_ports.sh b/package/wwan/luci-app-sms-tool/root/sbin/set_sms_ports.sh
new file mode 100755
index 000000000..e89eb1737
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/root/sbin/set_sms_ports.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+# Copyright 2020-2021 Rafał Wabik (IceG) - From eko.one.pl forum
+# Licensed to the GNU General Public License v3.0.
+
+work=false
+for port in /dev/ttyUSB*
+do
+    [[ -e $port ]] || continue
+    gcom -d $port info &> /tmp/testusb
+    testUSB=`cat /tmp/testusb | grep "Error\|Can't"`
+    if [ -z "$testUSB" ]; then 
+        work=$port
+        break
+    fi
+done
+rm -rf /tmp/testusb
+
+if [ $work != false ]; then
+uci set sms_tool.@sms_tool[0].readport=$work
+uci set sms_tool.@sms_tool[0].sendport=$work
+uci set sms_tool.@sms_tool[0].ussdport=$work
+uci set sms_tool.@sms_tool[0].atport=$work
+uci commit sms_tool
+fi
diff --git a/package/wwan/luci-app-sms-tool/root/sbin/smsled-init.sh b/package/wwan/luci-app-sms-tool/root/sbin/smsled-init.sh
new file mode 100755
index 000000000..fa7d86304
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/root/sbin/smsled-init.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+# Copyright 2020 Rafał Wabik (IceG) - From eko.one.pl forum
+# Licensed to the GNU General Public License v3.0.
+
+sleep 10
+CT=$(uci -q get sms_tool.general.checktime)
+TX=$(echo $CT | tr -dc '0-9')
+TM=$(($TX * 60))
+
+while [ 1 ]; do 
+	LED=$(uci -q get sms_tool.general.lednotify)
+	if [ $LED == "1" ]; then
+    	sleep $TM
+		(/sbin/smsled.sh >/dev/null 2>&1 )&
+		continue
+	fi
+	sleep 1
+done
+ 
+exit 0
+
diff --git a/package/wwan/luci-app-sms-tool/root/sbin/smsled.sh b/package/wwan/luci-app-sms-tool/root/sbin/smsled.sh
new file mode 100755
index 000000000..fffd8566c
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/root/sbin/smsled.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+# Copyright 2020-2021 Rafał Wabik (IceG) - From eko.one.pl forum
+# Licensed to the GNU General Public License v3.0.
+
+	DEV=$(uci -q get sms_tool.general.readport)
+	LEDX=$(uci -q get sms_tool.general.smsled)
+	MEM=$(uci -q get sms_tool.general.storage)
+	STX=$(sms_tool -s $MEM -d $DEV status | cut -c23-27)
+	SMS=$(echo $STX | tr -dc '0-9')
+	SMSC=$(cat /etc/config/sms_count)
+	LEDT="/sys/class/leds/$LEDX/trigger"
+	LEDON="/sys/class/leds/$LEDX/delay_on"
+	LEDOFF="/sys/class/leds/$LEDX/delay_off"
+	LED="/sys/class/leds/$LEDX/brightness"
+
+	LON=$(uci -q get sms_tool.general.ledtimeon)
+	TXON=$(echo $LON | tr -dc '0-9')
+	TMON=$(($TXON * 1000))
+
+	LOFF=$(uci -q get sms_tool.general.ledtimeoff)
+	TXOFF=$(echo $LOFF | tr -dc '0-9')
+	TMOFF=$(($TXOFF * 1000))
+
+if [ $SMS == $SMSC ]; then
+
+	exit 0
+fi
+
+if [ $SMS > $SMSC ]; then
+
+echo timer > $LEDT
+echo $TMOFF > $LEDOFF
+echo $TMON > $LEDON
+exit 0
+
+fi
+
+
+exit 0
diff --git a/package/wwan/luci-app-sms-tool/root/usr/share/rpcd/acl.d/luci-app-sms-tool.json b/package/wwan/luci-app-sms-tool/root/usr/share/rpcd/acl.d/luci-app-sms-tool.json
new file mode 100644
index 000000000..3f4e02f78
--- /dev/null
+++ b/package/wwan/luci-app-sms-tool/root/usr/share/rpcd/acl.d/luci-app-sms-tool.json
@@ -0,0 +1,24 @@
+{
+	"luci-app-sms-tool": {
+		"description": "Grant UCI and file access for luci-app-sms-tool",
+		"read": {
+			"file": {
+				"/usr/bin/sms_tool *": [ "exec" ],
+			},
+			"uci": [ "sms_tool" ]
+		},
+		"write": {
+			"file": {
+				"/usr/bin/sms_tool *": [ "exec" ],
+
+			},
+			"uci": [ "sms_tool" ]
+		}
+	}
+}
+
+
+
+
+
+			
diff --git a/package/wwan/meig-cm/Makefile b/package/wwan/meig-cm/Makefile
new file mode 100644
index 000000000..72efee8cc
--- /dev/null
+++ b/package/wwan/meig-cm/Makefile
@@ -0,0 +1,34 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:= meig-cm
+PKG_VERSION:=1.2.1
+PKG_RELEASE:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/meig-cm
+  SECTION:=utils
+  CATEGORY:=Utilities
+  TITLE:=meig-cm app
+endef
+
+define Build/Prepare
+	mkdir -p $(PKG_BUILD_DIR)
+	$(CP) ./src/* $(PKG_BUILD_DIR)/
+endef
+
+define Build/Compile
+	$(MAKE) -C "$(PKG_BUILD_DIR)" \
+		EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
+		CROSS_COMPILE="$(TARGET_CROSS)" \
+		ARCH="$(LINUX_KARCH)" \
+		M="$(PKG_BUILD_DIR)" \
+		CC="$(TARGET_CC)"
+endef
+
+define Package/meig-cm/install
+	$(INSTALL_DIR) $(1)/usr/bin
+	$(INSTALL_BIN) $(PKG_BUILD_DIR)/meig-cm $(1)/usr/bin
+endef
+
+$(eval $(call BuildPackage,meig-cm))
diff --git a/package/wwan/meig-cm/src/GobiNetCM.c b/package/wwan/meig-cm/src/GobiNetCM.c
new file mode 100644
index 000000000..0dbbcbbda
--- /dev/null
+++ b/package/wwan/meig-cm/src/GobiNetCM.c
@@ -0,0 +1,229 @@
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "QMIThread.h"
+
+#ifdef CONFIG_GOBINET
+
+// IOCTL to generate a client ID for this service type
+#define IOCTL_QMI_GET_SERVICE_FILE 0x8BE0 + 1
+
+// IOCTL to get the VIDPID of the device
+#define IOCTL_QMI_GET_DEVICE_VIDPID 0x8BE0 + 2
+
+// IOCTL to get the MEID of the device
+#define IOCTL_QMI_GET_DEVICE_MEID 0x8BE0 + 3
+
+static int GobiNetSendQMI(PQCQMIMSG pRequest) {
+    int ret, fd;
+
+    fd = qmiclientId[pRequest->QMIHdr.QMIType];
+
+    if (fd <= 0) {
+        dbg_time("%s QMIType: %d has no clientID", __func__, pRequest->QMIHdr.QMIType);
+        return -ENODEV;
+    }
+
+    // Always ready to write
+    if (1 == 1) {
+        ssize_t nwrites = le16_to_cpu(pRequest->QMIHdr.Length) + 1 - sizeof(QCQMI_HDR);
+        ret = write(fd, &pRequest->MUXMsg, nwrites);
+        if (ret == nwrites) {
+            ret = 0;
+        } else {
+            dbg_time("%s write=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
+        }
+    } else {
+        dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
+    }
+
+    return ret;
+}
+
+static int GobiNetGetClientID(const char *qcqmi, UCHAR QMIType) {
+    int ClientId;
+    ClientId = open(qcqmi, O_RDWR | O_NONBLOCK | O_NOCTTY);
+    if (ClientId == -1) {
+        dbg_time("failed to open %s, errno: %d (%s)", qcqmi, errno, strerror(errno));
+        return -1;
+    }
+
+    if (ioctl(ClientId, IOCTL_QMI_GET_SERVICE_FILE, QMIType) != 0) {
+        dbg_time("failed to get ClientID for 0x%02x errno: %d (%s)", QMIType, errno, strerror(errno));
+        close(ClientId);
+        ClientId = 0;
+    }
+
+    switch (QMIType) {
+        case QMUX_TYPE_WDS: dbg_time("Get clientWDS = %d", ClientId); break;
+        case QMUX_TYPE_DMS: dbg_time("Get clientDMS = %d", ClientId); break;
+        case QMUX_TYPE_NAS: dbg_time("Get clientNAS = %d", ClientId); break;
+        case QMUX_TYPE_QOS: dbg_time("Get clientQOS = %d", ClientId); break;
+        case QMUX_TYPE_WMS: dbg_time("Get clientWMS = %d", ClientId); break;
+        case QMUX_TYPE_PDS: dbg_time("Get clientPDS = %d", ClientId); break;
+        case QMUX_TYPE_UIM: dbg_time("Get clientUIM = %d", ClientId); break;
+        case QMUX_TYPE_WDS_ADMIN: dbg_time("Get clientWDA = %d", ClientId);
+        break;
+        default: break;
+    }
+
+    return ClientId;
+}
+
+static int GobiNetDeInit(void) {
+    unsigned int i;
+    for (i = 0; i < sizeof(qmiclientId)/sizeof(qmiclientId[0]); i++)
+    {
+        if (qmiclientId[i] != 0)
+        {
+            close(qmiclientId[i]);
+            qmiclientId[i] = 0;
+        }
+    }
+
+    return 0;
+}
+
+static void * GobiNetThread(void *pData) {
+    PROFILE_T *profile = (PROFILE_T *)pData;
+    const char *qcqmi = (const char *)profile->qmichannel;
+    int wait_for_request_quit = 0;   
+    
+    qmiclientId[QMUX_TYPE_WDS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS);
+    if (profile->enable_ipv6 || profile->IsDualIPSupported)
+        qmiclientId[QMUX_TYPE_WDS_IPV6] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS);
+    qmiclientId[QMUX_TYPE_DMS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_DMS);
+    qmiclientId[QMUX_TYPE_NAS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_NAS);
+    qmiclientId[QMUX_TYPE_UIM] = GobiNetGetClientID(qcqmi, QMUX_TYPE_UIM);
+    if (profile->qmap_mode == 0) //when QMAP enabled, set data format in GobiNet Driver
+        qmiclientId[QMUX_TYPE_WDS_ADMIN] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS_ADMIN);
+
+    //donot check clientWDA, there is only one client for WDA, if meig-cm is killed by SIGKILL, i cannot get client ID for WDA again!
+    if (qmiclientId[QMUX_TYPE_WDS] == 0)  /*|| (clientWDA == -1)*/ {
+        GobiNetDeInit();
+        dbg_time("%s Failed to open %s, errno: %d (%s)", __func__, qcqmi, errno, strerror(errno));
+        qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
+        pthread_exit(NULL);
+        return NULL;
+    }
+
+    qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED);
+
+    while (1) {
+        struct pollfd pollfds[16] = {{qmidevice_control_fd[1], POLLIN, 0}};
+        int ne, ret, nevents = 1;
+        unsigned int i;
+
+        for (i = 0; i < sizeof(qmiclientId)/sizeof(qmiclientId[0]); i++)
+        {
+            if (qmiclientId[i] != 0)
+            {
+                pollfds[nevents].fd = qmiclientId[i];
+                pollfds[nevents].events = POLLIN;
+                pollfds[nevents].revents = 0;
+                nevents++;
+            }
+        }
+
+        do {
+            ret = poll(pollfds, nevents, wait_for_request_quit ? 1000: -1);
+         } while ((ret < 0) && (errno == EINTR));
+
+	if (ret == 0 && wait_for_request_quit) {
+    		QmiThreadRecvQMI(NULL); //main thread may pending on QmiThreadSendQMI()
+		continue;
+	}
+
+        if (ret <= 0) {
+            dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
+            break;
+        }
+
+        for (ne = 0; ne < nevents; ne++) {
+            int fd = pollfds[ne].fd;
+            short revents = pollfds[ne].revents;
+
+            if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
+                dbg_time("%s poll err/hup/inval", __func__);
+                dbg_time("epoll fd = %d, events = 0x%04x", fd, revents);
+                if (fd == qmidevice_control_fd[1]) {
+                } else {
+                }
+                if (revents & (POLLERR | POLLHUP | POLLNVAL))
+                    goto __GobiNetThread_quit;
+            }
+
+            if ((revents & POLLIN) == 0)
+                continue;
+
+            if (fd == qmidevice_control_fd[1]) {
+                int triger_event;
+                if (read(fd, &triger_event, sizeof(triger_event)) == sizeof(triger_event)) {
+                    //DBG("triger_event = 0x%x", triger_event);
+                    switch (triger_event) {
+                        case RIL_REQUEST_QUIT:
+                            goto __GobiNetThread_quit;
+                        break;
+                        case SIGTERM:
+                        case SIGHUP:
+                        case SIGINT:
+                            wait_for_request_quit = 1;   
+                        break;
+                        default:
+                        break;
+                    }
+                }
+                continue;
+            }
+
+            {
+                ssize_t nreads;
+                UCHAR QMIBuf[512];
+                PQCQMIMSG pResponse = (PQCQMIMSG)QMIBuf;
+
+                nreads = read(fd, &pResponse->MUXMsg, sizeof(QMIBuf) - sizeof(QCQMI_HDR));
+                if (nreads <= 0)
+                {
+                    dbg_time("%s read=%d errno: %d (%s)",  __func__, (int)nreads, errno, strerror(errno));
+                    break;
+                }
+
+                for (i = 0; i < sizeof(qmiclientId)/sizeof(qmiclientId[0]); i++)
+                {
+                    if (qmiclientId[i] == fd)
+                    {
+                        pResponse->QMIHdr.QMIType = i;
+                    }
+                }
+
+                pResponse->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI;
+                pResponse->QMIHdr.Length = cpu_to_le16(nreads + sizeof(QCQMI_HDR)  - 1);
+                pResponse->QMIHdr.CtlFlags = 0x00;
+                pResponse->QMIHdr.ClientId = fd & 0xFF;
+
+                QmiThreadRecvQMI(pResponse);
+            }
+        }
+    }
+
+__GobiNetThread_quit:
+    GobiNetDeInit();
+    qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
+    QmiThreadRecvQMI(NULL); //main thread may pending on QmiThreadSendQMI()
+    dbg_time("%s exit", __func__);
+    pthread_exit(NULL);
+    return NULL;
+}
+
+#else
+static int GobiNetSendQMI(PQCQMIMSG pRequest) {return -1;}
+static void * GobiNetThread(void *pData) {dbg_time("please set CONFIG_GOBINET"); return NULL;}
+#endif
+
+const struct qmi_device_ops gobi_qmidev_ops = {
+	.deinit = GobiNetDeInit,
+	.send = GobiNetSendQMI,
+	.read = GobiNetThread,
+};
\ No newline at end of file
diff --git a/package/wwan/meig-cm/src/MPQCTL.h b/package/wwan/meig-cm/src/MPQCTL.h
new file mode 100644
index 000000000..046906557
--- /dev/null
+++ b/package/wwan/meig-cm/src/MPQCTL.h
@@ -0,0 +1,377 @@
+/*===========================================================================
+
+                            M P Q C T L. H
+DESCRIPTION:
+
+    This module contains QMI QCTL module.
+
+INITIALIZATION AND SEQUENCING REQUIREMENTS:
+
+Copyright (C) 2011 by Qualcomm Technologies, Incorporated.  All Rights Reserved.
+===========================================================================*/
+
+#ifndef MPQCTL_H
+#define MPQCTL_H
+
+#include "MPQMI.h"
+
+#pragma pack(push, 1)
+
+// ================= QMICTL ==================
+
+// QMICTL Control Flags
+#define QMICTL_CTL_FLAG_CMD     0x00
+#define QMICTL_CTL_FLAG_RSP     0x01
+#define QMICTL_CTL_FLAG_IND     0x02
+
+#if 0
+typedef struct _QMICTL_TRANSACTION_ITEM
+{
+   LIST_ENTRY  List;
+   UCHAR       TransactionId;  // QMICTL transaction id
+   PVOID       Context;        // Adapter or IocDev
+   PIRP        Irp;
+} QMICTL_TRANSACTION_ITEM, *PQMICTL_TRANSACTION_ITEM;
+#endif
+
+typedef struct _QCQMICTL_MSG_HDR
+{
+   UCHAR  CtlFlags;  // 00-cmd, 01-rsp, 10-ind
+   UCHAR  TransactionId;
+   USHORT QMICTLType;
+   USHORT Length;
+} __attribute__ ((packed)) QCQMICTL_MSG_HDR, *PQCQMICTL_MSG_HDR;
+
+#define QCQMICTL_MSG_HDR_SIZE sizeof(QCQMICTL_MSG_HDR)
+
+typedef struct _QCQMICTL_MSG_HDR_RESP
+{
+   UCHAR  CtlFlags;  // 00-cmd, 01-rsp, 10-ind
+   UCHAR  TransactionId;
+   USHORT QMICTLType;
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+} __attribute__ ((packed)) QCQMICTL_MSG_HDR_RESP, *PQCQMICTL_MSG_HDR_RESP;
+
+typedef struct _QCQMICTL_MSG
+{
+   UCHAR  CtlFlags;  // 00-cmd, 01-rsp, 10-ind
+   UCHAR  TransactionId;
+   USHORT QMICTLType;
+   USHORT Length;
+   UCHAR  Payload;
+} __attribute__ ((packed)) QCQMICTL_MSG, *PQCQMICTL_MSG;
+
+// TLV Header
+typedef struct _QCQMICTL_TLV_HDR
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+} __attribute__ ((packed)) QCQMICTL_TLV_HDR, *PQCQMICTL_TLV_HDR;
+
+#define QCQMICTL_TLV_HDR_SIZE sizeof(QCQMICTL_TLV_HDR)
+
+// QMICTL Type
+#define QMICTL_SET_INSTANCE_ID_REQ    0x0020
+#define QMICTL_SET_INSTANCE_ID_RESP   0x0020
+#define QMICTL_GET_VERSION_REQ        0x0021
+#define QMICTL_GET_VERSION_RESP       0x0021
+#define QMICTL_GET_CLIENT_ID_REQ      0x0022
+#define QMICTL_GET_CLIENT_ID_RESP     0x0022
+#define QMICTL_RELEASE_CLIENT_ID_REQ  0x0023
+#define QMICTL_RELEASE_CLIENT_ID_RESP 0x0023
+#define QMICTL_REVOKE_CLIENT_ID_IND   0x0024
+#define QMICTL_INVALID_CLIENT_ID_IND  0x0025
+#define QMICTL_SET_DATA_FORMAT_REQ    0x0026
+#define QMICTL_SET_DATA_FORMAT_RESP   0x0026
+#define QMICTL_SYNC_REQ               0x0027
+#define QMICTL_SYNC_RESP              0x0027
+#define QMICTL_SYNC_IND               0x0027
+
+#define QMICTL_FLAG_REQUEST    0x00
+#define QMICTL_FLAG_RESPONSE   0x01
+#define QMICTL_FLAG_INDICATION 0x02
+
+// QMICTL Message Definitions
+
+typedef struct _QMICTL_SET_INSTANCE_ID_REQ_MSG
+{
+   UCHAR  CtlFlags;        // QMICTL_FLAG_REQUEST
+   UCHAR  TransactionId;
+   USHORT QMICTLType;      // QMICTL_SET_INSTANCE_ID_REQ
+   USHORT Length;          // 4
+   UCHAR  TLVType;         // QCTLV_TYPE_REQUIRED_PARAMETER
+   USHORT TLVLength;       // 1
+   UCHAR  Value;           // Host-unique QMI instance for this device driver
+} __attribute__ ((packed)) QMICTL_SET_INSTANCE_ID_REQ_MSG, *PQMICTL_SET_INSTANCE_ID_REQ_MSG;
+
+typedef struct _QMICTL_SET_INSTANCE_ID_RESP_MSG
+{
+   UCHAR  CtlFlags;        // QMICTL_FLAG_RESPONSE
+   UCHAR  TransactionId;
+   USHORT QMICTLType;      // QMICTL_SET_INSTANCE_ID_RESP
+   USHORT Length;
+   UCHAR  TLVType;         // QCTLV_TYPE_RESULT_CODE
+   USHORT TLVLength;       // 0x0004
+   USHORT QMIResult;
+   USHORT QMIError;
+   UCHAR  TLV2Type;        // QCTLV_TYPE_REQUIRED_PARAMETER
+   USHORT TLV2Length;      // 0x0002
+   USHORT QMI_ID;          // Upper byte is assigned by MSM,
+                           // lower assigned by host
+} __attribute__ ((packed)) QMICTL_SET_INSTANCE_ID_RESP_MSG, *PQMICTL_SET_INSTANCE_ID_RESP_MSG;
+
+typedef struct _QMICTL_GET_VERSION_REQ_MSG
+{
+   UCHAR  CtlFlags;        // QMICTL_FLAG_REQUEST
+   UCHAR  TransactionId;
+   USHORT QMICTLType;      // QMICTL_GET_VERSION_REQ
+   USHORT Length;          // 0
+   UCHAR  TLVType;         // QCTLV_TYPE_REQUIRED_PARAMETER
+   USHORT TLVLength;       // var
+   UCHAR  QMUXTypes;       // List of one byte QMUX_TYPE values
+                           // 0xFF returns a list of versions for all
+                           // QMUX_TYPEs implemented on the device
+} __attribute__ ((packed)) QMICTL_GET_VERSION_REQ_MSG, *PQMICTL_GET_VERSION_REQ_MSG;
+
+typedef struct _QMUX_TYPE_VERSION_STRUCT
+{
+   UCHAR  QMUXType;
+   USHORT MajorVersion;
+   USHORT MinorVersion;
+} __attribute__ ((packed)) QMUX_TYPE_VERSION_STRUCT, *PQMUX_TYPE_VERSION_STRUCT;
+
+typedef struct _ADDENDUM_VERSION_PREAMBLE
+{
+   UCHAR LabelLength;
+   UCHAR Label;
+} __attribute__ ((packed)) ADDENDUM_VERSION_PREAMBLE, *PADDENDUM_VERSION_PREAMBLE;
+
+#define QMICTL_GET_VERSION_RSP_TLV_TYPE_VERSION     0x01
+#define QMICTL_GET_VERSION_RSP_TLV_TYPE_ADD_VERSION 0x10
+
+typedef struct _QMICTL_GET_VERSION_RESP_MSG
+{
+   UCHAR  CtlFlags;        // QMICTL_FLAG_RESPONSE
+   UCHAR  TransactionId;
+   USHORT QMICTLType;      // QMICTL_GET_VERSION_RESP
+   USHORT Length;
+   UCHAR  TLVType;         // QCTLV_TYPE_RESULT_CODE
+   USHORT TLVLength;       // 0x0004
+   USHORT QMIResult;
+   USHORT QMIError;
+   UCHAR  TLV2Type;        // QCTLV_TYPE_REQUIRED_PARAMETER
+   USHORT TLV2Length;      // var
+   UCHAR  NumElements;     // Num of QMUX_TYPE_VERSION_STRUCT
+   QMUX_TYPE_VERSION_STRUCT TypeVersion[0];
+} __attribute__ ((packed)) QMICTL_GET_VERSION_RESP_MSG, *PQMICTL_GET_VERSION_RESP_MSG;
+
+typedef struct _QMICTL_GET_CLIENT_ID_REQ_MSG
+{
+   UCHAR  CtlFlags;        // QMICTL_FLAG_REQUEST
+   UCHAR  TransactionId;
+   USHORT QMICTLType;      // QMICTL_GET_CLIENT_ID_REQ
+   USHORT Length;
+   UCHAR  TLVType;         // QCTLV_TYPE_REQUIRED_PARAMETER
+   USHORT TLVLength;       // 1
+   UCHAR  QMIType;         // QMUX type
+} __attribute__ ((packed)) QMICTL_GET_CLIENT_ID_REQ_MSG, *PQMICTL_GET_CLIENT_ID_REQ_MSG;
+
+typedef struct _QMICTL_GET_CLIENT_ID_RESP_MSG
+{
+   UCHAR  CtlFlags;        // QMICTL_FLAG_RESPONSE
+   UCHAR  TransactionId;
+   USHORT QMICTLType;      // QMICTL_GET_CLIENT_ID_RESP
+   USHORT Length;
+   UCHAR  TLVType;         // QCTLV_TYPE_RESULT_CODE
+   USHORT TLVLength;       // 0x0004
+   USHORT QMIResult;       // result code
+   USHORT QMIError;        // error code
+   UCHAR  TLV2Type;        // QCTLV_TYPE_REQUIRED_PARAMETER
+   USHORT TLV2Length;      // 2
+   UCHAR  QMIType;
+   UCHAR  ClientId;
+} __attribute__ ((packed)) QMICTL_GET_CLIENT_ID_RESP_MSG, *PQMICTL_GET_CLIENT_ID_RESP_MSG;
+
+typedef struct _QMICTL_RELEASE_CLIENT_ID_REQ_MSG
+{
+   UCHAR  CtlFlags;        // QMICTL_FLAG_REQUEST
+   UCHAR  TransactionId;
+   USHORT QMICTLType;      // QMICTL_RELEASE_CLIENT_ID_REQ
+   USHORT Length;
+   UCHAR  TLVType;         // QCTLV_TYPE_REQUIRED_PARAMETER
+   USHORT TLVLength;       // 0x0002
+   UCHAR  QMIType;
+   UCHAR  ClientId;
+} __attribute__ ((packed)) QMICTL_RELEASE_CLIENT_ID_REQ_MSG, *PQMICTL_RELEASE_CLIENT_ID_REQ_MSG;
+
+typedef struct _QMICTL_RELEASE_CLIENT_ID_RESP_MSG
+{
+   UCHAR  CtlFlags;        // QMICTL_FLAG_RESPONSE
+   UCHAR  TransactionId;
+   USHORT QMICTLType;      // QMICTL_RELEASE_CLIENT_ID_RESP
+   USHORT Length;
+   UCHAR  TLVType;         // QCTLV_TYPE_RESULT_CODE
+   USHORT TLVLength;       // 0x0004
+   USHORT QMIResult;       // result code
+   USHORT QMIError;        // error code
+   UCHAR  TLV2Type;        // QCTLV_TYPE_REQUIRED_PARAMETER
+   USHORT TLV2Length;      // 2
+   UCHAR  QMIType;
+   UCHAR  ClientId;
+} __attribute__ ((packed)) QMICTL_RELEASE_CLIENT_ID_RESP_MSG, *PQMICTL_RELEASE_CLIENT_ID_RESP_MSG;
+
+typedef struct _QMICTL_REVOKE_CLIENT_ID_IND_MSG
+{
+   UCHAR  CtlFlags;        // QMICTL_FLAG_INDICATION
+   UCHAR  TransactionId;
+   USHORT QMICTLType;      // QMICTL_REVOKE_CLIENT_ID_IND
+   USHORT Length;
+   UCHAR  TLVType;         // QCTLV_TYPE_REQUIRED_PARAMETER
+   USHORT TLVLength;       // 0x0002
+   UCHAR  QMIType;
+   UCHAR  ClientId;
+} __attribute__ ((packed)) QMICTL_REVOKE_CLIENT_ID_IND_MSG, *PQMICTL_REVOKE_CLIENT_ID_IND_MSG;
+
+typedef struct _QMICTL_INVALID_CLIENT_ID_IND_MSG
+{
+   UCHAR  CtlFlags;        // QMICTL_FLAG_INDICATION
+   UCHAR  TransactionId;
+   USHORT QMICTLType;      // QMICTL_REVOKE_CLIENT_ID_IND
+   USHORT Length;
+   UCHAR  TLVType;         // QCTLV_TYPE_REQUIRED_PARAMETER
+   USHORT TLVLength;       // 0x0002
+   UCHAR  QMIType;
+   UCHAR  ClientId;
+} __attribute__ ((packed)) QMICTL_INVALID_CLIENT_ID_IND_MSG, *PQMICTL_INVALID_CLIENT_ID_IND_MSG;
+
+typedef struct _QMICTL_SET_DATA_FORMAT_REQ_MSG
+{
+   UCHAR  CtlFlags;        // QMICTL_FLAG_REQUEST
+   UCHAR  TransactionId;
+   USHORT QMICTLType;      // QMICTL_SET_DATA_FORMAT_REQ
+   USHORT Length;
+   UCHAR  TLVType;         // QCTLV_TYPE_REQUIRED_PARAMETER
+   USHORT TLVLength;       // 1
+   UCHAR  DataFormat;      // 0-default; 1-QoS hdr present
+} __attribute__ ((packed)) QMICTL_SET_DATA_FORMAT_REQ_MSG, *PQMICTL_SET_DATA_FORMAT_REQ_MSG;
+
+#ifdef QC_IP_MODE
+#define SET_DATA_FORMAT_TLV_TYPE_LINK_PROTO 0x10
+#define SET_DATA_FORMAT_LINK_PROTO_ETH      0x0001
+#define SET_DATA_FORMAT_LINK_PROTO_IP       0x0002
+typedef struct _QMICTL_SET_DATA_FORMAT_TLV_LINK_PROT
+{
+   UCHAR  TLVType;         // Link-Layer Protocol
+   USHORT TLVLength;       // 2
+   USHORT LinkProt;        // 0x1: ETH; 0x2: IP
+} QMICTL_SET_DATA_FORMAT_TLV_LINK_PROT, *PQMICTL_SET_DATA_FORMAT_TLV_LINK_PROT;
+
+#ifdef QCMP_UL_TLP
+#define SET_DATA_FORMAT_TLV_TYPE_UL_TLP 0x11
+typedef struct _QMICTL_SET_DATA_FORMAT_TLV_UL_TLP
+{
+   UCHAR  TLVType;         // 0x11, Uplink TLP Setting
+   USHORT TLVLength;       // 1
+   UCHAR  UlTlpSetting;    // 0x0: Disable; 0x01: Enable
+} QMICTL_SET_DATA_FORMAT_TLV_UL_TLP, *PQMICTL_SET_DATA_FORMAT_TLV_UL_TLP;
+#endif // QCMP_UL_TLP
+
+#ifdef QCMP_DL_TLP
+#define SET_DATA_FORMAT_TLV_TYPE_DL_TLP 0x13
+typedef struct _QMICTL_SET_DATA_FORMAT_TLV_DL_TLP
+{
+   UCHAR  TLVType;         // 0x11, Uplink TLP Setting
+   USHORT TLVLength;       // 1
+   UCHAR  DlTlpSetting;    // 0x0: Disable; 0x01: Enable
+} QMICTL_SET_DATA_FORMAT_TLV_DL_TLP, *PQMICTL_SET_DATA_FORMAT_TLV_DL_TLP;
+#endif // QCMP_DL_TLP
+
+#endif // QC_IP_MODE
+
+#ifdef MP_QCQOS_ENABLED
+#define SET_DATA_FORMAT_TLV_TYPE_QOS_SETTING 0x12
+typedef struct _QMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING
+{
+   UCHAR  TLVType;       // 0x12, QoS setting
+   USHORT TLVLength;     // 1
+   UCHAR  QosSetting;    // 0x0: Disable; 0x01: Enable
+} QMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING, *PQMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING;
+#endif // MP_QCQOS_ENABLED
+
+typedef struct _QMICTL_SET_DATA_FORMAT_RESP_MSG
+{
+   UCHAR  CtlFlags;        // QMICTL_FLAG_RESPONSE
+   UCHAR  TransactionId;
+   USHORT QMICTLType;      // QMICTL_SET_DATA_FORMAT_RESP
+   USHORT Length;
+   UCHAR  TLVType;         // QCTLV_TYPE_RESULT_CODE
+   USHORT TLVLength;       // 0x0004
+   USHORT QMIResult;       // result code
+   USHORT QMIError;        // error code
+} __attribute__ ((packed)) QMICTL_SET_DATA_FORMAT_RESP_MSG, *PQMICTL_SET_DATA_FORMAT_RESP_MSG;
+
+typedef struct _QMICTL_SYNC_REQ_MSG
+{
+   UCHAR  CtlFlags;        // QMICTL_FLAG_REQUEST
+   UCHAR  TransactionId;
+   USHORT QMICTLType;      // QMICTL_CTL_SYNC_REQ
+   USHORT Length;          // 0
+} __attribute__ ((packed)) QMICTL_SYNC_REQ_MSG, *PQMICTL_SYNC_REQ_MSG;
+
+typedef struct _QMICTL_SYNC_RESP_MSG
+{
+   UCHAR  CtlFlags;        // QMICTL_FLAG_RESPONSE
+   UCHAR  TransactionId;
+   USHORT QMICTLType;      // QMICTL_CTL_SYNC_RESP
+   USHORT Length;
+   UCHAR  TLVType;         // QCTLV_TYPE_RESULT_CODE
+   USHORT TLVLength;       // 0x0004
+   USHORT QMIResult;
+   USHORT QMIError;
+} __attribute__ ((packed)) QMICTL_SYNC_RESP_MSG, *PQMICTL_SYNC_RESP_MSG;
+
+typedef struct _QMICTL_SYNC_IND_MSG
+{
+   UCHAR  CtlFlags;        // QMICTL_FLAG_INDICATION
+   UCHAR  TransactionId;
+   USHORT QMICTLType;      // QMICTL_REVOKE_CLIENT_ID_IND
+   USHORT Length;
+} __attribute__ ((packed)) QMICTL_SYNC_IND_MSG, *PQMICTL_SYNC_IND_MSG;
+
+typedef struct _QMICTL_MSG
+{
+   union
+   {
+      // Message Header
+      QCQMICTL_MSG_HDR                             QMICTLMsgHdr;
+      QCQMICTL_MSG_HDR_RESP                             QMICTLMsgHdrRsp;
+
+      // QMICTL Message
+      QMICTL_SET_INSTANCE_ID_REQ_MSG               SetInstanceIdReq;
+      QMICTL_SET_INSTANCE_ID_RESP_MSG              SetInstanceIdRsp;
+      QMICTL_GET_VERSION_REQ_MSG                   GetVersionReq;
+      QMICTL_GET_VERSION_RESP_MSG                  GetVersionRsp;
+      QMICTL_GET_CLIENT_ID_REQ_MSG                 GetClientIdReq;
+      QMICTL_GET_CLIENT_ID_RESP_MSG                GetClientIdRsp;
+      QMICTL_RELEASE_CLIENT_ID_REQ_MSG             ReleaseClientIdReq;
+      QMICTL_RELEASE_CLIENT_ID_RESP_MSG            ReleaseClientIdRsp;
+      QMICTL_REVOKE_CLIENT_ID_IND_MSG              RevokeClientIdInd;
+      QMICTL_INVALID_CLIENT_ID_IND_MSG             InvalidClientIdInd;
+      QMICTL_SET_DATA_FORMAT_REQ_MSG               SetDataFormatReq;
+      QMICTL_SET_DATA_FORMAT_RESP_MSG              SetDataFormatRsp;
+      QMICTL_SYNC_REQ_MSG                          SyncReq;
+      QMICTL_SYNC_RESP_MSG                         SyncRsp;
+      QMICTL_SYNC_IND_MSG                          SyncInd;
+   };
+} __attribute__ ((packed)) QMICTL_MSG, *PQMICTL_MSG;
+#pragma pack(pop)
+
+#endif // MPQCTL_H
diff --git a/package/wwan/meig-cm/src/MPQMI.h b/package/wwan/meig-cm/src/MPQMI.h
new file mode 100644
index 000000000..64ae390e5
--- /dev/null
+++ b/package/wwan/meig-cm/src/MPQMI.h
@@ -0,0 +1,301 @@
+/*===========================================================================
+
+                            M P Q M I. H
+DESCRIPTION:
+
+    This module contains forward references to the QMI module.
+
+INITIALIZATION AND SEQUENCING REQUIREMENTS:
+
+Copyright (C) 2011 by Qualcomm Technologies, Incorporated.  All Rights Reserved.
+===========================================================================*/
+/*===========================================================================
+
+                            EDIT HISTORY FOR FILE
+  $Header: //depot/QMI/win/qcdrivers/ndis/MPQMI.h#3 $
+
+when        who    what, where, why
+--------    ---    ----------------------------------------------------------
+11/20/04    hg     Initial version.
+===========================================================================*/
+
+#ifndef USBQMI_H
+#define USBQMI_H
+
+typedef char    CHAR;
+typedef unsigned char   UCHAR;
+typedef unsigned short   USHORT;
+typedef int   INT;
+typedef unsigned int   UINT;
+typedef long    LONG;
+typedef unsigned int   ULONG;
+typedef unsigned long long   ULONG64;
+typedef char   *PCHAR;
+typedef unsigned char   *PUCHAR;
+typedef int   *PINT;
+typedef int   BOOL;
+
+#define TRUE (1 == 1)
+#define FALSE (1 != 1)
+
+#define QMICTL_SUPPORTED_MAJOR_VERSION 1
+#define QMICTL_SUPPORTED_MINOR_VERSION 0
+
+#pragma pack(push, 1)
+
+// ========= USB Control Message ==========
+
+#define USB_CTL_MSG_TYPE_QMI 0x01
+
+// USB Control Message
+typedef struct _QCUSB_CTL_MSG_HDR
+{
+   UCHAR IFType;
+} __attribute__ ((packed)) QCUSB_CTL_MSG_HDR, *PQCUSB_CTL_MSG_HDR;
+
+#define QCUSB_CTL_MSG_HDR_SIZE sizeof(QCUSB_CTL_MSG_HDR)
+
+typedef struct _QCUSB_CTL_MSG
+{
+   UCHAR IFType;
+   UCHAR Message;
+} __attribute__ ((packed)) QCUSB_CTL_MSG, *PQCUSB_CTL_MSG;
+
+#define QCTLV_TYPE_REQUIRED_PARAMETER 0x01
+#define QCTLV_TYPE_RESULT_CODE        0x02
+
+// ================= QMI ==================
+
+// Define QMI Type
+typedef enum _QMI_SERVICE_TYPE
+{
+   QMUX_TYPE_CTL  = 0x00,
+   QMUX_TYPE_WDS  = 0x01,
+   QMUX_TYPE_DMS  = 0x02,
+   QMUX_TYPE_NAS  = 0x03,
+   QMUX_TYPE_QOS  = 0x04,
+   QMUX_TYPE_WMS  = 0x05,
+   QMUX_TYPE_PDS  = 0x06,
+   QMUX_TYPE_UIM  = 0x0B,
+   QMUX_TYPE_WDS_IPV6  = 0x11,
+   QMUX_TYPE_WDS_ADMIN  = 0x1A,
+   QMUX_TYPE_MAX  = 0xFF,
+   QMUX_TYPE_ALL  = 0xFF
+} QMI_SERVICE_TYPE;
+
+typedef enum _QMI_RESULT_CODE_TYPE
+{
+   QMI_RESULT_SUCCESS = 0x0000,
+   QMI_RESULT_FAILURE = 0x0001
+} QMI_RESULT_CODE_TYPE;
+
+typedef enum _QMI_ERROR_CODE_TYPE
+{
+    QMI_ERR_NONE  = 0x0000
+    ,QMI_ERR_MALFORMED_MSG  = 0x0001
+    ,QMI_ERR_NO_MEMORY  = 0x0002
+    ,QMI_ERR_INTERNAL  = 0x0003
+    ,QMI_ERR_ABORTED  = 0x0004
+    ,QMI_ERR_CLIENT_IDS_EXHAUSTED  = 0x0005
+    ,QMI_ERR_UNABORTABLE_TRANSACTION  = 0x0006
+    ,QMI_ERR_INVALID_CLIENT_ID  = 0x0007
+    ,QMI_ERR_NO_THRESHOLDS  = 0x0008
+    ,QMI_ERR_INVALID_HANDLE  = 0x0009
+    ,QMI_ERR_INVALID_PROFILE  = 0x000A
+    ,QMI_ERR_INVALID_PINID  = 0x000B
+    ,QMI_ERR_INCORRECT_PIN  = 0x000C
+    ,QMI_ERR_NO_NETWORK_FOUND  = 0x000D
+    ,QMI_ERR_CALL_FAILED  = 0x000E
+    ,QMI_ERR_OUT_OF_CALL  = 0x000F
+    ,QMI_ERR_NOT_PROVISIONED  = 0x0010
+    ,QMI_ERR_MISSING_ARG  = 0x0011
+    ,QMI_ERR_ARG_TOO_LONG  = 0x0013
+    ,QMI_ERR_INVALID_TX_ID  = 0x0016
+    ,QMI_ERR_DEVICE_IN_USE  = 0x0017
+    ,QMI_ERR_OP_NETWORK_UNSUPPORTED  = 0x0018
+    ,QMI_ERR_OP_DEVICE_UNSUPPORTED  = 0x0019
+    ,QMI_ERR_NO_EFFECT  = 0x001A
+    ,QMI_ERR_NO_FREE_PROFILE  = 0x001B
+    ,QMI_ERR_INVALID_PDP_TYPE  = 0x001C
+    ,QMI_ERR_INVALID_TECH_PREF  = 0x001D
+    ,QMI_ERR_INVALID_PROFILE_TYPE  = 0x001E
+    ,QMI_ERR_INVALID_SERVICE_TYPE  = 0x001F
+    ,QMI_ERR_INVALID_REGISTER_ACTION  = 0x0020
+    ,QMI_ERR_INVALID_PS_ATTACH_ACTION  = 0x0021
+    ,QMI_ERR_AUTHENTICATION_FAILED  = 0x0022
+    ,QMI_ERR_PIN_BLOCKED  = 0x0023
+    ,QMI_ERR_PIN_PERM_BLOCKED  = 0x0024
+    ,QMI_ERR_SIM_NOT_INITIALIZED  = 0x0025
+    ,QMI_ERR_MAX_QOS_REQUESTS_IN_USE  = 0x0026
+    ,QMI_ERR_INCORRECT_FLOW_FILTER  = 0x0027
+    ,QMI_ERR_NETWORK_QOS_UNAWARE  = 0x0028
+    ,QMI_ERR_INVALID_QOS_ID  = 0x0029
+    ,QMI_ERR_INVALID_ID  = 0x0029
+    ,QMI_ERR_REQUESTED_NUM_UNSUPPORTED  = 0x002A
+    ,QMI_ERR_INTERFACE_NOT_FOUND  = 0x002B
+    ,QMI_ERR_FLOW_SUSPENDED  = 0x002C
+    ,QMI_ERR_INVALID_DATA_FORMAT  = 0x002D
+    ,QMI_ERR_GENERAL  = 0x002E
+    ,QMI_ERR_UNKNOWN  = 0x002F
+    ,QMI_ERR_INVALID_ARG  = 0x0030
+    ,QMI_ERR_INVALID_INDEX  = 0x0031
+    ,QMI_ERR_NO_ENTRY  = 0x0032
+    ,QMI_ERR_DEVICE_STORAGE_FULL  = 0x0033
+    ,QMI_ERR_DEVICE_NOT_READY  = 0x0034
+    ,QMI_ERR_NETWORK_NOT_READY  = 0x0035
+    ,QMI_ERR_CAUSE_CODE  = 0x0036
+    ,QMI_ERR_MESSAGE_NOT_SENT  = 0x0037
+    ,QMI_ERR_MESSAGE_DELIVERY_FAILURE  = 0x0038
+    ,QMI_ERR_INVALID_MESSAGE_ID  = 0x0039
+    ,QMI_ERR_ENCODING  = 0x003A
+    ,QMI_ERR_AUTHENTICATION_LOCK  = 0x003B
+    ,QMI_ERR_INVALID_TRANSITION  = 0x003C
+    ,QMI_ERR_NOT_A_MCAST_IFACE  = 0x003D
+    ,QMI_ERR_MAX_MCAST_REQUESTS_IN_USE  = 0x003E
+    ,QMI_ERR_INVALID_MCAST_HANDLE = 0x003F
+    ,QMI_ERR_INVALID_IP_FAMILY_PREF  = 0x0040
+    ,QMI_ERR_SESSION_INACTIVE  = 0x0041
+    ,QMI_ERR_SESSION_INVALID  = 0x0042
+    ,QMI_ERR_SESSION_OWNERSHIP  = 0x0043
+    ,QMI_ERR_INSUFFICIENT_RESOURCES  = 0x0044
+    ,QMI_ERR_DISABLED  = 0x0045
+    ,QMI_ERR_INVALID_OPERATION  = 0x0046
+    ,QMI_ERR_INVALID_QMI_CMD  = 0x0047
+    ,QMI_ERR_TPDU_TYPE  = 0x0048
+    ,QMI_ERR_SMSC_ADDR  = 0x0049
+    ,QMI_ERR_INFO_UNAVAILABLE  = 0x004A
+    ,QMI_ERR_SEGMENT_TOO_LONG  = 0x004B
+    ,QMI_ERR_SEGMENT_ORDER  = 0x004C
+    ,QMI_ERR_BUNDLING_NOT_SUPPORTED  = 0x004D
+    ,QMI_ERR_OP_PARTIAL_FAILURE  = 0x004E
+    ,QMI_ERR_POLICY_MISMATCH  = 0x004F
+    ,QMI_ERR_SIM_FILE_NOT_FOUND  = 0x0050
+    ,QMI_ERR_EXTENDED_INTERNAL  = 0x0051
+    ,QMI_ERR_ACCESS_DENIED  = 0x0052
+    ,QMI_ERR_HARDWARE_RESTRICTED  = 0x0053
+    ,QMI_ERR_ACK_NOT_SENT  = 0x0054
+    ,QMI_ERR_INJECT_TIMEOUT  = 0x0055
+    ,QMI_ERR_INCOMPATIBLE_STATE = 0x005A
+    ,QMI_ERR_FDN_RESTRICT  = 0x005B
+    ,QMI_ERR_SUPS_FAILURE_CAUSE  = 0x005C
+    ,QMI_ERR_NO_RADIO  = 0x005D
+    ,QMI_ERR_NOT_SUPPORTED  = 0x005E
+    ,QMI_ERR_NO_SUBSCRIPTION  = 0x005F
+    ,QMI_ERR_CARD_CALL_CONTROL_FAILED  = 0x0060
+    ,QMI_ERR_NETWORK_ABORTED  = 0x0061
+    ,QMI_ERR_MSG_BLOCKED  = 0x0062
+    ,QMI_ERR_INVALID_SESSION_TYPE  = 0x0064
+    ,QMI_ERR_INVALID_PB_TYPE  = 0x0065
+    ,QMI_ERR_NO_SIM  = 0x0066
+    ,QMI_ERR_PB_NOT_READY  = 0x0067
+    ,QMI_ERR_PIN_RESTRICTION  = 0x0068
+    ,QMI_ERR_PIN2_RESTRICTION  = 0x0069
+    ,QMI_ERR_PUK_RESTRICTION  = 0x006A
+    ,QMI_ERR_PUK2_RESTRICTION  = 0x006B
+    ,QMI_ERR_PB_ACCESS_RESTRICTED  = 0x006C
+    ,QMI_ERR_PB_DELETE_IN_PROG  = 0x006D
+    ,QMI_ERR_PB_TEXT_TOO_LONG  = 0x006E
+    ,QMI_ERR_PB_NUMBER_TOO_LONG  = 0x006F
+    ,QMI_ERR_PB_HIDDEN_KEY_RESTRICTION  = 0x0070
+} QMI_ERROR_CODE_TYPE;
+
+#define QCQMI_CTL_FLAG_SERVICE   0x80
+#define QCQMI_CTL_FLAG_CTL_POINT 0x00
+
+typedef struct _QCQMI_HDR
+{
+   UCHAR  IFType;
+   USHORT Length;
+   UCHAR  CtlFlags;  // reserved
+   UCHAR  QMIType;
+   UCHAR  ClientId;
+} __attribute__ ((packed)) QCQMI_HDR, *PQCQMI_HDR;
+
+#define QCQMI_HDR_SIZE (sizeof(QCQMI_HDR)-1)
+
+typedef struct _QCQMI
+{
+   UCHAR  IFType;
+   USHORT Length;
+   UCHAR  CtlFlags;  // reserved
+   UCHAR  QMIType;
+   UCHAR  ClientId;
+   UCHAR  SDU;
+} __attribute__ ((packed)) QCQMI, *PQCQMI;
+
+typedef struct _QMI_SERVICE_VERSION
+{
+   USHORT Major;
+   USHORT Minor;
+   USHORT AddendumMajor;
+   USHORT AddendumMinor;
+} __attribute__ ((packed)) QMI_SERVICE_VERSION, *PQMI_SERVICE_VERSION;
+
+// ================= QMUX ==================
+
+#define QMUX_MSG_OVERHEAD_BYTES 4  // Type(USHORT) Length(USHORT) -- header
+
+#define QMUX_BROADCAST_CID 0xFF
+
+typedef struct _QCQMUX_HDR
+{
+   UCHAR  CtlFlags;      // 0: single QMUX Msg; 1:
+   USHORT TransactionId;
+} __attribute__ ((packed)) QCQMUX_HDR, *PQCQMUX_HDR;
+
+typedef struct _QCQMUX
+{
+   UCHAR  CtlFlags;      // 0: single QMUX Msg; 1:
+   USHORT TransactionId;
+   UCHAR  Message;  // Type(2), Length(2), Value
+} __attribute__ ((packed)) QCQMUX, *PQCQMUX;
+
+#define QCQMUX_HDR_SIZE sizeof(QCQMUX_HDR)
+
+typedef struct _QCQMUX_MSG_HDR
+{
+   USHORT Type;
+   USHORT Length;
+} __attribute__ ((packed)) QCQMUX_MSG_HDR, *PQCQMUX_MSG_HDR;
+
+#define QCQMUX_MSG_HDR_SIZE sizeof(QCQMUX_MSG_HDR)
+
+typedef struct _QCQMUX_MSG_HDR_RESP
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+} __attribute__ ((packed)) QCQMUX_MSG_HDR_RESP, *PQCQMUX_MSG_HDR_RESP;
+
+typedef struct _QCQMUX_TLV
+{
+   UCHAR Type;
+   USHORT Length;
+   UCHAR  Value;
+} __attribute__ ((packed)) QCQMUX_TLV, *PQCQMUX_TLV;
+
+typedef struct _QMI_TLV_HDR
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+} __attribute__ ((packed)) QMI_TLV_HDR, *PQMI_TLV_HDR;
+
+// QMUX Message Definitions -- QMI SDU
+#define QMUX_CTL_FLAG_SINGLE_MSG    0x00
+#define QMUX_CTL_FLAG_COMPOUND_MSG  0x01
+#define QMUX_CTL_FLAG_TYPE_CMD      0x00
+#define QMUX_CTL_FLAG_TYPE_RSP      0x02
+#define QMUX_CTL_FLAG_TYPE_IND      0x04
+#define QMUX_CTL_FLAG_MASK_COMPOUND 0x01
+#define QMUX_CTL_FLAG_MASK_TYPE     0x06 // 00-cmd, 01-rsp, 10-ind
+
+#pragma pack(pop)
+
+#endif // USBQMI_H
diff --git a/package/wwan/meig-cm/src/MPQMUX.c b/package/wwan/meig-cm/src/MPQMUX.c
new file mode 100644
index 000000000..6d0880d12
--- /dev/null
+++ b/package/wwan/meig-cm/src/MPQMUX.c
@@ -0,0 +1,426 @@
+#include "QMIThread.h"
+static char line[1024];
+static pthread_mutex_t dumpQMIMutex = PTHREAD_MUTEX_INITIALIZER;
+#undef dbg
+#define dbg( format, arg... ) do {if (strlen(line) < sizeof(line)) snprintf(&line[strlen(line)], sizeof(line) - strlen(line), format, ## arg);} while (0)
+
+PQMI_TLV_HDR GetTLV (PQCQMUX_MSG_HDR pQMUXMsgHdr, int TLVType);
+
+typedef struct {
+    UINT type;
+    const char *name;
+} QMI_NAME_T;
+
+#define qmi_name_item(type) {type, #type}
+
+#if 0
+static const QMI_NAME_T qmi_IFType[] = {
+{USB_CTL_MSG_TYPE_QMI, "USB_CTL_MSG_TYPE_QMI"},
+};
+
+static const QMI_NAME_T qmi_CtlFlags[] = {
+qmi_name_item(QMICTL_CTL_FLAG_CMD),
+qmi_name_item(QCQMI_CTL_FLAG_SERVICE),
+};
+
+static const QMI_NAME_T qmi_QMIType[] = {
+qmi_name_item(QMUX_TYPE_CTL),
+qmi_name_item(QMUX_TYPE_WDS),
+qmi_name_item(QMUX_TYPE_DMS),
+qmi_name_item(QMUX_TYPE_NAS),
+qmi_name_item(QMUX_TYPE_QOS),
+qmi_name_item(QMUX_TYPE_WMS),
+qmi_name_item(QMUX_TYPE_PDS),
+qmi_name_item(QMUX_TYPE_WDS_ADMIN),
+};
+
+static const QMI_NAME_T qmi_ctl_CtlFlags[] = {
+qmi_name_item(QMICTL_FLAG_REQUEST),
+qmi_name_item(QMICTL_FLAG_RESPONSE),
+qmi_name_item(QMICTL_FLAG_INDICATION),
+};
+#endif
+
+static const QMI_NAME_T qmux_ctl_QMICTLType[] = {
+// QMICTL Type
+qmi_name_item(QMICTL_SET_INSTANCE_ID_REQ), //    0x0020
+qmi_name_item(QMICTL_SET_INSTANCE_ID_RESP), //   0x0020
+qmi_name_item(QMICTL_GET_VERSION_REQ), //        0x0021
+qmi_name_item(QMICTL_GET_VERSION_RESP), //       0x0021
+qmi_name_item(QMICTL_GET_CLIENT_ID_REQ), //      0x0022
+qmi_name_item(QMICTL_GET_CLIENT_ID_RESP), //     0x0022
+qmi_name_item(QMICTL_RELEASE_CLIENT_ID_REQ), //  0x0023
+qmi_name_item(QMICTL_RELEASE_CLIENT_ID_RESP), // 0x0023
+qmi_name_item(QMICTL_REVOKE_CLIENT_ID_IND), //   0x0024
+qmi_name_item(QMICTL_INVALID_CLIENT_ID_IND), //  0x0025
+qmi_name_item(QMICTL_SET_DATA_FORMAT_REQ), //    0x0026
+qmi_name_item(QMICTL_SET_DATA_FORMAT_RESP), //   0x0026
+qmi_name_item(QMICTL_SYNC_REQ), //               0x0027
+qmi_name_item(QMICTL_SYNC_RESP), //              0x0027
+qmi_name_item(QMICTL_SYNC_IND), //               0x0027
+};
+
+static const QMI_NAME_T qmux_CtlFlags[] = {
+qmi_name_item(QMUX_CTL_FLAG_TYPE_CMD),
+qmi_name_item(QMUX_CTL_FLAG_TYPE_RSP),
+qmi_name_item(QMUX_CTL_FLAG_TYPE_IND),
+};
+
+
+static const QMI_NAME_T qmux_wds_Type[] = {
+qmi_name_item(QMIWDS_SET_EVENT_REPORT_REQ), //           0x0001
+qmi_name_item(QMIWDS_SET_EVENT_REPORT_RESP), //          0x0001
+qmi_name_item(QMIWDS_EVENT_REPORT_IND), //               0x0001
+qmi_name_item(QMIWDS_START_NETWORK_INTERFACE_REQ), //    0x0020
+qmi_name_item(QMIWDS_START_NETWORK_INTERFACE_RESP), //   0x0020
+qmi_name_item(QMIWDS_STOP_NETWORK_INTERFACE_REQ), //     0x0021
+qmi_name_item(QMIWDS_STOP_NETWORK_INTERFACE_RESP), //    0x0021
+qmi_name_item(QMIWDS_GET_PKT_SRVC_STATUS_REQ), //        0x0022
+qmi_name_item(QMIWDS_GET_PKT_SRVC_STATUS_RESP), //       0x0022
+qmi_name_item(QMIWDS_GET_PKT_SRVC_STATUS_IND), //        0x0022  
+qmi_name_item(QMIWDS_GET_CURRENT_CHANNEL_RATE_REQ), //   0x0023  
+qmi_name_item(QMIWDS_GET_CURRENT_CHANNEL_RATE_RESP), //  0x0023  
+qmi_name_item(QMIWDS_GET_PKT_STATISTICS_REQ), //         0x0024  
+qmi_name_item(QMIWDS_GET_PKT_STATISTICS_RESP), //        0x0024  
+qmi_name_item(QMIWDS_MODIFY_PROFILE_SETTINGS_REQ), //    0x0028
+qmi_name_item(QMIWDS_MODIFY_PROFILE_SETTINGS_RESP), //   0x0028
+qmi_name_item(QMIWDS_GET_PROFILE_SETTINGS_REQ), //    0x002B
+qmi_name_item(QMIWDS_GET_PROFILE_SETTINGS_RESP), //   0x002BD
+qmi_name_item(QMIWDS_GET_DEFAULT_SETTINGS_REQ), //       0x002C
+qmi_name_item(QMIWDS_GET_DEFAULT_SETTINGS_RESP), //      0x002C
+qmi_name_item(QMIWDS_GET_RUNTIME_SETTINGS_REQ), //       0x002D
+qmi_name_item(QMIWDS_GET_RUNTIME_SETTINGS_RESP), //      0x002D
+qmi_name_item(QMIWDS_GET_MIP_MODE_REQ), //               0x002F
+qmi_name_item(QMIWDS_GET_MIP_MODE_RESP), //              0x002F
+qmi_name_item(QMIWDS_GET_DATA_BEARER_REQ), //            0x0037
+qmi_name_item(QMIWDS_GET_DATA_BEARER_RESP), //           0x0037
+qmi_name_item(QMIWDS_DUN_CALL_INFO_REQ), //              0x0038
+qmi_name_item(QMIWDS_DUN_CALL_INFO_RESP), //             0x0038
+qmi_name_item(QMIWDS_DUN_CALL_INFO_IND), //              0x0038
+qmi_name_item(QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ), //  0x004D  
+qmi_name_item(QMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP), // 0x004D  
+qmi_name_item(QMIWDS_SET_AUTO_CONNECT_REQ), //  0x0051  
+qmi_name_item(QMIWDS_SET_AUTO_CONNECT_RESP), // 0x0051
+qmi_name_item(QMIWDS_BIND_MUX_DATA_PORT_REQ), //         0x00A2  
+qmi_name_item(QMIWDS_BIND_MUX_DATA_PORT_RESP), //        0x00A2  
+};
+
+static const QMI_NAME_T qmux_dms_Type[] = {
+// ======================= DMS ==============================
+qmi_name_item(QMIDMS_SET_EVENT_REPORT_REQ), //           0x0001
+qmi_name_item(QMIDMS_SET_EVENT_REPORT_RESP), //          0x0001
+qmi_name_item(QMIDMS_EVENT_REPORT_IND), //               0x0001
+qmi_name_item(QMIDMS_GET_DEVICE_CAP_REQ), //             0x0020
+qmi_name_item(QMIDMS_GET_DEVICE_CAP_RESP), //            0x0020
+qmi_name_item(QMIDMS_GET_DEVICE_MFR_REQ), //             0x0021
+qmi_name_item(QMIDMS_GET_DEVICE_MFR_RESP), //            0x0021
+qmi_name_item(QMIDMS_GET_DEVICE_MODEL_ID_REQ), //        0x0022
+qmi_name_item(QMIDMS_GET_DEVICE_MODEL_ID_RESP), //       0x0022
+qmi_name_item(QMIDMS_GET_DEVICE_REV_ID_REQ), //          0x0023
+qmi_name_item(QMIDMS_GET_DEVICE_REV_ID_RESP), //         0x0023
+qmi_name_item(QMIDMS_GET_MSISDN_REQ), //                 0x0024
+qmi_name_item(QMIDMS_GET_MSISDN_RESP), //                0x0024
+qmi_name_item(QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ), //  0x0025
+qmi_name_item(QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP), // 0x0025
+qmi_name_item(QMIDMS_UIM_SET_PIN_PROTECTION_REQ), //     0x0027
+qmi_name_item(QMIDMS_UIM_SET_PIN_PROTECTION_RESP), //    0x0027
+qmi_name_item(QMIDMS_UIM_VERIFY_PIN_REQ), //             0x0028
+qmi_name_item(QMIDMS_UIM_VERIFY_PIN_RESP), //            0x0028
+qmi_name_item(QMIDMS_UIM_UNBLOCK_PIN_REQ), //            0x0029
+qmi_name_item(QMIDMS_UIM_UNBLOCK_PIN_RESP), //           0x0029
+qmi_name_item(QMIDMS_UIM_CHANGE_PIN_REQ), //             0x002A
+qmi_name_item(QMIDMS_UIM_CHANGE_PIN_RESP), //            0x002A
+qmi_name_item(QMIDMS_UIM_GET_PIN_STATUS_REQ), //         0x002B
+qmi_name_item(QMIDMS_UIM_GET_PIN_STATUS_RESP), //        0x002B
+qmi_name_item(QMIDMS_GET_DEVICE_HARDWARE_REV_REQ), //    0x002C
+qmi_name_item(QMIDMS_GET_DEVICE_HARDWARE_REV_RESP), //   0x002C
+qmi_name_item(QMIDMS_GET_OPERATING_MODE_REQ), //         0x002D 
+qmi_name_item(QMIDMS_GET_OPERATING_MODE_RESP), //        0x002D 
+qmi_name_item(QMIDMS_SET_OPERATING_MODE_REQ), //         0x002E 
+qmi_name_item(QMIDMS_SET_OPERATING_MODE_RESP), //        0x002E 
+qmi_name_item(QMIDMS_GET_ACTIVATED_STATUS_REQ), //       0x0031 
+qmi_name_item(QMIDMS_GET_ACTIVATED_STATUS_RESP), //      0x0031 
+qmi_name_item(QMIDMS_ACTIVATE_AUTOMATIC_REQ), //         0x0032
+qmi_name_item(QMIDMS_ACTIVATE_AUTOMATIC_RESP), //        0x0032
+qmi_name_item(QMIDMS_ACTIVATE_MANUAL_REQ), //            0x0033
+qmi_name_item(QMIDMS_ACTIVATE_MANUAL_RESP), //           0x0033
+qmi_name_item(QMIDMS_UIM_GET_ICCID_REQ), //              0x003C 
+qmi_name_item(QMIDMS_UIM_GET_ICCID_RESP), //             0x003C 
+qmi_name_item(QMIDMS_UIM_GET_CK_STATUS_REQ), //          0x0040
+qmi_name_item(QMIDMS_UIM_GET_CK_STATUS_RESP), //         0x0040
+qmi_name_item(QMIDMS_UIM_SET_CK_PROTECTION_REQ), //      0x0041
+qmi_name_item(QMIDMS_UIM_SET_CK_PROTECTION_RESP), //     0x0041
+qmi_name_item(QMIDMS_UIM_UNBLOCK_CK_REQ), //             0x0042
+qmi_name_item(QMIDMS_UIM_UNBLOCK_CK_RESP), //            0x0042
+qmi_name_item(QMIDMS_UIM_GET_IMSI_REQ), //               0x0043 
+qmi_name_item(QMIDMS_UIM_GET_IMSI_RESP), //              0x0043 
+qmi_name_item(QMIDMS_UIM_GET_STATE_REQ), //              0x0044 
+qmi_name_item(QMIDMS_UIM_GET_STATE_RESP), //             0x0044 
+qmi_name_item(QMIDMS_GET_BAND_CAP_REQ), //               0x0045 
+qmi_name_item(QMIDMS_GET_BAND_CAP_RESP), //              0x0045 
+};
+
+static const QMI_NAME_T qmux_nas_Type[] = {
+// ======================= NAS ==============================
+qmi_name_item(QMINAS_SET_EVENT_REPORT_REQ), //             0x0002
+qmi_name_item(QMINAS_SET_EVENT_REPORT_RESP), //            0x0002
+qmi_name_item(QMINAS_EVENT_REPORT_IND), //                 0x0002
+qmi_name_item(QMINAS_GET_SIGNAL_STRENGTH_REQ), //          0x0020
+qmi_name_item(QMINAS_GET_SIGNAL_STRENGTH_RESP), //         0x0020
+qmi_name_item(QMINAS_PERFORM_NETWORK_SCAN_REQ), //         0x0021
+qmi_name_item(QMINAS_PERFORM_NETWORK_SCAN_RESP), //        0x0021
+qmi_name_item(QMINAS_INITIATE_NW_REGISTER_REQ), //         0x0022
+qmi_name_item(QMINAS_INITIATE_NW_REGISTER_RESP), //        0x0022
+qmi_name_item(QMINAS_INITIATE_ATTACH_REQ), //              0x0023
+qmi_name_item(QMINAS_INITIATE_ATTACH_RESP), //             0x0023
+qmi_name_item(QMINAS_GET_SERVING_SYSTEM_REQ), //           0x0024
+qmi_name_item(QMINAS_GET_SERVING_SYSTEM_RESP), //          0x0024
+qmi_name_item(QMINAS_SERVING_SYSTEM_IND), //               0x0024
+qmi_name_item(QMINAS_GET_HOME_NETWORK_REQ), //             0x0025
+qmi_name_item(QMINAS_GET_HOME_NETWORK_RESP), //            0x0025
+qmi_name_item(QMINAS_GET_PREFERRED_NETWORK_REQ), //        0x0026
+qmi_name_item(QMINAS_GET_PREFERRED_NETWORK_RESP), //       0x0026
+qmi_name_item(QMINAS_SET_PREFERRED_NETWORK_REQ), //        0x0027
+qmi_name_item(QMINAS_SET_PREFERRED_NETWORK_RESP), //       0x0027
+qmi_name_item(QMINAS_GET_FORBIDDEN_NETWORK_REQ), //        0x0028
+qmi_name_item(QMINAS_GET_FORBIDDEN_NETWORK_RESP), //       0x0028
+qmi_name_item(QMINAS_SET_FORBIDDEN_NETWORK_REQ), //        0x0029
+qmi_name_item(QMINAS_SET_FORBIDDEN_NETWORK_RESP), //       0x0029
+qmi_name_item(QMINAS_SET_TECHNOLOGY_PREF_REQ), //          0x002A
+qmi_name_item(QMINAS_SET_TECHNOLOGY_PREF_RESP), //         0x002A
+qmi_name_item(QMINAS_GET_RF_BAND_INFO_REQ), //             0x0031
+qmi_name_item(QMINAS_GET_RF_BAND_INFO_RESP), //            0x0031
+qmi_name_item(QMINAS_GET_PLMN_NAME_REQ), //                0x0044
+qmi_name_item(QMINAS_GET_PLMN_NAME_RESP), //               0x0044
+qmi_name_item(MEIG_PACKET_TRANSFER_START_IND), //                0X100
+qmi_name_item(MEIG_PACKET_TRANSFER_END_IND), //               0X101
+qmi_name_item(QMINAS_GET_SYS_INFO_REQ), //                 0x004D
+qmi_name_item(QMINAS_GET_SYS_INFO_RESP), //                0x004D
+qmi_name_item(QMINAS_SYS_INFO_IND), //                     0x004D
+};
+
+static const QMI_NAME_T qmux_wms_Type[] = {
+// ======================= WMS ==============================
+qmi_name_item(QMIWMS_SET_EVENT_REPORT_REQ), //           0x0001
+qmi_name_item(QMIWMS_SET_EVENT_REPORT_RESP), //          0x0001
+qmi_name_item(QMIWMS_EVENT_REPORT_IND), //               0x0001
+qmi_name_item(QMIWMS_RAW_SEND_REQ), //                   0x0020
+qmi_name_item(QMIWMS_RAW_SEND_RESP), //                  0x0020
+qmi_name_item(QMIWMS_RAW_WRITE_REQ), //                  0x0021
+qmi_name_item(QMIWMS_RAW_WRITE_RESP), //                 0x0021
+qmi_name_item(QMIWMS_RAW_READ_REQ), //                   0x0022
+qmi_name_item(QMIWMS_RAW_READ_RESP), //                  0x0022
+qmi_name_item(QMIWMS_MODIFY_TAG_REQ), //                 0x0023
+qmi_name_item(QMIWMS_MODIFY_TAG_RESP), //                0x0023
+qmi_name_item(QMIWMS_DELETE_REQ), //                     0x0024
+qmi_name_item(QMIWMS_DELETE_RESP), //                    0x0024
+qmi_name_item(QMIWMS_GET_MESSAGE_PROTOCOL_REQ), //       0x0030
+qmi_name_item(QMIWMS_GET_MESSAGE_PROTOCOL_RESP), //      0x0030
+qmi_name_item(QMIWMS_LIST_MESSAGES_REQ), //              0x0031
+qmi_name_item(QMIWMS_LIST_MESSAGES_RESP), //             0x0031
+qmi_name_item(QMIWMS_GET_SMSC_ADDRESS_REQ), //           0x0034
+qmi_name_item(QMIWMS_GET_SMSC_ADDRESS_RESP), //          0x0034
+qmi_name_item(QMIWMS_SET_SMSC_ADDRESS_REQ), //           0x0035
+qmi_name_item(QMIWMS_SET_SMSC_ADDRESS_RESP), //          0x0035
+qmi_name_item(QMIWMS_GET_STORE_MAX_SIZE_REQ), //         0x0036
+qmi_name_item(QMIWMS_GET_STORE_MAX_SIZE_RESP), //        0x0036
+};
+
+static const QMI_NAME_T qmux_wds_admin_Type[] = {
+qmi_name_item(QMIWDS_ADMIN_SET_DATA_FORMAT_REQ), //      0x0020
+qmi_name_item(QMIWDS_ADMIN_SET_DATA_FORMAT_RESP), //     0x0020
+qmi_name_item(QMIWDS_ADMIN_GET_DATA_FORMAT_REQ), //      0x0021
+qmi_name_item(QMIWDS_ADMIN_GET_DATA_FORMAT_RESP), //     0x0021
+qmi_name_item(QMIWDS_ADMIN_SET_QMAP_SETTINGS_REQ), //    0x002B
+qmi_name_item(QMIWDS_ADMIN_SET_QMAP_SETTINGS_RESP), //   0x002B
+qmi_name_item(QMIWDS_ADMIN_GET_QMAP_SETTINGS_REQ), //    0x002C
+qmi_name_item(QMIWDS_ADMIN_GET_QMAP_SETTINGS_RESP), //   0x002C
+};
+
+static const QMI_NAME_T qmux_uim_Type[] = {
+qmi_name_item( QMIUIM_READ_TRANSPARENT_REQ), //      0x0020
+qmi_name_item( QMIUIM_READ_TRANSPARENT_RESP), //     0x0020
+qmi_name_item( QMIUIM_READ_TRANSPARENT_IND), //      0x0020
+qmi_name_item( QMIUIM_READ_RECORD_REQ), //           0x0021
+qmi_name_item( QMIUIM_READ_RECORD_RESP), //          0x0021
+qmi_name_item( QMIUIM_READ_RECORD_IND), //           0x0021
+qmi_name_item( QMIUIM_WRITE_TRANSPARENT_REQ), //     0x0022
+qmi_name_item( QMIUIM_WRITE_TRANSPARENT_RESP), //    0x0022
+qmi_name_item( QMIUIM_WRITE_TRANSPARENT_IND), //     0x0022
+qmi_name_item( QMIUIM_WRITE_RECORD_REQ), //          0x0023
+qmi_name_item( QMIUIM_WRITE_RECORD_RESP), //         0x0023
+qmi_name_item( QMIUIM_WRITE_RECORD_IND), //          0x0023
+qmi_name_item( QMIUIM_SET_PIN_PROTECTION_REQ), //    0x0025
+qmi_name_item( QMIUIM_SET_PIN_PROTECTION_RESP), //   0x0025
+qmi_name_item( QMIUIM_SET_PIN_PROTECTION_IND), //    0x0025
+qmi_name_item( QMIUIM_VERIFY_PIN_REQ), //            0x0026
+qmi_name_item( QMIUIM_VERIFY_PIN_RESP), //           0x0026
+qmi_name_item( QMIUIM_VERIFY_PIN_IND), //            0x0026
+qmi_name_item( QMIUIM_UNBLOCK_PIN_REQ), //           0x0027
+qmi_name_item( QMIUIM_UNBLOCK_PIN_RESP), //          0x0027
+qmi_name_item( QMIUIM_UNBLOCK_PIN_IND), //           0x0027
+qmi_name_item( QMIUIM_CHANGE_PIN_REQ), //            0x0028
+qmi_name_item( QMIUIM_CHANGE_PIN_RESP), //           0x0028
+qmi_name_item( QMIUIM_CHANGE_PIN_IND), //            0x0028
+qmi_name_item( QMIUIM_DEPERSONALIZATION_REQ), //     0x0029
+qmi_name_item( QMIUIM_DEPERSONALIZATION_RESP), //    0x0029
+qmi_name_item( QMIUIM_EVENT_REG_REQ), //             0x002E
+qmi_name_item( QMIUIM_EVENT_REG_RESP), //            0x002E
+qmi_name_item( QMIUIM_GET_CARD_STATUS_REQ), //       0x002F
+qmi_name_item( QMIUIM_GET_CARD_STATUS_RESP), //      0x002F
+qmi_name_item( QMIUIM_STATUS_CHANGE_IND), //         0x0032
+};
+
+static const char * qmi_name_get(const QMI_NAME_T *table, size_t size, int type, const char *tag) {
+    static char unknow[40];
+    size_t i;
+
+    if (qmux_CtlFlags == table) {
+        if (!strcmp(tag, "_REQ"))
+            tag = "_CMD";
+        else  if (!strcmp(tag, "_RESP"))
+            tag = "_RSP";
+    }
+    
+    for (i = 0; i < size; i++) {
+        if (table[i].type == (UINT)type) {
+            if (!tag || (strstr(table[i].name, tag)))
+                return table[i].name;
+        }
+    }
+    sprintf(unknow, "unknow_%x", type);
+    return unknow;
+}
+
+#define QMI_NAME(table, type) qmi_name_get(table, sizeof(table) / sizeof(table[0]), type, 0)
+#define QMUX_NAME(table, type, tag) qmi_name_get(table, sizeof(table) / sizeof(table[0]), type, tag)
+
+void dump_tlv(PQCQMUX_MSG_HDR pQMUXMsgHdr) {
+    int TLVFind = 0;
+    int i;
+    //dbg("QCQMUX_TLV-----------------------------------\n");
+    //dbg("{Type,\tLength,\tValue}\n");
+
+    while (1) {
+        PQMI_TLV_HDR TLVHdr = GetTLV(pQMUXMsgHdr, 0x1000 + (++TLVFind));
+        if (TLVHdr == NULL)
+            break;
+
+        //if ((TLVHdr->TLVType == 0x02) && ((USHORT *)(TLVHdr+1))[0])
+        {        
+            dbg("{%02x,\t%04x,\t", TLVHdr->TLVType, le16_to_cpu(TLVHdr->TLVLength));
+            for (i = 0; i < le16_to_cpu(TLVHdr->TLVLength); i++) {
+                dbg("%02x ", ((UCHAR *)(TLVHdr+1))[i]);
+            }
+            dbg("}\n");
+        }
+    }  // while
+}
+
+void dump_ctl(PQCQMICTL_MSG_HDR CTLHdr) {
+    const char *tag;
+    
+    //dbg("QCQMICTL_MSG--------------------------------------------\n");
+    //dbg("CtlFlags:           %02x\t\t%s\n", CTLHdr->CtlFlags, QMI_NAME(qmi_ctl_CtlFlags, CTLHdr->CtlFlags));
+   dbg("TransactionId:      %02x\n", CTLHdr->TransactionId);
+        switch (CTLHdr->CtlFlags) {
+            case QMICTL_FLAG_REQUEST: tag = "_REQ"; break;
+            case QMICTL_FLAG_RESPONSE: tag = "_RESP"; break;
+            case QMICTL_FLAG_INDICATION: tag = "_IND"; break;
+            default: tag = 0; break;
+       }
+        dbg("QMICTLType:         %04x\t%s\n", le16_to_cpu(CTLHdr->QMICTLType),
+        QMUX_NAME(qmux_ctl_QMICTLType, le16_to_cpu(CTLHdr->QMICTLType), tag));     
+        dbg("Length:             %04x\n", le16_to_cpu(CTLHdr->Length));
+
+     dump_tlv((PQCQMUX_MSG_HDR)(&CTLHdr->QMICTLType));
+}
+
+int dump_qmux(QMI_SERVICE_TYPE serviceType, PQCQMUX_HDR QMUXHdr) {
+    PQCQMUX_MSG_HDR QMUXMsgHdr = (PQCQMUX_MSG_HDR) (QMUXHdr + 1);
+    CHAR *tag;
+
+    //dbg("QCQMUX--------------------------------------------\n");
+    switch (QMUXHdr->CtlFlags&QMUX_CTL_FLAG_MASK_TYPE) {
+        case QMUX_CTL_FLAG_TYPE_CMD: tag = "_REQ"; break;
+        case QMUX_CTL_FLAG_TYPE_RSP: tag = "_RESP"; break;
+        case QMUX_CTL_FLAG_TYPE_IND: tag = "_IND"; break;
+        default: tag = 0; break;
+    }
+    //dbg("CtlFlags:           %02x\t\t%s\n", QMUXHdr->CtlFlags, QMUX_NAME(qmux_CtlFlags, QMUXHdr->CtlFlags, tag));
+    dbg("TransactionId:    %04x\n", le16_to_cpu(QMUXHdr->TransactionId));
+
+    //dbg("QCQMUX_MSG_HDR-----------------------------------\n");
+    switch (serviceType) {
+        case QMUX_TYPE_DMS:
+            dbg("Type:               %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
+            QMUX_NAME(qmux_dms_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
+        break;
+        case QMUX_TYPE_NAS:
+            dbg("Type:               %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
+            QMUX_NAME(qmux_nas_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
+        break;
+        case QMUX_TYPE_WDS:
+            dbg("Type:               %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
+            QMUX_NAME(qmux_wds_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
+        break;
+        case QMUX_TYPE_WMS:
+            dbg("Type:               %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
+            QMUX_NAME(qmux_wms_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
+        break;
+        case QMUX_TYPE_WDS_ADMIN:
+            dbg("Type:               %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
+            QMUX_NAME(qmux_wds_admin_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
+        break;
+        case QMUX_TYPE_UIM:
+            dbg("Type:               %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
+            QMUX_NAME(qmux_uim_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
+        break;
+        case QMUX_TYPE_PDS:
+        case QMUX_TYPE_QOS:
+        case QMUX_TYPE_CTL:                
+        default:
+            dbg("Type:               %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type), "PDS/QOS/CTL/unknown!");
+        break;    
+    }
+    dbg("Length:             %04x\n", le16_to_cpu(QMUXMsgHdr->Length));
+
+    dump_tlv(QMUXMsgHdr);
+    
+    return 0;
+}
+
+void dump_qmi(void *dataBuffer, int dataLen) 
+{
+    PQCQMI_HDR QMIHdr = (PQCQMI_HDR)dataBuffer;
+    PQCQMUX_HDR QMUXHdr = (PQCQMUX_HDR) (QMIHdr + 1);
+    PQCQMICTL_MSG_HDR CTLHdr =  (PQCQMICTL_MSG_HDR) (QMIHdr + 1);
+
+    int i;
+
+    if (!debug_qmi)
+        return;
+
+    pthread_mutex_lock(&dumpQMIMutex);
+    line[0] = 0;
+    for (i = 0; i < dataLen; i++) {
+        dbg("%02x ", ((unsigned char *)dataBuffer)[i]);
+    }
+    dbg_time("%s", line);
+    line[0] = 0;
+    
+    //dbg("QCQMI_HDR-----------------------------------------");
+    //dbg("IFType:             %02x\t\t%s", QMIHdr->IFType, QMI_NAME(qmi_IFType, QMIHdr->IFType));
+    //dbg("Length:             %04x", le16_to_cpu(QMIHdr->Length));
+    //dbg("CtlFlags:           %02x\t\t%s", QMIHdr->CtlFlags, QMI_NAME(qmi_CtlFlags, QMIHdr->CtlFlags));
+    //dbg("QMIType:            %02x\t\t%s", QMIHdr->QMIType, QMI_NAME(qmi_QMIType, QMIHdr->QMIType));
+    //dbg("ClientId:           %02x", QMIHdr->ClientId);
+
+    if (QMIHdr->QMIType == QMUX_TYPE_CTL) {
+        dump_ctl(CTLHdr);
+    } else {
+        dump_qmux(QMIHdr->QMIType, QMUXHdr);
+    }
+    dbg_time("%s", line);
+    pthread_mutex_unlock(&dumpQMIMutex);
+}
diff --git a/package/wwan/meig-cm/src/MPQMUX.h b/package/wwan/meig-cm/src/MPQMUX.h
new file mode 100644
index 000000000..12993907f
--- /dev/null
+++ b/package/wwan/meig-cm/src/MPQMUX.h
@@ -0,0 +1,3322 @@
+/*===========================================================================
+
+                            M P Q M U X. H
+DESCRIPTION:
+
+    This file provides support for QMUX.
+
+INITIALIZATION AND SEQUENCING REQUIREMENTS:
+
+Copyright (C) 2011 by Qualcomm Technologies, Incorporated.  All Rights Reserved.
+===========================================================================*/
+
+#ifndef MPQMUX_H
+#define MPQMUX_H
+
+#include "MPQMI.h"
+
+#pragma pack(push, 1)
+
+#define QMIWDS_SET_EVENT_REPORT_REQ           0x0001
+#define QMIWDS_SET_EVENT_REPORT_RESP          0x0001
+#define QMIWDS_EVENT_REPORT_IND               0x0001
+#define QMIWDS_START_NETWORK_INTERFACE_REQ    0x0020
+#define QMIWDS_START_NETWORK_INTERFACE_RESP   0x0020
+#define QMIWDS_STOP_NETWORK_INTERFACE_REQ     0x0021
+#define QMIWDS_STOP_NETWORK_INTERFACE_RESP    0x0021
+#define QMIWDS_GET_PKT_SRVC_STATUS_REQ        0x0022
+#define QMIWDS_GET_PKT_SRVC_STATUS_RESP       0x0022
+#define QMIWDS_GET_PKT_SRVC_STATUS_IND        0x0022
+#define QMIWDS_GET_CURRENT_CHANNEL_RATE_REQ   0x0023
+#define QMIWDS_GET_CURRENT_CHANNEL_RATE_RESP  0x0023
+#define QMIWDS_GET_PKT_STATISTICS_REQ         0x0024
+#define QMIWDS_GET_PKT_STATISTICS_RESP        0x0024
+#define QMIWDS_MODIFY_PROFILE_SETTINGS_REQ    0x0028
+#define QMIWDS_MODIFY_PROFILE_SETTINGS_RESP   0x0028
+#define QMIWDS_GET_PROFILE_SETTINGS_REQ         0x002B
+#define QMIWDS_GET_PROFILE_SETTINGS_RESP        0x002B
+#define QMIWDS_GET_DEFAULT_SETTINGS_REQ       0x002C
+#define QMIWDS_GET_DEFAULT_SETTINGS_RESP      0x002C
+#define QMIWDS_GET_RUNTIME_SETTINGS_REQ       0x002D
+#define QMIWDS_GET_RUNTIME_SETTINGS_RESP      0x002D
+#define QMIWDS_GET_MIP_MODE_REQ               0x002F
+#define QMIWDS_GET_MIP_MODE_RESP              0x002F
+#define QMIWDS_GET_DATA_BEARER_REQ            0x0037
+#define QMIWDS_GET_DATA_BEARER_RESP           0x0037
+#define QMIWDS_DUN_CALL_INFO_REQ              0x0038
+#define QMIWDS_DUN_CALL_INFO_RESP             0x0038
+#define QMIWDS_DUN_CALL_INFO_IND              0x0038
+#define QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ  0x004D
+#define QMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP 0x004D
+#define QMIWDS_SET_AUTO_CONNECT_REQ     0x0051
+#define QMIWDS_SET_AUTO_CONNECT_RESP     0x0051
+#define QMIWDS_BIND_MUX_DATA_PORT_REQ         0x00A2
+#define QMIWDS_BIND_MUX_DATA_PORT_RESP        0x00A2
+
+
+// Stats masks
+#define QWDS_STAT_MASK_TX_PKT_OK 0x00000001
+#define QWDS_STAT_MASK_RX_PKT_OK 0x00000002
+#define QWDS_STAT_MASK_TX_PKT_ER 0x00000004
+#define QWDS_STAT_MASK_RX_PKT_ER 0x00000008
+#define QWDS_STAT_MASK_TX_PKT_OF 0x00000010
+#define QWDS_STAT_MASK_RX_PKT_OF 0x00000020
+
+// TLV Types for xfer statistics
+#define TLV_WDS_TX_GOOD_PKTS      0x10
+#define TLV_WDS_RX_GOOD_PKTS      0x11
+#define TLV_WDS_TX_ERROR          0x12
+#define TLV_WDS_RX_ERROR          0x13
+#define TLV_WDS_TX_OVERFLOW       0x14
+#define TLV_WDS_RX_OVERFLOW       0x15
+#define TLV_WDS_CHANNEL_RATE      0x16
+#define TLV_WDS_DATA_BEARER       0x17
+#define TLV_WDS_DORMANCY_STATUS   0x18
+
+#define QWDS_PKT_DATA_DISCONNECTED    0x01
+#define QWDS_PKT_DATA_CONNECTED        0x02
+#define QWDS_PKT_DATA_SUSPENDED        0x03
+#define QWDS_PKT_DATA_AUTHENTICATING   0x04
+
+#define QMIWDS_ADMIN_SET_DATA_FORMAT_REQ      0x0020
+#define QMIWDS_ADMIN_SET_DATA_FORMAT_RESP     0x0020
+#define QMIWDS_ADMIN_GET_DATA_FORMAT_REQ      0x0021
+#define QMIWDS_ADMIN_GET_DATA_FORMAT_RESP     0x0021
+#define QMIWDS_ADMIN_SET_QMAP_SETTINGS_REQ    0x002B
+#define QMIWDS_ADMIN_SET_QMAP_SETTINGS_RESP   0x002B
+#define QMIWDS_ADMIN_GET_QMAP_SETTINGS_REQ    0x002C
+#define QMIWDS_ADMIN_GET_QMAP_SETTINGS_RESP   0x002C
+
+#define NETWORK_DESC_ENCODING_OCTET       0x00
+#define NETWORK_DESC_ENCODING_EXTPROTOCOL 0x01
+#define NETWORK_DESC_ENCODING_7BITASCII   0x02
+#define NETWORK_DESC_ENCODING_IA5         0x03
+#define NETWORK_DESC_ENCODING_UNICODE     0x04
+#define NETWORK_DESC_ENCODING_SHIFTJIS    0x05
+#define NETWORK_DESC_ENCODING_KOREAN      0x06
+#define NETWORK_DESC_ENCODING_LATINH      0x07
+#define NETWORK_DESC_ENCODING_LATIN       0x08
+#define NETWORK_DESC_ENCODING_GSM7BIT     0x09
+#define NETWORK_DESC_ENCODING_GSMDATA     0x0A
+#define NETWORK_DESC_ENCODING_UNKNOWN     0xFF
+
+typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT
+{
+   USHORT Type;             // QMUX type 0x0000
+   USHORT Length;
+} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT, *PQMIWDS_ADMIN_SET_DATA_FORMAT;
+
+typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  QOSSetting;
+} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS, *PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS;
+
+typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_TLV
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   ULONG  Value;
+} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_TLV, *PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV;
+
+typedef struct _QMIWDS_ENDPOINT_TLV
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   ULONG  ep_type;
+   ULONG  iface_id;
+} __attribute__ ((packed)) QMIWDS_ENDPOINT_TLV, *PQMIWDS_ENDPOINT_TLV;
+
+
+typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+    QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS QosDataFormatTlv;
+    QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UnderlyingLinkLayerProtocolTlv;
+    QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationProtocolTlv;
+    QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationProtocolTlv;
+    QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationMaxDatagramsTlv;
+    QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationMaxSizeTlv;
+#if 0
+    QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationMaxDatagramsTlv;
+    QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationMaxSizeTlv;
+#else
+    QMIWDS_ENDPOINT_TLV epTlv;
+#endif
+} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG, *PQMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG;
+
+#if 0
+typedef enum _QMI_RETURN_CODES {
+   QMI_SUCCESS = 0,
+   QMI_SUCCESS_NOT_COMPLETE,
+   QMI_FAILURE
+}QMI_RETURN_CODES;
+
+typedef struct _QMIWDS_GET_PKT_SRVC_STATUS_REQ_MSG
+{
+   USHORT Type;    // 0x0022
+   USHORT Length;  // 0x0000
+} QMIWDS_GET_PKT_SRVC_STATUS_REQ_MSG, *PQMIWDS_GET_PKT_SRVC_STATUS_REQ_MSG;
+
+typedef struct _QMIWDS_GET_PKT_SRVC_STATUS_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+   UCHAR  TLVType2;
+   USHORT TLVLength2;
+   UCHAR  ConnectionStatus; // 0x01: QWDS_PKT_DATAC_DISCONNECTED
+                            // 0x02: QWDS_PKT_DATA_CONNECTED
+                            // 0x03: QWDS_PKT_DATA_SUSPENDED
+                            // 0x04: QWDS_PKT_DATA_AUTHENTICATING
+} QMIWDS_GET_PKT_SRVC_STATUS_RESP_MSG, *PQMIWDS_GET_PKT_SRVC_STATUS_RESP_MSG;
+
+typedef struct _QMIWDS_GET_PKT_SRVC_STATUS_IND_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  ConnectionStatus; // 0x01: QWDS_PKT_DATAC_DISCONNECTED
+                            // 0x02: QWDS_PKT_DATA_CONNECTED
+                            // 0x03: QWDS_PKT_DATA_SUSPENDED
+   UCHAR  ReconfigRequired; // 0x00: No need to reconfigure
+                            // 0x01: Reconfiguration required
+} QMIWDS_GET_PKT_SRVC_STATUS_IND_MSG, *PQMIWDS_GET_PKT_SRVC_STATUS_IND_MSG;
+
+typedef struct _WDS_PKT_SRVC_IP_FAMILY_TLV
+{
+   UCHAR  TLVType;     // 0x12
+   USHORT TLVLength;   // 1
+   UCHAR  IpFamily;    // IPV4-0x04, IPV6-0x06
+} WDS_PKT_SRVC_IP_FAMILY_TLV, *PWDS_PKT_SRVC_IP_FAMILY_TLV;
+
+typedef struct _QMIWDS_DUN_CALL_INFO_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   ULONG  Mask;
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   UCHAR  ReportConnectionStatus;
+} QMIWDS_DUN_CALL_INFO_REQ_MSG, *PQMIWDS_DUN_CALL_INFO_REQ_MSG;
+
+typedef struct _QMIWDS_DUN_CALL_INFO_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+} QMIWDS_DUN_CALL_INFO_RESP_MSG, *PQMIWDS_DUN_CALL_INFO_RESP_MSG;
+
+typedef struct _QMIWDS_DUN_CALL_INFO_IND_MSG
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  ConnectionStatus;
+} QMIWDS_DUN_CALL_INFO_IND_MSG, *PQMIWDS_DUN_CALL_INFO_IND_MSG;
+
+typedef struct _QMIWDS_GET_CURRENT_CHANNEL_RATE_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0040
+   USHORT Length;
+} QMIWDS_GET_CURRENT_CHANNEL_RATE_REQ_MSG, *PQMIWDS_GET_CURRENT_CHANNEL_RATE_REQ_MSG;
+
+typedef struct _QMIWDS_GET_CURRENT_CHANNEL_RATE_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0040
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+
+   UCHAR  TLV2Type;         // 0x01
+   USHORT TLV2Length;       // 16
+   //ULONG  CallHandle;       // Context corresponding to reported channel
+   ULONG  CurrentTxRate;       // bps
+   ULONG  CurrentRxRate;       // bps
+   ULONG  ServingSystemTxRate; // bps
+   ULONG  ServingSystemRxRate; // bps
+
+} QMIWDS_GET_CURRENT_CHANNEL_RATE_RESP_MSG, *PQMIWDS_GET_CURRENT_CHANNEL_RATE_RESP;
+
+#define QWDS_EVENT_REPORT_MASK_RATES 0x01
+#define QWDS_EVENT_REPORT_MASK_STATS 0x02
+
+#ifdef QCUSB_MUX_PROTOCOL
+#error code not present
+#endif // QCUSB_MUX_PROTOCOL
+
+typedef struct _QMIWDS_SET_EVENT_REPORT_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0042
+   USHORT Length;
+
+   UCHAR  TLVType;          // 0x10 -- current channel rate indicator
+   USHORT TLVLength;        // 1
+   UCHAR  Mode;             // 0-do not report; 1-report when rate changes
+
+   UCHAR  TLV2Type;         // 0x11
+   USHORT TLV2Length;       // 5
+   UCHAR  StatsPeriod;      // seconds between reports; 0-do not report
+   ULONG  StatsMask;        //
+
+   UCHAR  TLV3Type;          // 0x12 -- current data bearer indicator
+   USHORT TLV3Length;        // 1
+   UCHAR  Mode3;             // 0-do not report; 1-report when changes
+
+   UCHAR  TLV4Type;          // 0x13 -- dormancy status indicator
+   USHORT TLV4Length;        // 1
+   UCHAR  DormancyStatus;    // 0-do not report; 1-report when changes
+} QMIWDS_SET_EVENT_REPORT_REQ_MSG, *PQMIWDS_SET_EVENT_REPORT_REQ_MSG;
+
+typedef struct _QMIWDS_SET_EVENT_REPORT_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0042
+   USHORT Length;
+
+   UCHAR  TLVType;          // 0x02 result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_NO_BATTERY
+                            // QMI_ERR_FAULT
+} QMIWDS_SET_EVENT_REPORT_RESP_MSG, *PQMIWDS_SET_EVENT_REPORT_RESP_MSG;
+
+typedef struct _QMIWDS_EVENT_REPORT_IND_MSG
+{
+   USHORT Type;             // QMUX type 0x0001
+   USHORT Length;
+} QMIWDS_EVENT_REPORT_IND_MSG, *PQMIWDS_EVENT_REPORT_IND_MSG;
+
+// PQCTLV_PKT_STATISTICS
+
+typedef struct _QMIWDS_EVENT_REPORT_IND_CHAN_RATE_TLV
+{
+   UCHAR  Type;
+   USHORT Length;  // 8
+   ULONG  TxRate;
+   ULONG  RxRate;
+} QMIWDS_EVENT_REPORT_IND_CHAN_RATE_TLV, *PQMIWDS_EVENT_REPORT_IND_CHAN_RATE_TLV;
+
+#ifdef QCUSB_MUX_PROTOCOL
+#error code not present
+#endif // QCUSB_MUX_PROTOCOL
+
+typedef struct _QMIWDS_GET_PKT_STATISTICS_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0041
+   USHORT Length;
+   UCHAR  TLVType;          // 0x01
+   USHORT TLVLength;        // 4
+   ULONG  StateMask;        // 0x00000001  tx success packets
+                            // 0x00000002  rx success packets
+                            // 0x00000004  rx packet errors (checksum)
+                            // 0x00000008  rx packets dropped (memory)
+
+} QMIWDS_GET_PKT_STATISTICS_REQ_MSG, *PQMIWDS_GET_PKT_STATISTICS_REQ_MSG;
+
+typedef struct _QMIWDS_GET_PKT_STATISTICS_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0041
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+} QMIWDS_GET_PKT_STATISTICS_RESP_MSG, *PQMIWDS_GET_PKT_STATISTICS_RESP_MSG;
+
+// optional TLV for stats
+typedef struct _QCTLV_PKT_STATISTICS
+{
+   UCHAR  TLVType;          // see above definitions for TLV types
+   USHORT TLVLength;        // 4
+   ULONG  Count;
+} QCTLV_PKT_STATISTICS, *PQCTLV_PKT_STATISTICS;
+#endif
+
+//#ifdef QC_IP_MODE
+
+#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4DNS_ADDR 0x0010
+#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4_ADDR 0x0100
+#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4GATEWAY_ADDR 0x0200
+#define QMIWDS_GET_RUNTIME_SETTINGS_MASK_MTU              0x2000
+
+typedef struct _QMIWDS_GET_RUNTIME_SETTINGS_REQ_MSG
+{
+   USHORT Type;            // QMIWDS_GET_RUNTIME_SETTINGS_REQ
+   USHORT Length;
+   UCHAR  TLVType;         // 0x10
+   USHORT TLVLength;       // 0x0004
+   ULONG  Mask;            // mask, bit 8: IP addr -- 0x0100
+} __attribute__ ((packed)) QMIWDS_GET_RUNTIME_SETTINGS_REQ_MSG, *PQMIWDS_GET_RUNTIME_SETTINGS_REQ_MSG;
+
+typedef struct _QMIWDS_BIND_MUX_DATA_PORT_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   ULONG  ep_type;
+   ULONG  iface_id;
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   UCHAR  MuxId;
+   UCHAR  TLV3Type;
+   USHORT TLV3Length;
+   ULONG  client_type;
+} __attribute__ ((packed)) QMIWDS_BIND_MUX_DATA_PORT_REQ_MSG, *PQMIWDS_BIND_MUX_DATA_PORT_REQ_MSG;
+
+#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4PRIMARYDNS 0x15
+#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4SECONDARYDNS 0x16
+#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4 0x1E
+#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4GATEWAY 0x20
+#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4SUBNET 0x21
+
+#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6             0x25
+#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6GATEWAY      0x26
+#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6PRIMARYDNS   0x27
+#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6SECONDARYDNS 0x28
+#define QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_MTU              0x29
+
+typedef struct _QMIWDS_GET_RUNTIME_SETTINGS_TLV_MTU
+{
+   UCHAR  TLVType;         // QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_MTU
+   USHORT TLVLength;       // 4
+   ULONG  Mtu;             // MTU
+} __attribute__ ((packed)) QMIWDS_GET_RUNTIME_SETTINGS_TLV_MTU, *PQMIWDS_GET_RUNTIME_SETTINGS_TLV_MTU;
+
+typedef struct _QMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR
+{
+   UCHAR  TLVType;         // QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4
+   USHORT TLVLength;       // 4
+   ULONG  IPV4Address;     // address
+} __attribute__ ((packed)) QMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR, *PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR;
+
+typedef struct _QMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR
+{
+   UCHAR  TLVType;         // QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6
+   USHORT TLVLength;       // 16
+   UCHAR  IPV6Address[16]; // address
+   UCHAR  PrefixLength;    // prefix length
+} __attribute__ ((packed)) QMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR, *PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR;
+
+typedef struct _QMIWDS_GET_RUNTIME_SETTINGS_RESP_MSG
+{
+   USHORT Type;            // QMIWDS_GET_RUNTIME_SETTINGS_RESP
+   USHORT Length;
+   UCHAR  TLVType;         // QCTLV_TYPE_RESULT_CODE
+   USHORT TLVLength;       // 0x0004
+   USHORT QMUXResult;      // result code
+   USHORT QMUXError;       // error code
+} __attribute__ ((packed)) QMIWDS_GET_RUNTIME_SETTINGS_RESP_MSG, *PQMIWDS_GET_RUNTIME_SETTINGS_RESP_MSG;
+
+//#endif // QC_IP_MODE
+
+typedef struct _QMIWDS_IP_FAMILY_TLV
+{
+   UCHAR  TLVType;          // 0x12
+   USHORT TLVLength;        // 1
+   UCHAR  IpFamily;         // IPV4-0x04, IPV6-0x06
+} __attribute__ ((packed)) QMIWDS_IP_FAMILY_TLV, *PQMIWDS_IP_FAMILY_TLV;
+
+typedef struct _QMIWDS_PKT_SRVC_TLV
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  ConnectionStatus;
+   UCHAR  ReconfigReqd;
+} __attribute__ ((packed)) QMIWDS_PKT_SRVC_TLV, *PQMIWDS_PKT_SRVC_TLV;
+
+typedef struct _QMIWDS_CALL_END_REASON_TLV
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT CallEndReason;
+} __attribute__ ((packed)) QMIWDS_CALL_END_REASON_TLV, *PQMIWDS_CALL_END_REASON_TLV;
+
+typedef struct _QMIWDS_CALL_END_REASON_V_TLV
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT CallEndReasonType;
+   USHORT CallEndReason;
+} __attribute__ ((packed)) QMIWDS_CALL_END_REASON_V_TLV, *PQMIWDS_CALL_END_REASON_V_TLV;
+
+typedef struct _QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x004D
+   USHORT Length;
+   UCHAR  TLVType;          // 0x01
+   USHORT TLVLength;        // 1
+   UCHAR  IpPreference;     // IPV4-0x04, IPV6-0x06
+} __attribute__ ((packed)) QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ_MSG, *PQMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ_MSG;
+
+typedef struct _QMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0037
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS, QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INTERNAL, QMI_ERR_MALFORMED_MSG, QMI_ERR_INVALID_ARG
+} __attribute__ ((packed)) QMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP_MSG, *PQMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP_MSG;
+
+typedef struct _QMIWDS_SET_AUTO_CONNECT_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0051
+   USHORT Length;
+   UCHAR  TLVType;          // 0x01
+   USHORT TLVLength;        // 1
+   UCHAR  autoconnect_setting;     //  0x00 ?C Disabled, 0x01 ?C Enabled, 0x02 ?C Paused (resume on power cycle)
+} __attribute__ ((packed)) QMIWDS_SET_AUTO_CONNECT_REQ_MSG, *PQMIWDS_SET_AUTO_CONNECT_REQ_MSG;
+
+#if 0
+typedef struct _QMIWDS_GET_MIP_MODE_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0040
+   USHORT Length;
+} QMIWDS_GET_MIP_MODE_REQ_MSG, *PQMIWDS_GET_MIP_MODE_REQ_MSG;
+
+typedef struct _QMIWDS_GET_MIP_MODE_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0040
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+
+   UCHAR  TLV2Type;         // 0x01
+   USHORT TLV2Length;       // 20
+   UCHAR  MipMode;          //
+} QMIWDS_GET_MIP_MODE_RESP_MSG, *PQMIWDS_GET_MIP_MODE_RESP_MSG;
+#endif
+
+typedef struct _QMIWDS_TECHNOLOGY_PREFERECE
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  TechPreference;
+} __attribute__ ((packed)) QMIWDS_TECHNOLOGY_PREFERECE, *PQMIWDS_TECHNOLOGY_PREFERECE;
+
+typedef struct _QMIWDS_PROFILE_IDENTIFIER
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  ProfileIndex;
+} __attribute__ ((packed)) QMIWDS_PROFILE_IDENTIFIER, *PQMIWDS_PROFILE_IDENTIFIER;
+
+#if 0
+typedef struct _QMIWDS_IPADDRESS
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   ULONG  IPv4Address;
+}QMIWDS_IPADDRESS, *PQMIWDS_IPADDRESS;
+
+/*
+typedef struct _QMIWDS_UMTS_QOS
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  TrafficClass;
+   ULONG  MaxUplinkBitRate;
+   ULONG  MaxDownlinkBitRate;
+   ULONG  GuarUplinkBitRate;
+   ULONG  GuarDownlinkBitRate;
+   UCHAR  QOSDevOrder;
+   ULONG  MAXSDUSize;
+   UCHAR  SDUErrorRatio;
+   UCHAR  ResidualBerRatio;
+   UCHAR  DeliveryErrorSDUs;
+   ULONG  TransferDelay;
+   ULONG  TrafficHndPri;
+}QMIWDS_UMTS_QOS, *PQMIWDS_UMTS_QOS;
+
+typedef struct _QMIWDS_GPRS_QOS
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   ULONG  PrecedenceClass;
+   ULONG  DelayClass;
+   ULONG  ReliabilityClass;
+   ULONG  PeekThroClass;
+   ULONG  MeanThroClass;
+}QMIWDS_GPRS_QOS, *PQMIWDS_GPRS_QOS;
+*/
+#endif
+
+typedef struct _QMIWDS_PROFILENAME
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  ProfileName;
+} __attribute__ ((packed)) QMIWDS_PROFILENAME, *PQMIWDS_PROFILENAME;
+
+typedef struct _QMIWDS_PDPTYPE
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+// 0 ?C PDP-IP (IPv4)
+// 1 ?C PDP-PPP
+// 2 ?C PDP-IPv6
+// 3 ?C PDP-IPv4v6
+    UCHAR  PdpType;
+} __attribute__ ((packed)) QMIWDS_PDPTYPE, *PQMIWDS_PDPTYPE;
+
+typedef struct _QMIWDS_USERNAME
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  UserName;
+} __attribute__ ((packed)) QMIWDS_USERNAME, *PQMIWDS_USERNAME;
+
+typedef struct _QMIWDS_PASSWD
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  Passwd;
+} __attribute__ ((packed)) QMIWDS_PASSWD, *PQMIWDS_PASSWD;
+
+typedef struct _QMIWDS_AUTH_PREFERENCE
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  AuthPreference;
+} __attribute__ ((packed)) QMIWDS_AUTH_PREFERENCE, *PQMIWDS_AUTH_PREFERENCE;
+
+typedef struct _QMIWDS_APNNAME
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  ApnName;
+} __attribute__ ((packed)) QMIWDS_APNNAME, *PQMIWDS_APNNAME;
+
+typedef struct _QMIWDS_AUTOCONNECT
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  AutoConnect;
+} __attribute__ ((packed)) QMIWDS_AUTOCONNECT, *PQMIWDS_AUTOCONNECT;
+
+typedef struct _QMIWDS_START_NETWORK_INTERFACE_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+} __attribute__ ((packed)) QMIWDS_START_NETWORK_INTERFACE_REQ_MSG, *PQMIWDS_START_NETWORK_INTERFACE_REQ_MSG;
+
+typedef struct _QMIWDS_CALLENDREASON
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT Reason;
+}__attribute__ ((packed)) QMIWDS_CALLENDREASON, *PQMIWDS_CALLENDREASON;
+
+typedef struct _QMIWDS_START_NETWORK_INTERFACE_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0040
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+
+   UCHAR  TLV2Type;         // 0x01
+   USHORT TLV2Length;       // 20
+   ULONG  Handle;          //
+} __attribute__ ((packed)) QMIWDS_START_NETWORK_INTERFACE_RESP_MSG, *PQMIWDS_START_NETWORK_INTERFACE_RESP_MSG;
+
+typedef struct _QMIWDS_STOP_NETWORK_INTERFACE_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   ULONG  Handle;
+} __attribute__ ((packed)) QMIWDS_STOP_NETWORK_INTERFACE_REQ_MSG, *PQMIWDS_STOP_NETWORK_INTERFACE_REQ_MSG;
+
+typedef struct _QMIWDS_STOP_NETWORK_INTERFACE_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0040
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+
+} __attribute__ ((packed)) QMIWDS_STOP_NETWORK_INTERFACE_RESP_MSG, *PQMIWDS_STOP_NETWORK_INTERFACE_RESP_MSG;
+
+typedef struct _QMIWDS_GET_DEFAULT_SETTINGS_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  ProfileType;
+} __attribute__ ((packed)) QMIWDS_GET_DEFAULT_SETTINGS_REQ_MSG, *PQMIWDS_GET_DEFAULT_SETTINGS_REQ_MSG;
+
+typedef struct _QMIWDS_GET_DEFAULT_SETTINGS_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+} __attribute__ ((packed)) QMIWDS_GET_DEFAULT_SETTINGS_RESP_MSG, *PQMIWDS_GET_DEFAULT_SETTINGS_RESP_MSG;
+
+typedef struct _QMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  ProfileType;
+   UCHAR  ProfileIndex;
+} __attribute__ ((packed)) QMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG, *PQMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG;
+
+typedef struct _QMIWDS_MODIFY_PROFILE_SETTINGS_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+} __attribute__ ((packed)) QMIWDS_MODIFY_PROFILE_SETTINGS_RESP_MSG, *PQMIWDS_MODIFY_PROFILE_SETTINGS_RESP_MSG;
+
+typedef struct _QMIWDS_GET_PROFILE_SETTINGS_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  ProfileType;
+   UCHAR  ProfileIndex;
+} __attribute__ ((packed)) QMIWDS_GET_PROFILE_SETTINGS_REQ_MSG, *PQMIWDS_GET_PROFILE_SETTINGS_REQ_MSG;
+
+#if 0
+typedef struct _QMIWDS_EVENT_REPORT_IND_DATA_BEARER_TLV
+{
+   UCHAR  Type;
+   USHORT Length;
+   UCHAR  DataBearer;
+} QMIWDS_EVENT_REPORT_IND_DATA_BEARER_TLV, *PQMIWDS_EVENT_REPORT_IND_DATA_BEARER_TLV;
+
+typedef struct _QMIWDS_EVENT_REPORT_IND_DORMANCY_STATUS_TLV
+{
+   UCHAR  Type;
+   USHORT Length;
+   UCHAR  DormancyStatus;
+} QMIWDS_EVENT_REPORT_IND_DORMANCY_STATUS_TLV, *PQMIWDS_EVENT_REPORT_IND_DORMANCY_STATUS_TLV;
+
+
+typedef struct _QMIWDS_GET_DATA_BEARER_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0037
+   USHORT Length;
+} QMIWDS_GET_DATA_BEARER_REQ_MSG, *PQMIWDS_GET_DATA_BEARER_REQ_MSG;
+
+typedef struct _QMIWDS_GET_DATA_BEARER_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0037
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INTERNAL
+                            // QMI_ERR_MALFORMED_MSG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_OUT_OF_CALL
+                            // QMI_ERR_INFO_UNAVAILABLE
+   UCHAR  TLV2Type;         // 0x01
+   USHORT TLV2Length;       //
+   UCHAR  Technology;       //
+} QMIWDS_GET_DATA_BEARER_RESP_MSG, *PQMIWDS_GET_DATA_BEARER_RESP_MSG;
+#endif
+
+// ======================= DMS ==============================
+#define QMIDMS_SET_EVENT_REPORT_REQ           0x0001
+#define QMIDMS_SET_EVENT_REPORT_RESP          0x0001
+#define QMIDMS_EVENT_REPORT_IND               0x0001
+#define QMIDMS_GET_DEVICE_CAP_REQ             0x0020
+#define QMIDMS_GET_DEVICE_CAP_RESP            0x0020
+#define QMIDMS_GET_DEVICE_MFR_REQ             0x0021
+#define QMIDMS_GET_DEVICE_MFR_RESP            0x0021
+#define QMIDMS_GET_DEVICE_MODEL_ID_REQ        0x0022
+#define QMIDMS_GET_DEVICE_MODEL_ID_RESP       0x0022
+#define QMIDMS_GET_DEVICE_REV_ID_REQ          0x0023
+#define QMIDMS_GET_DEVICE_REV_ID_RESP         0x0023
+#define QMIDMS_GET_MSISDN_REQ                 0x0024
+#define QMIDMS_GET_MSISDN_RESP                0x0024
+#define QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ  0x0025
+#define QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP 0x0025
+#define QMIDMS_UIM_SET_PIN_PROTECTION_REQ     0x0027
+#define QMIDMS_UIM_SET_PIN_PROTECTION_RESP    0x0027
+#define QMIDMS_UIM_VERIFY_PIN_REQ             0x0028
+#define QMIDMS_UIM_VERIFY_PIN_RESP            0x0028
+#define QMIDMS_UIM_UNBLOCK_PIN_REQ            0x0029
+#define QMIDMS_UIM_UNBLOCK_PIN_RESP           0x0029
+#define QMIDMS_UIM_CHANGE_PIN_REQ             0x002A
+#define QMIDMS_UIM_CHANGE_PIN_RESP            0x002A
+#define QMIDMS_UIM_GET_PIN_STATUS_REQ         0x002B
+#define QMIDMS_UIM_GET_PIN_STATUS_RESP        0x002B
+#define QMIDMS_GET_DEVICE_HARDWARE_REV_REQ    0x002C
+#define QMIDMS_GET_DEVICE_HARDWARE_REV_RESP   0x002C
+#define QMIDMS_GET_OPERATING_MODE_REQ         0x002D
+#define QMIDMS_GET_OPERATING_MODE_RESP        0x002D
+#define QMIDMS_SET_OPERATING_MODE_REQ         0x002E
+#define QMIDMS_SET_OPERATING_MODE_RESP        0x002E
+#define QMIDMS_GET_ACTIVATED_STATUS_REQ       0x0031
+#define QMIDMS_GET_ACTIVATED_STATUS_RESP      0x0031
+#define QMIDMS_ACTIVATE_AUTOMATIC_REQ         0x0032
+#define QMIDMS_ACTIVATE_AUTOMATIC_RESP        0x0032
+#define QMIDMS_ACTIVATE_MANUAL_REQ            0x0033
+#define QMIDMS_ACTIVATE_MANUAL_RESP           0x0033
+#define QMIDMS_UIM_GET_ICCID_REQ              0x003C
+#define QMIDMS_UIM_GET_ICCID_RESP             0x003C
+#define QMIDMS_UIM_GET_CK_STATUS_REQ          0x0040
+#define QMIDMS_UIM_GET_CK_STATUS_RESP         0x0040
+#define QMIDMS_UIM_SET_CK_PROTECTION_REQ      0x0041
+#define QMIDMS_UIM_SET_CK_PROTECTION_RESP     0x0041
+#define QMIDMS_UIM_UNBLOCK_CK_REQ             0x0042
+#define QMIDMS_UIM_UNBLOCK_CK_RESP            0x0042
+#define QMIDMS_UIM_GET_IMSI_REQ               0x0043
+#define QMIDMS_UIM_GET_IMSI_RESP              0x0043
+#define QMIDMS_UIM_GET_STATE_REQ              0x0044
+#define QMIDMS_UIM_GET_STATE_RESP             0x0044
+#define QMIDMS_GET_BAND_CAP_REQ               0x0045
+#define QMIDMS_GET_BAND_CAP_RESP              0x0045
+
+#if 0
+typedef struct _QMIDMS_GET_DEVICE_MFR_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+} QMIDMS_GET_DEVICE_MFR_REQ_MSG, *PQMIDMS_GET_DEVICE_MFR_REQ_MSG;
+
+typedef struct _QMIDMS_GET_DEVICE_MFR_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;      // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;       // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+   UCHAR  TLV2Type;         // 0x01 - required parameter
+   USHORT TLV2Length;       // length of the mfr string
+   UCHAR  DeviceManufacturer; // first byte of string
+} QMIDMS_GET_DEVICE_MFR_RESP_MSG, *PQMIDMS_GET_DEVICE_MFR_RESP_MSG;
+
+typedef struct _QMIDMS_GET_DEVICE_MODEL_ID_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0004
+   USHORT Length;
+} QMIDMS_GET_DEVICE_MODEL_ID_REQ_MSG, *PQMIDMS_GET_DEVICE_MODEL_ID_REQ_MSG;
+
+typedef struct _QMIDMS_GET_DEVICE_MODEL_ID_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0004
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;      // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;       // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+   UCHAR  TLV2Type;         // 0x01 - required parameter
+   USHORT TLV2Length;       // length of the modem id string
+   UCHAR  DeviceModelID;    // device model id
+} QMIDMS_GET_DEVICE_MODEL_ID_RESP_MSG, *PQMIDMS_GET_DEVICE_MODEL_ID_RESP_MSG;
+#endif
+
+typedef struct _QMIDMS_GET_DEVICE_REV_ID_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0005
+   USHORT Length;
+} __attribute__ ((packed)) QMIDMS_GET_DEVICE_REV_ID_REQ_MSG, *PQMIDMS_GET_DEVICE_REV_ID_REQ_MSG;
+
+typedef struct _DEVICE_REV_ID
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  RevisionID;
+} __attribute__ ((packed)) DEVICE_REV_ID, *PDEVICE_REV_ID;
+
+#if 0
+typedef struct _QMIDMS_GET_DEVICE_REV_ID_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0023
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;      // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;       // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+} QMIDMS_GET_DEVICE_REV_ID_RESP_MSG, *PQMIDMS_GET_DEVICE_REV_ID_RESP_MSG;
+
+typedef struct _QMIDMS_GET_MSISDN_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+} QMIDMS_GET_MSISDN_REQ_MSG, *PQMIDMS_GET_MSISDN_REQ_MSG;
+
+typedef struct _QCTLV_DEVICE_VOICE_NUMBERS
+{
+   UCHAR  TLVType;            // as defined above
+   USHORT TLVLength;          // 4/7/7
+   UCHAR  VoideNumberString; // ESN, IMEI, or MEID
+
+} QCTLV_DEVICE_VOICE_NUMBERS, *PQCTLV_DEVICE_VOICE_NUMBERS;
+
+
+typedef struct _QMIDMS_GET_MSISDN_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+} QMIDMS_GET_MSISDN_RESP_MSG, *PQMIDMS_GET_MSISDN_RESP_MSG;
+#endif
+
+typedef struct _QMIDMS_UIM_GET_IMSI_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+} __attribute__ ((packed)) QMIDMS_UIM_GET_IMSI_REQ_MSG, *PQMIDMS_UIM_GET_IMSI_REQ_MSG;
+
+typedef struct _QMIDMS_UIM_GET_IMSI_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   UCHAR IMSI;
+} __attribute__ ((packed)) QMIDMS_UIM_GET_IMSI_RESP_MSG, *PQMIDMS_UIM_GET_IMSI_RESP_MSG;
+
+#if 0
+typedef struct _QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0007
+   USHORT Length;
+} QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ_MSG, *PQMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ_MSG;
+
+#define QCTLV_TYPE_SER_NUM_ESN  0x10
+#define QCTLV_TYPE_SER_NUM_IMEI 0x11
+#define QCTLV_TYPE_SER_NUM_MEID 0x12
+
+typedef struct _QCTLV_DEVICE_SERIAL_NUMBER
+{
+   UCHAR  TLVType;            // as defined above
+   USHORT TLVLength;          // 4/7/7
+   UCHAR  SerialNumberString; // ESN, IMEI, or MEID
+
+} QCTLV_DEVICE_SERIAL_NUMBER, *PQCTLV_DEVICE_SERIAL_NUMBER;
+
+typedef struct _QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0007
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;      // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;       // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+  // followed by optional TLV
+} QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP_MSG, *PQMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP;
+
+typedef struct _QMIDMS_GET_DMS_BAND_CAP
+{
+   USHORT  Type;
+   USHORT  Length;
+} QMIDMS_GET_BAND_CAP_REQ_MSG, *PQMIDMS_GET_BAND_CAP_REQ_MSG;
+
+typedef struct _QMIDMS_GET_BAND_CAP_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_NONE
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_MALFORMED_MSG
+                            // QMI_ERR_NO_MEMORY
+
+   UCHAR  TLV2Type;         // 0x01
+   USHORT TLV2Length;       // 2
+   ULONG64 BandCap;
+} QMIDMS_GET_BAND_CAP_RESP_MSG, *PQMIDMS_GET_BAND_CAP_RESP;
+
+typedef struct _QMIDMS_GET_DEVICE_CAP_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0002
+   USHORT Length;
+} QMIDMS_GET_DEVICE_CAP_REQ_MSG, *PQMIDMS_GET_DEVICE_CAP_REQ_MSG;
+
+typedef struct _QMIDMS_GET_DEVICE_CAP_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0002
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMUX_RESULT_SUCCESS
+                            // QMUX_RESULT_FAILURE
+   USHORT QMUXError;        // QMUX_ERR_INVALID_ARG
+                            // QMUX_ERR_NO_MEMORY
+                            // QMUX_ERR_INTERNAL
+                            // QMUX_ERR_FAULT
+   UCHAR  TLV2Type;         // 0x01
+   USHORT TLV2Length;       // 2
+
+   ULONG  MaxTxChannelRate;
+   ULONG  MaxRxChannelRate;
+   UCHAR  VoiceCap;
+   UCHAR  SimCap;
+
+   UCHAR  RadioIfListCnt;   // #elements in radio interface list
+   UCHAR  RadioIfList;      // N 1-byte elements
+} QMIDMS_GET_DEVICE_CAP_RESP_MSG, *PQMIDMS_GET_DEVICE_CAP_RESP_MSG;
+
+typedef struct _QMIDMS_GET_ACTIVATED_STATUS_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0002
+   USHORT Length;
+} QMIDMS_GET_ACTIVATED_STATUS_REQ_MSG, *PQMIDMS_GET_ACTIVATES_STATUD_REQ_MSG;
+
+typedef struct _QMIDMS_GET_ACTIVATED_STATUS_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0002
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMUX_RESULT_SUCCESS
+                            // QMUX_RESULT_FAILURE
+   USHORT QMUXError;        // QMUX_ERR_INVALID_ARG
+                            // QMUX_ERR_NO_MEMORY
+                            // QMUX_ERR_INTERNAL
+                            // QMUX_ERR_FAULT
+   UCHAR  TLV2Type;         // 0x01
+   USHORT TLV2Length;       // 2
+
+   USHORT ActivatedStatus;
+} QMIDMS_GET_ACTIVATED_STATUS_RESP_MSG, *PQMIDMS_GET_ACTIVATED_STATUS_RESP_MSG;
+
+typedef struct _QMIDMS_GET_OPERATING_MODE_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0002
+   USHORT Length;
+} QMIDMS_GET_OPERATING_MODE_REQ_MSG, *PQMIDMS_GET_OPERATING_MODE_REQ_MSG;
+
+typedef struct _OFFLINE_REASON
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT OfflineReason;
+} OFFLINE_REASON, *POFFLINE_REASON;
+
+typedef struct _HARDWARE_RESTRICTED_MODE
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  HardwareControlledMode;
+} HARDWARE_RESTRICTED_MODE, *PHARDWARE_RESTRICTED_MODE;
+
+typedef struct _QMIDMS_GET_OPERATING_MODE_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0002
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMUX_RESULT_SUCCESS
+                            // QMUX_RESULT_FAILURE
+   USHORT QMUXError;        // QMUX_ERR_INVALID_ARG
+                            // QMUX_ERR_NO_MEMORY
+                            // QMUX_ERR_INTERNAL
+                            // QMUX_ERR_FAULT
+   UCHAR  TLV2Type;         // 0x01
+   USHORT TLV2Length;       // 2
+
+   UCHAR  OperatingMode;
+} QMIDMS_GET_OPERATING_MODE_RESP_MSG, *PQMIDMS_GET_OPERATING_MODE_RESP_MSG;
+
+typedef struct _QMIDMS_UIM_GET_ICCID_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+} QMIDMS_UIM_GET_ICCID_REQ_MSG, *PQMIDMS_UIM_GET_ICCID_REQ_MSG;
+
+typedef struct _QMIDMS_UIM_GET_ICCID_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+   UCHAR  TLV2Type;         // 0x01 - required parameter
+   USHORT TLV2Length;       // var
+   UCHAR  ICCID;      // String of voice number
+} QMIDMS_UIM_GET_ICCID_RESP_MSG, *PQMIDMS_UIM_GET_ICCID_RESP_MSG;
+#endif
+
+typedef struct _QMIDMS_SET_OPERATING_MODE_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0002
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   UCHAR  OperatingMode;
+} __attribute__ ((packed)) QMIDMS_SET_OPERATING_MODE_REQ_MSG, *PQMIDMS_SET_OPERATING_MODE_REQ_MSG;
+
+typedef struct _QMIDMS_SET_OPERATING_MODE_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0002
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMUX_RESULT_SUCCESS
+                            // QMUX_RESULT_FAILURE
+   USHORT QMUXError;        // QMUX_ERR_INVALID_ARG
+                            // QMUX_ERR_NO_MEMORY
+                            // QMUX_ERR_INTERNAL
+                            // QMUX_ERR_FAULT
+} __attribute__ ((packed)) QMIDMS_SET_OPERATING_MODE_RESP_MSG, *PQMIDMS_SET_OPERATING_MODE_RESP_MSG;
+
+#if 0
+typedef struct _QMIDMS_ACTIVATE_AUTOMATIC_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        //
+   UCHAR  ActivateCodelen;
+   UCHAR  ActivateCode;
+} QMIDMS_ACTIVATE_AUTOMATIC_REQ_MSG, *PQMIDMS_ACTIVATE_AUTOMATIC_REQ_MSG;
+
+typedef struct _QMIDMS_ACTIVATE_AUTOMATIC_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+} QMIDMS_ACTIVATE_AUTOMATIC_RESP_MSG, *PQMIDMS_ACTIVATE_AUTOMATIC_RESP_MSG;
+
+
+typedef struct _SPC_MSG
+{
+   UCHAR SPC[6];
+   USHORT SID;
+} SPC_MSG, *PSPC_MSG;
+
+typedef struct _MDN_MSG
+{
+   UCHAR MDNLEN;
+   UCHAR MDN;
+} MDN_MSG, *PMDN_MSG;
+
+typedef struct _MIN_MSG
+{
+   UCHAR MINLEN;
+   UCHAR MIN;
+} MIN_MSG, *PMIN_MSG;
+
+typedef struct _PRL_MSG
+{
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        //
+   USHORT PRLLEN;
+   UCHAR PRL;
+} PRL_MSG, *PPRL_MSG;
+
+typedef struct _MN_HA_KEY_MSG
+{
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        //
+   UCHAR MN_HA_KEY_LEN;
+   UCHAR MN_HA_KEY;
+} MN_HA_KEY_MSG, *PMN_HA_KEY_MSG;
+
+typedef struct _MN_AAA_KEY_MSG
+{
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        //
+   UCHAR MN_AAA_KEY_LEN;
+   UCHAR MN_AAA_KEY;
+} MN_AAA_KEY_MSG, *PMN_AAA_KEY_MSG;
+
+typedef struct _QMIDMS_ACTIVATE_MANUAL_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        //
+   UCHAR  Value;
+} QMIDMS_ACTIVATE_MANUAL_REQ_MSG, *PQMIDMS_ACTIVATE_MANUAL_REQ_MSG;
+
+typedef struct _QMIDMS_ACTIVATE_MANUAL_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+} QMIDMS_ACTIVATE_MANUAL_RESP_MSG, *PQMIDMS_ACTIVATE_MANUAL_RESP_MSG;
+#endif
+
+typedef struct _QMIDMS_UIM_GET_STATE_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+} __attribute__ ((packed)) QMIDMS_UIM_GET_STATE_REQ_MSG, *PQMIDMS_UIM_GET_STATE_REQ_MSG;
+
+typedef struct _QMIDMS_UIM_GET_STATE_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   UCHAR  UIMState;
+} __attribute__ ((packed)) QMIDMS_UIM_GET_STATE_RESP_MSG, *PQMIDMS_UIM_GET_STATE_RESP_MSG;
+
+typedef struct _QMIDMS_UIM_GET_PIN_STATUS_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+} __attribute__ ((packed)) QMIDMS_UIM_GET_PIN_STATUS_REQ_MSG, *PQMIDMS_UIM_GET_PIN_STATUS_REQ_MSG;
+
+typedef struct _QMIDMS_UIM_PIN_STATUS
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  PINStatus;
+   UCHAR  PINVerifyRetriesLeft;
+   UCHAR  PINUnblockRetriesLeft;
+} __attribute__ ((packed)) QMIDMS_UIM_PIN_STATUS, *PQMIDMS_UIM_PIN_STATUS;
+
+#define QMI_PIN_STATUS_NOT_INIT      0
+#define QMI_PIN_STATUS_NOT_VERIF     1
+#define QMI_PIN_STATUS_VERIFIED      2
+#define QMI_PIN_STATUS_DISABLED      3
+#define QMI_PIN_STATUS_BLOCKED       4
+#define QMI_PIN_STATUS_PERM_BLOCKED  5
+#define QMI_PIN_STATUS_UNBLOCKED     6
+#define QMI_PIN_STATUS_CHANGED       7
+
+
+typedef struct _QMIDMS_UIM_GET_PIN_STATUS_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+   UCHAR PinStatus;
+} __attribute__ ((packed)) QMIDMS_UIM_GET_PIN_STATUS_RESP_MSG, *PQMIDMS_UIM_GET_PIN_STATUS_RESP_MSG;
+
+#if 0
+typedef struct _QMIDMS_UIM_GET_CK_STATUS_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  Facility;
+} QMIDMS_UIM_GET_CK_STATUS_REQ_MSG, *PQMIDMS_UIM_GET_CK_STATUS_REQ_MSG;
+
+
+typedef struct _QMIDMS_UIM_CK_STATUS
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  FacilityStatus;
+   UCHAR  FacilityVerifyRetriesLeft;
+   UCHAR  FacilityUnblockRetriesLeft;
+} QMIDMS_UIM_CK_STATUS, *PQMIDMS_UIM_CK_STATUS;
+
+typedef struct _QMIDMS_UIM_CK_OPERATION_STATUS
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  OperationBlocking;
+} QMIDMS_UIM_CK_OPERATION_STATUS, *PQMIDMS_UIM_CK_OPERATION_STATUS;
+
+typedef struct _QMIDMS_UIM_GET_CK_STATUS_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+   UCHAR  CkStatus;
+} QMIDMS_UIM_GET_CK_STATUS_RESP_MSG, *PQMIDMS_UIM_GET_CK_STATUS_RESP_MSG;
+#endif
+
+typedef struct _QMIDMS_UIM_VERIFY_PIN_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   UCHAR  PINID;
+   UCHAR  PINLen;
+   UCHAR  PINValue;
+} __attribute__ ((packed)) QMIDMS_UIM_VERIFY_PIN_REQ_MSG, *PQMIDMS_UIM_VERIFY_PIN_REQ_MSG;
+
+typedef struct _QMIDMS_UIM_VERIFY_PIN_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   UCHAR  PINVerifyRetriesLeft;
+   UCHAR  PINUnblockRetriesLeft;
+} __attribute__ ((packed)) QMIDMS_UIM_VERIFY_PIN_RESP_MSG, *PQMIDMS_UIM_VERIFY_PIN_RESP_MSG;
+
+#if 0
+typedef struct _QMIDMS_UIM_SET_PIN_PROTECTION_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   UCHAR  PINID;
+   UCHAR  ProtectionSetting;
+   UCHAR  PINLen;
+   UCHAR  PINValue;
+} QMIDMS_UIM_SET_PIN_PROTECTION_REQ_MSG, *PQMIDMS_UIM_SET_PIN_PROTECTION_REQ_MSG;
+
+typedef struct _QMIDMS_UIM_SET_PIN_PROTECTION_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   UCHAR  PINVerifyRetriesLeft;
+   UCHAR  PINUnblockRetriesLeft;
+} QMIDMS_UIM_SET_PIN_PROTECTION_RESP_MSG, *PQMIDMS_UIM_SET_PIN_PROTECTION_RESP_MSG;
+
+typedef struct _QMIDMS_UIM_SET_CK_PROTECTION_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  Facility;
+   UCHAR  FacilityState;
+   UCHAR  FacliltyLen;
+   UCHAR  FacliltyValue;
+} QMIDMS_UIM_SET_CK_PROTECTION_REQ_MSG, *PQMIDMS_UIM_SET_CK_PROTECTION_REQ_MSG;
+
+typedef struct _QMIDMS_UIM_SET_CK_PROTECTION_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   UCHAR  FacilityRetriesLeft;
+} QMIDMS_UIM_SET_CK_PROTECTION_RESP_MSG, *PQMIDMS_UIM_SET_CK_PROTECTION_RESP_MSG;
+
+
+typedef struct _UIM_PIN
+{
+   UCHAR  PinLength;
+   UCHAR  PinValue;
+} UIM_PIN, *PUIM_PIN;
+
+typedef struct _QMIDMS_UIM_CHANGE_PIN_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   UCHAR  PINID;
+   UCHAR  PinDetails;
+} QMIDMS_UIM_CHANGE_PIN_REQ_MSG, *PQMIDMS_UIM_CHANGE_PIN_REQ_MSG;
+
+typedef struct QMIDMS_UIM_CHANGE_PIN_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   UCHAR  PINVerifyRetriesLeft;
+   UCHAR  PINUnblockRetriesLeft;
+} QMIDMS_UIM_CHANGE_PIN_RESP_MSG, *PQMIDMS_UIM_CHANGE_PIN_RESP_MSG;
+
+typedef struct _UIM_PUK
+{
+   UCHAR  PukLength;
+   UCHAR  PukValue;
+} UIM_PUK, *PUIM_PUK;
+
+typedef struct _QMIDMS_UIM_UNBLOCK_PIN_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   UCHAR  PINID;
+   UCHAR  PinDetails;
+} QMIDMS_UIM_UNBLOCK_PIN_REQ_MSG, *PQMIDMS_UIM_BLOCK_PIN_REQ_MSG;
+
+typedef struct QMIDMS_UIM_UNBLOCK_PIN_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0024
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   UCHAR  PINVerifyRetriesLeft;
+   UCHAR  PINUnblockRetriesLeft;
+} QMIDMS_UIM_UNBLOCK_PIN_RESP_MSG, *PQMIDMS_UIM_UNBLOCK_PIN_RESP_MSG;
+
+typedef struct _QMIDMS_UIM_UNBLOCK_CK_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  Facility;
+   UCHAR  FacliltyUnblockLen;
+   UCHAR  FacliltyUnblockValue;
+} QMIDMS_UIM_UNBLOCK_CK_REQ_MSG, *PQMIDMS_UIM_BLOCK_CK_REQ_MSG;
+
+typedef struct QMIDMS_UIM_UNBLOCK_CK_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   UCHAR  FacilityUnblockRetriesLeft;
+} QMIDMS_UIM_UNBLOCK_CK_RESP_MSG, *PQMIDMS_UIM_UNBLOCK_CK_RESP_MSG;
+
+typedef struct _QMIDMS_SET_EVENT_REPORT_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+} QMIDMS_SET_EVENT_REPORT_REQ_MSG, *PQMIDMS_SET_EVENT_REPORT_REQ_MSG;
+
+typedef struct _QMIDMS_SET_EVENT_REPORT_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;      // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;       // QMI_ERR_INVALID_ARG
+} QMIDMS_SET_EVENT_REPORT_RESP_MSG, *PQMIDMS_SET_EVENT_REPORT_RESP_MSG;
+
+typedef struct _PIN_STATUS
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  ReportPinState;
+} PIN_STATUS, *PPIN_STATUS;
+
+typedef struct _POWER_STATUS
+{
+   UCHAR TLVType;
+   USHORT TLVLength;
+   UCHAR PowerStatus;
+   UCHAR BatteryLvl;
+} POWER_STATUS, *PPOWER_STATUS;
+
+typedef struct _ACTIVATION_STATE
+{
+   UCHAR TLVType;
+   USHORT TLVLength;
+   USHORT ActivationState;
+} ACTIVATION_STATE, *PACTIVATION_STATE;
+
+typedef struct _ACTIVATION_STATE_REQ
+{
+   UCHAR TLVType;
+   USHORT TLVLength;
+   UCHAR ActivationState;
+} ACTIVATION_STATE_REQ, *PACTIVATION_STATE_REQ;
+
+typedef struct _OPERATING_MODE
+{
+   UCHAR TLVType;
+   USHORT TLVLength;
+   UCHAR OperatingMode;
+} OPERATING_MODE, *POPERATING_MODE;
+
+typedef struct _UIM_STATE
+{
+   UCHAR TLVType;
+   USHORT TLVLength;
+   UCHAR UIMState;
+} UIM_STATE, *PUIM_STATE;
+
+typedef struct _WIRELESS_DISABLE_STATE
+{
+   UCHAR TLVType;
+   USHORT TLVLength;
+   UCHAR WirelessDisableState;
+} WIRELESS_DISABLE_STATE, *PWIRELESS_DISABLE_STATE;
+
+typedef struct _QMIDMS_EVENT_REPORT_IND_MSG
+{
+   USHORT Type;
+   USHORT Length;
+} QMIDMS_EVENT_REPORT_IND_MSG, *PQMIDMS_EVENT_REPORT_IND_MSG;
+#endif
+
+// ============================ END OF DMS ===============================
+
+// ======================= QOS ==============================
+typedef struct _MPIOC_DEV_INFO MPIOC_DEV_INFO, *PMPIOC_DEV_INFO;
+
+#define QMI_QOS_SET_EVENT_REPORT_REQ  0x0001
+#define QMI_QOS_SET_EVENT_REPORT_RESP 0x0001
+#define QMI_QOS_EVENT_REPORT_IND      0x0001
+
+#if 0
+typedef struct _QMI_QOS_SET_EVENT_REPORT_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0001
+   USHORT Length;
+   // UCHAR  TLVType;          // 0x01 - physical link state
+   // USHORT TLVLength;        // 1
+   // UCHAR  PhyLinkStatusRpt; // 0-enable; 1-disable
+   UCHAR  TLVType2;         // 0x02 = global flow reporting
+   USHORT TLVLength2;       // 1
+   UCHAR  GlobalFlowRpt;    // 1-enable; 0-disable
+} QMI_QOS_SET_EVENT_REPORT_REQ_MSG, *PQMI_QOS_SET_EVENT_REPORT_REQ_MSG;
+
+typedef struct _QMI_QOS_SET_EVENT_REPORT_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0010
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMUX_RESULT_SUCCESS
+                            // QMUX_RESULT_FAILURE
+   USHORT QMUXError;        // QMUX_ERR_INVALID_ARG
+                            // QMUX_ERR_NO_MEMORY
+                            // QMUX_ERR_INTERNAL
+                            // QMUX_ERR_FAULT
+} QMI_QOS_SET_EVENT_REPORT_RESP_MSG, *PQMI_QOS_SET_EVENT_REPORT_RESP_MSG;
+
+typedef struct _QMI_QOS_EVENT_REPORT_IND_MSG
+{
+   USHORT Type;             // QMUX type 0x0001
+   USHORT Length;
+   UCHAR  TLVs;
+} QMI_QOS_EVENT_REPORT_IND_MSG, *PQMI_QOS_EVENT_REPORT_IND_MSG;
+
+#define QOS_EVENT_RPT_IND_FLOW_ACTIVATED 0x01
+#define QOS_EVENT_RPT_IND_FLOW_MODIFIED  0x02
+#define QOS_EVENT_RPT_IND_FLOW_DELETED   0x03
+#define QOS_EVENT_RPT_IND_FLOW_SUSPENDED 0x04
+#define QOS_EVENT_RPT_IND_FLOW_ENABLED   0x05
+#define QOS_EVENT_RPT_IND_FLOW_DISABLED  0x06
+
+#define QOS_EVENT_RPT_IND_TLV_PHY_LINK_STATE_TYPE 0x01
+#define QOS_EVENT_RPT_IND_TLV_GLOBAL_FL_RPT_STATE 0x10
+#define QOS_EVENT_RPT_IND_TLV_GLOBAL_FL_RPT_TYPE  0x10
+#define QOS_EVENT_RPT_IND_TLV_TX_FLOW_TYPE        0x11
+#define QOS_EVENT_RPT_IND_TLV_RX_FLOW_TYPE        0x12
+#define QOS_EVENT_RPT_IND_TLV_TX_FILTER_TYPE      0x13
+#define QOS_EVENT_RPT_IND_TLV_RX_FILTER_TYPE      0x14
+#define QOS_EVENT_RPT_IND_TLV_FLOW_SPEC           0x10
+#define QOS_EVENT_RPT_IND_TLV_FILTER_SPEC         0x10
+
+typedef struct _QOS_EVENT_RPT_IND_TLV_PHY_LINK_STATE
+{
+   UCHAR  TLVType;       // 0x01
+   USHORT TLVLength;     // 1
+   UCHAR  PhyLinkState;  // 0-dormant, 1-active
+} QOS_EVENT_RPT_IND_TLV_PHY_LINK_STATE, *PQOS_EVENT_RPT_IND_TLV_PHY_LINK_STATE;
+
+typedef struct _QOS_EVENT_RPT_IND_TLV_GLOBAL_FL_RPT
+{
+   UCHAR  TLVType;       // 0x10
+   USHORT TLVLength;     // 6
+   ULONG  QosId;
+   UCHAR  NewFlow;       // 1: newly added flow; 0: existing flow
+   UCHAR  StateChange;   // 1: activated; 2: modified; 3: deleted;
+                         // 4: suspended(delete); 5: enabled; 6: disabled
+} QOS_EVENT_RPT_IND_TLV_GLOBAL_FL_RPT, *PQOS_EVENT_RPT_IND_TLV_GLOBAL_FL_RPT;
+
+// QOS Flow
+
+typedef struct _QOS_EVENT_RPT_IND_TLV_FLOW
+{
+   UCHAR  TLVType;       // 0x10-TX flow; 0x11-RX flow
+   USHORT TLVLength;     // var
+   // embedded TLV's
+} QOS_EVENT_RPT_IND_TLV_TX_FLOW, *PQOS_EVENT_RPT_IND_TLV_TX_FLOW;
+
+#define QOS_FLOW_TLV_IP_FLOW_IDX_TYPE                    0x10
+#define QOS_FLOW_TLV_IP_FLOW_TRAFFIC_CLASS_TYPE          0x11
+#define QOS_FLOW_TLV_IP_FLOW_DATA_RATE_MIN_MAX_TYPE      0x12
+#define QOS_FLOW_TLV_IP_FLOW_DATA_RATE_TOKEN_BUCKET_TYPE 0x13
+#define QOS_FLOW_TLV_IP_FLOW_LATENCY_TYPE                0x14
+#define QOS_FLOW_TLV_IP_FLOW_JITTER_TYPE                 0x15
+#define QOS_FLOW_TLV_IP_FLOW_PKT_ERR_RATE_TYPE           0x16
+#define QOS_FLOW_TLV_IP_FLOW_MIN_PKT_SIZE_TYPE           0x17
+#define QOS_FLOW_TLV_IP_FLOW_MAX_PKT_SIZE_TYPE           0x18
+#define QOS_FLOW_TLV_IP_FLOW_3GPP_BIT_ERR_RATE_TYPE      0x19
+#define QOS_FLOW_TLV_IP_FLOW_3GPP_TRAF_PRIORITY_TYPE     0x1A
+#define QOS_FLOW_TLV_IP_FLOW_3GPP2_PROFILE_ID_TYPE       0x1B
+
+typedef struct _QOS_FLOW_TLV_IP_FLOW_IDX
+{
+   UCHAR  TLVType;       // 0x10
+   USHORT TLVLength;     // 1
+   UCHAR  IpFlowIndex;
+}  QOS_FLOW_TLV_IP_FLOW_IDX, *PQOS_FLOW_TLV_IP_FLOW_IDX;
+
+typedef struct _QOS_FLOW_TLV_IP_FLOW_TRAFFIC_CLASS
+{
+   UCHAR  TLVType;       // 0x11
+   USHORT TLVLength;     // 1
+   UCHAR  TrafficClass;
+}  QOS_FLOW_TLV_IP_FLOW_TRAFFIC_CLASS, *PQOS_FLOW_TLV_IP_FLOW_TRAFFIC_CLASS;
+
+typedef struct _QOS_FLOW_TLV_IP_FLOW_DATA_RATE_MIN_MAX
+{
+   UCHAR  TLVType;       // 0x12
+   USHORT TLVLength;     // 8
+   ULONG  DataRateMax;
+   ULONG  GuaranteedRate;
+}  QOS_FLOW_TLV_IP_FLOW_DATA_RATE_MIN_MAX, *PQOS_FLOW_TLV_IP_FLOW_DATA_RATE_MIN_MAX;
+
+typedef struct _QOS_FLOW_TLV_IP_FLOW_DATA_RATE_TOKEN_BUCKET
+{
+   UCHAR  TLVType;       // 0x13
+   USHORT TLVLength;     // 12
+   ULONG  PeakRate;
+   ULONG  TokenRate;
+   ULONG  BucketSize;
+}  QOS_FLOW_TLV_IP_FLOW_DATA_RATE_TOKEN_BUCKET, *PQOS_FLOW_TLV_IP_FLOW_DATA_RATE_TOKEN_BUCKET;
+
+typedef struct _QOS_FLOW_TLV_IP_FLOW_LATENCY
+{
+   UCHAR  TLVType;       // 0x14
+   USHORT TLVLength;     // 4
+   ULONG  IpFlowLatency;
+}  QOS_FLOW_TLV_IP_FLOW_LATENCY, *PQOS_FLOW_TLV_IP_FLOW_LATENCY;
+
+typedef struct _QOS_FLOW_TLV_IP_FLOW_JITTER
+{
+   UCHAR  TLVType;       // 0x15
+   USHORT TLVLength;     // 4
+   ULONG  IpFlowJitter;
+}  QOS_FLOW_TLV_IP_FLOW_JITTER, *PQOS_FLOW_TLV_IP_FLOW_JITTER;
+
+typedef struct _QOS_FLOW_TLV_IP_FLOW_PKT_ERR_RATE
+{
+   UCHAR  TLVType;       // 0x16
+   USHORT TLVLength;     // 4
+   USHORT ErrRateMultiplier;
+   USHORT ErrRateExponent;
+}  QOS_FLOW_TLV_IP_FLOW_PKT_ERR_RATE, *PQOS_FLOW_TLV_IP_FLOW_PKT_ERR_RATE;
+
+typedef struct _QOS_FLOW_TLV_IP_FLOW_MIN_PKT_SIZE
+{
+   UCHAR  TLVType;       // 0x17
+   USHORT TLVLength;     // 4
+   ULONG  MinPolicedPktSize;
+}  QOS_FLOW_TLV_IP_FLOW_MIN_PKT_SIZE, *PQOS_FLOW_TLV_IP_FLOW_MIN_PKT_SIZE;
+
+typedef struct _QOS_FLOW_TLV_IP_FLOW_MAX_PKT_SIZE
+{
+   UCHAR  TLVType;       // 0x18
+   USHORT TLVLength;     // 4
+   ULONG  MaxAllowedPktSize;
+}  QOS_FLOW_TLV_IP_FLOW_MAX_PKT_SIZE, *PQOS_FLOW_TLV_IP_FLOW_MAX_PKT_SIZE;
+
+typedef struct _QOS_FLOW_TLV_IP_FLOW_3GPP_BIT_ERR_RATE
+{
+   UCHAR  TLVType;       // 0x19
+   USHORT TLVLength;     // 1
+   UCHAR  ResidualBitErrorRate;
+}  QOS_FLOW_TLV_IP_FLOW_3GPP_BIT_ERR_RATE, *PQOS_FLOW_TLV_IP_FLOW_3GPP_BIT_ERR_RATE;
+
+typedef struct _QOS_FLOW_TLV_IP_FLOW_3GPP_TRAF_PRIORITY
+{
+   UCHAR  TLVType;       // 0x1A
+   USHORT TLVLength;     // 1
+   UCHAR  TrafficHandlingPriority;
+}  QOS_FLOW_TLV_IP_FLOW_3GPP_TRAF_PRIORITY, *PQOS_FLOW_TLV_IP_FLOW_3GPP_TRAF_PRIORITY;
+
+typedef struct _QOS_FLOW_TLV_IP_FLOW_3GPP2_PROFILE_ID
+{
+   UCHAR  TLVType;       // 0x1B
+   USHORT TLVLength;     // 2
+   USHORT ProfileId;
+}  QOS_FLOW_TLV_IP_FLOW_3GPP2_PROFILE_ID, *PQOS_FLOW_TLV_IP_FLOW_3GPP2_PROFILE_ID;
+
+// QOS Filter
+
+#define QOS_FILTER_TLV_IP_FILTER_IDX_TYPE          0x10
+#define QOS_FILTER_TLV_IP_VERSION_TYPE             0x11
+#define QOS_FILTER_TLV_IPV4_SRC_ADDR_TYPE          0x12
+#define QOS_FILTER_TLV_IPV4_DEST_ADDR_TYPE         0x13
+#define QOS_FILTER_TLV_NEXT_HDR_PROTOCOL_TYPE      0x14
+#define QOS_FILTER_TLV_IPV4_TYPE_OF_SERVICE_TYPE   0x15
+#define QOS_FILTER_TLV_TCP_UDP_PORT_SRC_TCP_TYPE   0x1B
+#define QOS_FILTER_TLV_TCP_UDP_PORT_DEST_TCP_TYPE  0x1C
+#define QOS_FILTER_TLV_TCP_UDP_PORT_SRC_UDP_TYPE   0x1D
+#define QOS_FILTER_TLV_TCP_UDP_PORT_DEST_UDP_TYPE  0x1E
+#define QOS_FILTER_TLV_ICMP_FILTER_MSG_TYPE_TYPE   0x1F
+#define QOS_FILTER_TLV_ICMP_FILTER_MSG_CODE_TYPE   0x20
+#define QOS_FILTER_TLV_TCP_UDP_PORT_SRC_TYPE       0x24
+#define QOS_FILTER_TLV_TCP_UDP_PORT_DEST_TYPE      0x25
+
+typedef struct _QOS_EVENT_RPT_IND_TLV_FILTER
+{
+   UCHAR  TLVType;       // 0x12-TX filter; 0x13-RX filter
+   USHORT TLVLength;     // var
+   // embedded TLV's
+} QOS_EVENT_RPT_IND_TLV_RX_FILTER, *PQOS_EVENT_RPT_IND_TLV_RX_FILTER;
+
+typedef struct _QOS_FILTER_TLV_IP_FILTER_IDX
+{
+   UCHAR  TLVType;       // 0x10
+   USHORT TLVLength;     // 1
+   UCHAR  IpFilterIndex;
+}  QOS_FILTER_TLV_IP_FILTER_IDX, *PQOS_FILTER_TLV_IP_FILTER_IDX;
+
+typedef struct _QOS_FILTER_TLV_IP_VERSION
+{
+   UCHAR  TLVType;       // 0x11
+   USHORT TLVLength;     // 1
+   UCHAR  IpVersion;
+}  QOS_FILTER_TLV_IP_VERSION, *PQOS_FILTER_TLV_IP_VERSION;
+
+typedef struct _QOS_FILTER_TLV_IPV4_SRC_ADDR
+{
+   UCHAR  TLVType;       // 0x12
+   USHORT TLVLength;     // 8
+   ULONG  IpSrcAddr;
+   ULONG  IpSrcSubnetMask;
+}  QOS_FILTER_TLV_IPV4_SRC_ADDR, *PQOS_FILTER_TLV_IPV4_SRC_ADDR;
+
+typedef struct _QOS_FILTER_TLV_IPV4_DEST_ADDR
+{
+   UCHAR  TLVType;       // 0x13
+   USHORT TLVLength;     // 8
+   ULONG  IpDestAddr;
+   ULONG  IpDestSubnetMask;
+}  QOS_FILTER_TLV_IPV4_DEST_ADDR, *PQOS_FILTER_TLV_IPV4_DEST_ADDR;
+
+typedef struct _QOS_FILTER_TLV_NEXT_HDR_PROTOCOL
+{
+   UCHAR  TLVType;       // 0x14
+   USHORT TLVLength;     // 1
+   UCHAR  NextHdrProtocol;
+}  QOS_FILTER_TLV_NEXT_HDR_PROTOCOL, *PQOS_FILTER_TLV_NEXT_HDR_PROTOCOL;
+
+typedef struct _QOS_FILTER_TLV_IPV4_TYPE_OF_SERVICE
+{
+   UCHAR  TLVType;       // 0x15
+   USHORT TLVLength;     // 2
+   UCHAR  Ipv4TypeOfService;
+   UCHAR  Ipv4TypeOfServiceMask;
+}  QOS_FILTER_TLV_IPV4_TYPE_OF_SERVICE, *PQOS_FILTER_TLV_IPV4_TYPE_OF_SERVICE;
+
+typedef struct _QOS_FILTER_TLV_TCP_UDP_PORT
+{
+   UCHAR  TLVType;       // source port: 0x1B-TCP; 0x1D-UDP
+                         // dest port:   0x1C-TCP; 0x1E-UDP
+   USHORT TLVLength;     // 4
+   USHORT FilterPort;
+   USHORT FilterPortRange;
+}  QOS_FILTER_TLV_TCP_UDP_PORT, *PQOS_FILTER_TLV_TCP_UDP_PORT;
+
+typedef struct _QOS_FILTER_TLV_ICMP_FILTER_MSG_TYPE
+{
+   UCHAR  TLVType;       // 0x1F
+   USHORT TLVLength;     // 1
+   UCHAR  IcmpFilterMsgType;
+}  QOS_FILTER_TLV_ICMP_FILTER_MSG_TYPE, *PQOS_FILTER_TLV_ICMP_FILTER_MSG_TYPE;
+
+typedef struct _QOS_FILTER_TLV_ICMP_FILTER_MSG_CODE
+{
+   UCHAR  TLVType;       // 0x20
+   USHORT TLVLength;     // 1
+   UCHAR  IcmpFilterMsgCode;
+}  QOS_FILTER_TLV_ICMP_FILTER_MSG_CODE, *PQOS_FILTER_TLV_ICMP_FILTER_MSG_CODE;
+
+#define QOS_FILTER_PRECEDENCE_INVALID  256
+#define QOS_FILTER_TLV_PRECEDENCE_TYPE 0x22
+#define QOS_FILTER_TLV_ID_TYPE         0x23
+
+typedef struct _QOS_FILTER_TLV_PRECEDENCE
+{
+   UCHAR  TLVType;    // 0x22
+   USHORT TLVLength;  // 2
+   USHORT Precedence; // precedence of the filter
+}  QOS_FILTER_TLV_PRECEDENCE, *PQOS_FILTER_TLV_PRECEDENCE;
+
+typedef struct _QOS_FILTER_TLV_ID
+{
+   UCHAR  TLVType;    // 0x23
+   USHORT TLVLength;  // 2
+   USHORT FilterId;   // filter ID
+}  QOS_FILTER_TLV_ID, *PQOS_FILTER_TLV_ID;
+
+#ifdef QCQOS_IPV6
+
+#define QOS_FILTER_TLV_IPV6_SRC_ADDR_TYPE          0x16
+#define QOS_FILTER_TLV_IPV6_DEST_ADDR_TYPE         0x17
+#define QOS_FILTER_TLV_IPV6_NEXT_HDR_PROTOCOL_TYPE 0x14  // same as IPV4
+#define QOS_FILTER_TLV_IPV6_TRAFFIC_CLASS_TYPE     0x19
+#define QOS_FILTER_TLV_IPV6_FLOW_LABEL_TYPE        0x1A
+
+typedef struct _QOS_FILTER_TLV_IPV6_SRC_ADDR
+{
+   UCHAR  TLVType;       // 0x16
+   USHORT TLVLength;     // 17
+   UCHAR  IpSrcAddr[16];
+   UCHAR  IpSrcAddrPrefixLen;  // [0..128]
+}  QOS_FILTER_TLV_IPV6_SRC_ADDR, *PQOS_FILTER_TLV_IPV6_SRC_ADDR;
+
+typedef struct _QOS_FILTER_TLV_IPV6_DEST_ADDR
+{
+   UCHAR  TLVType;       // 0x17
+   USHORT TLVLength;     // 17
+   UCHAR  IpDestAddr[16];
+   UCHAR  IpDestAddrPrefixLen;  // [0..128]
+}  QOS_FILTER_TLV_IPV6_DEST_ADDR, *PQOS_FILTER_TLV_IPV6_DEST_ADDR;
+
+#define QOS_FILTER_IPV6_NEXT_HDR_PROTOCOL_TCP 0x06
+#define QOS_FILTER_IPV6_NEXT_HDR_PROTOCOL_UDP 0x11
+
+typedef struct _QOS_FILTER_TLV_IPV6_TRAFFIC_CLASS
+{
+   UCHAR  TLVType;       // 0x19
+   USHORT TLVLength;     // 2
+   UCHAR  TrafficClass;
+   UCHAR  TrafficClassMask; // compare the first 6 bits only
+}  QOS_FILTER_TLV_IPV6_TRAFFIC_CLASS, *PQOS_FILTER_TLV_IPV6_TRAFFIC_CLASS;
+
+typedef struct _QOS_FILTER_TLV_IPV6_FLOW_LABEL
+{
+   UCHAR  TLVType;       // 0x1A
+   USHORT TLVLength;     // 4
+   ULONG  FlowLabel;
+}  QOS_FILTER_TLV_IPV6_FLOW_LABEL, *PQOS_FILTER_TLV_IPV6_FLOW_LABEL;
+
+#endif // QCQOS_IPV6
+#endif
+
+// ======================= WMS ==============================
+#define QMIWMS_SET_EVENT_REPORT_REQ           0x0001
+#define QMIWMS_SET_EVENT_REPORT_RESP          0x0001
+#define QMIWMS_EVENT_REPORT_IND               0x0001
+#define QMIWMS_RAW_SEND_REQ                   0x0020
+#define QMIWMS_RAW_SEND_RESP                  0x0020
+#define QMIWMS_RAW_WRITE_REQ                  0x0021
+#define QMIWMS_RAW_WRITE_RESP                 0x0021
+#define QMIWMS_RAW_READ_REQ                   0x0022
+#define QMIWMS_RAW_READ_RESP                  0x0022
+#define QMIWMS_MODIFY_TAG_REQ                 0x0023
+#define QMIWMS_MODIFY_TAG_RESP                0x0023
+#define QMIWMS_DELETE_REQ                     0x0024
+#define QMIWMS_DELETE_RESP                    0x0024
+#define QMIWMS_GET_MESSAGE_PROTOCOL_REQ       0x0030
+#define QMIWMS_GET_MESSAGE_PROTOCOL_RESP      0x0030
+#define QMIWMS_LIST_MESSAGES_REQ              0x0031
+#define QMIWMS_LIST_MESSAGES_RESP             0x0031
+#define QMIWMS_GET_SMSC_ADDRESS_REQ           0x0034
+#define QMIWMS_GET_SMSC_ADDRESS_RESP          0x0034
+#define QMIWMS_SET_SMSC_ADDRESS_REQ           0x0035
+#define QMIWMS_SET_SMSC_ADDRESS_RESP          0x0035
+#define QMIWMS_GET_STORE_MAX_SIZE_REQ         0x0036
+#define QMIWMS_GET_STORE_MAX_SIZE_RESP        0x0036
+
+
+#define WMS_MESSAGE_PROTOCOL_CDMA             0x00
+#define WMS_MESSAGE_PROTOCOL_WCDMA            0x01
+
+#if 0
+typedef struct _QMIWMS_GET_MESSAGE_PROTOCOL_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+} QMIWMS_GET_MESSAGE_PROTOCOL_REQ_MSG, *PQMIWMS_GET_MESSAGE_PROTOCOL_REQ_MSG;
+
+typedef struct _QMIWMS_GET_MESSAGE_PROTOCOL_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   UCHAR  MessageProtocol;
+} QMIWMS_GET_MESSAGE_PROTOCOL_RESP_MSG, *PQMIWMS_GET_MESSAGE_PROTOCOL_RESP_MSG;
+
+typedef struct _QMIWMS_GET_STORE_MAX_SIZE_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  StorageType;
+} QMIWMS_GET_STORE_MAX_SIZE_REQ_MSG, *PQMIWMS_GET_STORE_MAX_SIZE_REQ_MSG;
+
+typedef struct _QMIWMS_GET_STORE_MAX_SIZE_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   ULONG  MemStoreMaxSize;
+} QMIWMS_GET_STORE_MAX_SIZE_RESP_MSG, *PQMIWMS_GET_STORE_MAX_SIZE_RESP_MSG;
+
+typedef struct _REQUEST_TAG
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  TagType;
+} REQUEST_TAG, *PREQUEST_TAG;
+
+typedef struct _QMIWMS_LIST_MESSAGES_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  StorageType;
+} QMIWMS_LIST_MESSAGES_REQ_MSG, *PQMIWMS_LIST_MESSAGES_REQ_MSG;
+
+typedef struct _QMIWMS_MESSAGE
+{
+   ULONG  MessageIndex;
+   UCHAR  TagType;
+} QMIWMS_MESSAGE, *PQMIWMS_MESSAGE;
+
+typedef struct _QMIWMS_LIST_MESSAGES_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   ULONG  NumMessages;
+} QMIWMS_LIST_MESSAGES_RESP_MSG, *PQMIWMS_LIST_MESSAGES_RESP_MSG;
+
+typedef struct _QMIWMS_RAW_READ_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  StorageType;
+   ULONG  MemoryIndex;
+} QMIWMS_RAW_READ_REQ_MSG, *PQMIWMS_RAW_READ_REQ_MSG;
+
+typedef struct _QMIWMS_RAW_READ_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   UCHAR  TagType;
+   UCHAR  Format;
+   USHORT MessageLength;
+   UCHAR  Message;
+} QMIWMS_RAW_READ_RESP_MSG, *PQMIWMS_RAW_READ_RESP_MSG;
+
+typedef struct _QMIWMS_MODIFY_TAG_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  StorageType;
+   ULONG  MemoryIndex;
+   UCHAR  TagType;
+} QMIWMS_MODIFY_TAG_REQ_MSG, *PQMIWMS_MODIFY_TAG_REQ_MSG;
+
+typedef struct _QMIWMS_MODIFY_TAG_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+} QMIWMS_MODIFY_TAG_RESP_MSG, *PQMIWMS_MODIFY_TAG_RESP_MSG;
+
+typedef struct _QMIWMS_RAW_SEND_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  SmsFormat;
+   USHORT SmsLength;
+   UCHAR  SmsMessage;
+} QMIWMS_RAW_SEND_REQ_MSG, *PQMIWMS_RAW_SEND_REQ_MSG;
+
+typedef struct _RAW_SEND_CAUSE_CODE
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT CauseCode;
+} RAW_SEND_CAUSE_CODE, *PRAW_SEND_CAUSE_CODE;
+
+
+typedef struct _QMIWMS_RAW_SEND_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+} QMIWMS_RAW_SEND_RESP_MSG, *PQMIWMS_RAW_SEND_RESP_MSG;
+
+
+typedef struct _WMS_DELETE_MESSAGE_INDEX
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   ULONG  MemoryIndex;
+} WMS_DELETE_MESSAGE_INDEX, *PWMS_DELETE_MESSAGE_INDEX;
+
+typedef struct _WMS_DELETE_MESSAGE_TAG
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  MessageTag;
+} WMS_DELETE_MESSAGE_TAG, *PWMS_DELETE_MESSAGE_TAG;
+
+typedef struct _QMIWMS_DELETE_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  StorageType;
+} QMIWMS_DELETE_REQ_MSG, *PQMIWMS_DELETE_REQ_MSG;
+
+typedef struct _QMIWMS_DELETE_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+} QMIWMS_DELETE_RESP_MSG, *PQMIWMS_DELETE_RESP_MSG;
+
+
+typedef struct _QMIWMS_GET_SMSC_ADDRESS_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+} QMIWMS_GET_SMSC_ADDRESS_REQ_MSG, *PQMIWMS_GET_SMSC_ADDRESS_REQ_MSG;
+
+typedef struct _QMIWMS_SMSC_ADDRESS
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  SMSCAddressType[3];
+   UCHAR  SMSCAddressLength;
+   UCHAR  SMSCAddressDigits;
+} QMIWMS_SMSC_ADDRESS, *PQMIWMS_SMSC_ADDRESS;
+
+
+typedef struct _QMIWMS_GET_SMSC_ADDRESS_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+   UCHAR  SMSCAddress;
+} QMIWMS_GET_SMSC_ADDRESS_RESP_MSG, *PQMIWMS_GET_SMSC_ADDRESS_RESP_MSG;
+
+typedef struct _QMIWMS_SET_SMSC_ADDRESS_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  SMSCAddress;
+} QMIWMS_SET_SMSC_ADDRESS_REQ_MSG, *PQMIWMS_SET_SMSC_ADDRESS_REQ_MSG;
+
+typedef struct _QMIWMS_SET_SMSC_ADDRESS_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+} QMIWMS_SET_SMSC_ADDRESS_RESP_MSG, *PQMIWMS_SET_SMSC_ADDRESS_RESP_MSG;
+
+typedef struct _QMIWMS_SET_EVENT_REPORT_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  ReportNewMessage;
+} QMIWMS_SET_EVENT_REPORT_REQ_MSG, *PQMIWMS_SET_EVENT_REPORT_REQ_MSG;
+
+typedef struct _QMIWMS_SET_EVENT_REPORT_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+} QMIWMS_SET_EVENT_REPORT_RESP_MSG, *PQMIWMS_SET_EVENT_REPORT_RESP_MSG;
+
+typedef struct _QMIWMS_EVENT_REPORT_IND_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  StorageType;
+   ULONG  StorageIndex;
+} QMIWMS_EVENT_REPORT_IND_MSG, *PQMIWMS_EVENT_REPORT_IND_MSG;
+#endif
+
+// ======================= End of WMS ==============================
+
+
+// ======================= NAS ==============================
+#define QMINAS_SET_EVENT_REPORT_REQ             0x0002
+#define QMINAS_SET_EVENT_REPORT_RESP            0x0002
+#define QMINAS_EVENT_REPORT_IND                 0x0002
+#define QMINAS_GET_SIGNAL_STRENGTH_REQ          0x0020
+#define QMINAS_GET_SIGNAL_STRENGTH_RESP         0x0020
+#define QMINAS_PERFORM_NETWORK_SCAN_REQ         0x0021
+#define QMINAS_PERFORM_NETWORK_SCAN_RESP        0x0021
+#define QMINAS_INITIATE_NW_REGISTER_REQ         0x0022
+#define QMINAS_INITIATE_NW_REGISTER_RESP        0x0022
+#define QMINAS_INITIATE_ATTACH_REQ              0x0023
+#define QMINAS_INITIATE_ATTACH_RESP             0x0023
+#define QMINAS_GET_SERVING_SYSTEM_REQ           0x0024
+#define QMINAS_GET_SERVING_SYSTEM_RESP          0x0024
+#define QMINAS_SERVING_SYSTEM_IND               0x0024
+#define QMINAS_GET_HOME_NETWORK_REQ             0x0025
+#define QMINAS_GET_HOME_NETWORK_RESP            0x0025
+#define QMINAS_GET_PREFERRED_NETWORK_REQ        0x0026
+#define QMINAS_GET_PREFERRED_NETWORK_RESP       0x0026
+#define QMINAS_SET_PREFERRED_NETWORK_REQ        0x0027
+#define QMINAS_SET_PREFERRED_NETWORK_RESP       0x0027
+#define QMINAS_GET_FORBIDDEN_NETWORK_REQ        0x0028
+#define QMINAS_GET_FORBIDDEN_NETWORK_RESP       0x0028
+#define QMINAS_SET_FORBIDDEN_NETWORK_REQ        0x0029
+#define QMINAS_SET_FORBIDDEN_NETWORK_RESP       0x0029
+#define QMINAS_SET_TECHNOLOGY_PREF_REQ          0x002A
+#define QMINAS_SET_TECHNOLOGY_PREF_RESP         0x002A
+#define QMINAS_GET_RF_BAND_INFO_REQ             0x0031
+#define QMINAS_GET_RF_BAND_INFO_RESP            0x0031
+#define QMINAS_GET_PLMN_NAME_REQ                0x0044
+#define QMINAS_GET_PLMN_NAME_RESP               0x0044
+#define MEIG_PACKET_TRANSFER_START_IND 0X100
+#define MEIG_PACKET_TRANSFER_END_IND 0X101
+#define QMINAS_GET_SYS_INFO_REQ                 0x004D
+#define QMINAS_GET_SYS_INFO_RESP                0x004D
+#define QMINAS_SYS_INFO_IND                     0x004D
+
+typedef struct _QMINAS_GET_HOME_NETWORK_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+} __attribute__ ((packed)) QMINAS_GET_HOME_NETWORK_REQ_MSG, *PQMINAS_GET_HOME_NETWORK_REQ_MSG;
+
+typedef struct _HOME_NETWORK_SYSTEMID
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT SystemID;
+   USHORT NetworkID;
+} __attribute__ ((packed)) HOME_NETWORK_SYSTEMID, *PHOME_NETWORK_SYSTEMID;
+
+typedef struct _HOME_NETWORK
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT MobileCountryCode;
+   USHORT MobileNetworkCode;
+   UCHAR  NetworkDesclen;
+   UCHAR  NetworkDesc;
+} __attribute__ ((packed)) HOME_NETWORK, *PHOME_NETWORK;
+
+#if 0
+typedef struct _HOME_NETWORK_EXT
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT MobileCountryCode;
+   USHORT MobileNetworkCode;
+   UCHAR  NetworkDescDisp;
+   UCHAR  NetworkDescEncoding;
+   UCHAR  NetworkDesclen;
+   UCHAR  NetworkDesc;
+} HOME_NETWORK_EXT, *PHOME_NETWORK_EXT;
+
+typedef struct _QMINAS_GET_HOME_NETWORK_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+} QMINAS_GET_HOME_NETWORK_RESP_MSG, *PQMINAS_GET_HOME_NETWORK_RESP_MSG;
+
+typedef struct _QMINAS_GET_PREFERRED_NETWORK_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+} QMINAS_GET_PREFERRED_NETWORK_REQ_MSG, *PQMINAS_GET_PREFERRED_NETWORK_REQ_MSG;
+
+
+typedef struct _PREFERRED_NETWORK
+{
+   USHORT MobileCountryCode;
+   USHORT MobileNetworkCode;
+   USHORT RadioAccess;
+} PREFERRED_NETWORK, *PPREFERRED_NETWORK;
+
+typedef struct _QMINAS_GET_PREFERRED_NETWORK_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;      // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;       // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+   UCHAR  TLV2Type;         // 0x01 - required parameter
+   USHORT TLV2Length;       // length of the mfr string
+   USHORT NumPreferredNetwork;
+} QMINAS_GET_PREFERRED_NETWORK_RESP_MSG, *PQMINAS_GET_PREFERRED_NETWORK_RESP_MSG;
+
+typedef struct _QMINAS_GET_FORBIDDEN_NETWORK_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+} QMINAS_GET_FORBIDDEN_NETWORK_REQ_MSG, *PQMINAS_GET_FORBIDDEN_NETWORK_REQ_MSG;
+
+typedef struct _FORBIDDEN_NETWORK
+{
+   USHORT MobileCountryCode;
+   USHORT MobileNetworkCode;
+} FORBIDDEN_NETWORK, *PFORBIDDEN_NETWORK;
+
+typedef struct _QMINAS_GET_FORBIDDEN_NETWORK_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;      // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;       // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+   UCHAR  TLV2Type;         // 0x01 - required parameter
+   USHORT TLV2Length;       // length of the mfr string
+   USHORT NumForbiddenNetwork;
+} QMINAS_GET_FORBIDDEN_NETWORK_RESP_MSG, *PQMINAS_GET_FORBIDDEN_NETWORK_RESP_MSG;
+
+typedef struct _QMINAS_GET_SERVING_SYSTEM_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+} QMINAS_GET_SERVING_SYSTEM_REQ_MSG, *PQMINAS_GET_SERVING_SYSTEM_REQ_MSG;
+
+typedef struct _QMINAS_ROAMING_INDICATOR_MSG
+{
+   UCHAR  TLVType;         // 0x01 - required parameter
+   USHORT TLVLength;       // length of the mfr string
+   UCHAR  RoamingIndicator;
+} QMINAS_ROAMING_INDICATOR_MSG, *PQMINAS_ROAMING_INDICATOR_MSG;
+#endif
+
+typedef struct _QMINAS_DATA_CAP
+{
+   UCHAR  TLVType;         // 0x01 - required parameter
+   USHORT TLVLength;       // length of the mfr string
+   UCHAR  DataCapListLen;
+   UCHAR  DataCap;
+} __attribute__ ((packed)) QMINAS_DATA_CAP, *PQMINAS_DATA_CAP;
+
+typedef struct _QMINAS_CURRENT_PLMN_MSG
+{
+   UCHAR  TLVType;         // 0x01 - required parameter
+   USHORT TLVLength;       // length of the mfr string
+   USHORT MobileCountryCode;
+   USHORT MobileNetworkCode;
+   UCHAR  NetworkDesclen;
+   UCHAR  NetworkDesc;
+} __attribute__ ((packed)) QMINAS_CURRENT_PLMN_MSG, *PQMINAS_CURRENT_PLMN_MSG;
+
+typedef struct _QMINAS_GET_SERVING_SYSTEM_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+} __attribute__ ((packed)) QMINAS_GET_SERVING_SYSTEM_RESP_MSG, *PQMINAS_GET_SERVING_SYSTEM_RESP_MSG;
+
+typedef struct _SERVING_SYSTEM
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  RegistrationState;
+   UCHAR  CSAttachedState;
+   UCHAR  PSAttachedState;
+   UCHAR  RegistredNetwork;
+   UCHAR  InUseRadioIF;
+   UCHAR  RadioIF;
+} __attribute__ ((packed)) SERVING_SYSTEM, *PSERVING_SYSTEM;
+
+typedef struct _QMINAS_GET_SYS_INFO_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+} __attribute__ ((packed)) QMINAS_GET_SYS_INFO_RESP_MSG, *PQMINAS_GET_SYS_INFO_RESP_MSG;
+
+typedef struct _QMINAS_SYS_INFO_IND_MSG
+{
+   USHORT Type;
+   USHORT Length;
+} __attribute__ ((packed)) QMINAS_SYS_INFO_IND_MSG, *PQMINAS_SYS_INFO_IND_MSG;
+
+typedef struct _SERVICE_STATUS_INFO
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  SrvStatus;
+   UCHAR  true_srv_status;
+   UCHAR  IsPrefDataPath;
+} __attribute__ ((packed)) SERVICE_STATUS_INFO, *PSERVICE_STATUS_INFO;
+
+typedef struct _CDMA_SYSTEM_INFO
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  SrvDomainValid;
+   UCHAR  SrvDomain;
+   UCHAR  SrvCapabilityValid;
+   UCHAR  SrvCapability;
+   UCHAR  RoamStatusValid;
+   UCHAR  RoamStatus;
+   UCHAR  IsSysForbiddenValid;
+   UCHAR  IsSysForbidden;
+   UCHAR  IsSysPrlMatchValid;
+   UCHAR  IsSysPrlMatch;
+   UCHAR  PRevInUseValid;
+   UCHAR  PRevInUse;
+   UCHAR  BSPRevValid;
+   UCHAR  BSPRev;
+   UCHAR  CCSSupportedValid;
+   UCHAR  CCSSupported;
+   UCHAR  CDMASysIdValid;
+   USHORT SID;
+   USHORT NID;
+   UCHAR  BSInfoValid;
+   USHORT BaseID;
+   ULONG  BaseLAT;
+   ULONG  BaseLONG;
+   UCHAR  PacketZoneValid;
+   USHORT PacketZone;
+   UCHAR  NetworkIdValid;
+   UCHAR  MCC[3];
+   UCHAR  MNC[3];
+} __attribute__ ((packed)) CDMA_SYSTEM_INFO, *PCDMA_SYSTEM_INFO;
+
+typedef struct _HDR_SYSTEM_INFO
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  SrvDomainValid;
+   UCHAR  SrvDomain;
+   UCHAR  SrvCapabilityValid;
+   UCHAR  SrvCapability;
+   UCHAR  RoamStatusValid;
+   UCHAR  RoamStatus;
+   UCHAR  IsSysForbiddenValid;
+   UCHAR  IsSysForbidden;
+   UCHAR  IsSysPrlMatchValid;
+   UCHAR  IsSysPrlMatch;
+   UCHAR  HdrPersonalityValid;
+   UCHAR  HdrPersonality;
+   UCHAR  HdrActiveProtValid;
+   UCHAR  HdrActiveProt;
+   UCHAR  is856SysIdValid;
+   UCHAR  is856SysId[16];
+} __attribute__ ((packed)) HDR_SYSTEM_INFO, *PHDR_SYSTEM_INFO;
+
+typedef struct _GSM_SYSTEM_INFO
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  SrvDomainValid;
+   UCHAR  SrvDomain;
+   UCHAR  SrvCapabilityValid;
+   UCHAR  SrvCapability;
+   UCHAR  RoamStatusValid;
+   UCHAR  RoamStatus;
+   UCHAR  IsSysForbiddenValid;
+   UCHAR  IsSysForbidden;
+   UCHAR  LacValid;
+   USHORT Lac;
+   UCHAR  CellIdValid;
+   ULONG  CellId;
+   UCHAR  RegRejectInfoValid;
+   UCHAR  RejectSrvDomain;
+   UCHAR  RejCause;
+   UCHAR  NetworkIdValid;
+   UCHAR  MCC[3];
+   UCHAR  MNC[3];
+   UCHAR  EgprsSuppValid;
+   UCHAR  EgprsSupp;
+   UCHAR  DtmSuppValid;
+   UCHAR  DtmSupp;
+} __attribute__ ((packed)) GSM_SYSTEM_INFO, *PGSM_SYSTEM_INFO;
+
+typedef struct _WCDMA_SYSTEM_INFO
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  SrvDomainValid;
+   UCHAR  SrvDomain;
+   UCHAR  SrvCapabilityValid;
+   UCHAR  SrvCapability;
+   UCHAR  RoamStatusValid;
+   UCHAR  RoamStatus;
+   UCHAR  IsSysForbiddenValid;
+   UCHAR  IsSysForbidden;
+   UCHAR  LacValid;
+   USHORT Lac;
+   UCHAR  CellIdValid;
+   ULONG  CellId;
+   UCHAR  RegRejectInfoValid;
+   UCHAR  RejectSrvDomain;
+   UCHAR  RejCause;
+   UCHAR  NetworkIdValid;
+   UCHAR  MCC[3];
+   UCHAR  MNC[3];
+   UCHAR  HsCallStatusValid;
+   UCHAR  HsCallStatus;
+   UCHAR  HsIndValid;
+   UCHAR  HsInd;
+   UCHAR  PscValid;
+   UCHAR  Psc;
+} __attribute__ ((packed)) WCDMA_SYSTEM_INFO, *PWCDMA_SYSTEM_INFO;
+
+typedef struct _LTE_SYSTEM_INFO
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  SrvDomainValid;
+   UCHAR  SrvDomain;
+   UCHAR  SrvCapabilityValid;
+   UCHAR  SrvCapability;
+   UCHAR  RoamStatusValid;
+   UCHAR  RoamStatus;
+   UCHAR  IsSysForbiddenValid;
+   UCHAR  IsSysForbidden;
+   UCHAR  LacValid;
+   USHORT Lac;
+   UCHAR  CellIdValid;
+   ULONG  CellId;
+   UCHAR  RegRejectInfoValid;
+   UCHAR  RejectSrvDomain;
+   UCHAR  RejCause;
+   UCHAR  NetworkIdValid;
+   UCHAR  MCC[3];
+   UCHAR  MNC[3];
+   UCHAR  TacValid;
+   USHORT Tac;
+} __attribute__ ((packed)) LTE_SYSTEM_INFO, *PLTE_SYSTEM_INFO;
+
+typedef struct _TDSCDMA_SYSTEM_INFO
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  SrvDomainValid;
+   UCHAR  SrvDomain;
+   UCHAR  SrvCapabilityValid;
+   UCHAR  SrvCapability;
+   UCHAR  RoamStatusValid;
+   UCHAR  RoamStatus;
+   UCHAR  IsSysForbiddenValid;
+   UCHAR  IsSysForbidden;
+   UCHAR  LacValid;
+   USHORT Lac;
+   UCHAR  CellIdValid;
+   ULONG  CellId;
+   UCHAR  RegRejectInfoValid;
+   UCHAR  RejectSrvDomain;
+   UCHAR  RejCause;
+   UCHAR  NetworkIdValid;
+   UCHAR  MCC[3];
+   UCHAR  MNC[3];
+   UCHAR  HsCallStatusValid;
+   UCHAR  HsCallStatus;
+   UCHAR  HsIndValid;
+   UCHAR  HsInd;
+   UCHAR  CellParameterIdValid;
+   USHORT CellParameterId;
+   UCHAR  CellBroadcastCapValid;
+   ULONG  CellBroadcastCap;
+   UCHAR  CsBarStatusValid;
+   ULONG  CsBarStatus;
+   UCHAR  PsBarStatusValid;
+   ULONG  PsBarStatus;
+   UCHAR  CipherDomainValid;
+   UCHAR  CipherDomain;
+} __attribute__ ((packed)) TDSCDMA_SYSTEM_INFO, *PTDSCDMA_SYSTEM_INFO;
+
+#if 0
+typedef struct _QMINAS_SERVING_SYSTEM_IND_MSG
+{
+   USHORT Type;
+   USHORT Length;
+} QMINAS_SERVING_SYSTEM_IND_MSG, *PQMINAS_SERVING_SYSTEM_IND_MSG;
+
+typedef struct _QMINAS_SET_PREFERRED_NETWORK_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT NumPreferredNetwork;
+   USHORT MobileCountryCode;
+   USHORT MobileNetworkCode;
+   USHORT RadioAccess;
+} QMINAS_SET_PREFERRED_NETWORK_REQ_MSG, *PQMINAS_SET_PREFERRED_NETWORK_REQ_MSG;
+
+typedef struct _QMINAS_SET_PREFERRED_NETWORK_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;      // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;       // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+} QMINAS_SET_PREFERRED_NETWORK_RESP_MSG, *PQMINAS_SET_PREFERRED_NETWORK_RESP_MSG;
+
+typedef struct _QMINAS_SET_FORBIDDEN_NETWORK_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT NumForbiddenNetwork;
+   USHORT MobileCountryCode;
+   USHORT MobileNetworkCode;
+} QMINAS_SET_FORBIDDEN_NETWORK_REQ_MSG, *PQMINAS_SET_FORBIDDEN_NETWORK_REQ_MSG;
+
+typedef struct _QMINAS_SET_FORBIDDEN_NETWORK_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;      // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;       // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+} QMINAS_SET_FORBIDDEN_NETWORK_RESP_MSG, *PQMINAS_SET_FORBIDDEN_NETWORK_RESP_MSG;
+
+typedef struct _QMINAS_PERFORM_NETWORK_SCAN_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+} QMINAS_PERFORM_NETWORK_SCAN_REQ_MSG, *PQMINAS_PERFORM_NETWORK_SCAN_REQ_MSG;
+
+typedef struct _VISIBLE_NETWORK
+{
+   USHORT MobileCountryCode;
+   USHORT MobileNetworkCode;
+   UCHAR  NetworkStatus;
+   UCHAR  NetworkDesclen;
+} VISIBLE_NETWORK, *PVISIBLE_NETWORK;
+
+typedef struct _QMINAS_PERFORM_NETWORK_SCAN_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;      // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;       // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+} QMINAS_PERFORM_NETWORK_SCAN_RESP_MSG, *PQMINAS_PERFORM_NETWORK_SCAN_RESP_MSG;
+
+typedef struct _QMINAS_PERFORM_NETWORK_SCAN_NETWORK_INFO
+{
+   UCHAR  TLVType;         // 0x010 - required parameter
+   USHORT TLVLength;       // length
+   USHORT NumNetworkInstances;
+} QMINAS_PERFORM_NETWORK_SCAN_NETWORK_INFO, *PQMINAS_PERFORM_NETWORK_SCAN_NETWORK_INFO;
+
+typedef struct _QMINAS_PERFORM_NETWORK_SCAN_RAT_INFO
+{
+   UCHAR  TLVType;         // 0x011 - required parameter
+   USHORT TLVLength;       // length
+   USHORT NumInst;
+} QMINAS_PERFORM_NETWORK_SCAN_RAT_INFO, *PQMINAS_PERFORM_NETWORK_SCAN_RAT_INFO;
+
+typedef struct _QMINAS_PERFORM_NETWORK_SCAN_RAT
+{
+   USHORT MCC;
+   USHORT MNC;
+   UCHAR  RAT;
+} QMINAS_PERFORM_NETWORK_SCAN_RAT, *PQMINAS_PERFORM_NETWORK_SCAN_RAT;
+
+
+typedef struct _QMINAS_MANUAL_NW_REGISTER
+{
+   UCHAR  TLV2Type;          // 0x02 - result code
+   USHORT TLV2Length;        // 4
+   USHORT MobileCountryCode;
+   USHORT MobileNetworkCode;
+   UCHAR  RadioAccess;
+} QMINAS_MANUAL_NW_REGISTER, *PQMINAS_MANUAL_NW_REGISTER;
+
+typedef struct _QMINAS_INITIATE_NW_REGISTER_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   UCHAR  RegisterAction;
+} QMINAS_INITIATE_NW_REGISTER_REQ_MSG, *PQMINAS_INITIATE_NW_REGISTER_REQ_MSG;
+
+typedef struct _QMINAS_INITIATE_NW_REGISTER_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;      // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;       // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+} QMINAS_INITIATE_NW_REGISTER_RESP_MSG, *PQMINAS_INITIATE_NW_REGISTER_RESP_MSG;
+
+typedef struct _QMINAS_SET_TECHNOLOGY_PREF_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT TechPref;
+   UCHAR  Duration;
+} QMINAS_SET_TECHNOLOGY_PREF_REQ_MSG, *PQMINAS_SET_TECHNOLOGY_PREF_REQ_MSG;
+
+typedef struct _QMINAS_SET_TECHNOLOGY_PREF_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;      // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;       // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+} QMINAS_SET_TECHNOLOGY_PREF_RESP_MSG, *PQMINAS_SET_TECHNOLOGY_PREF_RESP_MSG;
+
+typedef struct _QMINAS_GET_SIGNAL_STRENGTH_REQ_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+} QMINAS_GET_SIGNAL_STRENGTH_REQ_MSG, *PQMINAS_GET_SIGNAL_STRENGTH_REQ_MSG;
+
+typedef struct _QMINAS_SIGNAL_STRENGTH
+{
+   CHAR   SigStrength;
+   UCHAR  RadioIf;
+} QMINAS_SIGNAL_STRENGTH, *PQMINAS_SIGNAL_STRENGTH;
+
+typedef struct _QMINAS_SIGNAL_STRENGTH_LIST
+{
+   UCHAR  TLV3Type;
+   USHORT TLV3Length;
+   USHORT NumInstance;
+} QMINAS_SIGNAL_STRENGTH_LIST, *PQMINAS_SIGNAL_STRENGTH_LIST;
+
+
+typedef struct _QMINAS_GET_SIGNAL_STRENGTH_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;      // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;       // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   CHAR   SignalStrength;
+   UCHAR  RadioIf;
+} QMINAS_GET_SIGNAL_STRENGTH_RESP_MSG, *PQMINAS_GET_SIGNAL_STRENGTH_RESP_MSG;
+
+
+typedef struct _QMINAS_SET_EVENT_REPORT_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  ReportSigStrength;
+   UCHAR  NumTresholds;
+   CHAR   TresholdList[2];
+} QMINAS_SET_EVENT_REPORT_REQ_MSG, *PQMINAS_SET_EVENT_REPORT_REQ_MSG;
+
+typedef struct _QMINAS_SET_EVENT_REPORT_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;      // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;       // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+} QMINAS_SET_EVENT_REPORT_RESP_MSG, *PQMINAS_SET_EVENT_REPORT_RESP_MSG;
+
+typedef struct _QMINAS_SIGNAL_STRENGTH_TLV
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   CHAR   SigStrength;
+   UCHAR  RadioIf;
+} QMINAS_SIGNAL_STRENGTH_TLV, *PQMINAS_SIGNAL_STRENGTH_TLV;
+
+typedef struct _QMINAS_REJECT_CAUSE_TLV
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  ServiceDomain;
+   USHORT RejectCause;
+} QMINAS_REJECT_CAUSE_TLV, *PQMINAS_REJECT_CAUSE_TLV;
+
+typedef struct _QMINAS_EVENT_REPORT_IND_MSG
+{
+   USHORT Type;
+   USHORT Length;
+} QMINAS_EVENT_REPORT_IND_MSG, *PQMINAS_EVENT_REPORT_IND_MSG;
+
+typedef struct _QMINAS_GET_RF_BAND_INFO_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+} QMINAS_GET_RF_BAND_INFO_REQ_MSG, *PQMINAS_GET_RF_BAND_INFO_REQ_MSG;
+
+typedef struct _QMINASRF_BAND_INFO
+{
+   UCHAR  RadioIf;
+   USHORT ActiveBand;
+   USHORT ActiveChannel;
+} QMINASRF_BAND_INFO, *PQMINASRF_BAND_INFO;
+
+typedef struct _QMINAS_GET_RF_BAND_INFO_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;      // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;       // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   UCHAR  NumInstances;
+} QMINAS_GET_RF_BAND_INFO_RESP_MSG, *PQMINAS_GET_RF_BAND_INFO_RESP_MSG;
+
+
+typedef struct _QMINAS_GET_PLMN_NAME_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT MCC;
+   USHORT MNC;
+} QMINAS_GET_PLMN_NAME_REQ_MSG, *PQMINAS_GET_PLMN_NAME_REQ_MSG;
+
+typedef struct _QMINAS_GET_PLMN_NAME_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+} QMINAS_GET_PLMN_NAME_RESP_MSG, *PQMINAS_GET_PLMN_NAME_RESP_MSG;
+
+typedef struct _QMINAS_GET_PLMN_NAME_SPN
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  SPN_Enc;
+   UCHAR  SPN_Len;
+} QMINAS_GET_PLMN_NAME_SPN, *PQMINAS_GET_PLMN_NAME_SPN;
+
+typedef struct _QMINAS_GET_PLMN_NAME_PLMN
+{
+   UCHAR  PLMN_Enc;
+   UCHAR  PLMN_Ci;
+   UCHAR  PLMN_SpareBits;
+   UCHAR  PLMN_Len;
+} QMINAS_GET_PLMN_NAME_PLMN, *PQMINAS_GET_PLMN_NAME_PLMN;
+
+typedef struct _QMINAS_INITIATE_ATTACH_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  PsAttachAction;
+} QMINAS_INITIATE_ATTACH_REQ_MSG, *PQMINAS_INITIATE_ATTACH_REQ_MSG;
+
+typedef struct _QMINAS_INITIATE_ATTACH_RESP_MSG
+{
+   USHORT Type;             // QMUX type 0x0003
+   USHORT Length;
+   UCHAR  TLVType;          // 0x02 - result code
+   USHORT TLVLength;        // 4
+   USHORT QMUXResult;      // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   USHORT QMUXError;       // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+} QMINAS_INITIATE_ATTACH_RESP_MSG, *PQMINAS_INITIATE_ATTACH_RESP_MSG;
+#endif
+// ======================= End of NAS ==============================
+
+// ======================= UIM ==============================
+#define QMIUIM_READ_TRANSPARENT_REQ      0x0020
+#define QMIUIM_READ_TRANSPARENT_RESP     0x0020
+#define QMIUIM_READ_TRANSPARENT_IND      0x0020
+#define QMIUIM_READ_RECORD_REQ           0x0021
+#define QMIUIM_READ_RECORD_RESP          0x0021
+#define QMIUIM_READ_RECORD_IND           0x0021
+#define QMIUIM_WRITE_TRANSPARENT_REQ     0x0022
+#define QMIUIM_WRITE_TRANSPARENT_RESP    0x0022
+#define QMIUIM_WRITE_TRANSPARENT_IND     0x0022
+#define QMIUIM_WRITE_RECORD_REQ          0x0023
+#define QMIUIM_WRITE_RECORD_RESP         0x0023
+#define QMIUIM_WRITE_RECORD_IND          0x0023
+#define QMIUIM_SET_PIN_PROTECTION_REQ    0x0025
+#define QMIUIM_SET_PIN_PROTECTION_RESP   0x0025
+#define QMIUIM_SET_PIN_PROTECTION_IND    0x0025
+#define QMIUIM_VERIFY_PIN_REQ            0x0026
+#define QMIUIM_VERIFY_PIN_RESP           0x0026
+#define QMIUIM_VERIFY_PIN_IND            0x0026
+#define QMIUIM_UNBLOCK_PIN_REQ           0x0027
+#define QMIUIM_UNBLOCK_PIN_RESP          0x0027
+#define QMIUIM_UNBLOCK_PIN_IND           0x0027
+#define QMIUIM_CHANGE_PIN_REQ            0x0028
+#define QMIUIM_CHANGE_PIN_RESP           0x0028
+#define QMIUIM_CHANGE_PIN_IND            0x0028
+#define QMIUIM_DEPERSONALIZATION_REQ     0x0029
+#define QMIUIM_DEPERSONALIZATION_RESP    0x0029
+#define QMIUIM_EVENT_REG_REQ             0x002E
+#define QMIUIM_EVENT_REG_RESP            0x002E
+#define QMIUIM_GET_CARD_STATUS_REQ       0x002F
+#define QMIUIM_GET_CARD_STATUS_RESP      0x002F
+#define QMIUIM_STATUS_CHANGE_IND         0x0032
+
+
+typedef struct _QMIUIM_GET_CARD_STATUS_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+} __attribute__ ((packed)) QMIUIM_GET_CARD_STATUS_RESP_MSG, *PQMIUIM_GET_CARD_STATUS_RESP_MSG;
+
+typedef struct _QMIUIM_CARD_STATUS
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT IndexGWPri;
+   USHORT Index1XPri;
+   USHORT IndexGWSec;
+   USHORT Index1XSec;
+   UCHAR  NumSlot;
+   UCHAR  CardState;
+   UCHAR  UPINState;
+   UCHAR  UPINRetries;
+   UCHAR  UPUKRetries;
+   UCHAR  ErrorCode;
+   UCHAR  NumApp;
+   UCHAR  AppType;
+   UCHAR  AppState;
+   UCHAR  PersoState;
+   UCHAR  PersoFeature;
+   UCHAR  PersoRetries;
+   UCHAR  PersoUnblockRetries;
+   UCHAR  AIDLength;
+} __attribute__ ((packed)) QMIUIM_CARD_STATUS, *PQMIUIM_CARD_STATUS;
+
+typedef struct _QMIUIM_PIN_STATE
+{
+   UCHAR  UnivPIN;
+   UCHAR  PIN1State;
+   UCHAR  PIN1Retries;
+   UCHAR  PUK1Retries;
+   UCHAR  PIN2State;
+   UCHAR  PIN2Retries;
+   UCHAR  PUK2Retries;
+} __attribute__ ((packed)) QMIUIM_PIN_STATE, *PQMIUIM_PIN_STATE;
+
+typedef struct _QMIUIM_VERIFY_PIN_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  Session_Type;
+   UCHAR  Aid_Len;
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   UCHAR  PINID;
+   UCHAR  PINLen;
+   UCHAR  PINValue;
+} __attribute__ ((packed)) QMIUIM_VERIFY_PIN_REQ_MSG, *PQMIUIM_VERIFY_PIN_REQ_MSG;
+
+typedef struct _QMIUIM_VERIFY_PIN_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   UCHAR  PINVerifyRetriesLeft;
+   UCHAR  PINUnblockRetriesLeft;
+} __attribute__ ((packed)) QMIUIM_VERIFY_PIN_RESP_MSG, *PQMIUIM_VERIFY_PIN_RESP_MSG;
+
+typedef struct _QMIUIM_READ_TRANSPARENT_REQ_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   UCHAR  Session_Type;
+   UCHAR  Aid_Len;
+   UCHAR  TLV2Type;
+   USHORT TLV2Length;
+   USHORT file_id;
+   UCHAR  path_len;
+   UCHAR  path[];
+} __attribute__ ((packed)) QMIUIM_READ_TRANSPARENT_REQ_MSG, *PQMIUIM_READ_TRANSPARENT_REQ_MSG;
+
+typedef struct _READ_TRANSPARENT_TLV
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT Offset;
+   USHORT Length;
+} __attribute__ ((packed)) READ_TRANSPARENT_TLV, *PREAD_TRANSPARENT_TLV;
+
+typedef struct _QMIUIM_CONTENT
+{
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT content_len;
+   UCHAR  content[];
+} __attribute__ ((packed)) QMIUIM_CONTENT, *PQMIUIM_CONTENT;
+
+typedef struct _QMIUIM_READ_TRANSPARENT_RESP_MSG
+{
+   USHORT Type;
+   USHORT Length;
+   UCHAR  TLVType;
+   USHORT TLVLength;
+   USHORT QMUXResult;
+   USHORT QMUXError;
+} __attribute__ ((packed)) QMIUIM_READ_TRANSPARENT_RESP_MSG, *PQMIUIM_READ_TRANSPARENT_RESP_MSG;
+
+typedef struct _QMUX_MSG
+{
+   QCQMUX_HDR QMUXHdr;
+   union
+   {
+      // Message Header
+      QCQMUX_MSG_HDR                           QMUXMsgHdr;
+      QCQMUX_MSG_HDR_RESP                      QMUXMsgHdrResp;
+
+      // QMIWDS Message
+#if 0
+      QMIWDS_GET_PKT_SRVC_STATUS_REQ_MSG        PacketServiceStatusReq;
+      QMIWDS_GET_PKT_SRVC_STATUS_RESP_MSG       PacketServiceStatusRsp;
+      QMIWDS_GET_PKT_SRVC_STATUS_IND_MSG        PacketServiceStatusInd;
+      QMIWDS_EVENT_REPORT_IND_MSG               EventReportInd;
+      QMIWDS_GET_CURRENT_CHANNEL_RATE_REQ_MSG   GetCurrChannelRateReq;
+      QMIWDS_GET_CURRENT_CHANNEL_RATE_RESP_MSG  GetCurrChannelRateRsp;
+      QMIWDS_GET_PKT_STATISTICS_REQ_MSG         GetPktStatsReq;
+      QMIWDS_GET_PKT_STATISTICS_RESP_MSG        GetPktStatsRsp;
+      QMIWDS_SET_EVENT_REPORT_REQ_MSG           EventReportReq;
+      QMIWDS_SET_EVENT_REPORT_RESP_MSG          EventReportRsp;
+#endif
+      //#ifdef QC_IP_MODE
+      QMIWDS_GET_RUNTIME_SETTINGS_REQ_MSG       GetRuntimeSettingsReq;
+      QMIWDS_GET_RUNTIME_SETTINGS_RESP_MSG      GetRuntimeSettingsRsp;
+      //#endif // QC_IP_MODE
+      QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ_MSG  SetClientIpFamilyPrefReq;
+      QMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP_MSG SetClientIpFamilyPrefResp;
+      QMIWDS_SET_AUTO_CONNECT_REQ_MSG SetAutoConnectReq;
+#if 0
+      QMIWDS_GET_MIP_MODE_REQ_MSG               GetMipModeReq;
+      QMIWDS_GET_MIP_MODE_RESP_MSG              GetMipModeResp;
+#endif
+      QMIWDS_START_NETWORK_INTERFACE_REQ_MSG    StartNwInterfaceReq;
+      QMIWDS_START_NETWORK_INTERFACE_RESP_MSG   StartNwInterfaceResp;
+      QMIWDS_STOP_NETWORK_INTERFACE_REQ_MSG     StopNwInterfaceReq;
+      QMIWDS_STOP_NETWORK_INTERFACE_RESP_MSG    StopNwInterfaceResp;
+      QMIWDS_GET_DEFAULT_SETTINGS_REQ_MSG       GetDefaultSettingsReq;
+      QMIWDS_GET_DEFAULT_SETTINGS_RESP_MSG      GetDefaultSettingsResp;
+      QMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG    ModifyProfileSettingsReq;
+      QMIWDS_MODIFY_PROFILE_SETTINGS_RESP_MSG   ModifyProfileSettingsResp;
+      QMIWDS_GET_PROFILE_SETTINGS_REQ_MSG    GetProfileSettingsReq;
+#if 0
+      QMIWDS_GET_DATA_BEARER_REQ_MSG            GetDataBearerReq;
+      QMIWDS_GET_DATA_BEARER_RESP_MSG           GetDataBearerResp;
+      QMIWDS_DUN_CALL_INFO_REQ_MSG              DunCallInfoReq;
+      QMIWDS_DUN_CALL_INFO_RESP_MSG             DunCallInfoResp;
+#endif
+      QMIWDS_BIND_MUX_DATA_PORT_REQ_MSG         BindMuxDataPortReq;
+
+      // QMIDMS Messages
+#if 0
+      QMIDMS_GET_DEVICE_MFR_REQ_MSG             GetDeviceMfrReq;
+      QMIDMS_GET_DEVICE_MFR_RESP_MSG            GetDeviceMfrRsp;
+      QMIDMS_GET_DEVICE_MODEL_ID_REQ_MSG        GetDeviceModeIdReq;
+      QMIDMS_GET_DEVICE_MODEL_ID_RESP_MSG       GetDeviceModeIdRsp;
+      QMIDMS_GET_DEVICE_REV_ID_REQ_MSG          GetDeviceRevIdReq;
+      QMIDMS_GET_DEVICE_REV_ID_RESP_MSG         GetDeviceRevIdRsp;
+      QMIDMS_GET_MSISDN_REQ_MSG                 GetMsisdnReq;
+      QMIDMS_GET_MSISDN_RESP_MSG                GetMsisdnRsp;
+      QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ_MSG  GetDeviceSerialNumReq;
+      QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP_MSG GetDeviceSerialNumRsp;
+      QMIDMS_GET_DEVICE_CAP_REQ_MSG             GetDeviceCapReq;
+      QMIDMS_GET_DEVICE_CAP_RESP_MSG            GetDeviceCapResp;
+      QMIDMS_GET_BAND_CAP_REQ_MSG               GetBandCapReq;
+      QMIDMS_GET_BAND_CAP_RESP_MSG              GetBandCapRsp;
+      QMIDMS_GET_ACTIVATED_STATUS_REQ_MSG       GetActivatedStatusReq;
+      QMIDMS_GET_ACTIVATED_STATUS_RESP_MSG      GetActivatedStatusResp;
+      QMIDMS_GET_OPERATING_MODE_REQ_MSG         GetOperatingModeReq;
+      QMIDMS_GET_OPERATING_MODE_RESP_MSG        GetOperatingModeResp;
+#endif
+      QMIDMS_SET_OPERATING_MODE_REQ_MSG         SetOperatingModeReq;
+      QMIDMS_SET_OPERATING_MODE_RESP_MSG        SetOperatingModeResp;
+#if 0
+      QMIDMS_UIM_GET_ICCID_REQ_MSG              GetICCIDReq;
+      QMIDMS_UIM_GET_ICCID_RESP_MSG             GetICCIDResp;
+      QMIDMS_ACTIVATE_AUTOMATIC_REQ_MSG         ActivateAutomaticReq;
+      QMIDMS_ACTIVATE_AUTOMATIC_RESP_MSG        ActivateAutomaticResp;
+      QMIDMS_ACTIVATE_MANUAL_REQ_MSG            ActivateManualReq;
+      QMIDMS_ACTIVATE_MANUAL_RESP_MSG           ActivateManualResp;
+#endif
+      QMIDMS_UIM_GET_PIN_STATUS_REQ_MSG         UIMGetPinStatusReq;
+      QMIDMS_UIM_GET_PIN_STATUS_RESP_MSG        UIMGetPinStatusResp;
+      QMIDMS_UIM_VERIFY_PIN_REQ_MSG             UIMVerifyPinReq;
+      QMIDMS_UIM_VERIFY_PIN_RESP_MSG            UIMVerifyPinResp;
+#if 0
+      QMIDMS_UIM_SET_PIN_PROTECTION_REQ_MSG     UIMSetPinProtectionReq;
+      QMIDMS_UIM_SET_PIN_PROTECTION_RESP_MSG    UIMSetPinProtectionResp;
+      QMIDMS_UIM_CHANGE_PIN_REQ_MSG             UIMChangePinReq;
+      QMIDMS_UIM_CHANGE_PIN_RESP_MSG            UIMChangePinResp;
+      QMIDMS_UIM_UNBLOCK_PIN_REQ_MSG            UIMUnblockPinReq;
+      QMIDMS_UIM_UNBLOCK_PIN_RESP_MSG           UIMUnblockPinResp;
+      QMIDMS_SET_EVENT_REPORT_REQ_MSG           DmsSetEventReportReq;
+      QMIDMS_SET_EVENT_REPORT_RESP_MSG          DmsSetEventReportResp;
+      QMIDMS_EVENT_REPORT_IND_MSG               DmsEventReportInd;
+#endif
+      QMIDMS_UIM_GET_STATE_REQ_MSG              UIMGetStateReq;
+      QMIDMS_UIM_GET_STATE_RESP_MSG             UIMGetStateResp;
+      QMIDMS_UIM_GET_IMSI_REQ_MSG               UIMGetIMSIReq;
+      QMIDMS_UIM_GET_IMSI_RESP_MSG              UIMGetIMSIResp;
+#if 0
+      QMIDMS_UIM_GET_CK_STATUS_REQ_MSG          UIMGetCkStatusReq;
+      QMIDMS_UIM_GET_CK_STATUS_RESP_MSG         UIMGetCkStatusResp;
+      QMIDMS_UIM_SET_CK_PROTECTION_REQ_MSG      UIMSetCkProtectionReq;
+      QMIDMS_UIM_SET_CK_PROTECTION_RESP_MSG     UIMSetCkProtectionResp;
+      QMIDMS_UIM_UNBLOCK_CK_REQ_MSG             UIMUnblockCkReq;
+      QMIDMS_UIM_UNBLOCK_CK_RESP_MSG            UIMUnblockCkResp;
+#endif
+
+      // QMIQOS Messages
+#if 0
+      QMI_QOS_SET_EVENT_REPORT_REQ_MSG          QosSetEventReportReq;
+      QMI_QOS_SET_EVENT_REPORT_RESP_MSG         QosSetEventReportRsp;
+      QMI_QOS_EVENT_REPORT_IND_MSG              QosEventReportInd;
+#endif
+
+      // QMIWMS Messages
+#if 0
+      QMIWMS_GET_MESSAGE_PROTOCOL_REQ_MSG       GetMessageProtocolReq;
+      QMIWMS_GET_MESSAGE_PROTOCOL_RESP_MSG      GetMessageProtocolResp;
+      QMIWMS_GET_SMSC_ADDRESS_REQ_MSG           GetSMSCAddressReq;
+      QMIWMS_GET_SMSC_ADDRESS_RESP_MSG          GetSMSCAddressResp;
+      QMIWMS_SET_SMSC_ADDRESS_REQ_MSG           SetSMSCAddressReq;
+      QMIWMS_SET_SMSC_ADDRESS_RESP_MSG          SetSMSCAddressResp;
+      QMIWMS_GET_STORE_MAX_SIZE_REQ_MSG         GetStoreMaxSizeReq;
+      QMIWMS_GET_STORE_MAX_SIZE_RESP_MSG        GetStoreMaxSizeResp;
+      QMIWMS_LIST_MESSAGES_REQ_MSG              ListMessagesReq;
+      QMIWMS_LIST_MESSAGES_RESP_MSG             ListMessagesResp;
+      QMIWMS_RAW_READ_REQ_MSG                   RawReadMessagesReq;
+      QMIWMS_RAW_READ_RESP_MSG                  RawReadMessagesResp;
+      QMIWMS_SET_EVENT_REPORT_REQ_MSG           WmsSetEventReportReq;
+      QMIWMS_SET_EVENT_REPORT_RESP_MSG          WmsSetEventReportResp;
+      QMIWMS_EVENT_REPORT_IND_MSG               WmsEventReportInd;
+      QMIWMS_DELETE_REQ_MSG                     WmsDeleteReq;
+      QMIWMS_DELETE_RESP_MSG                    WmsDeleteResp;
+      QMIWMS_RAW_SEND_REQ_MSG                   RawSendMessagesReq;
+      QMIWMS_RAW_SEND_RESP_MSG                  RawSendMessagesResp;
+      QMIWMS_MODIFY_TAG_REQ_MSG                 WmsModifyTagReq;
+      QMIWMS_MODIFY_TAG_RESP_MSG                WmsModifyTagResp;
+#endif
+
+      // QMINAS Messages
+#if 0
+      QMINAS_GET_HOME_NETWORK_REQ_MSG           GetHomeNetworkReq;
+      QMINAS_GET_HOME_NETWORK_RESP_MSG          GetHomeNetworkResp;
+      QMINAS_GET_PREFERRED_NETWORK_REQ_MSG      GetPreferredNetworkReq;
+      QMINAS_GET_PREFERRED_NETWORK_RESP_MSG     GetPreferredNetworkResp;
+      QMINAS_GET_FORBIDDEN_NETWORK_REQ_MSG      GetForbiddenNetworkReq;
+      QMINAS_GET_FORBIDDEN_NETWORK_RESP_MSG     GetForbiddenNetworkResp;
+      QMINAS_GET_SERVING_SYSTEM_REQ_MSG         GetServingSystemReq;
+#endif
+      QMINAS_GET_SERVING_SYSTEM_RESP_MSG        GetServingSystemResp;
+      QMINAS_GET_SYS_INFO_RESP_MSG              GetSysInfoResp;
+      QMINAS_SYS_INFO_IND_MSG                   NasSysInfoInd;
+#if 0
+      QMINAS_SERVING_SYSTEM_IND_MSG             NasServingSystemInd;
+      QMINAS_SET_PREFERRED_NETWORK_REQ_MSG      SetPreferredNetworkReq;
+      QMINAS_SET_PREFERRED_NETWORK_RESP_MSG     SetPreferredNetworkResp;
+      QMINAS_SET_FORBIDDEN_NETWORK_REQ_MSG      SetForbiddenNetworkReq;
+      QMINAS_SET_FORBIDDEN_NETWORK_RESP_MSG     SetForbiddenNetworkResp;
+      QMINAS_PERFORM_NETWORK_SCAN_REQ_MSG       PerformNetworkScanReq;
+      QMINAS_PERFORM_NETWORK_SCAN_RESP_MSG      PerformNetworkScanResp;
+      QMINAS_INITIATE_NW_REGISTER_REQ_MSG       InitiateNwRegisterReq;
+      QMINAS_INITIATE_NW_REGISTER_RESP_MSG      InitiateNwRegisterResp;
+      QMINAS_SET_TECHNOLOGY_PREF_REQ_MSG        SetTechnologyPrefReq;
+      QMINAS_SET_TECHNOLOGY_PREF_RESP_MSG       SetTechnologyPrefResp;
+      QMINAS_GET_SIGNAL_STRENGTH_REQ_MSG        GetSignalStrengthReq;
+      QMINAS_GET_SIGNAL_STRENGTH_RESP_MSG       GetSignalStrengthResp;
+      QMINAS_SET_EVENT_REPORT_REQ_MSG           SetEventReportReq;
+      QMINAS_SET_EVENT_REPORT_RESP_MSG          SetEventReportResp;
+      QMINAS_EVENT_REPORT_IND_MSG               NasEventReportInd;
+      QMINAS_GET_RF_BAND_INFO_REQ_MSG           GetRFBandInfoReq;
+      QMINAS_GET_RF_BAND_INFO_RESP_MSG          GetRFBandInfoResp;
+      QMINAS_INITIATE_ATTACH_REQ_MSG            InitiateAttachReq;
+      QMINAS_INITIATE_ATTACH_RESP_MSG           InitiateAttachResp;
+      QMINAS_GET_PLMN_NAME_REQ_MSG              GetPLMNNameReq;
+      QMINAS_GET_PLMN_NAME_RESP_MSG             GetPLMNNameResp;
+#endif
+
+      // QMIUIM Messages
+      QMIUIM_GET_CARD_STATUS_RESP_MSG           UIMGetCardStatus;
+      QMIUIM_VERIFY_PIN_REQ_MSG                 UIMUIMVerifyPinReq;
+      QMIUIM_VERIFY_PIN_RESP_MSG                UIMUIMVerifyPinResp;
+#if 0
+      QMIUIM_SET_PIN_PROTECTION_REQ_MSG         UIMUIMSetPinProtectionReq;
+      QMIUIM_SET_PIN_PROTECTION_RESP_MSG        UIMUIMSetPinProtectionResp;
+      QMIUIM_CHANGE_PIN_REQ_MSG                 UIMUIMChangePinReq;
+      QMIUIM_CHANGE_PIN_RESP_MSG                UIMUIMChangePinResp;
+      QMIUIM_UNBLOCK_PIN_REQ_MSG                UIMUIMUnblockPinReq;
+      QMIUIM_UNBLOCK_PIN_RESP_MSG               UIMUIMUnblockPinResp;
+#endif
+      QMIUIM_READ_TRANSPARENT_REQ_MSG           UIMUIMReadTransparentReq;
+      QMIUIM_READ_TRANSPARENT_RESP_MSG          UIMUIMReadTransparentResp;
+
+      QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG      SetDataFormatReq;
+
+   };
+} __attribute__ ((packed)) QMUX_MSG, *PQMUX_MSG;
+
+#pragma pack(pop)
+
+#endif // MPQMUX_H
diff --git a/package/wwan/meig-cm/src/Makefile b/package/wwan/meig-cm/src/Makefile
new file mode 100644
index 000000000..cfe7aa1c7
--- /dev/null
+++ b/package/wwan/meig-cm/src/Makefile
@@ -0,0 +1,25 @@
+ifneq ($(CROSS_COMPILE),)
+CROSS-COMPILE:=$(CROSS_COMPILE)
+endif
+#CROSS-COMPILE:=/workspace/buildroot/buildroot-qemu_mips_malta_defconfig/output/host/usr/bin/mips-buildroot-linux-uclibc-
+#CROSS-COMPILE:=/workspace/buildroot/buildroot-qemu_arm_vexpress_defconfig/output/host/usr/bin/arm-buildroot-linux-uclibcgnueabi-
+#CROSS-COMPILE:=/workspace/buildroot-git/qemu_mips64_malta/output/host/usr/bin/mips-gnu-linux-
+ifeq ($(CC),cc)
+CC:=$(CROSS-COMPILE)gcc
+endif
+LD:=$(CROSS-COMPILE)ld
+
+MEIG_CM_SRC=QmiWwanCM.c GobiNetCM.c main.c MPQMUX.c QMIThread.c util.c udhcpc.c qmap_bridge_mode.c mbim-cm.c device.c
+
+release: clean qmi-proxy
+	$(CC) -Wall -s ${MEIG_CM_SRC} -o meig-cm -lpthread -ldl
+
+debug: clean
+	$(CC) -Wall -g -DCM_DEBUG ${MEIG_CM_SRC} -o meig-cm -lpthread -ldl
+
+qmi-proxy:
+	$(CC) -Wall -s meig-qmi-proxy.c  -o meig-qmi-proxy -lpthread -ldl
+
+clean:
+	rm -rf meig-cm *~
+	rm -rf meig-qmi-proxy
diff --git a/package/wwan/meig-cm/src/QMIThread.c b/package/wwan/meig-cm/src/QMIThread.c
new file mode 100644
index 000000000..2fadd5a27
--- /dev/null
+++ b/package/wwan/meig-cm/src/QMIThread.c
@@ -0,0 +1,1988 @@
+#include "QMIThread.h"
+extern char *strndup (const char *__string, size_t __n);
+
+#define qmi_rsp_check_and_return() do { \
+        if (err < 0 || pResponse == NULL) { \
+            dbg_time("%s err = %d", __func__, err); \
+            return err; \
+        } \
+        pMUXMsg = &pResponse->MUXMsg; \
+        if (le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXResult) || le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError)) { \
+            USHORT QMUXError = le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError); \
+            dbg_time("%s QMUXResult = 0x%x, QMUXError = 0x%x", __func__, \
+                le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXResult), QMUXError); \
+            free(pResponse); \
+            return QMUXError; \
+        } \
+} while(0)
+
+#define qmi_rsp_check() do { \
+        if (err < 0 || pResponse == NULL) { \
+            dbg_time("%s err = %d", __func__, err); \
+            return err; \
+        } \
+        pMUXMsg = &pResponse->MUXMsg; \
+        if (le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXResult) || le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError)) { \
+            USHORT QMUXError = le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError); \
+            dbg_time("%s QMUXResult = 0x%x, QMUXError = 0x%x", __func__, \
+                le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXResult), QMUXError); \
+        } \
+} while(0)
+
+int qmiclientId[QMUX_TYPE_WDS_ADMIN + 1]; //GobiNet use fd to indicate client ID, so type of qmiclientId must be int
+static uint32_t WdsConnectionIPv4Handle = 0;
+static uint32_t WdsConnectionIPv6Handle = 0;
+static int s_is_cdma = 0;
+static int s_hdr_personality = 0; // 0x01-HRPD, 0x02-eHRPD
+static char *qstrcpy(char *to, const char *from) { //no __strcpy_chk
+	char *save = to;
+	for (; (*to = *from) != '\0'; ++from, ++to);
+	return(save);
+}
+
+static int s_9x07 = -1;
+
+typedef USHORT (*CUSTOMQMUX)(PQMUX_MSG pMUXMsg, void *arg);
+
+// To retrieve the ith (Index) TLV
+PQMI_TLV_HDR GetTLV (PQCQMUX_MSG_HDR pQMUXMsgHdr, int TLVType) {
+    int TLVFind = 0;
+    USHORT Length = le16_to_cpu(pQMUXMsgHdr->Length);
+    PQMI_TLV_HDR pTLVHdr = (PQMI_TLV_HDR)(pQMUXMsgHdr + 1);
+
+    while (Length >= sizeof(QMI_TLV_HDR)) {
+        TLVFind++;
+        if (TLVType > 0x1000) {
+            if ((TLVFind + 0x1000) == TLVType)
+                return pTLVHdr;
+        } else  if (pTLVHdr->TLVType == TLVType) {
+            return pTLVHdr;
+        }
+
+        Length -= (le16_to_cpu((pTLVHdr->TLVLength)) + sizeof(QMI_TLV_HDR));
+        pTLVHdr = (PQMI_TLV_HDR)(((UCHAR *)pTLVHdr) + le16_to_cpu(pTLVHdr->TLVLength) + sizeof(QMI_TLV_HDR));
+    }
+
+   return NULL;
+}
+
+static USHORT GetQMUXTransactionId(void) {
+    static int TransactionId = 0;
+    if (++TransactionId > 0xFFFF)
+        TransactionId = 1;
+    return TransactionId;
+}
+
+static PQCQMIMSG ComposeQMUXMsg(UCHAR QMIType, USHORT Type, CUSTOMQMUX customQmuxMsgFunction, void *arg) {
+    UCHAR QMIBuf[WDM_DEFAULT_BUFSIZE];
+    PQCQMIMSG pRequest = (PQCQMIMSG)QMIBuf;
+    int Length;
+
+    memset(QMIBuf, 0x00, sizeof(QMIBuf));
+    pRequest->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI;
+    pRequest->QMIHdr.CtlFlags = 0x00;
+    pRequest->QMIHdr.QMIType = QMIType;
+    pRequest->QMIHdr.ClientId = qmiclientId[QMIType] & 0xFF;
+
+    if (qmiclientId[QMIType] == 0) {
+        dbg_time("QMIType %d has no clientID", QMIType);
+        return NULL;
+    }
+
+    pRequest->MUXMsg.QMUXHdr.CtlFlags = QMUX_CTL_FLAG_SINGLE_MSG | QMUX_CTL_FLAG_TYPE_CMD;
+    pRequest->MUXMsg.QMUXHdr.TransactionId = cpu_to_le16(GetQMUXTransactionId());
+    pRequest->MUXMsg.QMUXMsgHdr.Type = cpu_to_le16(Type);
+    if (customQmuxMsgFunction)
+        pRequest->MUXMsg.QMUXMsgHdr.Length = cpu_to_le16(customQmuxMsgFunction(&pRequest->MUXMsg, arg) - sizeof(QCQMUX_MSG_HDR));
+    else
+        pRequest->MUXMsg.QMUXMsgHdr.Length = cpu_to_le16(0x0000);
+
+    pRequest->QMIHdr.Length = cpu_to_le16(le16_to_cpu(pRequest->MUXMsg.QMUXMsgHdr.Length) + sizeof(QCQMUX_MSG_HDR) + sizeof(QCQMUX_HDR)
+        + sizeof(QCQMI_HDR) - 1);
+    Length = le16_to_cpu(pRequest->QMIHdr.Length) + 1;
+
+    pRequest = (PQCQMIMSG)malloc(Length);
+    if (pRequest == NULL) {
+        dbg_time("%s fail to malloc", __func__);
+    } else {
+        memcpy(pRequest, QMIBuf, Length);
+    }
+
+    return pRequest;
+}
+
+#if 0
+static USHORT NasSetEventReportReq(PQMUX_MSG pMUXMsg, void *arg) {
+    pMUXMsg->SetEventReportReq.TLVType = 0x10;
+    pMUXMsg->SetEventReportReq.TLVLength = 0x04;
+    pMUXMsg->SetEventReportReq.ReportSigStrength = 0x00;
+    pMUXMsg->SetEventReportReq.NumTresholds = 2;
+    pMUXMsg->SetEventReportReq.TresholdList[0] = -113;
+    pMUXMsg->SetEventReportReq.TresholdList[1] = -50;
+    return sizeof(QMINAS_SET_EVENT_REPORT_REQ_MSG);
+}
+
+static USHORT WdsSetEventReportReq(PQMUX_MSG pMUXMsg, void *arg) {
+    pMUXMsg->EventReportReq.TLVType = 0x10;          // 0x10 -- current channel rate indicator
+    pMUXMsg->EventReportReq.TLVLength = 0x0001;        // 1
+    pMUXMsg->EventReportReq.Mode = 0x00;             // 0-do not report; 1-report when rate changes
+
+    pMUXMsg->EventReportReq.TLV2Type = 0x11;         // 0x11
+    pMUXMsg->EventReportReq.TLV2Length = 0x0005;       // 5
+    pMUXMsg->EventReportReq.StatsPeriod = 0x00;      // seconds between reports; 0-do not report
+    pMUXMsg->EventReportReq.StatsMask = 0x000000ff;        //
+
+    pMUXMsg->EventReportReq.TLV3Type = 0x12;          // 0x12 -- current data bearer indicator
+    pMUXMsg->EventReportReq.TLV3Length = 0x0001;        // 1
+    pMUXMsg->EventReportReq.Mode3 = 0x01;             // 0-do not report; 1-report when changes
+
+    pMUXMsg->EventReportReq.TLV4Type = 0x13;          // 0x13 -- dormancy status indicator
+    pMUXMsg->EventReportReq.TLV4Length = 0x0001;        // 1
+    pMUXMsg->EventReportReq.DormancyStatus = 0x00;    // 0-do not report; 1-report when changes
+    return sizeof(QMIWDS_SET_EVENT_REPORT_REQ_MSG);
+}
+
+static USHORT DmsSetEventReportReq(PQMUX_MSG pMUXMsg) {
+    PPIN_STATUS pPinState = (PPIN_STATUS)(&pMUXMsg->DmsSetEventReportReq + 1);
+    PUIM_STATE pUimState = (PUIM_STATE)(pPinState + 1);
+    // Pin State
+    pPinState->TLVType = 0x12;
+    pPinState->TLVLength = 0x01;
+    pPinState->ReportPinState = 0x01;
+    // UIM State
+    pUimState->TLVType = 0x15;
+    pUimState->TLVLength = 0x01;
+    pUimState->UIMState = 0x01;
+    return sizeof(QMIDMS_SET_EVENT_REPORT_REQ_MSG) + sizeof(PIN_STATUS) + sizeof(UIM_STATE);
+}
+#endif
+
+static USHORT WdsStartNwInterfaceReq(PQMUX_MSG pMUXMsg, void *arg) {
+    PQMIWDS_TECHNOLOGY_PREFERECE pTechPref;
+    PQMIWDS_AUTH_PREFERENCE pAuthPref;
+    PQMIWDS_USERNAME pUserName;
+    PQMIWDS_PASSWD pPasswd;
+    PQMIWDS_APNNAME pApnName;
+    PQMIWDS_IP_FAMILY_TLV pIpFamily;
+    USHORT TLVLength = 0;
+    UCHAR *pTLV;
+    PROFILE_T *profile = (PROFILE_T *)arg;
+    const char *profile_user = profile->user;
+    const char *profile_password = profile->password;
+    int profile_auth = profile->auth;
+
+    if (s_is_cdma && (profile_user == NULL || profile_user[0] == '\0') && (profile_password == NULL || profile_password[0] == '\0')) {
+        profile_user = "ctnet@mycdma.cn";
+        profile_password = "vnet.mobi";
+        profile_auth = 2; //chap
+    }
+
+    pTLV = (UCHAR *)(&pMUXMsg->StartNwInterfaceReq + 1);
+    pMUXMsg->StartNwInterfaceReq.Length = 0;
+
+    // Set technology Preferece
+    pTechPref = (PQMIWDS_TECHNOLOGY_PREFERECE)(pTLV + TLVLength);
+    pTechPref->TLVType = 0x30;
+    pTechPref->TLVLength = cpu_to_le16(0x01);
+    if (s_is_cdma == 0)
+        pTechPref->TechPreference = 0x01;
+    else
+        pTechPref->TechPreference = 0x02;
+    TLVLength +=(le16_to_cpu(pTechPref->TLVLength) + sizeof(QCQMICTL_TLV_HDR));
+
+    // Set APN Name
+    if (profile->apn && !s_is_cdma) { //cdma no apn
+        pApnName = (PQMIWDS_APNNAME)(pTLV + TLVLength);
+        pApnName->TLVType = 0x14;
+        pApnName->TLVLength = cpu_to_le16(strlen(profile->apn));
+        qstrcpy((char *)&pApnName->ApnName, profile->apn);
+        TLVLength +=(le16_to_cpu(pApnName->TLVLength) + sizeof(QCQMICTL_TLV_HDR));
+    }
+
+    // Set User Name
+    if (profile_user) {
+        pUserName = (PQMIWDS_USERNAME)(pTLV + TLVLength);
+        pUserName->TLVType = 0x17;
+        pUserName->TLVLength = cpu_to_le16(strlen(profile_user));
+        qstrcpy((char *)&pUserName->UserName, profile_user);
+        TLVLength += (le16_to_cpu(pUserName->TLVLength) + sizeof(QCQMICTL_TLV_HDR));
+    }
+
+    // Set Password
+    if (profile_password) {
+        pPasswd = (PQMIWDS_PASSWD)(pTLV + TLVLength);
+        pPasswd->TLVType = 0x18;
+        pPasswd->TLVLength = cpu_to_le16(strlen(profile_password));
+        qstrcpy((char *)&pPasswd->Passwd, profile_password);
+	TLVLength += (le16_to_cpu(pPasswd->TLVLength) + sizeof(QCQMICTL_TLV_HDR));
+    }
+
+    // Set Auth Protocol
+    if (profile_user && profile_password) {
+        pAuthPref = (PQMIWDS_AUTH_PREFERENCE)(pTLV + TLVLength);
+        pAuthPref->TLVType = 0x16;
+        pAuthPref->TLVLength = cpu_to_le16(0x01);
+        pAuthPref->AuthPreference = profile_auth; // 0 ~ None, 1 ~ Pap, 2 ~ Chap, 3 ~ MsChapV2
+        TLVLength += (le16_to_cpu(pAuthPref->TLVLength) + sizeof(QCQMICTL_TLV_HDR));
+    }
+
+    // Add IP Family Preference
+    pIpFamily = (PQMIWDS_IP_FAMILY_TLV)(pTLV + TLVLength);
+    pIpFamily->TLVType = 0x19;
+    pIpFamily->TLVLength = cpu_to_le16(0x01);
+    pIpFamily->IpFamily = profile->curIpFamily;
+    TLVLength += (le16_to_cpu(pIpFamily->TLVLength) + sizeof(QCQMICTL_TLV_HDR));
+
+    //Set Profile Index
+    if (profile->pdp && !s_is_cdma) { //cdma only support one pdp, so no need to set profile index
+        PQMIWDS_PROFILE_IDENTIFIER pProfileIndex = (PQMIWDS_PROFILE_IDENTIFIER)(pTLV + TLVLength);
+        pProfileIndex->TLVLength = cpu_to_le16(0x01);
+        pProfileIndex->TLVType = 0x31;
+        pProfileIndex->ProfileIndex = profile->pdp;
+        if (s_is_cdma && s_hdr_personality == 0x02) {
+            pProfileIndex->TLVType = 0x32; //profile_index_3gpp2
+            pProfileIndex->ProfileIndex = 101;
+        }
+        TLVLength += (le16_to_cpu(pProfileIndex->TLVLength) + sizeof(QCQMICTL_TLV_HDR));
+    }
+
+    return sizeof(QMIWDS_START_NETWORK_INTERFACE_REQ_MSG) + TLVLength;
+}
+
+static USHORT WdsStopNwInterfaceReq(PQMUX_MSG pMUXMsg, void *arg) {
+    pMUXMsg->StopNwInterfaceReq.TLVType = 0x01;
+    pMUXMsg->StopNwInterfaceReq.TLVLength = cpu_to_le16(0x04);
+    if (*((int *)arg) == IpFamilyV4)
+        pMUXMsg->StopNwInterfaceReq.Handle =  cpu_to_le32(WdsConnectionIPv4Handle);
+    else
+        pMUXMsg->StopNwInterfaceReq.Handle =  cpu_to_le32(WdsConnectionIPv6Handle);
+    return sizeof(QMIWDS_STOP_NETWORK_INTERFACE_REQ_MSG);
+}
+
+static USHORT WdsSetClientIPFamilyPref(PQMUX_MSG pMUXMsg, void *arg) {
+    pMUXMsg->SetClientIpFamilyPrefReq.TLVType = 0x01;
+    pMUXMsg->SetClientIpFamilyPrefReq.TLVLength = cpu_to_le16(0x01);
+    pMUXMsg->SetClientIpFamilyPrefReq.IpPreference = *((UCHAR *)arg);
+    return sizeof(QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ_MSG);
+}
+
+static USHORT WdsSetAutoConnect(PQMUX_MSG pMUXMsg, void *arg) {
+    pMUXMsg->SetAutoConnectReq.TLVType = 0x01;
+    pMUXMsg->SetAutoConnectReq.TLVLength = cpu_to_le16(0x01);
+    pMUXMsg->SetAutoConnectReq.autoconnect_setting = *((UCHAR *)arg);
+    return sizeof(QMIWDS_SET_AUTO_CONNECT_REQ_MSG);
+}
+
+enum peripheral_ep_type {
+	DATA_EP_TYPE_RESERVED	= 0x0,
+	DATA_EP_TYPE_HSIC	= 0x1,
+	DATA_EP_TYPE_HSUSB	= 0x2,
+	DATA_EP_TYPE_PCIE	= 0x3,
+	DATA_EP_TYPE_EMBEDDED	= 0x4,
+	DATA_EP_TYPE_BAM_DMUX	= 0x5,
+};
+        
+typedef struct {
+   UINT rx_urb_size;
+   enum peripheral_ep_type ep_type;
+   UINT iface_id;
+   UCHAR MuxId;
+} QMAP_SETTING;
+static USHORT WdsSetQMUXBindMuxDataPort(PQMUX_MSG pMUXMsg, void *arg) {
+    QMAP_SETTING *qmap_settings = (QMAP_SETTING *)arg;
+
+    pMUXMsg->BindMuxDataPortReq.TLVType = 0x10;
+    pMUXMsg->BindMuxDataPortReq.TLVLength = cpu_to_le16(0x08);
+    pMUXMsg->BindMuxDataPortReq.ep_type = cpu_to_le32(qmap_settings->ep_type);
+    pMUXMsg->BindMuxDataPortReq.iface_id = cpu_to_le32(qmap_settings->iface_id);
+    pMUXMsg->BindMuxDataPortReq.TLV2Type = 0x11;
+    pMUXMsg->BindMuxDataPortReq.TLV2Length = cpu_to_le16(0x01);
+    pMUXMsg->BindMuxDataPortReq.MuxId = qmap_settings->MuxId;
+    pMUXMsg->BindMuxDataPortReq.TLV3Type = 0x13;
+    pMUXMsg->BindMuxDataPortReq.TLV3Length = cpu_to_le16(0x04);
+    pMUXMsg->BindMuxDataPortReq.client_type = cpu_to_le32(1); //WDS_CLIENT_TYPE_TETHERED
+    
+    return sizeof(QMIWDS_BIND_MUX_DATA_PORT_REQ_MSG);
+}
+
+static USHORT WdaSetDataFormat(PQMUX_MSG pMUXMsg, void *arg) {
+    QMAP_SETTING *qmap_settings = (QMAP_SETTING *)arg;
+
+    if (qmap_settings->rx_urb_size == 0) {
+        PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS pWdsAdminQosTlv;
+        PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV linkProto;
+        PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV dlTlp;
+
+        pWdsAdminQosTlv = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS)(&pMUXMsg->QMUXMsgHdr + 1);
+        pWdsAdminQosTlv->TLVType = 0x10;
+        pWdsAdminQosTlv->TLVLength = cpu_to_le16(0x0001);
+        pWdsAdminQosTlv->QOSSetting = 0; /* no-QOS header */
+
+        linkProto = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)(pWdsAdminQosTlv + 1);
+        linkProto->TLVType = 0x11;
+        linkProto->TLVLength = cpu_to_le16(4);
+        linkProto->Value = cpu_to_le32(0x01);     /* Set Ethernet  mode */
+
+        dlTlp = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)(linkProto + 1);;
+        dlTlp->TLVType = 0x13;
+        dlTlp->TLVLength = cpu_to_le16(4);
+        dlTlp->Value = cpu_to_le32(0x00);
+
+        if (sizeof(*linkProto) != 7 )
+            dbg_time("%s sizeof(*linkProto) = %zu, is not 7!", __func__, sizeof(*linkProto) );
+
+        return sizeof(QCQMUX_MSG_HDR) + sizeof(*pWdsAdminQosTlv) + sizeof(*linkProto) + sizeof(*dlTlp);
+    } 
+    else {
+    //Indicates whether the Quality of Service(QOS) data format is used by the client.
+        pMUXMsg->SetDataFormatReq.QosDataFormatTlv.TLVType = 0x10;
+        pMUXMsg->SetDataFormatReq.QosDataFormatTlv.TLVLength = cpu_to_le16(0x0001);
+        pMUXMsg->SetDataFormatReq.QosDataFormatTlv.QOSSetting = 0; /* no-QOS header */
+    //Underlying Link Layer Protocol
+        pMUXMsg->SetDataFormatReq.UnderlyingLinkLayerProtocolTlv.TLVType = 0x11; 
+        pMUXMsg->SetDataFormatReq.UnderlyingLinkLayerProtocolTlv.TLVLength = cpu_to_le16(4);
+        pMUXMsg->SetDataFormatReq.UnderlyingLinkLayerProtocolTlv.Value = cpu_to_le32(0x02);     /* Set IP  mode */
+    //Uplink (UL) data aggregation protocol to be used for uplink data transfer.
+        pMUXMsg->SetDataFormatReq.UplinkDataAggregationProtocolTlv.TLVType = 0x12; 
+        pMUXMsg->SetDataFormatReq.UplinkDataAggregationProtocolTlv.TLVLength = cpu_to_le16(4);
+        pMUXMsg->SetDataFormatReq.UplinkDataAggregationProtocolTlv.Value = cpu_to_le32(0x09); //UL QMAP is enabled //0x05
+    //Downlink (DL) data aggregation protocol to be used for downlink data transfer
+        pMUXMsg->SetDataFormatReq.DownlinkDataAggregationProtocolTlv.TLVType = 0x13; 
+        pMUXMsg->SetDataFormatReq.DownlinkDataAggregationProtocolTlv.TLVLength = cpu_to_le16(4);
+        pMUXMsg->SetDataFormatReq.DownlinkDataAggregationProtocolTlv.Value = cpu_to_le32(0x05); //UL QMAP is enabled
+    //Maximum number of datagrams in a single aggregated packet on downlink
+        pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.TLVType = 0x15; 
+        pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.TLVLength = cpu_to_le16(4);
+        pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.Value = cpu_to_le32(qmap_settings->rx_urb_size/512);
+    //Maximum size in bytes of a single aggregated packet allowed on downlink
+        pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.TLVType = 0x16; 
+        pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.TLVLength = cpu_to_le16(4);
+        pMUXMsg->SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.Value = cpu_to_le32(qmap_settings->rx_urb_size);
+    //Peripheral End Point ID
+        pMUXMsg->SetDataFormatReq.epTlv.TLVType = 0x17; 
+        pMUXMsg->SetDataFormatReq.epTlv.TLVLength = cpu_to_le16(8);
+        pMUXMsg->SetDataFormatReq.epTlv.ep_type = cpu_to_le32(qmap_settings->ep_type);
+        pMUXMsg->SetDataFormatReq.epTlv.iface_id = cpu_to_le32(qmap_settings->iface_id); 
+
+        return sizeof(QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG);
+    }
+}
+
+#ifdef CONFIG_SIM
+static USHORT DmsUIMVerifyPinReqSend(PQMUX_MSG pMUXMsg, void *arg) {
+    pMUXMsg->UIMVerifyPinReq.TLVType = 0x01;
+    pMUXMsg->UIMVerifyPinReq.PINID = 0x01; //Pin1, not Puk
+    pMUXMsg->UIMVerifyPinReq.PINLen = strlen((const char *)arg);
+    qstrcpy((PCHAR)&pMUXMsg->UIMVerifyPinReq.PINValue, ((const char *)arg));
+    pMUXMsg->UIMVerifyPinReq.TLVLength = cpu_to_le16(2 + strlen((const char *)arg));
+    return sizeof(QMIDMS_UIM_VERIFY_PIN_REQ_MSG) + (strlen((const char *)arg) - 1);
+}
+
+static USHORT UimVerifyPinReqSend(PQMUX_MSG pMUXMsg, void *arg)
+{
+    pMUXMsg->UIMUIMVerifyPinReq.TLVType = 0x01;
+    pMUXMsg->UIMUIMVerifyPinReq.TLVLength = cpu_to_le16(0x02);
+    pMUXMsg->UIMUIMVerifyPinReq.Session_Type = 0x00;
+    pMUXMsg->UIMUIMVerifyPinReq.Aid_Len = 0x00;
+    pMUXMsg->UIMUIMVerifyPinReq.TLV2Type = 0x02;
+    pMUXMsg->UIMUIMVerifyPinReq.TLV2Length = cpu_to_le16(2 + strlen((const char *)arg));
+    pMUXMsg->UIMUIMVerifyPinReq.PINID = 0x01;  //Pin1, not Puk
+    pMUXMsg->UIMUIMVerifyPinReq.PINLen= strlen((const char *)arg);
+    qstrcpy((PCHAR)&pMUXMsg->UIMUIMVerifyPinReq.PINValue, ((const char *)arg));
+    return sizeof(QMIUIM_VERIFY_PIN_REQ_MSG) + (strlen((const char *)arg) - 1);
+}
+
+#ifdef CONFIG_IMSI_ICCID
+static USHORT UimReadTransparentIMSIReqSend(PQMUX_MSG pMUXMsg, void *arg) {
+    PREAD_TRANSPARENT_TLV pReadTransparent;
+
+    pMUXMsg->UIMUIMReadTransparentReq.TLVType =  0x01;
+    pMUXMsg->UIMUIMReadTransparentReq.TLVLength = cpu_to_le16(0x02);
+    if (!strcmp((char *)arg, "EF_ICCID")) {
+        pMUXMsg->UIMUIMReadTransparentReq.Session_Type = 0x06;
+        pMUXMsg->UIMUIMReadTransparentReq.Aid_Len = 0x00;
+
+        pMUXMsg->UIMUIMReadTransparentReq.TLV2Type = 0x02;
+        pMUXMsg->UIMUIMReadTransparentReq.file_id = cpu_to_le16(0x2FE2);
+        pMUXMsg->UIMUIMReadTransparentReq.path_len = 0x02;
+        pMUXMsg->UIMUIMReadTransparentReq.path[0] = 0x00;
+        pMUXMsg->UIMUIMReadTransparentReq.path[1] = 0x3F;
+    }
+    else if(!strcmp((char *)arg, "EF_IMSI")) {
+        pMUXMsg->UIMUIMReadTransparentReq.Session_Type = 0x00;
+        pMUXMsg->UIMUIMReadTransparentReq.Aid_Len = 0x00;
+
+        pMUXMsg->UIMUIMReadTransparentReq.TLV2Type = 0x02;
+        pMUXMsg->UIMUIMReadTransparentReq.file_id = cpu_to_le16(0x6F07);
+        pMUXMsg->UIMUIMReadTransparentReq.path_len = 0x04;
+        pMUXMsg->UIMUIMReadTransparentReq.path[0] = 0x00;
+        pMUXMsg->UIMUIMReadTransparentReq.path[1] = 0x3F;
+        pMUXMsg->UIMUIMReadTransparentReq.path[2] = 0xFF;
+        pMUXMsg->UIMUIMReadTransparentReq.path[3] = 0x7F;
+    }
+
+    pMUXMsg->UIMUIMReadTransparentReq.TLV2Length = cpu_to_le16(3 +  pMUXMsg->UIMUIMReadTransparentReq.path_len);
+
+    pReadTransparent = (PREAD_TRANSPARENT_TLV)(&pMUXMsg->UIMUIMReadTransparentReq.path[pMUXMsg->UIMUIMReadTransparentReq.path_len]);
+    pReadTransparent->TLVType = 0x03;
+    pReadTransparent->TLVLength = cpu_to_le16(0x04);
+    pReadTransparent->Offset = cpu_to_le16(0x00);
+    pReadTransparent->Length = cpu_to_le16(0x00);
+
+    return (sizeof(QMIUIM_READ_TRANSPARENT_REQ_MSG) + pMUXMsg->UIMUIMReadTransparentReq.path_len + sizeof(READ_TRANSPARENT_TLV));
+}
+#endif
+#endif
+
+#ifdef CONFIG_APN
+static USHORT WdsGetProfileSettingsReqSend(PQMUX_MSG pMUXMsg, void *arg) {
+    PROFILE_T *profile = (PROFILE_T *)arg;
+    pMUXMsg->GetProfileSettingsReq.Length = cpu_to_le16(sizeof(QMIWDS_GET_PROFILE_SETTINGS_REQ_MSG) - 4);
+    pMUXMsg->GetProfileSettingsReq.TLVType = 0x01;
+    pMUXMsg->GetProfileSettingsReq.TLVLength = cpu_to_le16(0x02);
+    pMUXMsg->GetProfileSettingsReq.ProfileType = 0x00; // 0 ~ 3GPP, 1 ~ 3GPP2
+    pMUXMsg->GetProfileSettingsReq.ProfileIndex = profile->pdp;
+    return sizeof(QMIWDS_GET_PROFILE_SETTINGS_REQ_MSG);
+}
+
+static USHORT WdsModifyProfileSettingsReq(PQMUX_MSG pMUXMsg, void *arg) {
+    USHORT TLVLength = 0;
+    UCHAR *pTLV;
+    PROFILE_T *profile = (PROFILE_T *)arg;
+    PQMIWDS_PDPTYPE pPdpType;
+
+    pMUXMsg->ModifyProfileSettingsReq.Length = cpu_to_le16(sizeof(QMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG) - 4);
+    pMUXMsg->ModifyProfileSettingsReq.TLVType = 0x01;
+    pMUXMsg->ModifyProfileSettingsReq.TLVLength = cpu_to_le16(0x02);
+    pMUXMsg->ModifyProfileSettingsReq.ProfileType = 0x00; // 0 ~ 3GPP, 1 ~ 3GPP2
+    pMUXMsg->ModifyProfileSettingsReq.ProfileIndex = profile->pdp;
+
+    pTLV = (UCHAR *)(&pMUXMsg->ModifyProfileSettingsReq + 1);
+
+    pPdpType = (PQMIWDS_PDPTYPE)(pTLV + TLVLength);
+    pPdpType->TLVType = 0x11;
+    pPdpType->TLVLength = cpu_to_le16(0x01);
+// 0 ?C PDP-IP (IPv4)
+// 1 ?C PDP-PPP
+// 2 ?C PDP-IPv6
+// 3 ?C PDP-IPv4v6
+    if (profile->IsDualIPSupported)
+        pPdpType->PdpType = 3;
+    else if (profile->enable_ipv6)
+		pPdpType->PdpType = 2;
+	else
+        pPdpType->PdpType = 0;
+    TLVLength +=(le16_to_cpu(pPdpType->TLVLength) + sizeof(QCQMICTL_TLV_HDR));
+
+    // Set APN Name
+    if (profile->apn) {
+        PQMIWDS_APNNAME pApnName = (PQMIWDS_APNNAME)(pTLV + TLVLength);
+        pApnName->TLVType = 0x14;
+        pApnName->TLVLength = cpu_to_le16(strlen(profile->apn));
+        qstrcpy((char *)&pApnName->ApnName, profile->apn);
+        TLVLength +=(le16_to_cpu(pApnName->TLVLength) + sizeof(QCQMICTL_TLV_HDR));
+    }
+
+    // Set User Name
+    if (profile->user) {
+        PQMIWDS_USERNAME pUserName = (PQMIWDS_USERNAME)(pTLV + TLVLength);
+        pUserName->TLVType = 0x1B;
+        pUserName->TLVLength = cpu_to_le16(strlen(profile->user));
+        qstrcpy((char *)&pUserName->UserName, profile->user);
+        TLVLength += (le16_to_cpu(pUserName->TLVLength) + sizeof(QCQMICTL_TLV_HDR));
+    }
+
+    // Set Password
+    if (profile->password) {
+        PQMIWDS_PASSWD pPasswd = (PQMIWDS_PASSWD)(pTLV + TLVLength);
+        pPasswd->TLVType = 0x1C;
+        pPasswd->TLVLength = cpu_to_le16(strlen(profile->password));
+        qstrcpy((char *)&pPasswd->Passwd, profile->password);
+        TLVLength +=(le16_to_cpu(pPasswd->TLVLength) + sizeof(QCQMICTL_TLV_HDR));
+    }
+
+    // Set Auth Protocol
+    if (profile->user && profile->password) {
+        PQMIWDS_AUTH_PREFERENCE pAuthPref = (PQMIWDS_AUTH_PREFERENCE)(pTLV + TLVLength);
+        pAuthPref->TLVType = 0x1D;
+        pAuthPref->TLVLength = cpu_to_le16(0x01);
+        pAuthPref->AuthPreference = profile->auth; // 0 ~ None, 1 ~ Pap, 2 ~ Chap, 3 ~ MsChapV2
+        TLVLength += (le16_to_cpu(pAuthPref->TLVLength) + sizeof(QCQMICTL_TLV_HDR));
+    }
+
+    return sizeof(QMIWDS_MODIFY_PROFILE_SETTINGS_REQ_MSG) + TLVLength;
+}
+#endif
+
+static USHORT WdsGetRuntimeSettingReq(PQMUX_MSG pMUXMsg, void *arg) {
+   pMUXMsg->GetRuntimeSettingsReq.TLVType = 0x10;
+   pMUXMsg->GetRuntimeSettingsReq.TLVLength = cpu_to_le16(0x04);
+   // the following mask also applies to IPV6
+   pMUXMsg->GetRuntimeSettingsReq.Mask = cpu_to_le32(QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4DNS_ADDR |
+                                          QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4_ADDR |
+                                          QMIWDS_GET_RUNTIME_SETTINGS_MASK_MTU |
+                                          QMIWDS_GET_RUNTIME_SETTINGS_MASK_IPV4GATEWAY_ADDR); // |
+                                          // QMIWDS_GET_RUNTIME_SETTINGS_MASK_PCSCF_SV_ADDR |
+                                          // QMIWDS_GET_RUNTIME_SETTINGS_MASK_PCSCF_DOM_NAME;
+
+    return sizeof(QMIWDS_GET_RUNTIME_SETTINGS_REQ_MSG);
+}
+
+static PQCQMIMSG s_pRequest;
+static PQCQMIMSG s_pResponse;
+static pthread_mutex_t s_commandmutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t s_commandcond = PTHREAD_COND_INITIALIZER;
+
+static int is_response(const PQCQMIMSG pRequest, const PQCQMIMSG pResponse) {
+    if ((pRequest->QMIHdr.QMIType == pResponse->QMIHdr.QMIType)
+        && (pRequest->QMIHdr.ClientId == pResponse->QMIHdr.ClientId)) {
+            USHORT requestTID, responseTID;
+        if (pRequest->QMIHdr.QMIType == QMUX_TYPE_CTL) {
+            requestTID = pRequest->CTLMsg.QMICTLMsgHdr.TransactionId;
+            responseTID = pResponse->CTLMsg.QMICTLMsgHdr.TransactionId;
+        } else {
+            requestTID = le16_to_cpu(pRequest->MUXMsg.QMUXHdr.TransactionId);
+            responseTID = le16_to_cpu(pResponse->MUXMsg.QMUXHdr.TransactionId);
+        }
+        return (requestTID == responseTID);
+    }
+    return 0;
+}
+
+
+int (*qmidev_send)(PQCQMIMSG pRequest);
+
+int QmiThreadSendQMITimeout(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse, unsigned msecs) {
+    int ret;
+
+    static int flag = 0;
+    if (!flag) {
+        cond_setclock_attr(&s_commandcond, CLOCK_MONOTONIC);
+        flag = 1;
+    }
+    
+    if (!pRequest)
+        return -EINVAL;
+
+    pthread_mutex_lock(&s_commandmutex);
+
+    if (ppResponse)
+        *ppResponse = NULL;
+
+    dump_qmi(pRequest, le16_to_cpu(pRequest->QMIHdr.Length) + 1);
+
+    s_pRequest = pRequest;
+    s_pResponse = NULL;
+
+    ret = qmidev_send(pRequest);
+
+    if (ret == 0) {
+        ret = pthread_cond_timeout_np(&s_commandcond, &s_commandmutex, msecs);
+        if (!ret) {
+            if (s_pResponse && ppResponse) {
+                *ppResponse = s_pResponse;
+            } else {
+                if (s_pResponse) {
+                    free(s_pResponse);
+                    s_pResponse = NULL;
+                }
+            }
+        } else {
+            dbg_time("%s pthread_cond_timeout_np timeout", __func__);
+        }
+    }
+
+    pthread_mutex_unlock(&s_commandmutex);
+
+    return ret;
+}
+
+int QmiThreadSendQMI(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse) {
+    return QmiThreadSendQMITimeout(pRequest, ppResponse, 30 * 1000);
+}
+
+void QmiThreadRecvQMI(PQCQMIMSG pResponse) {
+    pthread_mutex_lock(&s_commandmutex);
+    if (pResponse == NULL) {
+        if (s_pRequest) {
+            free(s_pRequest);
+            s_pRequest = NULL;
+            s_pResponse = NULL;
+            pthread_cond_signal(&s_commandcond);
+        }
+        pthread_mutex_unlock(&s_commandmutex);
+        return;
+    }
+    dump_qmi(pResponse, le16_to_cpu(pResponse->QMIHdr.Length) + 1);
+    if (s_pRequest && is_response(s_pRequest, pResponse)) {
+        free(s_pRequest);
+        s_pRequest = NULL;
+        s_pResponse = malloc(le16_to_cpu(pResponse->QMIHdr.Length) + 1);
+        if (s_pResponse != NULL) {
+            memcpy(s_pResponse, pResponse, le16_to_cpu(pResponse->QMIHdr.Length) + 1);
+        }
+        pthread_cond_signal(&s_commandcond);
+    } else if ((pResponse->QMIHdr.QMIType == QMUX_TYPE_NAS)
+                    && (le16_to_cpu(pResponse->MUXMsg.QMUXMsgHdrResp.Type) == QMINAS_SERVING_SYSTEM_IND)) {
+        qmidevice_send_event_to_main(RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED);
+    } else if ((pResponse->QMIHdr.QMIType == QMUX_TYPE_WDS)
+                    && (le16_to_cpu(pResponse->MUXMsg.QMUXMsgHdrResp.Type) == QMIWDS_GET_PKT_SRVC_STATUS_IND)) {
+        qmidevice_send_event_to_main(RIL_UNSOL_DATA_CALL_LIST_CHANGED);
+    } else if ((pResponse->QMIHdr.QMIType == QMUX_TYPE_NAS)
+                    && (le16_to_cpu(pResponse->MUXMsg.QMUXMsgHdrResp.Type) == QMINAS_SYS_INFO_IND)) {
+        qmidevice_send_event_to_main(RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED);
+    } else {
+        if (debug_qmi)
+            dbg_time("nobody care this qmi msg!!");
+    }
+    pthread_mutex_unlock(&s_commandmutex);
+}
+
+int requestSetEthMode(PROFILE_T *profile) {
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse = NULL;
+    PQMUX_MSG pMUXMsg;
+    int err;
+    PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV linkProto;
+    UCHAR IpPreference;
+    UCHAR autoconnect_setting = 0;
+    QMAP_SETTING qmap_settings = {0, 0, 0, 0};
+
+    if (profile->qmap_mode) {
+        profile->rawIP = 1;
+        s_9x07 = profile->rawIP;
+        
+        qmap_settings.MuxId = profile->muxid;
+
+        if (qmidev_is_pciemhi(profile->qmichannel)) { //SDX20_PCIE
+            qmap_settings.rx_urb_size = 32*1024; //SDX24&SDX55 support 32KB 
+            qmap_settings.ep_type = DATA_EP_TYPE_PCIE;
+            qmap_settings.iface_id = 0x04;
+        }
+        else { // for MDM9x07&MDM9x40&SDX20 USB
+            qmap_settings.rx_urb_size = 32*1024; //SDX24&SDX55 support 32KB 
+            qmap_settings.ep_type = DATA_EP_TYPE_HSUSB;
+            qmap_settings.iface_id = 0x04;
+        }
+
+        if (qmidev_is_gobinet(profile->qmichannel)) { //GobiNet set data format in GobiNet driver
+            goto skip_WdaSetDataFormat;
+        } else if (profile->qmap_mode > 1) {//QMAP MUX enabled, set data format in meig-qmi-proxy
+            goto skip_WdaSetDataFormat;
+        }
+    }
+
+    pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS_ADMIN, QMIWDS_ADMIN_SET_DATA_FORMAT_REQ, WdaSetDataFormat, (void *)&qmap_settings);
+    err = QmiThreadSendQMI(pRequest, &pResponse);
+    qmi_rsp_check_and_return();
+
+    linkProto = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11);
+    if (linkProto != NULL) {
+        profile->rawIP = (le32_to_cpu(linkProto->Value) == 2);
+        s_9x07 = profile->rawIP;
+    }
+
+    linkProto = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x16);
+    if (linkProto != NULL && profile->qmap_mode) {
+        qmap_settings.rx_urb_size = le32_to_cpu(linkProto->Value);
+        dbg_time("qmap_settings.rx_urb_size = %u", qmap_settings.rx_urb_size); //must same as rx_urb_size defined in GobiNet&qmi_wwan driver
+    }
+    
+    free(pResponse);
+
+skip_WdaSetDataFormat:
+	if (profile->enable_ipv6) {
+		if (profile->qmapnet_adapter) {
+			// bind wds mux data port
+			pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_BIND_MUX_DATA_PORT_REQ , WdsSetQMUXBindMuxDataPort, (void *)&qmap_settings);
+			err = QmiThreadSendQMI(pRequest, &pResponse);
+			qmi_rsp_check_and_return();
+			if (pResponse) free(pResponse);
+		}
+
+		// set ipv6
+		IpPreference = IpFamilyV6;
+		pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS_IPV6, QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ, WdsSetClientIPFamilyPref, (void *)&IpPreference);
+		err = QmiThreadSendQMI(pRequest, &pResponse);
+		qmi_rsp_check_and_return();
+		if (pResponse) free(pResponse);
+	} else {
+		if (profile->qmapnet_adapter) {
+			// bind wds mux data port
+			pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_BIND_MUX_DATA_PORT_REQ , WdsSetQMUXBindMuxDataPort, (void *)&qmap_settings);
+			err = QmiThreadSendQMI(pRequest, &pResponse);
+			qmi_rsp_check_and_return();
+			if (pResponse) free(pResponse);
+		}
+
+    	// set ipv4
+    	IpPreference = IpFamilyV4;
+    	pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ, WdsSetClientIPFamilyPref, (void *)&IpPreference);
+    	err = QmiThreadSendQMI(pRequest, &pResponse);
+    	if (pResponse) free(pResponse);
+	}
+
+    if (profile->IsDualIPSupported) {
+        if (profile->qmapnet_adapter) {
+            // bind wds ipv6 mux data port
+            pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS_IPV6, QMIWDS_BIND_MUX_DATA_PORT_REQ , WdsSetQMUXBindMuxDataPort, (void *)&qmap_settings);
+            err = QmiThreadSendQMI(pRequest, &pResponse);
+            qmi_rsp_check_and_return();
+            if (pResponse) free(pResponse);
+        }
+
+        // set ipv6
+        IpPreference = IpFamilyV6;
+        pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS_IPV6, QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ, WdsSetClientIPFamilyPref, (void *)&IpPreference);
+        err = QmiThreadSendQMI(pRequest, &pResponse);
+        qmi_rsp_check_and_return();
+        if (pResponse) free(pResponse);
+    }
+
+    pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_SET_AUTO_CONNECT_REQ , WdsSetAutoConnect, (void *)&autoconnect_setting);
+    QmiThreadSendQMI(pRequest, &pResponse);
+    if (pResponse) free(pResponse);
+
+    return 0;
+}
+
+#ifdef CONFIG_SIM
+int requestGetPINStatus(SIM_Status *pSIMStatus) {
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    int err;
+    PQMIDMS_UIM_PIN_STATUS pPin1Status = NULL;
+    //PQMIDMS_UIM_PIN_STATUS pPin2Status = NULL;
+
+    if (s_9x07 && qmiclientId[QMUX_TYPE_UIM])
+        pRequest = ComposeQMUXMsg(QMUX_TYPE_UIM, QMIUIM_GET_CARD_STATUS_REQ, NULL, NULL);
+    else
+        pRequest = ComposeQMUXMsg(QMUX_TYPE_DMS, QMIDMS_UIM_GET_PIN_STATUS_REQ, NULL, NULL);
+    err = QmiThreadSendQMI(pRequest, &pResponse);
+    qmi_rsp_check_and_return();
+
+    pPin1Status = (PQMIDMS_UIM_PIN_STATUS)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11);
+    //pPin2Status = (PQMIDMS_UIM_PIN_STATUS)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x12);
+
+    if (pPin1Status != NULL) {
+        if (pPin1Status->PINStatus == QMI_PIN_STATUS_NOT_VERIF) {
+            *pSIMStatus = SIM_PIN;
+        } else if (pPin1Status->PINStatus == QMI_PIN_STATUS_BLOCKED) {
+            *pSIMStatus = SIM_PUK;
+        } else if (pPin1Status->PINStatus == QMI_PIN_STATUS_PERM_BLOCKED) {
+            *pSIMStatus = SIM_BAD;
+        }
+    }
+
+    free(pResponse);
+    return 0;
+}
+
+int requestGetSIMStatus(SIM_Status *pSIMStatus) { //RIL_REQUEST_GET_SIM_STATUS
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    int err;
+    const char * SIM_Status_String[] = {
+        "SIM_ABSENT",
+        "SIM_NOT_READY",
+        "SIM_READY", /* SIM_READY means the radio state is RADIO_STATE_SIM_READY */
+        "SIM_PIN",
+        "SIM_PUK",
+        "SIM_NETWORK_PERSONALIZATION"
+    };
+
+    if (s_9x07 && qmiclientId[QMUX_TYPE_UIM])
+        pRequest = ComposeQMUXMsg(QMUX_TYPE_UIM, QMIUIM_GET_CARD_STATUS_REQ, NULL, NULL);
+    else
+        pRequest = ComposeQMUXMsg(QMUX_TYPE_DMS, QMIDMS_UIM_GET_STATE_REQ, NULL, NULL);
+
+    err = QmiThreadSendQMI(pRequest, &pResponse);
+    qmi_rsp_check_and_return();
+
+    *pSIMStatus = SIM_ABSENT;
+    if (s_9x07 && qmiclientId[QMUX_TYPE_UIM])
+    {
+        PQMIUIM_CARD_STATUS pCardStatus = NULL;
+        PQMIUIM_PIN_STATE pPINState = NULL;
+        UCHAR CardState = 0x01;
+        UCHAR PIN1State = QMI_PIN_STATUS_NOT_VERIF;
+        //UCHAR PIN1Retries;
+        //UCHAR PUK1Retries;
+        //UCHAR PIN2State;
+        //UCHAR PIN2Retries;
+        //UCHAR PUK2Retries;
+
+        pCardStatus = (PQMIUIM_CARD_STATUS)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x10);
+        if (pCardStatus != NULL)
+        {
+            pPINState = (PQMIUIM_PIN_STATE)((PUCHAR)pCardStatus + sizeof(QMIUIM_CARD_STATUS) + pCardStatus->AIDLength);
+            CardState  = pCardStatus->CardState;
+            if (pPINState->UnivPIN == 1)
+            {
+               PIN1State = pCardStatus->UPINState;
+               //PIN1Retries = pCardStatus->UPINRetries;
+               //PUK1Retries = pCardStatus->UPUKRetries;
+            }
+            else
+            {
+               PIN1State = pPINState->PIN1State;
+               //PIN1Retries = pPINState->PIN1Retries;
+               //PUK1Retries = pPINState->PUK1Retries;
+            }
+            //PIN2State = pPINState->PIN2State;
+            //PIN2Retries = pPINState->PIN2Retries;
+            //PUK2Retries = pPINState->PUK2Retries;
+        }
+
+        *pSIMStatus = SIM_ABSENT;
+        if ((CardState == 0x01) &&  ((PIN1State == QMI_PIN_STATUS_VERIFIED)|| (PIN1State == QMI_PIN_STATUS_DISABLED)))
+        {
+            *pSIMStatus = SIM_READY;
+        }
+        else if (CardState == 0x01)
+        {
+            if (PIN1State == QMI_PIN_STATUS_NOT_VERIF)
+            {
+                *pSIMStatus = SIM_PIN;
+            }
+            if ( PIN1State == QMI_PIN_STATUS_BLOCKED)
+            {
+                *pSIMStatus = SIM_PUK;
+            }
+            else if (PIN1State == QMI_PIN_STATUS_PERM_BLOCKED)
+            {
+                *pSIMStatus = SIM_BAD;
+            }
+            else if (PIN1State == QMI_PIN_STATUS_NOT_INIT || PIN1State == QMI_PIN_STATUS_VERIFIED || PIN1State == QMI_PIN_STATUS_DISABLED)
+            {
+                *pSIMStatus = SIM_READY;
+            }
+        }
+        else if (CardState == 0x00 || CardState == 0x02)
+        {
+        }
+        else
+        {
+        }
+    }
+    else
+    {
+    //UIM state. Values:
+    // 0x00  UIM initialization completed
+    // 0x01  UIM is locked or the UIM failed
+    // 0x02  UIM is not present
+    // 0x03  Reserved
+    // 0xFF  UIM state is currently
+    //unavailable
+        if (pResponse->MUXMsg.UIMGetStateResp.UIMState == 0x00) {
+            *pSIMStatus = SIM_READY;
+        } else if (pResponse->MUXMsg.UIMGetStateResp.UIMState == 0x01) {
+            *pSIMStatus = SIM_ABSENT;
+            err = requestGetPINStatus(pSIMStatus);
+        } else if ((pResponse->MUXMsg.UIMGetStateResp.UIMState == 0x02) || (pResponse->MUXMsg.UIMGetStateResp.UIMState == 0xFF)) {
+            *pSIMStatus = SIM_ABSENT;
+        } else {
+            *pSIMStatus = SIM_ABSENT;
+        }
+    }
+    dbg_time("%s SIMStatus: %s", __func__, SIM_Status_String[*pSIMStatus]);
+
+    free(pResponse);
+
+    return 0;
+}
+
+int requestEnterSimPin(const CHAR *pPinCode) {
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    int err;
+
+    if (s_9x07 && qmiclientId[QMUX_TYPE_UIM])
+        pRequest = ComposeQMUXMsg(QMUX_TYPE_UIM, QMIUIM_VERIFY_PIN_REQ, UimVerifyPinReqSend, (void *)pPinCode);
+    else
+        pRequest = ComposeQMUXMsg(QMUX_TYPE_DMS, QMIDMS_UIM_VERIFY_PIN_REQ, DmsUIMVerifyPinReqSend, (void *)pPinCode);
+    err = QmiThreadSendQMI(pRequest, &pResponse);
+    qmi_rsp_check_and_return();
+
+    free(pResponse);
+    return 0;
+}
+
+#ifdef CONFIG_IMSI_ICCID
+int requestGetICCID(void) { //RIL_REQUEST_GET_IMSI
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    PQMIUIM_CONTENT pUimContent;
+    int err;
+
+    if (s_9x07 && qmiclientId[QMUX_TYPE_UIM]) {
+        pRequest = ComposeQMUXMsg(QMUX_TYPE_UIM, QMIUIM_READ_TRANSPARENT_REQ, UimReadTransparentIMSIReqSend, (void *)"EF_ICCID");
+        err = QmiThreadSendQMI(pRequest, &pResponse);
+    } else {
+        return 0;
+    }
+    qmi_rsp_check_and_return();
+
+    pUimContent = (PQMIUIM_CONTENT)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11);
+    if (pUimContent != NULL) {
+        static char DeviceICCID[32] = {'\0'};
+        int i = 0, j = 0;
+
+        for (i = 0, j = 0; i < le16_to_cpu(pUimContent->content_len); ++i) {
+            char charmaps[] = "0123456789ABCDEF";
+
+            DeviceICCID[j++] = charmaps[(pUimContent->content[i] & 0x0F)];
+            DeviceICCID[j++] = charmaps[((pUimContent->content[i] & 0xF0) >> 0x04)];
+        }
+        DeviceICCID[j] = '\0';
+
+        dbg_time("%s DeviceICCID: %s", __func__, DeviceICCID);
+    }
+
+    free(pResponse);
+    return 0;
+}
+
+int requestGetIMSI(void) { //RIL_REQUEST_GET_IMSI
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    PQMIUIM_CONTENT pUimContent;
+    int err;
+
+    if (s_9x07 && qmiclientId[QMUX_TYPE_UIM]) {
+        pRequest = ComposeQMUXMsg(QMUX_TYPE_UIM, QMIUIM_READ_TRANSPARENT_REQ, UimReadTransparentIMSIReqSend, (void *)"EF_IMSI");
+        err = QmiThreadSendQMI(pRequest, &pResponse);
+    } else {
+        return 0;
+    }
+    qmi_rsp_check_and_return();
+
+    pUimContent = (PQMIUIM_CONTENT)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11);
+    if (pUimContent != NULL) {
+        static char DeviceIMSI[32] = {'\0'};
+        int i = 0, j = 0;
+
+        for (i = 0, j = 0; i < le16_to_cpu(pUimContent->content[0]); ++i) {
+            if (i != 0)
+                DeviceIMSI[j++] = (pUimContent->content[i+1] & 0x0F) + '0';
+            DeviceIMSI[j++] = ((pUimContent->content[i+1] & 0xF0) >> 0x04) + '0';
+        }
+        DeviceIMSI[j] = '\0';
+
+        dbg_time("%s DeviceIMSI: %s", __func__, DeviceIMSI);
+    }
+
+    free(pResponse);
+    return 0;
+}
+#endif
+#endif
+
+#if 1
+static void meig_convert_cdma_mcc_2_ascii_mcc( USHORT *p_mcc, USHORT mcc )
+{
+  unsigned int d1, d2, d3, buf = mcc + 111;
+
+  if ( mcc == 0x3FF ) // wildcard
+  {
+    *p_mcc = 3;
+  }
+  else
+  {
+    d3 = buf % 10;
+    buf = ( d3 == 0 ) ? (buf-10)/10 : buf/10;
+
+    d2 = buf % 10;
+    buf = ( d2 == 0 ) ? (buf-10)/10 : buf/10;
+
+    d1 = ( buf == 10 ) ? 0 : buf;
+
+//dbg_time("d1:%d, d2:%d,d3:%d",d1,d2,d3);
+    if ( d1<10 && d2<10 && d3<10 )
+    {
+    *p_mcc = d1*100+d2*10+d3;
+#if 0
+      *(p_mcc+0) = '0' + d1;
+      *(p_mcc+1) = '0' + d2;
+      *(p_mcc+2) = '0' + d3;
+#endif
+    }
+    else
+    {
+      //dbg_time( "invalid digits %d %d %d", d1, d2, d3 );
+      *p_mcc = 0;
+    }
+  }
+}
+
+static void meig_convert_cdma_mnc_2_ascii_mnc( USHORT *p_mnc, USHORT imsi_11_12)
+{
+  unsigned int d1, d2, buf = imsi_11_12 + 11;
+
+  if ( imsi_11_12 == 0x7F ) // wildcard
+  {
+    *p_mnc = 7;
+  }
+  else
+  {
+    d2 = buf % 10;
+    buf = ( d2 == 0 ) ? (buf-10)/10 : buf/10;
+
+    d1 = ( buf == 10 ) ? 0 : buf;
+
+    if ( d1<10 && d2<10 )
+    {
+     *p_mnc = d1*10 + d2;
+    }
+    else
+    {
+      //dbg_time( "invalid digits %d %d", d1, d2, 0 );
+      *p_mnc = 0;
+    }
+  }
+}
+
+int requestGetHomeNetwork(USHORT *p_mcc, USHORT *p_mnc, USHORT *p_sid, USHORT *p_nid) {
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    int err;
+    PHOME_NETWORK pHomeNetwork;
+    PHOME_NETWORK_SYSTEMID pHomeNetworkSystemID;
+
+    pRequest = ComposeQMUXMsg(QMUX_TYPE_NAS, QMINAS_GET_HOME_NETWORK_REQ, NULL, NULL);
+    err = QmiThreadSendQMI(pRequest, &pResponse);
+    qmi_rsp_check_and_return();
+
+    pHomeNetwork = (PHOME_NETWORK)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x01);
+    if (pHomeNetwork && p_mcc && p_mnc ) {
+        *p_mcc = le16_to_cpu(pHomeNetwork->MobileCountryCode);
+        *p_mnc = le16_to_cpu(pHomeNetwork->MobileNetworkCode);
+        //dbg_time("%s MobileCountryCode: %d, MobileNetworkCode: %d", __func__, *pMobileCountryCode, *pMobileNetworkCode);
+    }
+
+    pHomeNetworkSystemID = (PHOME_NETWORK_SYSTEMID)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x10);
+    if (pHomeNetworkSystemID && p_sid && p_nid) {
+        *p_sid = le16_to_cpu(pHomeNetworkSystemID->SystemID); //china-hefei: sid 14451
+        *p_nid = le16_to_cpu(pHomeNetworkSystemID->NetworkID);
+        //dbg_time("%s SystemID: %d, NetworkID: %d", __func__, *pSystemID, *pNetworkID);
+    }
+
+    free(pResponse);
+
+    return 0;
+}
+#endif
+
+#if 0
+// Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length.
+static const char * MCCMNC_CODES_HAVING_3DIGITS_MNC[] = {
+    "302370", "302720", "310260",
+    "405025", "405026", "405027", "405028", "405029", "405030", "405031", "405032",
+    "405033", "405034", "405035", "405036", "405037", "405038", "405039", "405040",
+    "405041", "405042", "405043", "405044", "405045", "405046", "405047", "405750",
+    "405751", "405752", "405753", "405754", "405755", "405756", "405799", "405800",
+    "405801", "405802", "405803", "405804", "405805", "405806", "405807", "405808",
+    "405809", "405810", "405811", "405812", "405813", "405814", "405815", "405816",
+    "405817", "405818", "405819", "405820", "405821", "405822", "405823", "405824",
+    "405825", "405826", "405827", "405828", "405829", "405830", "405831", "405832",
+    "405833", "405834", "405835", "405836", "405837", "405838", "405839", "405840",
+    "405841", "405842", "405843", "405844", "405845", "405846", "405847", "405848",
+    "405849", "405850", "405851", "405852", "405853", "405875", "405876", "405877",
+    "405878", "405879", "405880", "405881", "405882", "405883", "405884", "405885",
+    "405886", "405908", "405909", "405910", "405911", "405912", "405913", "405914",
+    "405915", "405916", "405917", "405918", "405919", "405920", "405921", "405922",
+    "405923", "405924", "405925", "405926", "405927", "405928", "405929", "405930",
+    "405931", "405932", "502142", "502143", "502145", "502146", "502147", "502148"
+};
+
+static const char * MCC_CODES_HAVING_3DIGITS_MNC[] = {
+    "302",    //Canada
+    "310",    //United States of America
+    "311",    //United States of America
+    "312",    //United States of America
+    "313",    //United States of America
+    "314",    //United States of America
+    "315",    //United States of America
+    "316",    //United States of America
+    "334",    //Mexico
+    "338",    //Jamaica
+    "342", //Barbados
+    "344",    //Antigua and Barbuda
+    "346",    //Cayman Islands
+    "348",    //British Virgin Islands
+    "365",    //Anguilla
+    "708",    //Honduras (Republic of)
+    "722",    //Argentine Republic
+    "732"    //Colombia (Republic of)
+};
+
+int requestGetIMSI(const char **pp_imsi, USHORT *pMobileCountryCode, USHORT *pMobileNetworkCode) {
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    int err;
+
+    if (pp_imsi) *pp_imsi = NULL;
+    if (pMobileCountryCode) *pMobileCountryCode = 0;
+    if (pMobileNetworkCode) *pMobileNetworkCode = 0;
+
+    pRequest = ComposeQMUXMsg(QMUX_TYPE_DMS, QMIDMS_UIM_GET_IMSI_REQ, NULL, NULL);
+    err = QmiThreadSendQMI(pRequest, &pResponse);
+    qmi_rsp_check_and_return();
+
+    if (pMUXMsg->UIMGetIMSIResp.TLV2Type == 0x01 &&  le16_to_cpu(pMUXMsg->UIMGetIMSIResp.TLV2Length) >= 5) {
+        int mnc_len = 2;
+        unsigned i;
+        char tmp[4];
+
+        if (pp_imsi) *pp_imsi = strndup((const char *)(&pMUXMsg->UIMGetIMSIResp.IMSI), le16_to_cpu(pMUXMsg->UIMGetIMSIResp.TLV2Length));
+
+        for (i = 0; i < sizeof(MCCMNC_CODES_HAVING_3DIGITS_MNC)/sizeof(MCCMNC_CODES_HAVING_3DIGITS_MNC[0]); i++) {
+            if (!strncmp((const char *)(&pMUXMsg->UIMGetIMSIResp.IMSI), MCCMNC_CODES_HAVING_3DIGITS_MNC[i], 6)) {
+                mnc_len = 3;
+                break;
+            }
+        }
+        if (mnc_len == 2) {
+            for (i = 0; i < sizeof(MCC_CODES_HAVING_3DIGITS_MNC)/sizeof(MCC_CODES_HAVING_3DIGITS_MNC[0]); i++) {
+                if (!strncmp((const char *)(&pMUXMsg->UIMGetIMSIResp.IMSI), MCC_CODES_HAVING_3DIGITS_MNC[i], 3)) {
+                    mnc_len = 3;
+                    break;
+                }
+            }
+        }
+
+        tmp[0] = (&pMUXMsg->UIMGetIMSIResp.IMSI)[0];
+        tmp[1] = (&pMUXMsg->UIMGetIMSIResp.IMSI)[1];
+        tmp[2] = (&pMUXMsg->UIMGetIMSIResp.IMSI)[2];
+        tmp[3] = 0;
+        if (pMobileCountryCode) *pMobileCountryCode = atoi(tmp);
+        tmp[0] = (&pMUXMsg->UIMGetIMSIResp.IMSI)[3];
+        tmp[1] = (&pMUXMsg->UIMGetIMSIResp.IMSI)[4];
+        tmp[2] = 0;
+        if (mnc_len == 3) {
+            tmp[2] = (&pMUXMsg->UIMGetIMSIResp.IMSI)[6];
+        }
+        if (pMobileNetworkCode) *pMobileNetworkCode = atoi(tmp);
+    }
+
+    free(pResponse);
+
+    return 0;
+}
+#endif
+
+struct wwan_data_class_str class2str[] = {
+    {WWAN_DATA_CLASS_NONE, "UNKNOWN"},
+    {WWAN_DATA_CLASS_GPRS, "GPRS"},
+    {WWAN_DATA_CLASS_EDGE, "EDGE"},
+    {WWAN_DATA_CLASS_UMTS, "UMTS"},
+    {WWAN_DATA_CLASS_HSDPA, "HSDPA"},
+    {WWAN_DATA_CLASS_HSUPA, "HSUPA"},
+    {WWAN_DATA_CLASS_LTE, "LTE"},
+    {WWAN_DATA_CLASS_1XRTT, "1XRTT"},
+    {WWAN_DATA_CLASS_1XEVDO, "1XEVDO"},
+    {WWAN_DATA_CLASS_1XEVDO_REVA, "1XEVDO_REVA"},
+    {WWAN_DATA_CLASS_1XEVDV, "1XEVDV"},
+    {WWAN_DATA_CLASS_3XRTT, "3XRTT"},
+    {WWAN_DATA_CLASS_1XEVDO_REVB, "1XEVDO_REVB"},
+    {WWAN_DATA_CLASS_UMB, "UMB"},
+    {WWAN_DATA_CLASS_CUSTOM, "CUSTOM"},
+};
+
+CHAR *wwan_data_class2str(ULONG class)
+{
+    unsigned int i = 0;
+    for (i = 0; i < sizeof(class2str)/sizeof(class2str[0]); i++) {
+        if (class2str[i].class == class) {
+            return class2str[i].str;
+        }
+    }
+    return "UNKNOWN";
+}
+
+int requestRegistrationState2(UCHAR *pPSAttachedState) {
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    int err;
+    USHORT MobileCountryCode = 0;
+    USHORT MobileNetworkCode = 0;
+    const char *pDataCapStr = "UNKNOW";
+    LONG remainingLen;
+    PSERVICE_STATUS_INFO pServiceStatusInfo;
+    int is_lte = 0;
+    PCDMA_SYSTEM_INFO pCdmaSystemInfo;
+    PHDR_SYSTEM_INFO pHdrSystemInfo;
+    PGSM_SYSTEM_INFO pGsmSystemInfo;
+    PWCDMA_SYSTEM_INFO pWcdmaSystemInfo;
+    PLTE_SYSTEM_INFO pLteSystemInfo;
+    PTDSCDMA_SYSTEM_INFO pTdscdmaSystemInfo;
+    UCHAR DeviceClass = 0;
+    ULONG DataCapList = 0;
+
+    *pPSAttachedState = 0;
+    pRequest = ComposeQMUXMsg(QMUX_TYPE_NAS, QMINAS_GET_SYS_INFO_REQ, NULL, NULL);
+    err = QmiThreadSendQMI(pRequest, &pResponse);
+    qmi_rsp_check_and_return();
+
+    pServiceStatusInfo = (PSERVICE_STATUS_INFO)(((PCHAR)&pMUXMsg->GetSysInfoResp) + QCQMUX_MSG_HDR_SIZE);
+    remainingLen = le16_to_cpu(pMUXMsg->GetSysInfoResp.Length);
+
+    s_is_cdma = 0;
+    s_hdr_personality = 0;
+    while (remainingLen > 0) {
+        switch (pServiceStatusInfo->TLVType) {
+        case 0x10: // CDMA
+            if (pServiceStatusInfo->SrvStatus == 0x02) {
+                DataCapList = WWAN_DATA_CLASS_1XRTT|
+                              WWAN_DATA_CLASS_1XEVDO|
+                              WWAN_DATA_CLASS_1XEVDO_REVA|
+                              WWAN_DATA_CLASS_1XEVDV|
+                              WWAN_DATA_CLASS_1XEVDO_REVB;
+                DeviceClass = DEVICE_CLASS_CDMA;
+                s_is_cdma = (0 == is_lte);
+            }
+            break;
+        case 0x11: // HDR
+            if (pServiceStatusInfo->SrvStatus == 0x02) {
+                DataCapList = WWAN_DATA_CLASS_3XRTT|
+                              WWAN_DATA_CLASS_UMB;
+                DeviceClass = DEVICE_CLASS_CDMA;
+                s_is_cdma = (0 == is_lte);
+            }
+            break;
+        case 0x12: // GSM
+            if (pServiceStatusInfo->SrvStatus == 0x02) {
+                DataCapList = WWAN_DATA_CLASS_GPRS|
+                              WWAN_DATA_CLASS_EDGE;
+                DeviceClass = DEVICE_CLASS_GSM;
+            }
+            break;
+        case 0x13: // WCDMA
+            if (pServiceStatusInfo->SrvStatus == 0x02) {
+                DataCapList = WWAN_DATA_CLASS_UMTS;
+                DeviceClass = DEVICE_CLASS_GSM;
+            }
+            break;
+        case 0x14: // LTE
+            if (pServiceStatusInfo->SrvStatus == 0x02) {
+                DataCapList = WWAN_DATA_CLASS_LTE;
+                DeviceClass = DEVICE_CLASS_GSM;
+                is_lte = 1;
+                s_is_cdma = 0;
+            }
+            break;
+        case 0x24: // TDSCDMA
+            if (pServiceStatusInfo->SrvStatus == 0x02) {
+                pDataCapStr = "TD-SCDMA";
+            }
+            break;
+        case 0x15: // CDMA
+            // CDMA_SYSTEM_INFO
+            pCdmaSystemInfo = (PCDMA_SYSTEM_INFO)pServiceStatusInfo;
+            if (pCdmaSystemInfo->SrvDomainValid == 0x01) {
+                *pPSAttachedState = 0;
+                if (pCdmaSystemInfo->SrvDomain & 0x02) {
+                    *pPSAttachedState = 1;
+                    s_is_cdma = (0 == is_lte);
+                }
+            }
+#if 0
+            if (pCdmaSystemInfo->SrvCapabilityValid == 0x01) {
+                *pPSAttachedState = 0;
+                if (pCdmaSystemInfo->SrvCapability & 0x02) {
+                    *pPSAttachedState = 1;
+                    s_is_cdma = (0 == is_lte);
+                }
+            }
+#endif
+            if (pCdmaSystemInfo->NetworkIdValid == 0x01) {
+                int i;
+                CHAR temp[10];
+                strncpy(temp, (CHAR *)pCdmaSystemInfo->MCC, 3);
+                temp[3] = '\0';
+                for (i = 0; i < 4; i++) {
+                    if ((UCHAR)temp[i] == 0xFF) {
+                        temp[i] = '\0';
+                    }
+                }
+                MobileCountryCode = (USHORT)atoi(temp);
+
+                strncpy(temp, (CHAR *)pCdmaSystemInfo->MNC, 3);
+                temp[3] = '\0';
+                for (i = 0; i < 4; i++) {
+                    if ((UCHAR)temp[i] == 0xFF) {
+                        temp[i] = '\0';
+                    }
+                }
+                MobileNetworkCode = (USHORT)atoi(temp);
+            }
+            break;
+        case 0x16: // HDR
+            // HDR_SYSTEM_INFO
+            pHdrSystemInfo = (PHDR_SYSTEM_INFO)pServiceStatusInfo;
+            if (pHdrSystemInfo->SrvDomainValid == 0x01) {
+                *pPSAttachedState = 0;
+                if (pHdrSystemInfo->SrvDomain & 0x02) {
+                    *pPSAttachedState = 1;
+                    s_is_cdma = (0 == is_lte);
+                }
+            }
+#if 0
+            if (pHdrSystemInfo->SrvCapabilityValid == 0x01) {
+                *pPSAttachedState = 0;
+                if (pHdrSystemInfo->SrvCapability & 0x02) {
+                    *pPSAttachedState = 1;
+                    s_is_cdma = (0 == is_lte);
+                }
+            }
+#endif
+            if (*pPSAttachedState && pHdrSystemInfo->HdrPersonalityValid == 0x01) {
+                if (pHdrSystemInfo->HdrPersonality == 0x03)
+                    s_hdr_personality = 0x02;
+                //else if (pHdrSystemInfo->HdrPersonality == 0x02)
+                //    s_hdr_personality = 0x01;
+            }
+            USHORT cmda_mcc = 0, cdma_mnc = 0;
+            if(!requestGetHomeNetwork(&cmda_mcc, &cdma_mnc,NULL, NULL) && cmda_mcc) {
+                meig_convert_cdma_mcc_2_ascii_mcc(&MobileCountryCode, cmda_mcc);
+                meig_convert_cdma_mnc_2_ascii_mnc(&MobileNetworkCode, cdma_mnc);
+            }
+            break;
+        case 0x17: // GSM
+            // GSM_SYSTEM_INFO
+            pGsmSystemInfo = (PGSM_SYSTEM_INFO)pServiceStatusInfo;
+            if (pGsmSystemInfo->SrvDomainValid == 0x01) {
+                *pPSAttachedState = 0;
+                if (pGsmSystemInfo->SrvDomain & 0x02) {
+                    *pPSAttachedState = 1;
+                }
+            }
+#if 0
+            if (pGsmSystemInfo->SrvCapabilityValid == 0x01) {
+                *pPSAttachedState = 0;
+                if (pGsmSystemInfo->SrvCapability & 0x02) {
+                    *pPSAttachedState = 1;
+                }
+            }
+#endif
+            if (pGsmSystemInfo->NetworkIdValid == 0x01) {
+                int i;
+                CHAR temp[10];
+                strncpy(temp, (CHAR *)pGsmSystemInfo->MCC, 3);
+                temp[3] = '\0';
+                for (i = 0; i < 4; i++) {
+                    if ((UCHAR)temp[i] == 0xFF) {
+                        temp[i] = '\0';
+                    }
+                }
+                MobileCountryCode = (USHORT)atoi(temp);
+
+                strncpy(temp, (CHAR *)pGsmSystemInfo->MNC, 3);
+                temp[3] = '\0';
+                for (i = 0; i < 4; i++) {
+                    if ((UCHAR)temp[i] == 0xFF) {
+                        temp[i] = '\0';
+                    }
+                }
+                MobileNetworkCode = (USHORT)atoi(temp);
+            }
+            break;
+        case 0x18: // WCDMA
+            // WCDMA_SYSTEM_INFO
+            pWcdmaSystemInfo = (PWCDMA_SYSTEM_INFO)pServiceStatusInfo;
+            if (pWcdmaSystemInfo->SrvDomainValid == 0x01) {
+                *pPSAttachedState = 0;
+                if (pWcdmaSystemInfo->SrvDomain & 0x02) {
+                    *pPSAttachedState = 1;
+                }
+            }
+#if 0
+            if (pWcdmaSystemInfo->SrvCapabilityValid == 0x01) {
+                *pPSAttachedState = 0;
+                if (pWcdmaSystemInfo->SrvCapability & 0x02) {
+                    *pPSAttachedState = 1;
+                }
+            }
+#endif
+            if (pWcdmaSystemInfo->NetworkIdValid == 0x01) {
+                int i;
+                CHAR temp[10];
+                strncpy(temp, (CHAR *)pWcdmaSystemInfo->MCC, 3);
+                temp[3] = '\0';
+                for (i = 0; i < 4; i++) {
+                    if ((UCHAR)temp[i] == 0xFF) {
+                        temp[i] = '\0';
+                    }
+                }
+                MobileCountryCode = (USHORT)atoi(temp);
+
+                strncpy(temp, (CHAR *)pWcdmaSystemInfo->MNC, 3);
+                temp[3] = '\0';
+                for (i = 0; i < 4; i++) {
+                    if ((UCHAR)temp[i] == 0xFF) {
+                        temp[i] = '\0';
+                    }
+                }
+                MobileNetworkCode = (USHORT)atoi(temp);
+            }
+            break;
+        case 0x19: // LTE_SYSTEM_INFO
+            // LTE_SYSTEM_INFO
+            pLteSystemInfo = (PLTE_SYSTEM_INFO)pServiceStatusInfo;
+            if (pLteSystemInfo->SrvDomainValid == 0x01) {
+                *pPSAttachedState = 0;
+                if (pLteSystemInfo->SrvDomain & 0x02) {
+                    *pPSAttachedState = 1;
+                    is_lte = 1;
+                    s_is_cdma = 0;
+                }
+            }
+#if 0
+            if (pLteSystemInfo->SrvCapabilityValid == 0x01) {
+                *pPSAttachedState = 0;
+                if (pLteSystemInfo->SrvCapability & 0x02) {
+                    *pPSAttachedState = 1;
+                    is_lte = 1;
+                    s_is_cdma = 0;
+                }
+            }
+#endif
+            if (pLteSystemInfo->NetworkIdValid == 0x01) {
+                int i;
+                CHAR temp[10];
+                strncpy(temp, (CHAR *)pLteSystemInfo->MCC, 3);
+                temp[3] = '\0';
+                for (i = 0; i < 4; i++) {
+                    if ((UCHAR)temp[i] == 0xFF) {
+                        temp[i] = '\0';
+                    }
+                }
+                MobileCountryCode = (USHORT)atoi(temp);
+
+                strncpy(temp, (CHAR *)pLteSystemInfo->MNC, 3);
+                temp[3] = '\0';
+                for (i = 0; i < 4; i++) {
+                    if ((UCHAR)temp[i] == 0xFF) {
+                        temp[i] = '\0';
+                    }
+                }
+                MobileNetworkCode = (USHORT)atoi(temp);
+            }
+            break;
+        case 0x25: // TDSCDMA
+            // TDSCDMA_SYSTEM_INFO
+            pTdscdmaSystemInfo = (PTDSCDMA_SYSTEM_INFO)pServiceStatusInfo;
+            if (pTdscdmaSystemInfo->SrvDomainValid == 0x01) {
+                *pPSAttachedState = 0;
+                if (pTdscdmaSystemInfo->SrvDomain & 0x02) {
+                    *pPSAttachedState = 1;
+                }
+            }
+#if 0
+            if (pTdscdmaSystemInfo->SrvCapabilityValid == 0x01) {
+                *pPSAttachedState = 0;
+                if (pTdscdmaSystemInfo->SrvCapability & 0x02) {
+                    *pPSAttachedState = 1;
+                }
+            }
+#endif
+            if (pTdscdmaSystemInfo->NetworkIdValid == 0x01) {
+                int i;
+                CHAR temp[10];
+                strncpy(temp, (CHAR *)pTdscdmaSystemInfo->MCC, 3);
+                temp[3] = '\0';
+                for (i = 0; i < 4; i++) {
+                    if ((UCHAR)temp[i] == 0xFF) {
+                        temp[i] = '\0';
+                    }
+                }
+                MobileCountryCode = (USHORT)atoi(temp);
+
+                strncpy(temp, (CHAR *)pTdscdmaSystemInfo->MNC, 3);
+                temp[3] = '\0';
+                for (i = 0; i < 4; i++) {
+                    if ((UCHAR)temp[i] == 0xFF) {
+                        temp[i] = '\0';
+                    }
+                }
+                MobileNetworkCode = (USHORT)atoi(temp);
+            }
+            break;
+        default:
+            break;
+        } /* switch (pServiceStatusInfo->TLYType) */
+        remainingLen -= (le16_to_cpu(pServiceStatusInfo->TLVLength) + 3);
+        pServiceStatusInfo = (PSERVICE_STATUS_INFO)((PCHAR)&pServiceStatusInfo->TLVLength + le16_to_cpu(pServiceStatusInfo->TLVLength) + sizeof(USHORT));
+    } /* while (remainingLen > 0) */
+
+    if (DeviceClass == DEVICE_CLASS_CDMA) {
+        if (s_hdr_personality == 2) {
+            pDataCapStr = s_hdr_personality == 2 ? "eHRPD" : "HRPD";
+        } else if (DataCapList & WWAN_DATA_CLASS_1XEVDO_REVB) {
+            pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_1XEVDO_REVB);
+        } else if (DataCapList & WWAN_DATA_CLASS_1XEVDO_REVA) {
+            pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_1XEVDO_REVA);
+        } else if (DataCapList & WWAN_DATA_CLASS_1XEVDO) {
+            pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_1XEVDO);
+        } else if (DataCapList & WWAN_DATA_CLASS_1XRTT) {
+            pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_1XRTT);
+        } else if (DataCapList & WWAN_DATA_CLASS_3XRTT) {
+            pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_3XRTT);
+        } else if (DataCapList & WWAN_DATA_CLASS_UMB) {
+            pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_UMB);
+        }
+    } else {
+        if (DataCapList & WWAN_DATA_CLASS_LTE) {
+            pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_LTE);
+        } else if ((DataCapList & WWAN_DATA_CLASS_HSDPA) && (DataCapList & WWAN_DATA_CLASS_HSUPA)) {
+            pDataCapStr = "HSDPA_HSUPA";
+        } else if (DataCapList & WWAN_DATA_CLASS_HSDPA) {
+            pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_HSDPA);
+        } else if (DataCapList & WWAN_DATA_CLASS_HSUPA) {
+            pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_HSUPA);
+        } else if (DataCapList & WWAN_DATA_CLASS_UMTS) {
+            pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_UMTS);
+        } else if (DataCapList & WWAN_DATA_CLASS_EDGE) {
+            pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_EDGE);
+        } else if (DataCapList & WWAN_DATA_CLASS_GPRS) {
+            pDataCapStr = wwan_data_class2str(WWAN_DATA_CLASS_GPRS);
+        }
+    }
+
+    dbg_time("%s MCC: %d, MNC: %d, PS: %s, DataCap: %s", __func__,
+        MobileCountryCode, MobileNetworkCode, (*pPSAttachedState == 1) ? "Attached" : "Detached" , pDataCapStr);
+
+    free(pResponse);
+
+    return 0;
+}
+
+int requestRegistrationState(UCHAR *pPSAttachedState) {
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    int err;
+    PQMINAS_CURRENT_PLMN_MSG pCurrentPlmn;
+    PSERVING_SYSTEM pServingSystem;
+    PQMINAS_DATA_CAP pDataCap;
+    USHORT MobileCountryCode = 0;
+    USHORT MobileNetworkCode = 0;
+    const char *pDataCapStr = "UNKNOW";
+
+    if (s_9x07) {
+        return requestRegistrationState2(pPSAttachedState);
+    }
+
+    pRequest = ComposeQMUXMsg(QMUX_TYPE_NAS, QMINAS_GET_SERVING_SYSTEM_REQ, NULL, NULL);
+    err = QmiThreadSendQMI(pRequest, &pResponse);
+    qmi_rsp_check_and_return();
+
+    pCurrentPlmn = (PQMINAS_CURRENT_PLMN_MSG)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x12);
+    if (pCurrentPlmn) {
+        MobileCountryCode = le16_to_cpu(pCurrentPlmn->MobileCountryCode);
+        MobileNetworkCode = le16_to_cpu(pCurrentPlmn->MobileNetworkCode);
+    }
+
+    *pPSAttachedState = 0;
+    pServingSystem = (PSERVING_SYSTEM)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x01);
+    if (pServingSystem) {
+    //Packet-switched domain attach state of the mobile.
+    //0x00    PS_UNKNOWN ?Unknown or not applicable
+    //0x01    PS_ATTACHED ?Attached
+    //0x02    PS_DETACHED ?Detached
+        *pPSAttachedState = pServingSystem->RegistrationState;
+        if (pServingSystem->RegistrationState == 0x01) //0x01 ?C REGISTERED ?C Registered with a network
+            *pPSAttachedState  = pServingSystem->PSAttachedState;
+        else {
+            //MobileCountryCode = MobileNetworkCode = 0;
+            *pPSAttachedState  = 0x02;
+        }
+    }
+
+    pDataCap = (PQMINAS_DATA_CAP)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11);
+    if (pDataCap && pDataCap->DataCapListLen) {
+        UCHAR *DataCap = &pDataCap->DataCap;
+        if (pDataCap->DataCapListLen == 2) {
+            if ((DataCap[0] == 0x06) && ((DataCap[1] == 0x08) || (DataCap[1] == 0x0A)))
+                DataCap[0] = DataCap[1];
+        }
+        switch (DataCap[0]) {
+             case 0x01: pDataCapStr = "GPRS"; break;
+             case 0x02: pDataCapStr = "EDGE"; break;
+             case 0x03: pDataCapStr = "HSDPA"; break;
+             case 0x04: pDataCapStr = "HSUPA"; break;
+             case 0x05: pDataCapStr = "UMTS"; break;
+             case 0x06: pDataCapStr = "1XRTT"; break;
+             case 0x07: pDataCapStr = "1XEVDO"; break;
+             case 0x08: pDataCapStr = "1XEVDO_REVA"; break;
+             case 0x09: pDataCapStr = "GPRS"; break;
+             case 0x0A: pDataCapStr = "1XEVDO_REVB"; break;
+             case 0x0B: pDataCapStr = "LTE"; break;
+             case 0x0C: pDataCapStr = "HSDPA"; break;
+             case 0x0D: pDataCapStr = "HSDPA"; break;
+             default: pDataCapStr = "UNKNOW"; break;
+        }
+    }
+
+    if (pServingSystem && pServingSystem->RegistrationState == 0x01 && pServingSystem->InUseRadioIF && pServingSystem->RadioIF == 0x09) {
+        pDataCapStr = "TD-SCDMA";
+    }
+
+    s_is_cdma = 0;
+    if (pServingSystem && pServingSystem->RegistrationState == 0x01 && pServingSystem->InUseRadioIF && (pServingSystem->RadioIF == 0x01 || pServingSystem->RadioIF == 0x02)) {
+        USHORT cmda_mcc = 0, cdma_mnc = 0;
+        s_is_cdma = 1;
+        if(!requestGetHomeNetwork(&cmda_mcc, &cdma_mnc,NULL, NULL) && cmda_mcc) {
+            meig_convert_cdma_mcc_2_ascii_mcc(&MobileCountryCode, cmda_mcc);
+            meig_convert_cdma_mnc_2_ascii_mnc(&MobileNetworkCode, cdma_mnc);
+        }
+        if (1) {
+            PQCQMUX_TLV pTLV = (PQCQMUX_TLV)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x23);
+            if (pTLV)
+                s_hdr_personality = pTLV->Value;
+            else
+                s_hdr_personality = 0;
+            if (s_hdr_personality == 2)
+                pDataCapStr = "eHRPD";
+        }
+    }
+
+    dbg_time("%s MCC: %d, MNC: %d, PS: %s, DataCap: %s", __func__,
+        MobileCountryCode, MobileNetworkCode, (*pPSAttachedState == 1) ? "Attached" : "Detached" , pDataCapStr);
+
+    free(pResponse);
+
+    return 0;
+}
+
+int requestQueryDataCall(UCHAR  *pConnectionStatus, int curIpFamily) {
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    int err;
+    PQMIWDS_PKT_SRVC_TLV pPktSrvc;
+    UCHAR oldConnectionStatus = *pConnectionStatus;
+    UCHAR QMIType = (curIpFamily == IpFamilyV4) ? QMUX_TYPE_WDS : QMUX_TYPE_WDS_IPV6;
+
+    pRequest = ComposeQMUXMsg(QMIType, QMIWDS_GET_PKT_SRVC_STATUS_REQ, NULL, NULL);
+    err = QmiThreadSendQMI(pRequest, &pResponse);
+    qmi_rsp_check_and_return();
+
+    *pConnectionStatus = QWDS_PKT_DATA_DISCONNECTED;
+    pPktSrvc = (PQMIWDS_PKT_SRVC_TLV)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x01);
+    if (pPktSrvc) {
+        *pConnectionStatus = pPktSrvc->ConnectionStatus;
+        if ((le16_to_cpu(pPktSrvc->TLVLength) == 2) && (pPktSrvc->ReconfigReqd == 0x01))
+            *pConnectionStatus = QWDS_PKT_DATA_DISCONNECTED;
+    }
+
+    if (*pConnectionStatus == QWDS_PKT_DATA_DISCONNECTED) {
+        if (curIpFamily == IpFamilyV4)
+            WdsConnectionIPv4Handle = 0;
+        else
+            WdsConnectionIPv6Handle = 0;
+    }
+
+    if (oldConnectionStatus != *pConnectionStatus || debug_qmi) {
+        dbg_time("%s %sConnectionStatus: %s", __func__, (curIpFamily == IpFamilyV4) ? "IPv4" : "IPv6",
+            (*pConnectionStatus == QWDS_PKT_DATA_CONNECTED) ? "CONNECTED" : "DISCONNECTED");
+    }
+
+    free(pResponse);
+    return 0;
+}
+
+int requestSetupDataCall(PROFILE_T *profile, int curIpFamily) {
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    int err = 0;
+    UCHAR QMIType = (curIpFamily == IpFamilyV4) ? QMUX_TYPE_WDS : QMUX_TYPE_WDS_IPV6;
+
+//DualIPSupported means can get ipv4 & ipv6 address at the same time, one wds for ipv4, the other wds for ipv6
+    profile->curIpFamily = curIpFamily;
+    pRequest = ComposeQMUXMsg(QMIType, QMIWDS_START_NETWORK_INTERFACE_REQ, WdsStartNwInterfaceReq, profile);
+    err = QmiThreadSendQMITimeout(pRequest, &pResponse, 120 * 1000);
+    qmi_rsp_check();
+
+    if (le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXResult) || le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError)) {
+        PQMI_TLV_HDR pTLVHdr;
+
+        pTLVHdr = GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x10);
+        if (pTLVHdr) {
+            uint16_t *data16 = (uint16_t *)(pTLVHdr+1);
+            uint16_t call_end_reason = le16_to_cpu(data16[0]);
+            dbg_time("call_end_reason is %d", call_end_reason);
+        }
+
+        pTLVHdr = GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x11);
+        if (pTLVHdr) {
+            uint16_t *data16 = (uint16_t *)(pTLVHdr+1);
+            uint16_t call_end_reason_type = le16_to_cpu(data16[0]);
+            uint16_t verbose_call_end_reason  = le16_to_cpu(data16[1]);
+
+            dbg_time("call_end_reason_type is %d", call_end_reason_type);
+            dbg_time("call_end_reason_verbose is %d", verbose_call_end_reason);
+        }
+
+        free(pResponse);
+        return le16_to_cpu(pMUXMsg->QMUXMsgHdrResp.QMUXError);
+    }
+
+    if (curIpFamily == IpFamilyV4) {
+        WdsConnectionIPv4Handle = le32_to_cpu(pResponse->MUXMsg.StartNwInterfaceResp.Handle);
+        dbg_time("%s WdsConnectionIPv4Handle: 0x%08x", __func__, WdsConnectionIPv4Handle);
+    } else {
+        WdsConnectionIPv6Handle = le32_to_cpu(pResponse->MUXMsg.StartNwInterfaceResp.Handle);
+        dbg_time("%s WdsConnectionIPv6Handle: 0x%08x", __func__, WdsConnectionIPv6Handle);
+    }
+
+    free(pResponse);
+
+    return 0;
+}
+
+int requestDeactivateDefaultPDP(PROFILE_T *profile, int curIpFamily) {
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    int err;
+    UCHAR QMIType = (curIpFamily == 0x04) ? QMUX_TYPE_WDS : QMUX_TYPE_WDS_IPV6;
+
+    if (curIpFamily == IpFamilyV4 && WdsConnectionIPv4Handle == 0)
+        return 0;
+    if (curIpFamily == IpFamilyV6 && WdsConnectionIPv6Handle == 0)
+        return 0;
+
+    pRequest = ComposeQMUXMsg(QMIType, QMIWDS_STOP_NETWORK_INTERFACE_REQ , WdsStopNwInterfaceReq, &curIpFamily);
+    err = QmiThreadSendQMI(pRequest, &pResponse);
+    qmi_rsp_check_and_return();
+
+    if (curIpFamily == IpFamilyV4)
+        WdsConnectionIPv4Handle = 0;
+    else
+         WdsConnectionIPv6Handle = 0;
+    free(pResponse);
+    return 0;
+}
+
+int requestGetIPAddress(PROFILE_T *profile, int curIpFamily) {
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    int err;
+    PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR pIpv4Addr;
+    PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR pIpv6Addr = NULL;
+    PQMIWDS_GET_RUNTIME_SETTINGS_TLV_MTU pMtu;
+    IPV4_T *pIpv4 = &profile->ipv4;
+    IPV6_T *pIpv6 = &profile->ipv6;
+    UCHAR QMIType = (curIpFamily == 0x04) ? QMUX_TYPE_WDS : QMUX_TYPE_WDS_IPV6;
+
+    if (curIpFamily == IpFamilyV4) {
+        memset(pIpv4, 0x00, sizeof(IPV4_T));
+        if (WdsConnectionIPv4Handle == 0)
+            return 0;
+    } else if (curIpFamily == IpFamilyV6) {
+        memset(pIpv6, 0x00, sizeof(IPV6_T));
+        if (WdsConnectionIPv6Handle == 0)
+            return 0;
+    }
+
+    pRequest = ComposeQMUXMsg(QMIType, QMIWDS_GET_RUNTIME_SETTINGS_REQ, WdsGetRuntimeSettingReq, NULL);
+    err = QmiThreadSendQMI(pRequest, &pResponse);
+    qmi_rsp_check_and_return();
+
+    pIpv4Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4PRIMARYDNS);
+    if (pIpv4Addr) {
+        pIpv4->DnsPrimary = pIpv4Addr->IPV4Address;
+    }
+
+    pIpv4Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4SECONDARYDNS);
+    if (pIpv4Addr) {
+        pIpv4->DnsSecondary = pIpv4Addr->IPV4Address;
+    }
+
+    pIpv4Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4GATEWAY);
+    if (pIpv4Addr) {
+        pIpv4->Gateway = pIpv4Addr->IPV4Address;
+    }
+
+    pIpv4Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4SUBNET);
+    if (pIpv4Addr) {
+        pIpv4->SubnetMask = pIpv4Addr->IPV4Address;
+    }
+
+    pIpv4Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV4_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV4);
+    if (pIpv4Addr) {
+        pIpv4->Address = pIpv4Addr->IPV4Address;
+    }
+
+    pIpv6Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6PRIMARYDNS);
+    if (pIpv6Addr) {
+        memcpy(pIpv6->DnsPrimary, pIpv6Addr->IPV6Address, 16);
+    }
+
+    pIpv6Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6SECONDARYDNS);
+    if (pIpv6Addr) {
+        memcpy(pIpv6->DnsSecondary, pIpv6Addr->IPV6Address, 16);
+    }
+
+    pIpv6Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6GATEWAY);
+    if (pIpv6Addr) {
+        memcpy(pIpv6->Gateway, pIpv6Addr->IPV6Address, 16);
+        pIpv6->PrefixLengthGateway = pIpv6Addr->PrefixLength;
+    }
+
+    pIpv6Addr = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_IPV6_ADDR)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_IPV6);
+    if (pIpv6Addr) {
+        memcpy(pIpv6->Address, pIpv6Addr->IPV6Address, 16);
+        pIpv6->PrefixLengthIPAddr = pIpv6Addr->PrefixLength;
+    }
+
+    pMtu = (PQMIWDS_GET_RUNTIME_SETTINGS_TLV_MTU)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, QMIWDS_GET_RUNTIME_SETTINGS_TLV_TYPE_MTU);
+    if (pMtu) {
+        pIpv4->Mtu =  pIpv6->Mtu =  le32_to_cpu(pMtu->Mtu);
+    }
+
+    free(pResponse);
+    return 0;
+}
+
+#ifdef CONFIG_APN
+int requestSetProfile(PROFILE_T *profile) {
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    int err;
+
+    if (!profile->pdp)
+        return 0;
+
+    dbg_time("%s[%d] %s/%s/%s/%d", __func__, profile->pdp, profile->apn, profile->user, profile->password, profile->auth);
+    pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_MODIFY_PROFILE_SETTINGS_REQ, WdsModifyProfileSettingsReq, profile);
+    err = QmiThreadSendQMI(pRequest, &pResponse);
+    qmi_rsp_check_and_return();
+
+    free(pResponse);
+    return 0;
+}
+
+int requestGetProfile(PROFILE_T *profile) {
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    int err;
+    char *apn = NULL;
+    char *user = NULL;
+    char *password = NULL;
+    int auth = 0;
+    PQMIWDS_APNNAME pApnName;
+    PQMIWDS_USERNAME pUserName;
+    PQMIWDS_PASSWD pPassWd;
+    PQMIWDS_AUTH_PREFERENCE pAuthPref;
+
+    if (!profile->pdp)
+        return 0;
+
+    pRequest = ComposeQMUXMsg(QMUX_TYPE_WDS, QMIWDS_GET_PROFILE_SETTINGS_REQ, WdsGetProfileSettingsReqSend, profile);
+    err = QmiThreadSendQMI(pRequest, &pResponse);
+    qmi_rsp_check_and_return();
+
+    pApnName = (PQMIWDS_APNNAME)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x14);
+    pUserName = (PQMIWDS_USERNAME)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x1B);
+    pPassWd = (PQMIWDS_PASSWD)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x1C);
+    pAuthPref = (PQMIWDS_AUTH_PREFERENCE)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x1D);
+
+    if (pApnName/* && le16_to_cpu(pApnName->TLVLength)*/)
+        apn = strndup((const char *)(&pApnName->ApnName), le16_to_cpu(pApnName->TLVLength));
+    if (pUserName/*  && pUserName->UserName*/)
+        user = strndup((const char *)(&pUserName->UserName), le16_to_cpu(pUserName->TLVLength));
+    if (pPassWd/*  && le16_to_cpu(pPassWd->TLVLength)*/)
+        password = strndup((const char *)(&pPassWd->Passwd), le16_to_cpu(pPassWd->TLVLength));
+    if (pAuthPref/*  && le16_to_cpu(pAuthPref->TLVLength)*/) {
+        auth = pAuthPref->AuthPreference;
+    }
+
+#if 0
+    if (profile) {
+        profile->apn = apn;
+        profile->user = user;
+        profile->password = password;
+        profile->auth = auth;
+    }
+#endif
+
+    dbg_time("%s[%d] %s/%s/%s/%d", __func__, profile->pdp, apn, user, password, auth);
+
+    free(pResponse);
+    return 0;
+}
+#endif
+
+#ifdef CONFIG_VERSION
+int requestBaseBandVersion(const char **pp_reversion) {
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    PDEVICE_REV_ID revId;
+    int err;
+
+    if (pp_reversion) *pp_reversion = NULL;
+
+    pRequest = ComposeQMUXMsg(QMUX_TYPE_DMS, QMIDMS_GET_DEVICE_REV_ID_REQ, NULL, NULL);
+    err = QmiThreadSendQMI(pRequest, &pResponse);
+    qmi_rsp_check_and_return();
+
+    revId = (PDEVICE_REV_ID)GetTLV(&pResponse->MUXMsg.QMUXMsgHdr, 0x01);
+
+    if (revId && le16_to_cpu(revId->TLVLength))
+    {
+        char *DeviceRevisionID = strndup((const char *)(&revId->RevisionID), le16_to_cpu(revId->TLVLength));
+        dbg_time("%s %s", __func__, DeviceRevisionID);
+        if (s_9x07 == -1) { //fail to get QMUX_TYPE_WDS_ADMIN
+            if (strncmp(DeviceRevisionID, "EC20", strlen("EC20")))
+                s_9x07 = 1;
+            else
+                s_9x07 = DeviceRevisionID[5] == 'F' || DeviceRevisionID[6] == 'F'; //EC20CF,EC20EF,EC20CEF
+        }
+        if (pp_reversion) *pp_reversion = DeviceRevisionID;
+    }
+
+    free(pResponse);
+    return 0;
+}
+#endif
+
+#ifdef CONFIG_RESET_RADIO
+static USHORT DmsSetOperatingModeReq(PQMUX_MSG pMUXMsg, void *arg) {
+    pMUXMsg->SetOperatingModeReq.TLVType = 0x01;
+    pMUXMsg->SetOperatingModeReq.TLVLength = cpu_to_le16(1);
+    pMUXMsg->SetOperatingModeReq.OperatingMode = *((UCHAR *)arg);
+
+    return sizeof(QMIDMS_SET_OPERATING_MODE_REQ_MSG);
+}
+
+int requestSetOperatingMode(UCHAR OperatingMode) {
+    PQCQMIMSG pRequest;
+    PQCQMIMSG pResponse;
+    PQMUX_MSG pMUXMsg;
+    int err;
+
+    dbg_time("%s(%d)", __func__, OperatingMode);
+    
+    pRequest = ComposeQMUXMsg(QMUX_TYPE_DMS, QMIDMS_SET_OPERATING_MODE_REQ, DmsSetOperatingModeReq, &OperatingMode);
+    err = QmiThreadSendQMI(pRequest, &pResponse);
+    qmi_rsp_check_and_return();
+
+    free(pResponse);
+    return 0;
+}
+#endif
diff --git a/package/wwan/meig-cm/src/QMIThread.h b/package/wwan/meig-cm/src/QMIThread.h
new file mode 100644
index 000000000..59208887d
--- /dev/null
+++ b/package/wwan/meig-cm/src/QMIThread.h
@@ -0,0 +1,228 @@
+#ifndef __QMI_THREAD_H__
+#define __QMI_THREAD_H__
+
+#define CONFIG_GOBINET
+#define CONFIG_QMIWWAN
+#define CONFIG_SIM
+#define CONFIG_APN
+#define CONFIG_VERSION
+#define CONFIG_DEFAULT_PDP 1
+//#define CONFIG_IMSI_ICCID
+
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <time.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/epoll.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <stddef.h>
+
+#include "MPQMI.h"
+#include "MPQCTL.h"
+#include "MPQMUX.h"
+#include "util.h"
+
+#define DEVICE_CLASS_UNKNOWN           0
+#define DEVICE_CLASS_CDMA              1
+#define DEVICE_CLASS_GSM               2
+
+#define WWAN_DATA_CLASS_NONE            0x00000000
+#define WWAN_DATA_CLASS_GPRS            0x00000001
+#define WWAN_DATA_CLASS_EDGE            0x00000002 /* EGPRS */
+#define WWAN_DATA_CLASS_UMTS            0x00000004
+#define WWAN_DATA_CLASS_HSDPA           0x00000008
+#define WWAN_DATA_CLASS_HSUPA           0x00000010
+#define WWAN_DATA_CLASS_LTE             0x00000020
+#define WWAN_DATA_CLASS_1XRTT           0x00010000
+#define WWAN_DATA_CLASS_1XEVDO          0x00020000
+#define WWAN_DATA_CLASS_1XEVDO_REVA     0x00040000
+#define WWAN_DATA_CLASS_1XEVDV          0x00080000
+#define WWAN_DATA_CLASS_3XRTT           0x00100000
+#define WWAN_DATA_CLASS_1XEVDO_REVB     0x00200000 /* for future use */
+#define WWAN_DATA_CLASS_UMB             0x00400000
+#define WWAN_DATA_CLASS_CUSTOM          0x80000000
+
+struct wwan_data_class_str {
+    ULONG class;
+    CHAR *str;
+};
+
+#pragma pack(push, 1)
+
+typedef struct _QCQMIMSG {
+    QCQMI_HDR QMIHdr;
+    union {
+        QMICTL_MSG CTLMsg;
+        QMUX_MSG MUXMsg;
+    };
+} __attribute__ ((packed)) QCQMIMSG, *PQCQMIMSG;
+
+typedef struct __IPV4 {
+    uint32_t Address;
+    uint32_t Gateway;
+    uint32_t SubnetMask;
+    uint32_t DnsPrimary;
+    uint32_t DnsSecondary;
+    uint32_t Mtu;
+} IPV4_T;
+
+typedef struct __IPV6 {
+    UCHAR Address[16];
+    UCHAR Gateway[16];
+    UCHAR SubnetMask[16];
+    UCHAR DnsPrimary[16];
+    UCHAR DnsSecondary[16];
+    UCHAR PrefixLengthIPAddr;
+    UCHAR PrefixLengthGateway;
+    ULONG Mtu;
+} IPV6_T;
+
+#define IpFamilyV4 (0x04)
+#define IpFamilyV6 (0x06)
+
+struct __PROFILE;
+struct qmi_device_ops {
+	int (*init)(struct __PROFILE *profile);
+	int (*deinit)(void);
+	int (*send)(PQCQMIMSG pRequest);
+	void* (*read)(void *pData);
+
+    
+    // int (*thread_read)(struct __PROFILE *profile);
+    // int (*init)(struct __PROFILE *profile);
+    // int (*open)(struct __PROFILE *profile);
+    // int (*close)(struct __PROFILE *profile);
+    // int (*reopen)(struct __PROFILE *profile);
+    // int (*start_network)(struct __PROFILE *profile);
+    // int (*stop_network)(struct __PROFILE *profile);
+    // int (*query_network)(struct __PROFILE *profile);
+};
+extern int (*qmidev_send)(PQCQMIMSG pRequest);
+
+typedef struct __PROFILE {
+    char *qmichannel;
+    char *usbnet_adapter;
+    char *qmapnet_adapter;
+    char *driver_name;
+    int qmap_mode;
+    int qmap_version;
+    const char *apn;
+    const char *user;
+    const char *password;
+    const char *pincode;
+    int auth;
+    int pdp;
+    int IsDualIPSupported;
+    int curIpFamily;
+    int rawIP;
+    int muxid;
+    IPV4_T ipv4;
+    IPV6_T ipv6;
+    int enable_ipv6;
+    int ipv4_flag;
+    int ipv6_flag;
+    int apntype;
+    const struct qmi_device_ops *qmi_ops;
+} PROFILE_T;
+
+typedef enum {
+    SIM_ABSENT = 0,
+    SIM_NOT_READY = 1,
+    SIM_READY = 2, /* SIM_READY means the radio state is RADIO_STATE_SIM_READY */
+    SIM_PIN = 3,
+    SIM_PUK = 4,
+    SIM_NETWORK_PERSONALIZATION = 5,
+    SIM_BAD = 6,
+} SIM_Status;
+
+#pragma pack(pop)
+
+#define WDM_DEFAULT_BUFSIZE	256
+#define RIL_REQUEST_QUIT    0x1000
+#define RIL_INDICATE_DEVICE_CONNECTED    0x1002
+#define RIL_INDICATE_DEVICE_DISCONNECTED    0x1003
+#define RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED    0x1004
+#define RIL_UNSOL_DATA_CALL_LIST_CHANGED    0x1005
+
+extern int pthread_cond_timeout_np(pthread_cond_t *cond, pthread_mutex_t * mutex, unsigned msecs);
+extern int QmiThreadSendQMI(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse);
+extern int QmiThreadSendQMITimeout(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse, unsigned msecs);
+extern void QmiThreadRecvQMI(PQCQMIMSG pResponse);
+extern void udhcpc_start(PROFILE_T *profile);
+extern void udhcpc_stop(PROFILE_T *profile);
+extern void dump_qmi(void *dataBuffer, int dataLen);
+extern void qmidevice_send_event_to_main(int triger_event);
+extern int requestSetEthMode(PROFILE_T *profile);
+extern int requestGetSIMStatus(SIM_Status *pSIMStatus);
+extern int requestEnterSimPin(const CHAR *pPinCode);
+extern int requestGetICCID(void);
+extern int requestGetIMSI(void);
+extern int requestRegistrationState(UCHAR *pPSAttachedState);
+extern int requestQueryDataCall(UCHAR  *pConnectionStatus, int curIpFamily);
+extern int requestSetupDataCall(PROFILE_T *profile, int curIpFamily);
+extern int requestDeactivateDefaultPDP(PROFILE_T *profile, int curIpFamily);
+extern int requestSetProfile(PROFILE_T *profile);
+extern int requestGetProfile(PROFILE_T *profile);
+extern int requestBaseBandVersion(const char **pp_reversion);
+extern int requestGetIPAddress(PROFILE_T *profile, int curIpFamily);
+extern int requestSetOperatingMode(UCHAR OperatingMode);
+
+extern void cond_setclock_attr(pthread_cond_t *cond, clockid_t clock);
+extern int mbim_main(PROFILE_T *profile);
+extern int varify_driver(PROFILE_T *profile);
+extern BOOL qmidevice_detect(char *qmichannel, char *usbnet_adapter, unsigned bufsize);
+extern int meig_bridge_mode_detect(PROFILE_T *profile);
+extern int meig_enable_qmi_wwan_rawip_mode(PROFILE_T *profile);
+extern int meig_driver_type_detect(PROFILE_T *profile);
+extern int meig_qmap_mode_detect(PROFILE_T *profile);
+extern const struct qmi_device_ops gobi_qmidev_ops;
+extern const struct qmi_device_ops qmiwwan_qmidev_ops;
+
+#define qmidev_is_gobinet(_qmichannel) (strncmp(_qmichannel, "/dev/qcqmi", strlen("/dev/qcqmi")) == 0)
+#define qmidev_is_qmiwwan(_qmichannel) (strncmp(_qmichannel, "/dev/cdc-wdm", strlen("/dev/cdc-wdm")) == 0)
+#define qmidev_is_pciemhi(_qmichannel) (strncmp(_qmichannel, "/dev/mhi_", strlen("/dev/mhi_")) == 0)
+
+#define driver_is_qmi(_drv_name) (strncasecmp(_drv_name, "qmi_wwan", strlen("qmi_wwan")) == 0)
+#define driver_is_mbim(_drv_name) (strncasecmp(_drv_name, "cdc_mbim", strlen("cdc_mbim")) == 0)
+
+extern FILE *logfilefp;
+extern int debug_qmi;
+extern int qmidevice_control_fd[2];
+extern int qmiclientId[QMUX_TYPE_WDS_ADMIN + 1];
+extern USHORT le16_to_cpu(USHORT v16);
+extern UINT  le32_to_cpu (UINT v32);
+extern UINT  meig_swap32(UINT v32);
+extern USHORT cpu_to_le16(USHORT v16);
+extern UINT cpu_to_le32(UINT v32);
+extern void update_resolv_conf(int iptype, const char *ifname, const char *dns1, const char *dns2);
+
+#define CM_MAX_BUFF 256
+#define strset(k, v) {if (k) free(k); k = strdup(v);}
+#define mfree(v) {if (v) {free(v); v = NULL;}
+
+#ifdef CM_DEBUG
+#define dbg_time(fmt, args...) do { \
+    fprintf(stdout, "[%s-%04d: %s] " fmt "\n", __FILE__, __LINE__, get_time(), ##args); \
+    if (logfilefp) fprintf(logfilefp, "[%s-%04d: %s] " fmt "\n", __FILE__, __LINE__, get_time(), ##args); \
+} while(0);
+#else
+#define dbg_time(fmt, args...) do { \
+    fprintf(stdout, "[%s] " fmt "\n", get_time(), ##args); \
+    if (logfilefp) fprintf(logfilefp, "[%s] " fmt "\n", get_time(), ##args); \
+} while(0);
+#endif
+#endif
diff --git a/package/wwan/meig-cm/src/QmiWwanCM.c b/package/wwan/meig-cm/src/QmiWwanCM.c
new file mode 100644
index 000000000..a8c979f21
--- /dev/null
+++ b/package/wwan/meig-cm/src/QmiWwanCM.c
@@ -0,0 +1,371 @@
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <stdio.h>
+#include <ctype.h>
+typedef unsigned short sa_family_t;
+#include <linux/un.h>
+#include "QMIThread.h"
+
+#ifdef CONFIG_QMIWWAN
+static int cdc_wdm_fd = -1;
+static UCHAR GetQCTLTransactionId(void) {
+    static int TransactionId = 0;
+    if (++TransactionId > 0xFF)
+        TransactionId = 1;
+    return TransactionId;
+}
+
+typedef USHORT (*CUSTOMQCTL)(PQMICTL_MSG pCTLMsg, void *arg);
+
+static PQCQMIMSG ComposeQCTLMsg(USHORT QMICTLType, CUSTOMQCTL customQctlMsgFunction, void *arg) {
+    UCHAR QMIBuf[WDM_DEFAULT_BUFSIZE];
+    PQCQMIMSG pRequest = (PQCQMIMSG)QMIBuf;
+    int Length;
+
+    pRequest->QMIHdr.IFType   = USB_CTL_MSG_TYPE_QMI;
+    pRequest->QMIHdr.CtlFlags = 0x00;
+    pRequest->QMIHdr.QMIType  = QMUX_TYPE_CTL;
+    pRequest->QMIHdr.ClientId= 0x00;
+
+    pRequest->CTLMsg.QMICTLMsgHdr.CtlFlags = QMICTL_FLAG_REQUEST;
+    pRequest->CTLMsg.QMICTLMsgHdr.TransactionId = GetQCTLTransactionId();
+    pRequest->CTLMsg.QMICTLMsgHdr.QMICTLType = cpu_to_le16(QMICTLType);
+    if (customQctlMsgFunction)
+        pRequest->CTLMsg.QMICTLMsgHdr.Length = cpu_to_le16(customQctlMsgFunction(&pRequest->CTLMsg, arg) - sizeof(QCQMICTL_MSG_HDR));
+    else
+        pRequest->CTLMsg.QMICTLMsgHdr.Length = cpu_to_le16(0x0000);
+
+    pRequest->QMIHdr.Length = cpu_to_le16(le16_to_cpu(pRequest->CTLMsg.QMICTLMsgHdr.Length) + sizeof(QCQMICTL_MSG_HDR) + sizeof(QCQMI_HDR) - 1);
+    Length = le16_to_cpu(pRequest->QMIHdr.Length) + 1;
+
+    pRequest = (PQCQMIMSG)malloc(Length);
+    if (pRequest == NULL) {
+        dbg_time("%s fail to malloc", __func__);
+    } else {
+        memcpy(pRequest, QMIBuf, Length);
+    }
+
+    return pRequest;
+}
+
+static USHORT CtlGetVersionReq(PQMICTL_MSG QCTLMsg, void *arg) {
+   QCTLMsg->GetVersionReq.TLVType       = QCTLV_TYPE_REQUIRED_PARAMETER;
+   QCTLMsg->GetVersionReq.TLVLength     = cpu_to_le16(0x0001);
+   QCTLMsg->GetVersionReq.QMUXTypes     = QMUX_TYPE_ALL;
+   return sizeof(QMICTL_GET_VERSION_REQ_MSG);
+}
+
+static USHORT CtlGetClientIdReq(PQMICTL_MSG QCTLMsg, void *arg) {
+   QCTLMsg->GetClientIdReq.TLVType       = QCTLV_TYPE_REQUIRED_PARAMETER;
+   QCTLMsg->GetClientIdReq.TLVLength     = cpu_to_le16(0x0001);
+   QCTLMsg->GetClientIdReq.QMIType     = ((UCHAR *)arg)[0];
+   return sizeof(QMICTL_GET_CLIENT_ID_REQ_MSG);
+}
+
+static USHORT CtlReleaseClientIdReq(PQMICTL_MSG QCTLMsg, void *arg) {
+   QCTLMsg->ReleaseClientIdReq.TLVType       = QCTLV_TYPE_REQUIRED_PARAMETER;
+   QCTLMsg->ReleaseClientIdReq.TLVLength     = cpu_to_le16(0x0002);
+   QCTLMsg->ReleaseClientIdReq.QMIType     = ((UCHAR *)arg)[0];
+   QCTLMsg->ReleaseClientIdReq.ClientId = ((UCHAR *)arg)[1] ;
+   return sizeof(QMICTL_RELEASE_CLIENT_ID_REQ_MSG);
+}
+
+static int QmiWwanSendQMI(PQCQMIMSG pRequest) {
+    struct pollfd pollfds[]= {{cdc_wdm_fd, POLLOUT, 0}};
+    int ret;
+
+    if (cdc_wdm_fd == -1) {
+        dbg_time("%s cdc_wdm_fd = -1", __func__);
+        return -ENODEV;
+    }
+
+    if (pRequest->QMIHdr.QMIType == QMUX_TYPE_WDS_IPV6)
+        pRequest->QMIHdr.QMIType = QMUX_TYPE_WDS;
+
+    do {
+        ret = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), 5000);
+    } while ((ret < 0) && (errno == EINTR));
+
+    if (pollfds[0].revents & POLLOUT) {
+        ssize_t nwrites = le16_to_cpu(pRequest->QMIHdr.Length) + 1;
+        ret = write(cdc_wdm_fd, pRequest, nwrites);
+        if (ret == nwrites) {
+            ret = 0;
+        } else {
+            dbg_time("%s write=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
+        }
+    } else {
+        dbg_time("%s poll=%d, revents = 0x%x, errno: %d (%s)", __func__, ret, pollfds[0].revents, errno, strerror(errno));
+    }
+
+    return ret;
+}
+
+static int QmiWwanGetClientID(UCHAR QMIType) {
+    PQCQMIMSG pResponse;
+
+    QmiThreadSendQMI(ComposeQCTLMsg(QMICTL_GET_CLIENT_ID_REQ, CtlGetClientIdReq, &QMIType), &pResponse);
+
+    if (pResponse) {
+        USHORT QMUXResult = cpu_to_le16(pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXResult);       // QMI_RESULT_SUCCESS
+        USHORT QMUXError = cpu_to_le16(pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXError);        // QMI_ERR_INVALID_ARG
+        //UCHAR QMIType = pResponse->CTLMsg.GetClientIdRsp.QMIType;
+        UCHAR ClientId = pResponse->CTLMsg.GetClientIdRsp.ClientId;
+
+        if (!QMUXResult && !QMUXError && (QMIType == pResponse->CTLMsg.GetClientIdRsp.QMIType)) {
+            switch (QMIType) {
+                case QMUX_TYPE_WDS: dbg_time("Get clientWDS = %d", ClientId); break;
+                case QMUX_TYPE_DMS: dbg_time("Get clientDMS = %d", ClientId); break;
+                case QMUX_TYPE_NAS: dbg_time("Get clientNAS = %d", ClientId); break;
+                case QMUX_TYPE_QOS: dbg_time("Get clientQOS = %d", ClientId); break;
+                case QMUX_TYPE_WMS: dbg_time("Get clientWMS = %d", ClientId); break;
+                case QMUX_TYPE_PDS: dbg_time("Get clientPDS = %d", ClientId); break;
+                case QMUX_TYPE_UIM: dbg_time("Get clientUIM = %d", ClientId); break;
+                case QMUX_TYPE_WDS_ADMIN: dbg_time("Get clientWDA = %d", ClientId);
+                break;
+                default: break;
+            }
+            return ClientId;
+        }
+    }
+    return 0;
+}
+
+static int QmiWwanReleaseClientID(QMI_SERVICE_TYPE QMIType, UCHAR ClientId) {
+    UCHAR argv[] = {QMIType, ClientId};
+    QmiThreadSendQMI(ComposeQCTLMsg(QMICTL_RELEASE_CLIENT_ID_REQ, CtlReleaseClientIdReq, argv), NULL);
+    return 0;
+}
+
+static int QmiWwanInit(PROFILE_T *profile) {
+    unsigned i;
+    int ret;
+    PQCQMIMSG pResponse;
+
+    if (profile->qmap_mode == 0 || profile->qmap_mode == 1)
+    {
+        for (i = 0; i < 10; i++) {
+            ret = QmiThreadSendQMITimeout(ComposeQCTLMsg(QMICTL_SYNC_REQ, NULL, NULL), NULL, 1 * 1000);
+            if (!ret)
+                break;
+            sleep(1);
+        }
+        if (ret)
+            return ret;
+    }
+
+    QmiThreadSendQMI(ComposeQCTLMsg(QMICTL_GET_VERSION_REQ, CtlGetVersionReq, NULL), &pResponse);
+    if (profile->qmap_mode) {
+        if (pResponse) {
+            if (pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXResult == 0 && pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXError == 0) {
+                uint8_t  NumElements = 0;
+
+                for (NumElements = 0; NumElements < pResponse->CTLMsg.GetVersionRsp.NumElements; NumElements++) {
+#if 0
+                    dbg_time("QMUXType = %02x Version = %d.%d",
+                        pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].QMUXType,
+                        pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MajorVersion,
+                        pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MinorVersion);
+#endif
+                    if (pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].QMUXType == QMUX_TYPE_WDS_ADMIN)
+                        profile->qmap_version = (pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MinorVersion > 16);
+                }
+            }
+        }
+    }
+    if (pResponse) free(pResponse);
+    qmiclientId[QMUX_TYPE_WDS] = QmiWwanGetClientID(QMUX_TYPE_WDS);
+    if (profile->enable_ipv6 || profile->IsDualIPSupported)
+        qmiclientId[QMUX_TYPE_WDS_IPV6] = QmiWwanGetClientID(QMUX_TYPE_WDS);
+    qmiclientId[QMUX_TYPE_DMS] = QmiWwanGetClientID(QMUX_TYPE_DMS);
+    qmiclientId[QMUX_TYPE_NAS] = QmiWwanGetClientID(QMUX_TYPE_NAS);
+    qmiclientId[QMUX_TYPE_UIM] = QmiWwanGetClientID(QMUX_TYPE_UIM);
+    if (profile->qmap_mode == 0 || profile->qmap_mode == 1)
+        qmiclientId[QMUX_TYPE_WDS_ADMIN] = QmiWwanGetClientID(QMUX_TYPE_WDS_ADMIN);
+
+    return 0;
+}
+
+static int QmiWwanDeInit(void) {
+    unsigned int i;
+    for (i = 0; i < sizeof(qmiclientId)/sizeof(qmiclientId[0]); i++)
+    {
+        if (qmiclientId[i] != 0)
+        {
+                QmiWwanReleaseClientID(i, qmiclientId[i]);
+                qmiclientId[i] = 0;
+        }
+    }
+
+    return 0;
+}
+
+#define MEIG_QMI_PROXY "meig-qmi-proxy"
+static int qmi_proxy_open(const char *name) {
+    int sockfd = -1;
+    int reuse_addr = 1;
+    struct sockaddr_un sockaddr;
+    socklen_t alen;
+
+    /*Create server socket*/
+    (sockfd = socket(AF_LOCAL, SOCK_STREAM, 0));
+    if (sockfd < 0)
+        return sockfd;
+
+    memset(&sockaddr, 0, sizeof(sockaddr));
+    sockaddr.sun_family = AF_LOCAL;
+    sockaddr.sun_path[0] = 0;
+    memcpy(sockaddr.sun_path + 1, name, strlen(name) );
+
+    alen = strlen(name) + offsetof(struct sockaddr_un, sun_path) + 1;
+    if(connect(sockfd, (struct sockaddr *)&sockaddr, alen) < 0) {
+        close(sockfd);
+        dbg_time("%s connect %s errno: %d (%s)\n", __func__, name, errno, strerror(errno));
+        return -1;
+    }
+    (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,sizeof(reuse_addr)));
+
+    dbg_time("connect to %s sockfd = %d\n", name, sockfd);
+
+    return sockfd;
+}
+
+static ssize_t qmi_proxy_read (int fd, void *buf, size_t size) {
+    ssize_t nreads;
+    PQCQMI_HDR pHdr = (PQCQMI_HDR)buf;
+
+    nreads = read(fd, pHdr, sizeof(QCQMI_HDR));
+    if (nreads == sizeof(QCQMI_HDR)) {
+        nreads += read(fd, pHdr+1, le16_to_cpu(pHdr->Length) + 1 - sizeof(QCQMI_HDR));
+    }
+
+    return nreads;
+}
+
+static void * QmiWwanThread(void *pData) {
+    PROFILE_T *profile = (PROFILE_T *)pData;
+    const char *cdc_wdm = (const char *)profile->qmichannel;
+    int wait_for_request_quit = 0;
+    
+    if (profile->qmap_mode == 0 || profile->qmap_mode == 1)
+        cdc_wdm_fd = open(cdc_wdm, O_RDWR | O_NONBLOCK | O_NOCTTY);
+    else
+        cdc_wdm_fd = qmi_proxy_open(MEIG_QMI_PROXY);
+
+    if (cdc_wdm_fd == -1) {
+        dbg_time("%s Failed to open %s, errno: %d (%s)", __func__, cdc_wdm, errno, strerror(errno));
+        qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
+        pthread_exit(NULL);
+        return NULL;
+    }
+
+    fcntl(cdc_wdm_fd, F_SETFL, fcntl(cdc_wdm_fd,F_GETFL) | O_NONBLOCK);
+    fcntl(cdc_wdm_fd, F_SETFD, FD_CLOEXEC);
+
+    dbg_time("cdc_wdm_fd = %d", cdc_wdm_fd);
+
+    qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED);
+    while (1) {
+        struct pollfd pollfds[] = {{qmidevice_control_fd[1], POLLIN, 0}, {cdc_wdm_fd, POLLIN, 0}};
+        int ne, ret, nevents = sizeof(pollfds)/sizeof(pollfds[0]);
+
+        do {
+            ret = poll(pollfds, nevents, wait_for_request_quit ? 1000 : -1);
+         } while ((ret < 0) && (errno == EINTR));
+
+	if (ret == 0 && wait_for_request_quit) {
+            QmiThreadRecvQMI(NULL);
+            continue;
+	}
+
+        if (ret <= 0) {
+            dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
+            break;
+        }
+
+        for (ne = 0; ne < nevents; ne++) {
+            int fd = pollfds[ne].fd;
+            short revents = pollfds[ne].revents;
+
+            //dbg_time("{%d, %x, %x}", pollfds[ne].fd, pollfds[ne].events, pollfds[ne].revents);
+
+            if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
+                dbg_time("%s poll err/hup/inval", __func__);
+                dbg_time("poll fd = %d, events = 0x%04x", fd, revents);
+                if (fd == cdc_wdm_fd) {
+                } else {
+                }
+                if (revents & (POLLHUP | POLLNVAL)) //EC20 bug, Can get POLLERR
+                    goto __QmiWwanThread_quit;
+            }
+
+            if ((revents & POLLIN) == 0)
+                continue;
+
+            if (fd == qmidevice_control_fd[1]) {
+                int triger_event;
+                if (read(fd, &triger_event, sizeof(triger_event)) == sizeof(triger_event)) {
+                    //DBG("triger_event = 0x%x", triger_event);
+                    switch (triger_event) {
+                        case RIL_REQUEST_QUIT:
+                            goto __QmiWwanThread_quit;
+                        break;
+                        case SIGTERM:
+                        case SIGHUP:
+                        case SIGINT:
+                            wait_for_request_quit = 1;
+                        break;
+                        default:
+                        break;
+                    }
+                }
+            }
+
+            if (fd == cdc_wdm_fd) {
+                ssize_t nreads;
+                UCHAR QMIBuf[512];
+                PQCQMIMSG pResponse = (PQCQMIMSG)QMIBuf;
+                
+                if (profile->qmap_mode == 0 || profile->qmap_mode == 1)
+                    nreads = read(fd, QMIBuf, sizeof(QMIBuf));
+                else
+                    nreads = qmi_proxy_read(fd, QMIBuf, sizeof(QMIBuf));
+                //dbg_time("%s read=%d errno: %d (%s)",  __func__, (int)nreads, errno, strerror(errno));
+                if (nreads <= 0) {
+                    dbg_time("%s read=%d errno: %d (%s)",  __func__, (int)nreads, errno, strerror(errno));
+                    break;
+                }
+
+                if (nreads != (le16_to_cpu(pResponse->QMIHdr.Length) + 1)) {
+                    dbg_time("%s nreads=%d,  pQCQMI->QMIHdr.Length = %d",  __func__, (int)nreads, le16_to_cpu(pResponse->QMIHdr.Length));
+                    continue;
+                }
+
+                QmiThreadRecvQMI(pResponse);
+            }
+        }
+    }
+
+__QmiWwanThread_quit:
+    if (cdc_wdm_fd != -1) { close(cdc_wdm_fd); cdc_wdm_fd = -1; }
+    qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
+    QmiThreadRecvQMI(NULL); //main thread may pending on QmiThreadSendQMI()
+    dbg_time("%s exit", __func__);
+    pthread_exit(NULL);
+    return NULL;
+}
+
+#else
+static int QmiWwanSendQMI(PQCQMIMSG pRequest) {return -1;}
+static int QmiWwanInit(PROFILE_T *profile) {return -1;}
+static int QmiWwanDeInit(void) {return -1;}
+static void * QmiWwanThread(void *pData) {dbg_time("please set CONFIG_QMIWWAN"); return NULL;}
+#endif
+
+const struct qmi_device_ops qmiwwan_qmidev_ops = {
+    .init = QmiWwanInit,
+    .deinit = QmiWwanDeInit,
+    .send = QmiWwanSendQMI,
+    .read = QmiWwanThread,
+};
diff --git a/package/wwan/meig-cm/src/ReleaseNote.txt b/package/wwan/meig-cm/src/ReleaseNote.txt
new file mode 100644
index 000000000..c6f344c5a
--- /dev/null
+++ b/package/wwan/meig-cm/src/ReleaseNote.txt
@@ -0,0 +1,61 @@
+Release Notes
+[WCDMA&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
+	 
diff --git a/package/wwan/meig-cm/src/build.sh b/package/wwan/meig-cm/src/build.sh
new file mode 100644
index 000000000..3d9f7fb88
--- /dev/null
+++ b/package/wwan/meig-cm/src/build.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+#export PATH=/home/zhaopengfei/ruiming/aarch64-himix200-linux/bin:$PATH
+make clean
+make CROSS_COMPILE=arm-hisiv500-linux-
+
diff --git a/package/wwan/meig-cm/src/default.script b/package/wwan/meig-cm/src/default.script
new file mode 100644
index 000000000..ddce8d4c5
--- /dev/null
+++ b/package/wwan/meig-cm/src/default.script
@@ -0,0 +1,63 @@
+#!/bin/sh
+# Busybox udhcpc dispatcher script. Copyright (C) 2009 by Axel Beckert.
+#
+# Based on the busybox example scripts and the old udhcp source
+# package default.* scripts.
+
+RESOLV_CONF="/etc/resolv.conf"
+
+case $1 in
+    bound|renew)
+	[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
+	[ -n "$subnet" ] && NETMASK="netmask $subnet"
+
+	/sbin/ifconfig $interface $ip $BROADCAST $NETMASK
+
+	if [ -n "$router" ]; then
+	    echo "$0: Resetting default routes"
+	    while /sbin/route del default gw 0.0.0.0 dev $interface; do :; done
+
+	    metric=0
+	    for i in $router; do
+		/sbin/route add default gw $i dev $interface metric $metric
+		metric=$(($metric + 1))
+	    done
+	fi
+
+	# Update resolver configuration file
+	R=""
+	[ -n "$domain" ] && R="domain $domain
+"
+	for i in $dns; do
+	    echo "$0: Adding DNS $i"
+	    R="${R}nameserver $i
+"
+	done
+
+	if [ ! -x /sbin/resolvconf ]; then
+	    echo -n "$R" | resolvconf -a "${interface}.udhcpc"
+	else
+	    echo -n "$R" > "$RESOLV_CONF"
+	fi
+	;;
+
+    deconfig)
+	if [ -x /sbin/resolvconf ]; then
+	    resolvconf -d "${interface}.udhcpc"
+	fi
+	/sbin/ifconfig $interface 0.0.0.0
+	;;
+
+    leasefail)
+	echo "$0: Lease failed: $message"
+	;;
+
+    nak)
+	echo "$0: Received a NAK: $message"
+	;;
+
+    *)
+	echo "$0: Unknown udhcpc command: $1";
+	exit 1;
+	;;
+esac
diff --git a/package/wwan/meig-cm/src/device.c b/package/wwan/meig-cm/src/device.c
new file mode 100644
index 000000000..3f812333d
--- /dev/null
+++ b/package/wwan/meig-cm/src/device.c
@@ -0,0 +1,231 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "QMIThread.h"
+
+#define CM_MAX_PATHLEN 256
+
+#define CM_INVALID_VAL (~((int)0))
+/* get first line from file 'fname'
+ * And convert the content into a hex number, then return this number */
+static int file_get_value(const char *fname)
+{
+    FILE *fp = NULL;
+    int hexnum;
+    char buff[32 + 1] = {'\0'};
+    char *endptr = NULL;
+
+    fp = fopen(fname, "r");
+    if (!fp) goto error;
+    if (fgets(buff, sizeof(buff), fp) == NULL)
+        goto error;
+    fclose(fp);
+
+    hexnum = strtol(buff, &endptr, 16);
+    if (errno == ERANGE && (hexnum == LONG_MAX || hexnum == LONG_MIN))
+        goto error;
+    /* if there is no digit in buff */
+    if (endptr == buff)
+        goto error;
+    return (int)hexnum;
+
+error:
+    if (fp) fclose(fp);
+    return CM_INVALID_VAL;
+}
+
+/*
+ * This function will search the directory 'dirname' and return the first child.
+ * '.' and '..' is ignored by default
+ */
+int dir_get_child(const char *dirname, char *buff, unsigned bufsize)
+{
+    struct dirent *entptr = NULL;
+    DIR *dirptr = opendir(dirname);
+    if (!dirptr)
+        goto error;
+    while ((entptr = readdir(dirptr))) {
+        if (entptr->d_name[0] == '.')
+            continue;
+        snprintf(buff, bufsize, "%s", entptr->d_name);
+        break;
+    }
+
+    closedir(dirptr);
+    return 0;
+error:
+    buff[0] = '\0';
+    if (dirptr) closedir(dirptr);
+    return -1;
+}
+
+int conf_get_val(const char *fname, const char *key)
+{
+    char buff[CM_MAX_BUFF] = {'\0'};
+    FILE *fp = fopen(fname, "r");
+    if (!fp)
+        goto error;
+    
+    while (fgets(buff, CM_MAX_BUFF, fp)) {
+        char prefix[CM_MAX_BUFF] = {'\0'};
+        char tail[CM_MAX_BUFF] = {'\0'};
+        /* To eliminate cppcheck warnning: Assume string length is no more than 15 */
+        sscanf(buff, "%15[^=]=%15s", prefix, tail);
+        if (!strncasecmp(prefix, key, strlen(key))) {
+            fclose(fp);
+            return atoi(tail);
+        }
+    }
+
+error:
+    fclose(fp);
+    return CM_INVALID_VAL;
+}
+
+/* To detect the device info of the modem.
+ * return:
+ *  FALSE -> fail
+ *  TRUE -> ok
+ */
+BOOL qmidevice_detect(char *qmichannel, char *usbnet_adapter, unsigned bufsize) {
+    struct dirent* ent = NULL;
+    DIR *pDir;
+    const char *rootdir = "/sys/bus/usb/devices";
+    struct {
+        char path[255*2];
+        char uevent[255*3];
+    } *pl;
+    pl = (typeof(pl)) malloc(sizeof(*pl));
+    memset(pl, 0x00, sizeof(*pl));
+
+    pDir = opendir(rootdir);
+    if (!pDir) {
+        dbg_time("opendir %s failed: %s", rootdir, strerror(errno));
+        goto error;
+    }
+
+    while ((ent = readdir(pDir)) != NULL)  {
+        int idVendor;
+        int idProduct;
+        char netcard[32+1] = {'\0'};
+        char device[32+1] = {'\0'};
+        char devname[32+1+6] = {'\0'};
+
+        snprintf(pl->path, sizeof(pl->path), "%s/%s/idVendor", rootdir, ent->d_name);
+        idVendor = file_get_value(pl->path);
+
+        snprintf(pl->path, sizeof(pl->path), "%s/%s/idProduct", rootdir, ent->d_name);
+        idProduct = file_get_value(pl->path);
+
+        if (idVendor != 0x05c6 && idVendor != 0x2c7c && idVendor != 0x2dee)
+            continue;
+        
+        dbg_time("Find %s/%s idVendor=0x%x idProduct=0x%x", rootdir, ent->d_name, idVendor, idProduct);
+
+        /* get network interface */
+        snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4/net", rootdir, ent->d_name);
+        dir_get_child(pl->path, netcard, sizeof(netcard));
+        if (netcard[0] == '\0')
+            continue;
+
+        if (usbnet_adapter[0] && strcmp(usbnet_adapter, netcard))
+            continue;
+
+        snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4/GobiQMI", rootdir, ent->d_name);
+        if (access(pl->path, R_OK)) {
+            snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4/usbmisc", rootdir, ent->d_name);
+            if (access(pl->path, R_OK)) {
+                snprintf(pl->path, sizeof(pl->path), "%s/%s:1.4/usb", rootdir, ent->d_name);
+                if (access(pl->path, R_OK)) {
+                    dbg_time("no GobiQMI/usbmic/usb found in %s/%s:1.4", rootdir, ent->d_name);
+                    continue;
+                }
+            }
+        }
+
+        /* get device */
+        dir_get_child(pl->path, device, sizeof(device));
+        if (device[0] == '\0')
+            continue;
+
+        /* There is a chance that, no device(qcqmiX|cdc-wdmX) is generated. We should warn user about that! */
+        snprintf(devname, sizeof(devname), "/dev/%s", device);
+        if (access(devname, R_OK | F_OK) && errno == ENOENT) {
+            int major;
+            int minor;
+            int ret;
+
+            dbg_time("%s access failed, errno: %d (%s)", devname, errno, strerror(errno));
+            snprintf(pl->uevent, sizeof(pl->uevent), "%s/%s/uevent", pl->path, device);
+            major = conf_get_val(pl->uevent, "MAJOR");
+            minor = conf_get_val(pl->uevent, "MINOR");
+            if(major == CM_INVALID_VAL || minor == CM_INVALID_VAL)
+                dbg_time("get major and minor failed");
+
+            ret = mknod(devname, S_IFCHR|0666, (((major & 0xfff) << 8) | (minor & 0xff) | ((minor & 0xfff00) << 12)));
+            if (ret)
+                dbg_time("please mknod %s c %d %d", devname, major, minor);
+        }
+
+        if (netcard[0] && device[0]) {
+            snprintf(qmichannel, bufsize, "/dev/%s", device);
+            snprintf(usbnet_adapter, bufsize, "%s", netcard);
+            dbg_time("Auto find qmichannel = %s", qmichannel);
+            dbg_time("Auto find usbnet_adapter = %s", usbnet_adapter);
+            break;
+        }
+    }
+    closedir(pDir);
+    
+    if (qmichannel[0] == '\0' || usbnet_adapter[0] == '\0') {
+        dbg_time("network interface '%s' or qmidev '%s' is not exist", usbnet_adapter, qmichannel);
+        goto error;
+    }
+
+    free(pl);
+    return TRUE;
+error:
+    free(pl);
+    return FALSE;
+}
+
+#define USB_CLASS_COMM			2
+#define USB_CLASS_VENDOR_SPEC		0xff
+#define USB_CDC_SUBCLASS_MBIM			0x0e
+
+/*
+ * To check whether the system load the wrong driver:
+ *      error1: usbnet 2(MBIM) match the QMI driver(qmi_wwan|GobiNet)
+ *      error2: usbnet 0(QMI) match the MBIM driver(cdc_mbim)
+ * return:
+ *  0 for ok, or ignorance
+ *  others for failure or error
+ */
+int varify_driver(PROFILE_T *profile)
+{
+    char path[CM_MAX_PATHLEN+1] = {'\0'};
+    int bInterfaceClass = -1;
+            
+    snprintf(path, sizeof(path), "/sys/class/net/%s/device/bInterfaceClass", profile->usbnet_adapter);
+    bInterfaceClass = file_get_value(path);
+
+    /* QMI_WWAN */
+    if (driver_is_qmi(profile->driver_name) && bInterfaceClass != USB_CLASS_VENDOR_SPEC) {
+        dbg_time("module register driver %s, but at+qcfg=\"usbnet\" is not QMI mode!", profile->driver_name);
+        return 1;
+    }
+
+    /* CDC_MBIM */
+    if (driver_is_mbim(profile->driver_name) && bInterfaceClass != USB_CLASS_COMM) {
+        dbg_time("module register driver %s, but at+qcfg=\"usbnet\" is not MBIM mode!", profile->driver_name);
+        return 1;
+    }
+            
+    return 0;
+}
diff --git a/package/wwan/meig-cm/src/dhcpclient.c b/package/wwan/meig-cm/src/dhcpclient.c
new file mode 100644
index 000000000..de8602f33
--- /dev/null
+++ b/package/wwan/meig-cm/src/dhcpclient.c
@@ -0,0 +1,90 @@
+#ifdef ANDROID
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include "QMIThread.h"
+#ifdef USE_NDK
+extern int (*ifc_init)(void);
+extern void (*ifc_close)(void);
+extern int (*do_dhcp)(const char *iname);
+extern void (*get_dhcp_info)(uint32_t *ipaddr, uint32_t *gateway, uint32_t *prefixLength,
+                   uint32_t *dns1, uint32_t *dns2, uint32_t *server,
+                   uint32_t *lease);  
+extern int (*property_set)(const char *key, const char *value);
+#else
+#include <cutils/properties.h>
+#include <netutils/ifc.h>
+extern int do_dhcp(const char *iname);
+extern void get_dhcp_info(uint32_t *ipaddr, uint32_t *gateway, uint32_t *prefixLength,
+                   uint32_t *dns1, uint32_t *dns2, uint32_t *server,
+                   uint32_t *lease);  
+#endif
+
+static const char *ipaddr_to_string(in_addr_t addr)
+{
+    struct in_addr in_addr;
+
+    in_addr.s_addr = addr;
+    return inet_ntoa(in_addr);
+}
+
+void do_dhcp_request(PROFILE_T *profile) {
+#ifdef USE_NDK
+    if (!ifc_init ||!ifc_close ||!do_dhcp || !get_dhcp_info || !property_set) {
+        return;
+    }    
+#endif
+
+    char *ifname = profile->usbnet_adapter;
+    uint32_t ipaddr, gateway, prefixLength, dns1, dns2, server, lease;
+    char propKey[128];
+
+#if 0
+    if (profile->rawIP && ((profile->IPType==0x04 && profile->ipv4.Address)))
+    {
+        snprintf(propKey, sizeof(propKey), "net.%s.dns1", ifname);
+        property_set(propKey, profile->ipv4.DnsPrimary ? ipaddr_to_string(meig_swap32(profile->ipv4.DnsPrimary)) : "8.8.8.8");
+        snprintf(propKey, sizeof(propKey), "net.%s.dns2", ifname);
+        property_set(propKey, profile->ipv4.DnsSecondary ? ipaddr_to_string(meig_swap32(profile->ipv4.DnsSecondary)) : "8.8.8.8");
+        snprintf(propKey, sizeof(propKey), "net.%s.gw", ifname);
+        property_set(propKey, profile->ipv4.Gateway ? ipaddr_to_string(meig_swap32(profile->ipv4.Gateway)) : "0.0.0.0");
+        return;
+    }
+#endif
+
+    if(ifc_init()) {
+        dbg_time("failed to ifc_init(%s): %s\n", ifname, strerror(errno));
+    }
+
+    if (do_dhcp(ifname) < 0) {
+        dbg_time("failed to do_dhcp(%s): %s\n", ifname, strerror(errno));
+    }
+
+    ifc_close();
+
+    get_dhcp_info(&ipaddr,  &gateway,  &prefixLength, &dns1, &dns2, &server, &lease);
+    snprintf(propKey, sizeof(propKey), "net.%s.gw", ifname);
+    property_set(propKey, gateway ? ipaddr_to_string(gateway) : "0.0.0.0");
+}
+#endif
diff --git a/package/wwan/meig-cm/src/main.c b/package/wwan/meig-cm/src/main.c
new file mode 100644
index 000000000..121c6e9a6
--- /dev/null
+++ b/package/wwan/meig-cm/src/main.c
@@ -0,0 +1,796 @@
+#include "QMIThread.h"
+#include <sys/wait.h>
+#include <sys/utsname.h>
+#include <sys/time.h>
+#include <dirent.h>
+
+#include "util.h"
+//#define CONFIG_EXIT_WHEN_DIAL_FAILED
+//#define CONFIG_BACKGROUND_WHEN_GET_IP
+//#define CONFIG_PID_FILE_FORMAT "/var/run/meig-cm-%s.pid" //for example /var/run/meig-cm-wwan0.pid
+#define MAJOR 1
+#define MINOR 3
+#define REVISION 8
+/*
+* Generally, we do not modify version info, so several modifications will share the same version code.
+* SUBVERSION is used for customized modification to distinguise this version from previous one. 
+* SUBVERSION adds up before you send the code to customers and it should be set to 0 if VERSION_STRING info is changed.
+*/
+#define SUBVERSION 0
+#define STRINGIFY_HELPER(v) #v
+#define STRINGIFY(v) STRINGIFY_HELPER(v)
+#define VERSION_STRING() STRINGIFY(MAJOR) "." \
+                        STRINGIFY(MINOR) "." \
+                        STRINGIFY(REVISION)
+int debug_qmi = 0;
+int main_loop = 0;
+int qmidevice_control_fd[2];
+static int signal_control_fd[2];
+
+extern const struct qmi_device_ops gobi_qmidev_ops;
+extern const struct qmi_device_ops qmiwwan_qmidev_ops;
+extern int meig_ifconfig(int argc, char *argv[]);
+
+#ifdef CONFIG_BACKGROUND_WHEN_GET_IP
+static int daemon_pipe_fd[2];
+
+static void meig_prepare_daemon(void) {
+    pid_t daemon_child_pid;
+
+    if (pipe(daemon_pipe_fd) < 0) {
+        dbg_time("%s Faild to create daemon_pipe_fd: %d (%s)", __func__, errno, strerror(errno));
+        return;
+    }
+
+    daemon_child_pid = fork();
+    if (daemon_child_pid > 0) {
+        struct pollfd pollfds[] = {{daemon_pipe_fd[0], POLLIN, 0}, {0, POLLIN, 0}};
+        int ne, ret, nevents = sizeof(pollfds)/sizeof(pollfds[0]);
+        int signo;
+
+        //dbg_time("father");
+
+        close(daemon_pipe_fd[1]);
+
+        if (socketpair( AF_LOCAL, SOCK_STREAM, 0, signal_control_fd) < 0 ) {
+            dbg_time("%s Faild to create main_control_fd: %d (%s)", __func__, errno, strerror(errno));
+            return;
+        }
+
+        pollfds[1].fd = signal_control_fd[1];
+
+        while (1) {
+            do {
+                ret = poll(pollfds, nevents, -1);
+            } while ((ret < 0) && (errno == EINTR));
+
+            if (ret < 0) {
+                dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
+                goto __daemon_quit;
+            }
+
+            for (ne = 0; ne < nevents; ne++) {
+                int fd = pollfds[ne].fd;
+                short revents = pollfds[ne].revents;
+
+                if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
+                    //dbg_time("%s poll err/hup", __func__);
+                    //dbg_time("poll fd = %d, events = 0x%04x", fd, revents);
+                    if (revents & POLLHUP)
+                        goto __daemon_quit;
+                }
+
+                if ((revents & POLLIN) &&  read(fd, &signo, sizeof(signo)) == sizeof(signo)) {
+                    if (signal_control_fd[1] == fd) {
+                        if (signo == SIGCHLD) {
+                            int status;
+                            int pid = waitpid(daemon_child_pid, &status, 0);
+                            dbg_time("waitpid pid=%d, status=%x", pid, status);
+                            goto __daemon_quit;
+                        } else {
+                            kill(daemon_child_pid, signo);
+                        }
+                    } else if (daemon_pipe_fd[0] == fd) {
+                        //dbg_time("daemon_pipe_signo = %d", signo);
+                        goto __daemon_quit;
+                    }
+                }
+            }
+        }
+__daemon_quit:
+        //dbg_time("father exit");
+        _exit(0);
+    } else if (daemon_child_pid == 0) {
+        close(daemon_pipe_fd[0]);
+        //dbg_time("child", getpid());
+    } else {
+        close(daemon_pipe_fd[0]);
+        close(daemon_pipe_fd[1]);
+        dbg_time("%s Faild to create daemon_child_pid: %d (%s)", __func__, errno, strerror(errno));
+    }
+}
+
+static void meig_enter_daemon(int signo) {
+    if (daemon_pipe_fd[1] > 0)
+        if (signo) {
+            write(daemon_pipe_fd[1], &signo, sizeof(signo));
+            sleep(1);
+        }
+        close(daemon_pipe_fd[1]);
+        daemon_pipe_fd[1] = -1;
+        setsid();
+    }
+#endif
+
+//UINT ifc_get_addr(const char *ifname);
+static void usbnet_link_change(int link, PROFILE_T *profile) {
+    static int s_link = -1;
+    int curIpFamily = profile->enable_ipv6 ? IpFamilyV6 : IpFamilyV4;
+
+    if (s_link == link)
+        return;
+
+    s_link = link;
+
+    if (link) {
+        requestGetIPAddress(profile, curIpFamily);
+        if (profile->IsDualIPSupported)
+            requestGetIPAddress(profile, IpFamilyV6);
+        udhcpc_start(profile);
+    } else {
+        udhcpc_stop(profile);
+    }
+
+#ifdef CONFIG_BACKGROUND_WHEN_GET_IP
+    if (link && daemon_pipe_fd[1] > 0) {
+        int timeout = 6;
+        while (timeout-- /*&& ifc_get_addr(profile->usbnet_adapter) == 0*/) {
+            sleep(1);
+        }
+        meig_enter_daemon(SIGUSR1);
+    }
+#endif
+}
+
+static int check_ipv4_address(PROFILE_T *now_profile) {
+    PROFILE_T new_profile_v;
+    PROFILE_T *new_profile = &new_profile_v;
+
+    memcpy(new_profile, now_profile, sizeof(PROFILE_T));
+    if (requestGetIPAddress(new_profile, 0x04) == 0) {
+         if (new_profile->ipv4.Address != now_profile->ipv4.Address || debug_qmi) {
+             unsigned char *l = (unsigned char *)&now_profile->ipv4.Address;
+             unsigned char *r = (unsigned char *)&new_profile->ipv4.Address;
+             dbg_time("localIP: %d.%d.%d.%d VS remoteIP: %d.%d.%d.%d",
+                     l[3], l[2], l[1], l[0], r[3], r[2], r[1], r[0]);
+        }
+        return (new_profile->ipv4.Address == now_profile->ipv4.Address);
+    }
+    return 0;
+}
+
+static void main_send_event_to_qmidevice(int triger_event) {
+     write(qmidevice_control_fd[0], &triger_event, sizeof(triger_event));
+}
+
+static void send_signo_to_main(int signo) {
+     write(signal_control_fd[0], &signo, sizeof(signo));
+}
+
+void qmidevice_send_event_to_main(int triger_event) {
+     write(qmidevice_control_fd[1], &triger_event, sizeof(triger_event));
+}
+
+#define MAX_PATH 256
+
+static int ls_dir(const char *dir, int (*match)(const char *dir, const char *file, void *argv[]), void *argv[])
+{
+    DIR *pDir;
+    struct dirent* ent = NULL;
+    int match_times = 0;
+
+    pDir = opendir(dir);
+    if (pDir == NULL)  {
+        dbg_time("Cannot open directory: %s, errno: %d (%s)", dir, errno, strerror(errno));
+        return 0;
+    }
+
+    while ((ent = readdir(pDir)) != NULL)  {
+        match_times += match(dir, ent->d_name, argv);
+    }
+    closedir(pDir);
+
+    return match_times;
+}
+
+static int is_same_linkfile(const char *dir, const char *file,  void *argv[])
+{
+    const char *qmichannel = (const char *)argv[1];
+    char linkname[MAX_PATH];
+    char filename[MAX_PATH];
+    int linksize;
+
+    snprintf(linkname, MAX_PATH, "%s/%s", dir, file);
+    linksize = readlink(linkname, filename, MAX_PATH);
+    if (linksize <= 0)
+        return 0;
+
+    filename[linksize] = 0;
+    if (strcmp(filename, qmichannel))
+        return 0;
+
+    dbg_time("%s -> %s", linkname, filename);
+    return 1;
+}
+
+static int is_brother_process(const char *dir, const char *file, void *argv[])
+{
+    //const char *myself = (const char *)argv[0];
+    char linkname[MAX_PATH];
+    char filename[MAX_PATH];
+    int linksize;
+    int i = 0, kill_timeout = 15;
+    pid_t pid;
+
+    //dbg_time("%s", file);
+    while (file[i]) {
+        if (!isdigit(file[i]))
+            break;
+        i++;
+    }
+
+    if (file[i]) {
+        //dbg_time("%s not digit", file);
+        return 0;
+    }
+
+    snprintf(linkname, MAX_PATH, "%s/%s/exe", dir, file);
+    linksize = readlink(linkname, filename, MAX_PATH);
+    if (linksize <= 0)
+        return 0;
+
+    filename[linksize] = 0;
+
+    pid = atoi(file);
+    if (pid >= getpid())
+        return 0;
+
+    snprintf(linkname, MAX_PATH, "%s/%s/fd", dir, file);
+    if (!ls_dir(linkname, is_same_linkfile, argv))
+        return 0;
+
+    dbg_time("%s/%s/exe -> %s", dir, file, filename);
+    while (kill_timeout-- && !kill(pid, 0))
+    {
+        kill(pid, SIGTERM);
+        sleep(1);
+    }
+    if (!kill(pid, 0))
+    {
+        dbg_time("force kill %s/%s/exe -> %s", dir, file, filename);
+        kill(pid, SIGKILL);
+        sleep(1);
+    }
+
+    return 1;
+}
+
+static int kill_brothers(const char *qmichannel)
+{
+    char myself[MAX_PATH];
+    int filenamesize;
+    void *argv[2] = {myself, (void *)qmichannel};
+
+    filenamesize = readlink("/proc/self/exe", myself, MAX_PATH);
+    if (filenamesize <= 0)
+        return 0;
+    myself[filenamesize] = 0;
+
+    if (ls_dir("/proc", is_brother_process, argv))
+        sleep(1);
+
+    return 0;
+}
+
+static void meig_sigaction(int signo) {
+     if (SIGCHLD == signo)
+         waitpid(-1, NULL, WNOHANG);
+     else if (SIGALRM == signo)
+         send_signo_to_main(SIGUSR1);
+     else
+     {
+        if (SIGTERM == signo || SIGHUP == signo || SIGINT == signo)
+            main_loop = 0;
+         send_signo_to_main(signo);
+        main_send_event_to_qmidevice(signo); //main may be wating qmi response
+    }
+}
+
+pthread_t gQmiThreadID;
+
+
+static int usage(const char *progname) {
+     dbg_time("Usage: %s [options]", progname);
+     dbg_time("-s [apn [user password auth]]          Set apn/user/password/auth get from your network provider");
+     dbg_time("-p pincode                             Verify sim card pin if sim card is locked");
+     dbg_time("-f logfilename                         Save log message of this program to file");
+     dbg_time("-i interface                           Specify network interface(default auto-detect)");
+     dbg_time("-4                                     IPv4 protocol");
+     dbg_time("-6                                     IPv6 protocol");
+     dbg_time("-m muxID                               Specify muxid when set multi-pdn data connection.");
+     dbg_time("-n channelID                           Specify channelID when set multi-pdn data connection(default 1).");
+	 dbg_time("[Examples]");
+     dbg_time("Example 1: %s ", progname);
+     dbg_time("Example 2: %s -s 3gnet ", progname);
+     dbg_time("Example 3: %s -s 3gnet carl 1234 0 -p 1234 -f gobinet_log.txt", progname);
+     return 0;
+}
+
+int qmi_main(PROFILE_T *profile)
+{
+    int triger_event = 0;
+    int signo;
+#ifdef CONFIG_SIM
+    SIM_Status SIMStatus;
+#endif
+    UCHAR PSAttachedState;
+    UCHAR  IPv4ConnectionStatus = 0xff; //unknow state
+    UCHAR  IPV6ConnectionStatus = 0xff; //unknow state
+    int qmierr = 0;
+    char * save_usbnet_adapter = NULL;
+
+    signal(SIGUSR1, meig_sigaction);
+    signal(SIGUSR2, meig_sigaction);
+    signal(SIGINT, meig_sigaction);
+    signal(SIGTERM, meig_sigaction);
+    signal(SIGHUP, meig_sigaction);
+    signal(SIGCHLD, meig_sigaction);
+    signal(SIGALRM, meig_sigaction);
+
+#ifdef CONFIG_BACKGROUND_WHEN_GET_IP
+    meig_prepare_daemon();
+#endif
+
+    if (socketpair( AF_LOCAL, SOCK_STREAM, 0, signal_control_fd) < 0 ) {
+        dbg_time("%s Faild to create main_control_fd: %d (%s)", __func__, errno, strerror(errno));
+        return -1;
+    }
+
+    if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, qmidevice_control_fd ) < 0 ) {
+        dbg_time("%s Failed to create thread control socket pair: %d (%s)", __func__, errno, strerror(errno));
+        return 0;
+    }
+
+//sudo apt-get install udhcpc
+//sudo apt-get remove ModemManager
+__main_loop:
+    while (!profile->qmichannel) {
+        char qmichannel[32+1] = {'\0'};
+        char usbnet_adapter[32+1] = {'\0'};
+        
+        if (!qmidevice_detect(qmichannel, usbnet_adapter, sizeof(qmichannel))) {
+            dbg_time("qmidevice_detect failed");
+            continue;
+        } else {
+            if (!(profile->qmichannel))
+                strset(profile->qmichannel, qmichannel);
+            if (!(profile->usbnet_adapter))
+                strset(profile->usbnet_adapter, usbnet_adapter);
+                break;
+        }
+        if (main_loop) {
+            int wait_for_device = 3000;
+            dbg_time("Wait for Meig modules connect");
+            while (wait_for_device && main_loop) {
+                wait_for_device -= 100;
+                usleep(100*1000);
+            }
+            continue;
+        }
+        dbg_time("Cannot find qmichannel(%s) usbnet_adapter(%s) for Meig modules", profile->qmichannel, profile->usbnet_adapter);
+        return -ENODEV;
+    }
+
+    if (qmidev_is_gobinet(profile->qmichannel)) {
+        profile->qmi_ops = &gobi_qmidev_ops;
+    }
+    else {
+        profile->qmi_ops = &qmiwwan_qmidev_ops;
+    }
+    qmidev_send = profile->qmi_ops->send;
+    meig_qmap_mode_detect(profile);
+    dbg_time("[zpf]qmap_mode=%d", profile->qmap_mode);
+    if (profile->qmap_mode == 0 || profile->qmap_mode == 1)
+        kill_brothers(profile->qmichannel);
+
+    if (pthread_create( &gQmiThreadID, 0, profile->qmi_ops->read, (void *)profile) != 0) {
+        dbg_time("%s Failed to create QMIThread: %d (%s)", __func__, errno, strerror(errno));
+            return 0;
+    }
+
+    if ((read(qmidevice_control_fd[0], &triger_event, sizeof(triger_event)) != sizeof(triger_event))
+        || (triger_event != RIL_INDICATE_DEVICE_CONNECTED)) {
+        dbg_time("%s Failed to init QMIThread: %d (%s)", __func__, errno, strerror(errno));
+        return 0;
+    }
+
+    if (profile->qmi_ops->init && profile->qmi_ops->init(profile)) {
+        dbg_time("%s Failed to qmi init: %d (%s)", __func__, errno, strerror(errno));
+            return 0;
+    }
+
+#ifdef CONFIG_VERSION
+    requestBaseBandVersion(NULL);
+#endif
+    requestSetEthMode(profile);
+#ifdef CONFIG_SIM
+    qmierr = requestGetSIMStatus(&SIMStatus);
+    while (qmierr == QMI_ERR_OP_DEVICE_UNSUPPORTED) {
+        sleep(1);
+        qmierr = requestGetSIMStatus(&SIMStatus);
+    }
+    if ((SIMStatus == SIM_PIN) && profile->pincode) {
+        requestEnterSimPin(profile->pincode);
+    }
+#ifdef CONFIG_IMSI_ICCID
+    if (SIMStatus == SIM_READY) {
+        requestGetICCID();
+        requestGetIMSI();
+   }
+#endif
+#endif
+#ifdef CONFIG_APN
+    if (profile->apn || profile->user || profile->password) {
+        requestSetProfile(profile);
+    }
+    requestGetProfile(profile);
+#endif
+    requestRegistrationState(&PSAttachedState);
+
+    if (!requestQueryDataCall(&IPv4ConnectionStatus, IpFamilyV4) && (QWDS_PKT_DATA_CONNECTED == IPv4ConnectionStatus)){
+        usbnet_link_change(1, profile);
+     } else
+        usbnet_link_change(0, profile);
+
+    send_signo_to_main(SIGUSR1);
+
+#ifdef CONFIG_PID_FILE_FORMAT
+    {
+        char cmd[255];
+        sprintf(cmd, "echo %d > " CONFIG_PID_FILE_FORMAT, getpid(), profile->usbnet_adapter);
+        system(cmd);
+    }
+#endif
+
+    while (1)
+    {
+        struct pollfd pollfds[] = {{signal_control_fd[1], POLLIN, 0}, {qmidevice_control_fd[0], POLLIN, 0}};
+        int ne, ret, nevents = sizeof(pollfds)/sizeof(pollfds[0]);
+		UCHAR *pConnectionStatus = (profile->enable_ipv6) ? &IPV6ConnectionStatus : &IPv4ConnectionStatus;
+		int curIpFamily = (profile->enable_ipv6) ? IpFamilyV6 : IpFamilyV4;
+
+        do {
+            ret = poll(pollfds, nevents,  15*1000);
+        } while ((ret < 0) && (errno == EINTR));
+
+        if (ret == 0)
+        {
+            send_signo_to_main(SIGUSR2);
+            continue;
+        }
+
+        if (ret <= 0) {
+            dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
+            goto __main_quit;
+        }
+
+        for (ne = 0; ne < nevents; ne++) {
+            int fd = pollfds[ne].fd;
+            short revents = pollfds[ne].revents;
+
+            if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
+                dbg_time("%s poll err/hup", __func__);
+                dbg_time("epoll fd = %d, events = 0x%04x", fd, revents);
+                main_send_event_to_qmidevice(RIL_REQUEST_QUIT);
+                if (revents & POLLHUP)
+                    goto __main_quit;
+            }
+
+            if ((revents & POLLIN) == 0)
+                continue;
+
+            if (fd == signal_control_fd[1])
+            {
+                if (read(fd, &signo, sizeof(signo)) == sizeof(signo))
+                {
+                    alarm(0);
+                    switch (signo)
+                    {
+                        case SIGUSR1:
+                            requestQueryDataCall(pConnectionStatus, curIpFamily);
+                            if (QWDS_PKT_DATA_CONNECTED != *pConnectionStatus)
+                            {
+                                usbnet_link_change(0, profile);
+                                requestRegistrationState(&PSAttachedState);
+
+                                if (PSAttachedState == 1) {
+                                    qmierr = requestSetupDataCall(profile, curIpFamily);
+
+                                    if ((qmierr > 0) && profile->user && profile->user[0] && profile->password && profile->password[0]) {
+                                        int old_auto =  profile->auth;
+
+                                        //may be fail because wrong auth mode, try pap->chap, or chap->pap
+                                        profile->auth = (profile->auth == 1) ? 2 : 1;
+                                        qmierr = requestSetupDataCall(profile, curIpFamily);
+
+                                        if (qmierr)
+                                            profile->auth = old_auto; //still fail, restore old auth moe
+                                    }
+
+                                    //succssful setup data call
+                                    if (!qmierr && profile->IsDualIPSupported) {
+                                        requestSetupDataCall(profile, IpFamilyV6);
+                                    }
+
+                                    if (!qmierr)
+                                        continue;
+                                }
+                                
+#ifdef CONFIG_EXIT_WHEN_DIAL_FAILED
+                                kill(getpid(), SIGTERM);
+#endif
+                                alarm(5); //try to setup data call 5 seconds later
+                            }
+                        break;
+
+                        case SIGUSR2:
+                            if (QWDS_PKT_DATA_CONNECTED == *pConnectionStatus)
+                                 requestQueryDataCall(pConnectionStatus, curIpFamily);
+
+                            //local ip is different with remote ip
+                            if (QWDS_PKT_DATA_CONNECTED == IPv4ConnectionStatus && check_ipv4_address(profile) == 0) {
+                                requestDeactivateDefaultPDP(profile, curIpFamily);
+                                *pConnectionStatus = QWDS_PKT_DATA_DISCONNECTED;
+                            }
+                            
+                            if (QWDS_PKT_DATA_CONNECTED != *pConnectionStatus)
+                                send_signo_to_main(SIGUSR1);
+                        break;
+
+                        case SIGTERM:
+                        case SIGHUP:
+                        case SIGINT:
+                            if (QWDS_PKT_DATA_CONNECTED == *pConnectionStatus) {
+                                requestDeactivateDefaultPDP(profile, curIpFamily);
+                                if (profile->IsDualIPSupported)
+                                    requestDeactivateDefaultPDP(profile, IpFamilyV6);
+                           }
+                            usbnet_link_change(0, profile);
+                            if (profile->qmi_ops->deinit)
+                                profile->qmi_ops->deinit();
+                            main_send_event_to_qmidevice(RIL_REQUEST_QUIT);
+                            goto __main_quit;
+                        break;
+
+                        default:
+                        break;
+                    }
+                }
+            }
+
+            if (fd == qmidevice_control_fd[0]) {
+                if (read(fd, &triger_event, sizeof(triger_event)) == sizeof(triger_event)) {
+                    switch (triger_event) {
+                        case RIL_INDICATE_DEVICE_DISCONNECTED:
+                            usbnet_link_change(0, profile);
+                            if (main_loop)
+                            {
+                                if (pthread_join(gQmiThreadID, NULL)) {
+                                    dbg_time("%s Error joining to listener thread (%s)", __func__, strerror(errno));
+                                }
+                                profile->qmichannel = NULL;
+                                profile->usbnet_adapter = save_usbnet_adapter;
+                                goto __main_loop;
+                            }
+                            goto __main_quit;
+                        break;
+
+                        case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED:
+                            requestRegistrationState(&PSAttachedState);
+                            if (PSAttachedState == 1 && QWDS_PKT_DATA_DISCONNECTED == *pConnectionStatus)
+                                send_signo_to_main(SIGUSR1);
+                        break;
+
+                        case RIL_UNSOL_DATA_CALL_LIST_CHANGED:
+                        {
+                            UCHAR oldConnectionStatus = *pConnectionStatus;
+                            requestQueryDataCall(pConnectionStatus, curIpFamily);
+                            if (profile->IsDualIPSupported)
+                                requestQueryDataCall(&IPV6ConnectionStatus, IpFamilyV6);
+                            if (QWDS_PKT_DATA_CONNECTED != *pConnectionStatus)
+                            {
+                                usbnet_link_change(0, profile);
+                                //connected change to disconnect
+                                if (oldConnectionStatus == QWDS_PKT_DATA_CONNECTED)
+                                    send_signo_to_main(SIGUSR1);
+                            } else if (QWDS_PKT_DATA_CONNECTED == *pConnectionStatus) {
+                                usbnet_link_change(1, profile);
+                                if (oldConnectionStatus == QWDS_PKT_DATA_CONNECTED) { //receive two CONNECT IND?
+                                    send_signo_to_main(SIGUSR2);
+                                }
+                            }
+                        }
+                        break;
+
+                        default:
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+__main_quit:
+    usbnet_link_change(0, profile);
+    if (pthread_join(gQmiThreadID, NULL)) {
+        dbg_time("%s Error joining to listener thread (%s)", __func__, strerror(errno));
+    }
+    close(signal_control_fd[0]);
+    close(signal_control_fd[1]);
+    close(qmidevice_control_fd[0]);
+    close(qmidevice_control_fd[1]);
+    dbg_time("%s exit", __func__);
+    if (logfilefp)
+        fclose(logfilefp);
+
+#ifdef CONFIG_PID_FILE_FORMAT
+    {
+        char cmd[255];
+        sprintf(cmd, "rm  " CONFIG_PID_FILE_FORMAT, profile.usbnet_adapter);
+        system(cmd);
+    }
+#endif
+
+    return 0;
+}
+
+#define has_more_argv() ((opt < argc) && (argv[opt][0] != '-'))
+int main(int argc, char *argv[])
+{
+    int opt = 1;
+    char * save_usbnet_adapter = NULL;
+    PROFILE_T profile;
+
+    dbg_time("Meig_QConnectManager_Linux_V%s", VERSION_STRING());
+    memset(&profile, 0x00, sizeof(profile));
+    profile.pdp = CONFIG_DEFAULT_PDP;
+
+    if (!strcmp(argv[argc-1], "&"))
+        argc--;
+
+    opt = 1;
+    while  (opt < argc) {
+        if (argv[opt][0] != '-')
+            return usage(argv[0]);
+
+        switch (argv[opt++][1])
+        {
+            case 's':
+                profile.apn = profile.user = profile.password = "";
+                if (has_more_argv())
+                    profile.apn = argv[opt++];
+                if (has_more_argv())
+                    profile.user = argv[opt++];
+                if (has_more_argv())
+                {
+                    profile.password = argv[opt++];
+                    if (profile.password && profile.password[0])
+                        profile.auth = 2; //default chap, customers may miss auth
+                }
+                if (has_more_argv())
+                    profile.auth = argv[opt++][0] - '0';
+            break;
+
+            case 'm':
+                if (has_more_argv())
+                    profile.muxid = argv[opt++][0] - '0';
+                break;
+
+            case 'p':
+                if (has_more_argv())
+                    profile.pincode = argv[opt++];
+            break;
+
+            case 'n':
+                if (has_more_argv())
+                    profile.pdp = argv[opt++][0] - '0';
+            break;
+
+            case 'f':
+                if (has_more_argv())
+                {
+                    const char * filename = argv[opt++];
+                    logfilefp = fopen(filename, "a+");
+                    if (!logfilefp) {
+                        dbg_time("Fail to open %s, errno: %d(%s)", filename, errno, strerror(errno));
+                     }
+                }
+            break;
+
+            case 'i':
+                if (has_more_argv())
+                    profile.usbnet_adapter = save_usbnet_adapter = argv[opt++];
+            break;
+
+            case 'v':
+                debug_qmi = 1;
+            break;
+
+            case 'l':
+                main_loop = 1;
+            break;
+
+            case '4':
+                profile.ipv4_flag = 1; 
+            break;
+
+            case '6':
+                profile.ipv6_flag = 1;
+            break;
+
+            case 'd':
+                if (has_more_argv()) {
+                    profile.qmichannel = argv[opt++];
+                    if (qmidev_is_pciemhi(profile.qmichannel))
+                        profile.usbnet_adapter = "mhi0.1";
+                }
+            break;
+
+            default:
+                return usage(argv[0]);
+            break;
+        }
+    }
+
+    if (profile.ipv4_flag == 1 && profile.ipv6_flag == 1) {
+        profile.IsDualIPSupported |= (1 << IpFamilyV6);	
+    } else if (profile.ipv6_flag) {
+        profile.enable_ipv6 = 1;
+    }
+    
+    if (profile.ipv4_flag != 1 && profile.ipv6_flag != 1) { // default enable IPv4
+        profile.ipv4_flag = 1;
+    }
+
+    if (!(profile.qmichannel) || !(profile.usbnet_adapter)) {
+        char qmichannel[32+1] = {'\0'};
+        char usbnet_adapter[32+1] = {'\0'};
+
+        if (profile.usbnet_adapter)
+            strcpy(usbnet_adapter, profile.usbnet_adapter);
+        
+        if (!qmidevice_detect(qmichannel, usbnet_adapter, sizeof(qmichannel))) {
+            dbg_time("qmidevice_detect failed");
+            goto error;
+        }
+        if (!(profile.qmichannel))
+            strset(profile.qmichannel, qmichannel);
+        if (!(profile.usbnet_adapter))
+            strset(profile.usbnet_adapter, usbnet_adapter);
+    }
+    
+    meig_qmap_mode_detect(&profile);
+    if (varify_driver(&profile))
+        return -1;
+
+    if (driver_is_mbim(profile.driver_name) || !strncmp(profile.qmichannel, "/dev/mhi_MBIM", strlen("/dev/mhi_MBIM"))) {
+        dbg_time("Modem works in MBIM mode");
+        return mbim_main(&profile);
+    } else {
+        dbg_time("Modem works in QMI mode");
+        return qmi_main(&profile);
+    }
+
+error:
+    return -1;
+}
diff --git a/package/wwan/meig-cm/src/mbim-cm.c b/package/wwan/meig-cm/src/mbim-cm.c
new file mode 100644
index 000000000..278af7d91
--- /dev/null
+++ b/package/wwan/meig-cm/src/mbim-cm.c
@@ -0,0 +1,1765 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <pthread.h>
+#include <errno.h>
+#include <time.h>
+#include <signal.h>
+#include <getopt.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <endian.h>
+#include <time.h>
+#include <inttypes.h>
+
+#include "QMIThread.h"
+
+#define mbim_debug dbg_time
+
+#define UUID_BASIC_CONNECT "a289cc33-bcbb-8b4f-b6b0-133ec2aae6df"
+#define UUID_SMS             "533fbeeb-14fe-4467-9f90-33a223e56c3f"
+#define UUID_USSD             "e550a0c8-5e82-479e-82f7-10abf4c3351f"
+#define UUID_PHONEBOOK     "4bf38476-1e6a-41db-b1d8-bed289c25bdb"
+#define UUID_STK             "d8f20131-fcb5-4e17-8602-d6ed3816164c"
+#define UUID_AUTH             "1d2b5ff7-0aa1-48b2-aa52-50f15767174e"
+#define UUID_DSS             "c08a26dd-7718-4382-8482-6e0d583c4d0e"
+
+#define UUID_MBIMContextTypeInternet "7E5E2A7E-4E6F-7272-736B-656E7E5E2A7E"
+
+typedef unsigned char UINT8;
+typedef unsigned short UINT16;
+typedef unsigned int UINT32;
+typedef unsigned long long UINT64;
+
+/* Donot use lexxtoh directly, it will change the variable itself, that's what we cannnot telerate */
+#define LE16TOH(v) ({UINT16 _v = (v); le32toh(_v);})
+#define LE32TOH(v) ({UINT32 _v = (v); le32toh(_v);})
+#define LE64TOH(v) ({UINT64 _v = (v); le64toh(_v);})
+
+#define STRINGFY(v) #v
+/* The function name will be _ENUM_NAMEStr */
+#define enumstrfunc(_ENUM_NAME, _ENUM_MEMS) \
+static const char *_ENUM_NAME##Str(int _val) { \
+    struct { int val;char *name;} _enumstr[] = { _ENUM_MEMS }; \
+    int idx; for (idx = 0; idx < sizeof(_enumstr)/sizeof(_enumstr[0]); idx++) { \
+        if (_val == _enumstr[idx].val) return _enumstr[idx].name;} \
+    return STRINGFY(_ENUM_NAME##Unknow); \
+}
+
+#pragma pack(4)
+typedef enum {
+    MBIM_CID_CMD_TYPE_QUERY = 0,
+    MBIM_CID_CMD_TYPE_SET = 1,
+} MBIM_CID_CMD_TYPE_E;
+
+//Set Query Notification
+#define UUID_BASIC_CONNECT_CIDs \
+    MBIM_ENUM_HELPER(MBIM_CID_DEVICE_CAPS, 1) \
+    MBIM_ENUM_HELPER(MBIM_CID_SUBSCRIBER_READY_STATUS, 2) \
+    MBIM_ENUM_HELPER(MBIM_CID_RADIO_STATE, 3) \
+    MBIM_ENUM_HELPER(MBIM_CID_PIN, 4) \
+    MBIM_ENUM_HELPER(MBIM_CID_PIN_LIS, 5) \
+    MBIM_ENUM_HELPER(MBIM_CID_HOME_PROVIDER, 6) \
+    MBIM_ENUM_HELPER(MBIM_CID_PREFERRED_PROVIDERS, 7) \
+    MBIM_ENUM_HELPER(MBIM_CID_VISIBLE_PROVIDERS, 8) \
+    MBIM_ENUM_HELPER(MBIM_CID_REGISTER_STATE, 9) \
+    MBIM_ENUM_HELPER(MBIM_CID_PACKET_SERVICE, 10) \
+    MBIM_ENUM_HELPER(MBIM_CID_SIGNAL_STATE, 11) \
+    MBIM_ENUM_HELPER(MBIM_CID_CONNECT, 12) \
+    MBIM_ENUM_HELPER(MBIM_CID_PROVISIONED_CONTEXTS, 13) \
+    MBIM_ENUM_HELPER(MBIM_CID_SERVICE_ACTIVATION, 14) \
+    MBIM_ENUM_HELPER(MBIM_CID_IP_CONFIGURATION, 15) \
+    MBIM_ENUM_HELPER(MBIM_CID_DEVICE_SERVICES, 16) \
+    MBIM_ENUM_HELPER(MBIM_CID_DEVICE_SERVICE_SUBSCRIBE_LIST, 19) \
+    MBIM_ENUM_HELPER(MBIM_CID_PACKET_STATISTICS, 20) \
+    MBIM_ENUM_HELPER(MBIM_CID_NETWORK_IDLE_HINT, 21) \
+    MBIM_ENUM_HELPER(MBIM_CID_EMERGENCY_MODE, 22) \
+    MBIM_ENUM_HELPER(MBIM_CID_IP_PACKET_FILTERS, 23) \
+    MBIM_ENUM_HELPER(MBIM_CID_MULTICARRIER_PROVIDERS, 24)
+
+#define MBIM_ENUM_HELPER(k, v) k = v,
+typedef enum{
+    UUID_BASIC_CONNECT_CIDs
+} UUID_BASIC_CONNECT_CID_E;
+#undef MBIM_ENUM_HELPER
+#define MBIM_ENUM_HELPER(k, v) {k, #k},
+enumstrfunc(CID2, UUID_BASIC_CONNECT_CIDs);
+#undef MBIM_ENUM_HELPER
+
+typedef enum {
+    MBIM_CID_SMS_CONFIGURATION = 1, // Y Y Y
+    MBIM_CID_SMS_READ = 2, // N Y Y
+    MBIM_CID_SMS_SEND = 3, // Y N N
+    MBIM_CID_SMS_DELETE = 4, // Y N N
+    MBIM_CID_SMS_MESSAGE_STORE_STATUS = 5, // N Y Y
+} UUID_SMS_CID_E;
+
+typedef enum {
+    MBIM_CID_DSS_CONNECT = 1, // Y N N
+} UUID_DSS_CID_E;
+
+#define MBIM_MSGS \
+    MBIM_ENUM_HELPER(MBIM_OPEN_MSG, 1) \
+    MBIM_ENUM_HELPER(MBIM_CLOSE_MSG, 2) \
+    MBIM_ENUM_HELPER(MBIM_COMMAND_MSG, 3) \
+    MBIM_ENUM_HELPER(MBIM_HOST_ERROR_MSG, 4) \
+    \
+    MBIM_ENUM_HELPER(MBIM_OPEN_DONE, 0x80000001) \
+    MBIM_ENUM_HELPER(MBIM_CLOSE_DONE, 0x80000002) \
+    MBIM_ENUM_HELPER(MBIM_COMMAND_DONE, 0x80000003) \
+    MBIM_ENUM_HELPER(MBIM_FUNCTION_ERROR_MSG, 0x80000004) \
+    MBIM_ENUM_HELPER(MBIM_INDICATE_STATUS_MSG, 0x80000007)
+
+#define MBIM_ENUM_HELPER(k, v) k = v,
+typedef enum{
+    MBIM_MSGS
+} MBIM_MSG_Type_E;
+#undef MBIM_ENUM_HELPER
+#define MBIM_ENUM_HELPER(k, v) {k, #k},
+enumstrfunc(MBIMMSGType, MBIM_MSGS);
+#undef MBIM_ENUM_HELPER
+
+typedef enum {
+    MBIM_ERROR_TIMEOUT_FRAGMENT = 1,
+    MBIM_ERROR_FRAGMENT_OUT_OF_SEQUENCE = 2,
+    MBIM_ERROR_LENGTH_MISMATCH = 3,
+    MBIM_ERROR_DUPLICATED_TID = 4,
+    MBIM_ERROR_NOT_OPENED = 5,
+    MBIM_ERROR_UNKNOWN = 6,
+    MBIM_ERROR_CANCEL = 7,
+    MBIM_ERROR_MAX_TRANSFER = 8,
+} MBIM_ERROR_E;
+
+typedef enum {
+    MBIM_STATUS_SUCCESS = 0,
+    MBIM_STATUS_BUSY =  1,
+    MBIM_STATUS_FAILURE = 2,
+    MBIM_STATUS_SIM_NOT_INSERTED = 3,
+    MBIM_STATUS_BAD_SIM = 4,
+    MBIM_STATUS_PIN_REQUIRED = 5,
+    MBIM_STATUS_PIN_DISABLED = 6,
+    MBIM_STATUS_NOT_REGISTERED = 7,
+    MBIM_STATUS_PROVIDERS_NOT_FOUND = 8,
+    MBIM_STATUS_NO_DEVICE_SUPPORT = 9,
+    MBIM_STATUS_PROVIDER_NOT_VISIBLE = 10,
+    MBIM_STATUS_DATA_CLASS_NOT_AVAILABL = 11,
+    MBIM_STATUS_PACKET_SERVICE_DETACHED = 12,
+}  MBIM_STATUS_CODES_E;
+
+typedef enum {
+    MBIMPacketServiceActionAttach = 0,
+    MBIMPacketServiceActionDetach = 1,
+} MBIM_PACKET_SERVICE_ACTION_E;
+
+typedef enum {
+    MBIMPacketServiceStateUnknown = 0,
+    MBIMPacketServiceStateAttaching = 1,
+    MBIMPacketServiceStateAttached = 2,
+    MBIMPacketServiceStateDetaching = 3,
+    MBIMPacketServiceStateDetached = 4,
+} MBIM_PACKET_SERVICE_STATE_E;
+
+static const char *MBIMPacketServiceStateStr(int _val) {
+    struct { int val;char *name;} _enumstr[] = {
+        {MBIMPacketServiceStateUnknown, "Unknown"},
+        {MBIMPacketServiceStateAttaching, "Attaching"},
+        {MBIMPacketServiceStateAttached, "Attached"},
+        {MBIMPacketServiceStateDetaching, "Detaching"},
+        {MBIMPacketServiceStateDetached, "Detached"},
+    };
+    int idx; 
+
+    for (idx = 0; idx < sizeof(_enumstr)/sizeof(_enumstr[0]); idx++) {
+        if (_val == _enumstr[idx].val)
+            return _enumstr[idx].name;
+    } 
+
+    return "Undefined";
+};
+
+#define MBIMDataClasss \
+    MBIM_ENUM_HELPER(MBIMDataClassNone, 0x0) \
+    MBIM_ENUM_HELPER(MBIMDataClassGPRS, 0x1) \
+    MBIM_ENUM_HELPER(MBIMDataClassEDGE, 0x2) \
+    MBIM_ENUM_HELPER(MBIMDataClassUMTS, 0x4) \
+    MBIM_ENUM_HELPER(MBIMDataClassHSDPA, 0x8) \
+    MBIM_ENUM_HELPER(MBIMDataClassHSUPA, 0x10) \
+    MBIM_ENUM_HELPER(MBIMDataClassLTE, 0x20) \
+    \
+    MBIM_ENUM_HELPER(MBIMDataClass1XRTT, 0x10000) \
+    MBIM_ENUM_HELPER(MBIMDataClass1XEVDO, 0x20000) \
+    MBIM_ENUM_HELPER(MBIMDataClass1XEVDORevA, 0x40000) \
+    \
+    MBIM_ENUM_HELPER(MBIMDataClass1XEVDV, 0x80000) \
+    MBIM_ENUM_HELPER(MBIMDataClass3XRTT, 0x100000) \
+    MBIM_ENUM_HELPER(MBIMDataClass1XEVDORevB, 0x200000) \
+    MBIM_ENUM_HELPER(MBIMDataClassUMB, 0x400000) \
+    MBIM_ENUM_HELPER(MBIMDataClassCustom, 0x80000000)
+
+#define MBIM_ENUM_HELPER(k, v) k = v,
+typedef enum {
+    MBIMDataClasss
+} MBIM_DATA_CLASS_E;
+#undef MBIM_ENUM_HELPER
+#define MBIM_ENUM_HELPER(k, v) {k, #k},
+enumstrfunc(MBIMDataClass, MBIMDataClasss);
+#undef MBIM_ENUM_HELPER
+
+typedef struct {
+    UINT32 NwError;
+    UINT32 PacketServiceState; //MBIM_PACKET_SERVICE_STATE_E
+    UINT32 HighestAvailableDataClass; //MBIM_DATA_CLASS_E
+    UINT64 UplinkSpeed;
+    UINT64 DownlinkSpeed;    
+} MBIM_PACKET_SERVICE_INFO_T;
+
+typedef enum {
+    MBIMSubscriberReadyStateNotInitialized = 0, 
+    MBIMSubscriberReadyStateInitialized = 1, 
+    MBIMSubscriberReadyStateSimNotInserted = 2, 
+    MBIMSubscriberReadyStateBadSim = 3, 
+    MBIMSubscriberReadyStateFailure = 4, 
+    MBIMSubscriberReadyStateNotActivated = 5, 
+    MBIMSubscriberReadyStateDeviceLocked = 6,
+}MBIM_SUBSCRIBER_READY_STATE_E;
+
+static const char *MBIMSubscriberReadyStateStr(int _val) { 
+    struct { int val;char *name;} _enumstr[] = { 
+        {MBIMSubscriberReadyStateNotInitialized, "NotInitialized"},
+        {MBIMSubscriberReadyStateInitialized, "Initialized"},
+        {MBIMSubscriberReadyStateSimNotInserted, "NotInserted"}, 
+        {MBIMSubscriberReadyStateBadSim, "BadSim"}, 
+        {MBIMSubscriberReadyStateFailure, "Failure"}, 
+        {MBIMSubscriberReadyStateNotActivated, "NotActivated"}, 
+        {MBIMSubscriberReadyStateDeviceLocked, "DeviceLocked"}, 
+    }; 
+    int idx;
+    
+    for (idx = 0; idx < sizeof(_enumstr)/sizeof(_enumstr[0]); idx++) { 
+        if (_val == _enumstr[idx].val)
+            return _enumstr[idx].name;
+    }
+    
+    return "Undefined";
+};
+
+typedef struct {
+    UINT32 DeviceType; //MBIM_DEVICE_TYPE
+    UINT32 CellularClass; //MBIM_CELLULAR_CLASS
+    UINT32 VoiceClass; //MBIM_VOICE_CLASS
+    UINT32 SimClass; //MBIM_SIM_CLASS
+    UINT32 DataClass; //MBIM_DATA_CLASS
+    UINT32 SmsCaps; //MBIM_SMS_CAPS
+    UINT32 ControlCaps; //MBIM_CTRL_CAPS
+    UINT32 MaxSessions;
+    UINT32 CustomDataClassOffset;
+    UINT32 CustomDataClassSize;
+    UINT32 DeviceIdOffset;
+    UINT32 DeviceIdSize;
+    UINT32 FirmwareInfoOffset;
+    UINT32 FirmwareInfoSize;
+    UINT32 HardwareInfoOffset;
+    UINT32 HardwareInfoSize;
+    UINT8 DataBuffer[0]; //DeviceId FirmwareInfo HardwareInfo
+} MBIM_DEVICE_CAPS_INFO_T;
+
+typedef enum {
+    MBIMReadyInfoFlagsNone,
+    MBIMReadyInfoFlagsProtectUniqueID,
+}MBIM_UNIQUE_ID_FLAGS;
+
+typedef struct {
+    UINT32 ReadyState;
+    UINT32 SubscriberIdOffset;
+    UINT32 SubscriberIdSize;
+    UINT32 SimIccIdOffset;
+    UINT32 SimIccIdSize;
+    UINT32 ReadyInfo;
+    UINT32 ElementCount;
+    UINT8 *TelephoneNumbersRefList;
+    UINT8 *DataBuffer;
+} MBIM_SUBSCRIBER_READY_STATUS_T;
+
+typedef enum {
+    MBIMRegisterActionAutomatic,
+    MBIMRegisterActionManual,
+}MBIM_REGISTER_ACTION_E;
+
+typedef enum {
+    MBIMRegisterStateUnknown = 0,
+    MBIMRegisterStateDeregistered = 1,
+    MBIMRegisterStateSearching = 2, 
+    MBIMRegisterStateHome = 3, 
+    MBIMRegisterStateRoaming = 4,
+    MBIMRegisterStatePartner = 5,
+    MBIMRegisterStateDenied = 6,
+}MBIM_REGISTER_STATE_E;
+
+typedef enum {
+    MBIMRegisterModeUnknown = 0, 
+    MBIMRegisterModeAutomatic = 1,
+    MBIMRegisterModeManual = 2,
+}MBIM_REGISTER_MODE_E;
+
+static const char *MBIMRegisterStateStr(int _val) {
+    struct { int val;char *name;} _enumstr[] ={
+        {MBIMRegisterStateUnknown, "Unknown"},
+        {MBIMRegisterStateDeregistered, "Deregistered"}, 
+        {MBIMRegisterStateSearching, "Searching"}, 
+        {MBIMRegisterStateHome, "Home"}, 
+        {MBIMRegisterStateRoaming, "Roaming"}, 
+        {MBIMRegisterStatePartner, "Partner"}, 
+        {MBIMRegisterStateDenied, "Denied"}, 
+    }; 
+    int idx; 
+    
+    for (idx = 0; idx < sizeof(_enumstr)/sizeof(_enumstr[0]); idx++) {
+        if (_val == _enumstr[idx].val) 
+            return _enumstr[idx].name;
+    } 
+
+    return "Undefined";
+};
+
+static const char *MBIMRegisterModeStr(int _val) { 
+    struct { int val;char *name;} _enumstr[] = {
+        {MBIMRegisterModeUnknown, "Unknown"}, 
+        {MBIMRegisterModeAutomatic, "Automatic"}, 
+        {MBIMRegisterModeManual, "Manual"}, 
+    }; 
+    int idx; 
+
+    for (idx = 0; idx < sizeof(_enumstr)/sizeof(_enumstr[0]); idx++) { 
+        if (_val == _enumstr[idx].val) 
+            return _enumstr[idx].name;
+    } 
+
+    return "Undefined"; 
+};
+
+typedef enum {
+    MBIM_REGISTRATION_NONE,
+    MBIM_REGISTRATION_MANUAL_SELECTION_NOT_AVAILABLE,
+    MBIM_REGISTRATION_PACKET_SERVICE_AUTOMATIC_ATTACH,
+}MBIM_REGISTRATION_FLAGS_E;
+
+typedef struct {
+    UINT32 NwError;
+    UINT32 RegisterState; //MBIM_REGISTER_STATE_E
+    UINT32 RegisterMode;
+    UINT32 AvailableDataClasses;
+    UINT32 CurrentCellularClass;
+    UINT32 ProviderIdOffset;
+    UINT32 ProviderIdSize;
+    UINT32 ProviderNameOffset;
+    UINT32 ProviderNameSize;
+    UINT32 RoamingTextOffset;
+    UINT32 RoamingTextSize;
+    UINT32 RegistrationFlag;
+    UINT8 *DataBuffer;
+} MBIM_REGISTRATION_STATE_INFO_T;
+
+typedef struct {
+    UINT32 MessageType; //Specifies the MBIM message type. 
+    UINT32 MessageLength; //Specifies the total length of this MBIM message in bytes.
+    /* Specifies the MBIM message id value.  This value is used to match host sent messages with function responses. 
+    This value must be unique among all outstanding transactions.    
+    For notifications, the TransactionId must be set to 0 by the function */
+    UINT32 TransactionId;
+} MBIM_MESSAGE_HEADER;
+
+typedef struct {
+    UINT32 TotalFragments; //this field indicates how many fragments there are intotal. 
+    UINT32 CurrentFragment; //This field indicates which fragment this message is.  Values are 0 to TotalFragments?\1
+} MBIM_FRAGMENT_HEADER;
+
+typedef struct {
+    MBIM_MESSAGE_HEADER MessageHeader;
+    UINT32 MaxControlTransfer;
+} MBIM_OPEN_MSG_T;
+
+typedef struct {
+    MBIM_MESSAGE_HEADER MessageHeader;
+    UINT32 Status;
+} MBIM_OPEN_DONE_T;
+
+typedef struct {
+    MBIM_MESSAGE_HEADER MessageHeader;
+} MBIM_CLOSE_MSG_T;
+
+typedef struct {
+    MBIM_MESSAGE_HEADER MessageHeader;
+    UINT32 Status;
+} MBIM_CLOSE_DONE_T;
+
+typedef struct {
+    UINT8 uuid[16];
+} UUID_T;
+
+typedef struct {
+    MBIM_MESSAGE_HEADER MessageHeader;
+    MBIM_FRAGMENT_HEADER FragmentHeader;
+    UUID_T DeviceServiceId; //A 16 byte UUID that identifies the device service the following CID value applies. 
+    UINT32 CID; //Specifies the CID that identifies the parameter being queried for
+    UINT32 CommandType; //0 for a query operation, 1 for a Set operation
+    UINT32 InformationBufferLength; //Size of the Total InformationBuffer, may be larger than current message if fragmented.
+    UINT8 InformationBuffer[0]; //Data supplied to device specific to the CID
+} MBIM_COMMAND_MSG_T;
+
+typedef struct {
+    MBIM_MESSAGE_HEADER MessageHeader;
+    MBIM_FRAGMENT_HEADER FragmentHeader;
+    UUID_T DeviceServiceId; //A 16 byte UUID that identifies the device service the following CID value applies. 
+    UINT32 CID; //Specifies the CID that identifies the parameter being queried for
+    UINT32 Status;
+    UINT32 InformationBufferLength; //Size of the Total InformationBuffer, may be larger than current message if fragmented.
+    UINT8 InformationBuffer[0]; //Data supplied to device specific to the CID
+} MBIM_COMMAND_DONE_T;
+
+typedef struct {
+    MBIM_MESSAGE_HEADER MessageHeader;
+    UINT32 ErrorStatusCode;
+} MBIM_HOST_ERROR_MSG_T;
+
+typedef struct {
+    MBIM_MESSAGE_HEADER MessageHeader;
+    UINT32 ErrorStatusCode;
+} MBIM_FUNCTION_ERROR_MSG_T;
+
+typedef struct {
+    MBIM_MESSAGE_HEADER MessageHeader;
+    MBIM_FRAGMENT_HEADER FragmentHeader;
+    UUID_T DeviceServiceId; //A 16 byte UUID that identifies the device service the following CID value applies. 
+    UINT32 CID; //Specifies the CID that identifies the parameter being queried for
+    UINT32 InformationBufferLength; //Size of the Total InformationBuffer, may be larger than current message if fragmented.
+    UINT8 InformationBuffer[0]; //Data supplied to device specific to the CID
+} MBIM_INDICATE_STATUS_MSG_T;
+
+typedef enum {
+    MBIMActivationCommandDeactivate = 0,
+    MBIMActivationCommandActivate = 1,
+} MBIM_ACTIVATION_COMMAND_E;
+
+typedef enum {
+    MBIMCompressionNone =  0,
+    MBIMCompressionEnable =  1,
+} MBIM_COMPRESSION_E;
+
+typedef enum {
+    MBIMAuthProtocolNone = 0,
+    MBIMAuthProtocolPap = 1,
+    MBIMAuthProtocolChap = 2,
+    MBIMAuthProtocolMsChapV2 = 3,
+} MBIM_AUTH_PROTOCOL_E;
+
+#define MBIMContextIPTypes \
+    MBIM_ENUM_HELPER(MBIMContextIPTypeDefault, 0) \
+    MBIM_ENUM_HELPER(MBIMContextIPTypeIPv4, 1) \
+    MBIM_ENUM_HELPER(MBIMContextIPTypeIPv6, 2) \
+    MBIM_ENUM_HELPER(MBIMContextIPTypeIPv4v6, 3) \
+    MBIM_ENUM_HELPER(MBIMContextIPTypeIPv4AndIPv6, 4)
+
+#define MBIM_ENUM_HELPER(k, v) k = v,
+typedef enum {
+    MBIMContextIPTypes
+} MBIM_CONTEXT_IP_TYPE_E;
+#undef MBIM_ENUM_HELPER
+#define MBIM_ENUM_HELPER(k, v) {k, #k},
+enumstrfunc(MBIMContextIPType, MBIMContextIPTypes);
+#undef MBIM_ENUM_HELPER
+
+typedef enum {
+    MBIMActivationStateUnknown = 0,
+        MBIMActivationStateActivated = 1,
+        MBIMActivationStateActivating = 2,
+        MBIMActivationStateDeactivated = 3,
+        MBIMActivationStateDeactivating = 4,
+} MBIM_ACTIVATION_STATE_E;
+
+typedef enum {
+    MBIMVoiceCallStateNone = 0,
+        MBIMVoiceCallStateInProgress = 1,
+        MBIMVoiceCallStateHangUp = 2,
+} MBIM_VOICECALL_STATE_E;
+
+static const char *MBIMActivationStateStr(int _val) {
+    struct { int val;char *name;} _enumstr[] = {
+        {MBIMActivationStateUnknown, "Unknown"}, 
+        {MBIMActivationStateActivated, "Activated"},
+        {MBIMActivationStateActivating, "Activating"}, 
+        {MBIMActivationStateDeactivated, "Deactivated"}, 
+        {MBIMActivationStateDeactivating, "Deactivating"}, 
+    }; 
+    int idx;
+
+    for (idx = 0; idx < sizeof(_enumstr)/sizeof(_enumstr[0]); idx++) {
+        if (_val == _enumstr[idx].val) 
+            return _enumstr[idx].name;
+    } 
+
+    return "Undefined"; 
+};
+
+static const char *MBIMVoiceCallStateStr(int _val) { 
+    struct { int val;char *name;} _enumstr[] = { 
+        {MBIMVoiceCallStateNone, "None"},
+        {MBIMVoiceCallStateInProgress, "InProgress"}, 
+        {MBIMVoiceCallStateHangUp, "HangUp"},
+    };
+    int idx;
+
+    for (idx = 0; idx < sizeof(_enumstr)/sizeof(_enumstr[0]); idx++) {
+        if (_val == _enumstr[idx].val)
+        return _enumstr[idx].name;
+    }
+
+    return "Undefined";
+};
+
+typedef struct {
+    UINT32 SessionId;
+    UINT32 ActivationCommand; //MBIM_ACTIVATION_COMMAND_E
+    UINT32 AccessStringOffset;
+    UINT32 AccessStringSize;
+    UINT32 UserNameOffset;
+    UINT32 UserNameSize;
+    UINT32 PasswordOffset;
+    UINT32 PasswordSize;
+    UINT32 Compression; //MBIM_COMPRESSION_E
+    UINT32 AuthProtocol; //MBIM_AUTH_PROTOCOL_E
+    UINT32 IPType; //MBIM_CONTEXT_IP_TYPE_E
+    UUID_T ContextType;
+    UINT8 DataBuffer[0];  /* apn, username, password */
+} MBIM_SET_CONNECT_T;
+
+typedef struct {
+    UINT32 SessionId;
+    UINT32 ActivationState; //MBIM_ACTIVATION_STATE_E
+    UINT32 VoiceCallState;
+    UINT32 IPType; //MBIM_CONTEXT_IP_TYPE_E
+    UUID_T ContextType;
+    UINT32 NwError;
+} MBIM_CONNECT_T;
+
+typedef struct {
+    UINT32 OnLinkPrefixLength;
+    UINT8 IPv4Address[4];
+} MBIM_IPV4_ELEMENT_T;
+
+typedef struct {
+    UINT32 OnLinkPrefixLength;
+    UINT8 IPv6Address[16];
+} MBIM_IPV6_ELEMENT_T;
+
+typedef struct {
+    UINT32 SessionId;
+    UINT32 IPv4ConfigurationAvailable; //bit0~Address, bit1~gateway, bit2~DNS, bit3~MTU
+    UINT32 IPv6ConfigurationAvailable; //bit0~Address, bit1~gateway, bit2~DNS, bit3~MTU
+    UINT32 IPv4AddressCount;
+    UINT32 IPv4AddressOffset;
+    UINT32 IPv6AddressCount;
+    UINT32 IPv6AddressOffset;
+    UINT32 IPv4GatewayOffset;
+    UINT32 IPv6GatewayOffset;
+    UINT32 IPv4DnsServerCount;
+    UINT32 IPv4DnsServerOffset;
+    UINT32 IPv6DnsServerCount;
+    UINT32 IPv6DnsServerOffset;
+    UINT32 IPv4Mtu;
+    UINT32 IPv6Mtu;
+    UINT8 DataBuffer[];
+} MBIM_IP_CONFIGURATION_INFO_T;
+
+typedef struct {
+    UINT32 Rssi;
+    UINT32 ErrorRate;
+    UINT32 SignalStrengthInterval;
+    UINT32 RssiThreshold;
+    UINT32 ErrorRateThreshold;    
+} MBIM_SIGNAL_STATE_INFO_T;
+
+typedef struct {
+    UINT32 SignalStrengthInterval;
+    UINT32 RssiThreshold;
+    UINT32 ErrorRateThreshold;
+} MBIM_SET_SIGNAL_STATE_T;
+
+#pragma pack()
+
+static pthread_t read_tid = 0;
+static int mbim_verbose = 0;
+static UINT32 TransactionId = 1;
+static unsigned mbim_default_timeout  = 10000;
+static const char *mbim_netcard = "wwan0";
+static const char *mbim_dev = "/dev/cdc-wdm0";
+static const char *mbim_apn = NULL;
+static const char *mbim_user = NULL;
+static const char *mbim_passwd = NULL;
+static int mbim_iptype = MBIMContextIPTypeDefault;
+static int mbim_auth = MBIMAuthProtocolNone;
+static int mbim_fd = -1;
+static pthread_mutex_t mbim_command_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t mbim_command_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t mbim_state_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t mbim_state_cond = PTHREAD_COND_INITIALIZER;
+static MBIM_MESSAGE_HEADER *mbim_pRequest;
+static MBIM_MESSAGE_HEADER *mbim_pResponse;
+extern int meig_ifconfig(int argc, char *argv[]);
+
+static int mysystem(const char *cmd)
+{
+    int status = system(cmd);
+    mbim_debug("system(%s)=%d", cmd, status);
+
+    return status;
+}
+
+static void mbim_ifconfig(int iptype, const char *ifname, const unsigned char *ipaddr, const unsigned char *gw,
+    const unsigned char *dns1, const unsigned char *dns2, UINT32 prefix, UINT32 mtu) {
+    char shell_cmd[256] = {'\0'};
+    char dns1str[128], dns2str[128];
+
+    if (ipaddr) {
+        snprintf(shell_cmd, sizeof(shell_cmd), "busybox ip link set dev %s up", ifname);
+        mysystem(shell_cmd);
+    } else {
+        /* remove all address */
+        snprintf(shell_cmd, sizeof(shell_cmd), "busybox ip address flush dev %s", ifname);
+        mysystem(shell_cmd);
+        snprintf(shell_cmd, sizeof(shell_cmd), "busybox ip link set dev %s down", ifname);
+        mysystem(shell_cmd);
+
+        update_resolv_conf(4, ifname, NULL, NULL);
+        update_resolv_conf(6, ifname, NULL, NULL);
+        return;
+    }
+
+    if (ipaddr) {
+        const unsigned char *d = ipaddr;
+        snprintf(shell_cmd, sizeof(shell_cmd), "busybox ip -%d address flush dev %s", iptype, ifname);
+        mysystem(shell_cmd);
+        if (iptype == 4)
+            snprintf(shell_cmd, sizeof(shell_cmd), "busybox ip -%d address add %u.%u.%u.%u/%u dev %s",
+                iptype, d[0], d[1], d[2], d[3], prefix, ifname);
+        if (iptype == 6)
+            snprintf(shell_cmd, sizeof(shell_cmd),
+                "busybox ip -%d address add %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x/%u dev %s",
+                iptype, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
+                prefix, ifname);
+        mysystem(shell_cmd);
+    }
+
+    if (gw) {
+        const unsigned char *d = gw;
+        if (iptype == 4)
+            snprintf(shell_cmd, sizeof(shell_cmd), "busybox ip -%d route add default via %d.%d.%d.%d dev %s",
+                iptype, d[0], d[1], d[2], d[3], ifname);
+        if (iptype == 6)
+            snprintf(shell_cmd, sizeof(shell_cmd),
+                "busybox ip -%d route add default via %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x dev %s",
+                iptype, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
+                ifname);
+        mysystem(shell_cmd);
+    } else {
+        snprintf(shell_cmd, sizeof(shell_cmd), "busybox ip -%d route add default dev %s", iptype, ifname);
+        mysystem(shell_cmd);
+    }
+
+    if (mtu) {
+        snprintf(shell_cmd, sizeof(shell_cmd), "busybox ip -%d link set dev %s mtu %u", iptype, ifname, mtu);
+        mysystem(shell_cmd);
+    }
+
+    if (dns1) {
+        const unsigned char *d = dns1;
+        if (iptype == 4)
+            snprintf(dns1str, sizeof(dns1str), "%d.%d.%d.%d", d[0], d[1], d[2], d[3]);
+        if (iptype == 6)
+            snprintf(dns1str, sizeof(dns1str), "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+                d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+    }
+
+    if (dns2) {
+        const unsigned char *d = dns2;
+        if (iptype == 4)
+            snprintf(dns2str, sizeof(dns2str), "%d.%d.%d.%d", d[0], d[1], d[2], d[3]);
+        if (iptype == 6)
+            snprintf(dns2str, sizeof(dns2str), "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+                d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+    }
+    
+    update_resolv_conf(iptype, ifname, dns1 ? dns1str : NULL, dns2 ? dns2str : NULL);
+}
+
+static const UUID_T * str2uuid(const char *str) {
+    static UUID_T uuid;
+    UINT32 d[16];
+    char tmp[16*2+4+1];
+    unsigned i = 0;
+
+    while (str[i]) {
+        tmp[i] = tolower(str[i]);
+        i++;
+    }
+    tmp[i] = '\0';
+    
+    sscanf(tmp, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+        &d[0], &d[1], &d[2], &d[3], &d[4], &d[5], &d[6], &d[7],
+        &d[8], &d[9], &d[10], &d[11], &d[12], &d[13], &d[14], &d[15]);
+
+    for (i = 0; i < 16; i++) {
+        uuid.uuid[i] = d[i]&0xFF;
+    }
+    
+    return &uuid;
+}
+
+#define mbim_alloc( _size)  malloc(_size)
+#define mbim_free(_mem) do { if (_mem) { free(_mem); _mem = NULL;}} while(0)
+
+static int mbim_quit = 0;
+static int mbim_open_state = 0;
+static MBIM_SUBSCRIBER_READY_STATE_E ReadyState = MBIMSubscriberReadyStateNotInitialized;
+static MBIM_REGISTER_STATE_E RegisterState = MBIMRegisterStateUnknown;
+static MBIM_PACKET_SERVICE_STATE_E PacketServiceState = MBIMPacketServiceStateUnknown;
+static MBIM_ACTIVATION_STATE_E ActivationState = MBIMActivationStateUnknown;
+static MBIM_SUBSCRIBER_READY_STATE_E oldReadyState = MBIMSubscriberReadyStateNotInitialized;
+static MBIM_REGISTER_STATE_E oldRegisterState = MBIMRegisterStateUnknown;
+static MBIM_PACKET_SERVICE_STATE_E oldPacketServiceState = MBIMPacketServiceStateUnknown;
+static MBIM_ACTIVATION_STATE_E oldActivationState = MBIMActivationStateUnknown;
+
+static void _notify_state_chage(void) {
+    pthread_mutex_lock(&mbim_state_mutex);
+    pthread_cond_signal(&mbim_state_cond);
+    pthread_mutex_unlock(&mbim_state_mutex);
+}
+
+#define notify_state_chage(_var, _new) do {if (_var != _new) {_var = _new; _notify_state_chage();}} while (0)
+
+static int wait_state_change(uint32_t seconds) {
+    int retval = 0;
+
+    pthread_mutex_lock(&mbim_state_mutex);
+    retval = pthread_cond_timeout_np(&mbim_state_cond, &mbim_state_mutex, seconds*1000);
+    pthread_mutex_unlock(&mbim_state_mutex);
+
+    if (retval !=0 && retval != ETIMEDOUT) mbim_debug("seconds=%u, retval=%d", seconds, retval);
+    return retval;
+}
+
+static MBIM_MESSAGE_HEADER *compose_open_command(UINT32 MaxControlTransfer)
+{
+    MBIM_OPEN_MSG_T *pRequest = (MBIM_OPEN_MSG_T *)mbim_alloc(sizeof(MBIM_OPEN_MSG_T));
+
+    if(!pRequest)
+        return NULL;
+
+    pRequest->MessageHeader.MessageType = MBIM_OPEN_MSG;
+    pRequest->MessageHeader.MessageLength = sizeof(MBIM_COMMAND_MSG_T);
+    pRequest->MessageHeader.TransactionId = TransactionId++;
+    pRequest->MaxControlTransfer = MaxControlTransfer;
+
+    return &pRequest->MessageHeader;
+}
+
+static MBIM_MESSAGE_HEADER *compose_close_command(void)
+{
+    MBIM_CLOSE_MSG_T *pRequest = (MBIM_CLOSE_MSG_T *)mbim_alloc(sizeof(MBIM_CLOSE_MSG_T));
+
+    if(!pRequest)
+        return NULL;
+
+    pRequest->MessageHeader.MessageType = MBIM_CLOSE_MSG;
+    pRequest->MessageHeader.MessageLength = sizeof(MBIM_CLOSE_MSG_T);
+    pRequest->MessageHeader.TransactionId = TransactionId++;
+
+    return &pRequest->MessageHeader;
+}
+
+static MBIM_MESSAGE_HEADER *compose_basic_connect_command(UINT32 CID, UINT32 CommandType, void *pInformationBuffer, UINT32 InformationBufferLength)
+{
+    MBIM_COMMAND_MSG_T *pRequest = (MBIM_COMMAND_MSG_T *)mbim_alloc(sizeof(MBIM_COMMAND_MSG_T) + InformationBufferLength);
+
+    pRequest->MessageHeader.MessageType = MBIM_COMMAND_MSG;
+    pRequest->MessageHeader.MessageLength = (sizeof(MBIM_COMMAND_MSG_T) + InformationBufferLength);
+    pRequest->MessageHeader.TransactionId = TransactionId++;
+
+    pRequest->FragmentHeader.TotalFragments = 1;
+    pRequest->FragmentHeader.CurrentFragment= 0;
+
+    memcpy(pRequest->DeviceServiceId.uuid, str2uuid(UUID_BASIC_CONNECT), 16);
+    
+    pRequest->CID = CID;
+    pRequest->CommandType = CommandType;
+    if (InformationBufferLength && pInformationBuffer) {
+        pRequest->InformationBufferLength = InformationBufferLength;
+        memcpy(pRequest->InformationBuffer, pInformationBuffer, InformationBufferLength);
+    } else {
+        pRequest->InformationBufferLength = 0;
+    }
+
+    return &pRequest->MessageHeader;
+}
+
+static const char * uuid2str(const UUID_T *pUUID) {
+    static char str[16*2+4+1];
+    const UINT8 *d = pUUID->uuid;
+
+    snprintf(str, sizeof(str), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+        d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
+        d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+
+    return str;
+}
+
+static const char *DeviceServiceId2str(const UUID_T *pUUID) {
+    const char *str = "UUID_UNKNOW";
+    
+    if (!strcmp(uuid2str(pUUID), UUID_BASIC_CONNECT))
+        str = "UUID_BASIC_CONNECT";
+    else if (!strcmp(uuid2str(pUUID), UUID_SMS))
+        str = "UUID_SMS";
+    else if (!strcmp(uuid2str(pUUID), UUID_USSD))
+        str = "UUID_USSD";
+    else if (!strcmp(uuid2str(pUUID), UUID_PHONEBOOK))
+        str = "UUID_PHONEBOOK";
+    else if (!strcmp(uuid2str(pUUID), UUID_STK))
+        str = "UUID_STK";
+    else if (!strcmp(uuid2str(pUUID), UUID_AUTH))
+        str = "UUID_AUTH";
+    else if (!strcmp(uuid2str(pUUID), UUID_DSS))
+        str = "UUID_DSS";
+    else
+        str = "UUID_UNKNOW";
+    
+    return str;
+}
+
+static const char *mbim_get_segment(void *_pMsg, UINT32 offset, UINT32 len)
+{
+    int idx;
+    static char buff[256] = {'\0'};
+    UINT8 *pMsg = (UINT8*)_pMsg;
+
+    for (idx = 0; idx < len/2; idx++)
+        buff[idx] = pMsg[offset+idx*2];
+    buff[idx] = '\0';
+    return buff;
+}
+
+static void mbim_dump_header(MBIM_MESSAGE_HEADER *pMsg, const char *direction) {
+    mbim_debug("%s Header:", direction);
+    mbim_debug("%s MessageLength = %u", direction, LE32TOH(pMsg->MessageLength));
+    mbim_debug("%s MessageType =  %s (0x%08x)", direction, MBIMMSGTypeStr(LE32TOH(pMsg->MessageType)), LE32TOH(pMsg->MessageType));
+    mbim_debug("%s TransactionId = %u", direction, LE32TOH(pMsg->TransactionId));
+    mbim_debug("%s Contents:", direction);
+}
+
+static void mbim_dump_command_msg(MBIM_COMMAND_MSG_T *pCmdMsg, const char *direction) {
+    mbim_debug("%s DeviceServiceId = %s (%s)", direction, DeviceServiceId2str(&pCmdMsg->DeviceServiceId), uuid2str(&pCmdMsg->DeviceServiceId));
+    mbim_debug("%s CID = %s (%u)", direction, CID2Str(LE32TOH(pCmdMsg->CID)), LE32TOH(pCmdMsg->CID));
+    mbim_debug("%s CommandType = %s (%u)", direction, LE32TOH(pCmdMsg->CommandType) ? "set" : "query", LE32TOH(pCmdMsg->CommandType));
+    mbim_debug("%s InformationBufferLength = %u", direction, LE32TOH(pCmdMsg->InformationBufferLength));
+}
+
+static void mbim_dump_command_done(MBIM_COMMAND_DONE_T *pCmdDone, const char *direction) {
+    mbim_debug("%s DeviceServiceId = %s (%s)", direction, DeviceServiceId2str(&pCmdDone->DeviceServiceId), uuid2str(&pCmdDone->DeviceServiceId));
+    mbim_debug("%s CID = %s (%u)", direction, CID2Str(LE32TOH(pCmdDone->CID)), LE32TOH(pCmdDone->CID));
+    mbim_debug("%s Status = %u", direction, LE32TOH(pCmdDone->Status));
+    mbim_debug("%s InformationBufferLength = %u", direction, LE32TOH(pCmdDone->InformationBufferLength));
+}
+
+static void mbim_dump_indicate_msg(MBIM_INDICATE_STATUS_MSG_T *pIndMsg, const char *direction) {
+    mbim_debug("%s DeviceServiceId = %s (%s)", direction, DeviceServiceId2str(&pIndMsg->DeviceServiceId), uuid2str(&pIndMsg->DeviceServiceId));
+    mbim_debug("%s CID = %s (%u)", direction, CID2Str(LE32TOH(pIndMsg->CID)), LE32TOH(pIndMsg->CID));
+    mbim_debug("%s InformationBufferLength = %u", direction, LE32TOH(pIndMsg->InformationBufferLength));
+}
+
+static void mbim_dump_connect(MBIM_CONNECT_T *pInfo, const char *direction) {
+    mbim_debug("%s SessionId = %u", direction, LE32TOH(pInfo->SessionId));
+    mbim_debug("%s ActivationState = %s (%u)", direction, MBIMActivationStateStr(LE32TOH(pInfo->ActivationState)), LE32TOH(pInfo->ActivationState));
+    mbim_debug("%s IPType = %s", direction, MBIMContextIPTypeStr(LE32TOH(pInfo->IPType)));
+    mbim_debug("%s VoiceCallState = %s", direction, MBIMVoiceCallStateStr(LE32TOH(pInfo->VoiceCallState)));
+    mbim_debug("%s ContextType = %s", direction, uuid2str(&pInfo->ContextType));
+    mbim_debug("%s NwError = %u", direction, LE32TOH(pInfo->NwError));
+}
+
+static void mbim_dump_signal_state(MBIM_SIGNAL_STATE_INFO_T *pInfo, const char *direction)
+{
+    mbim_debug("%s Rssi = %u", direction, LE32TOH(pInfo->Rssi));
+    mbim_debug("%s ErrorRate = %u", direction, LE32TOH(pInfo->ErrorRate));
+    mbim_debug("%s SignalStrengthInterval = %u", direction, LE32TOH(pInfo->SignalStrengthInterval));
+    mbim_debug("%s RssiThreshold = %u", direction, LE32TOH(pInfo->RssiThreshold));
+    mbim_debug("%s ErrorRateThreshold = %u", direction, LE32TOH(pInfo->ErrorRateThreshold));
+}
+
+static void mbim_dump_packet_service(MBIM_PACKET_SERVICE_INFO_T *pInfo, const char *direction)
+{
+    mbim_debug("%s NwError = %u", direction, pInfo->NwError);
+    mbim_debug("%s PacketServiceState = %s", direction, MBIMPacketServiceStateStr(pInfo->PacketServiceState));
+    mbim_debug("%s HighestAvailableDataClass = %s", direction, MBIMDataClassStr(LE32TOH(pInfo->HighestAvailableDataClass)));
+    mbim_debug("%s UplinkSpeed = %ld", direction, (long)LE64TOH(pInfo->UplinkSpeed));
+    mbim_debug("%s DownlinkSpeed = %ld", direction, (long)LE64TOH(pInfo->DownlinkSpeed));
+}
+
+static void mbim_dump_subscriber_status(MBIM_SUBSCRIBER_READY_STATUS_T *pInfo, const char *direction)
+{
+    mbim_debug("%s ReadyState = %s", direction, MBIMSubscriberReadyStateStr(pInfo->ReadyState));
+    mbim_debug("%s SIMICCID = %s", direction, mbim_get_segment(pInfo, LE32TOH(pInfo->SimIccIdOffset), LE32TOH(pInfo->SimIccIdSize)));
+    mbim_debug("%s SubscriberID = %s", direction, mbim_get_segment(pInfo, LE32TOH(pInfo->SubscriberIdOffset), LE32TOH(pInfo->SubscriberIdSize)));
+    /* maybe more than one number */
+    int idx;
+    for (idx = 0; idx < LE32TOH(pInfo->ElementCount); idx++) {
+        UINT32 offset = ((UINT32*)((UINT8*)pInfo+offsetof(MBIM_SUBSCRIBER_READY_STATUS_T, TelephoneNumbersRefList)))[0];
+        UINT32 length = ((UINT32*)((UINT8*)pInfo+offsetof(MBIM_SUBSCRIBER_READY_STATUS_T, TelephoneNumbersRefList)))[1];
+        mbim_debug("%s Number = %s", direction, mbim_get_segment(pInfo, LE32TOH(offset), LE32TOH(length)));
+    }
+}
+
+static void mbim_dump_regiester_status(MBIM_REGISTRATION_STATE_INFO_T *pInfo, const char *direction)
+{
+    mbim_debug("%s NwError = %u", direction, LE32TOH(pInfo->NwError));
+    mbim_debug("%s RegisterState = %s", direction, MBIMRegisterStateStr(pInfo->RegisterState));
+    mbim_debug("%s RegisterMode = %s", direction, MBIMRegisterModeStr(pInfo->RegisterMode));
+}
+
+static void mbim_dump_ipconfig(MBIM_IP_CONFIGURATION_INFO_T *pInfo, const char *direction)
+{
+    UINT8 prefix = 0, *ipv4=NULL, *ipv6=NULL, *gw=NULL, *dns1=NULL, *dns2=NULL;
+
+    mbim_debug("%s SessionId = %u", direction, LE32TOH(pInfo->SessionId));
+    mbim_debug("%s IPv4ConfigurationAvailable = 0x%x", direction, LE32TOH(pInfo->IPv4ConfigurationAvailable));
+    mbim_debug("%s IPv6ConfigurationAvailable = 0x%x", direction, LE32TOH(pInfo->IPv6ConfigurationAvailable));
+    mbim_debug("%s IPv4AddressCount = 0x%x", direction, LE32TOH(pInfo->IPv4AddressCount));
+    mbim_debug("%s IPv4AddressOffset = 0x%x", direction, LE32TOH(pInfo->IPv4AddressOffset));
+    mbim_debug("%s IPv6AddressCount = 0x%x", direction, LE32TOH(pInfo->IPv6AddressCount));
+    mbim_debug("%s IPv6AddressOffset = 0x%x", direction, LE32TOH(pInfo->IPv6AddressOffset));
+
+    /* IPv4 */
+    if (LE32TOH(pInfo->IPv4ConfigurationAvailable)&0x1) {
+        MBIM_IPV4_ELEMENT_T *pAddress = (MBIM_IPV4_ELEMENT_T *)(&pInfo->DataBuffer[LE32TOH(pInfo->IPv4AddressOffset)-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]);
+        prefix = LE32TOH(pAddress->OnLinkPrefixLength);
+        ipv4 = pAddress->IPv4Address;
+        mbim_debug("%s IPv4 = %u.%u.%u.%u/%u", direction, ipv4[0], ipv4[1], ipv4[2], ipv4[3], prefix);
+    }
+    if (LE32TOH(pInfo->IPv4ConfigurationAvailable)&0x2) {
+        gw = (UINT8 *)(&pInfo->DataBuffer[LE32TOH(pInfo->IPv4GatewayOffset)-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]);
+        mbim_debug("%s gw = %u.%u.%u.%u", direction, gw[0], gw[1], gw[2], gw[3]);
+    }
+    if (LE32TOH(pInfo->IPv4ConfigurationAvailable)&0x3) {
+        dns1 = (UINT8 *)(&pInfo->DataBuffer[LE32TOH(pInfo->IPv4DnsServerOffset) -sizeof(MBIM_IP_CONFIGURATION_INFO_T)]);
+        mbim_debug("%s dns1 = %u.%u.%u.%u", direction, dns1[0], dns1[1], dns1[2], dns1[3]);
+        if (LE32TOH(pInfo->IPv4DnsServerCount) == 2) {
+            dns2 = dns1 + 4;
+            mbim_debug("%s dns2 = %u.%u.%u.%u", direction, dns2[0], dns2[1], dns2[2], dns2[3]);
+        }
+    }
+    if (pInfo->IPv4Mtu) mbim_debug("%s ipv4 mtu = %u", direction, pInfo->IPv4Mtu);
+
+    /* IPv6 */
+    if (LE32TOH(pInfo->IPv6ConfigurationAvailable)&0x1) {
+        MBIM_IPV6_ELEMENT_T *pAddress = (MBIM_IPV6_ELEMENT_T *)(&pInfo->DataBuffer[LE32TOH(pInfo->IPv6AddressOffset)-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]);
+        prefix = LE32TOH(pAddress->OnLinkPrefixLength);
+        ipv6 = pAddress->IPv6Address;
+        mbim_debug("%s IPv6 = %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x/%d", \
+            direction, ipv6[0], ipv6[1], ipv6[2], ipv6[3], ipv6[4], ipv6[5], ipv6[6], ipv6[7], \
+            ipv6[8], ipv6[9], ipv6[10], ipv6[11], ipv6[12], ipv6[13], ipv6[14], ipv6[15], prefix);
+    }
+    if (LE32TOH(pInfo->IPv6ConfigurationAvailable)&0x2) {
+        gw = (UINT8 *)(&pInfo->DataBuffer[LE32TOH(pInfo->IPv6GatewayOffset)-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]);
+        mbim_debug("%s gw = %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", \
+            direction, gw[0], gw[1], gw[2], gw[3], gw[4], gw[5], gw[6], gw[7], \
+            gw[8], gw[9], gw[10], gw[11], gw[12], gw[13], gw[14], gw[15]);
+    }
+    if (LE32TOH(pInfo->IPv6ConfigurationAvailable)&0x3) {
+        dns1 = (UINT8 *)(&pInfo->DataBuffer[LE32TOH(pInfo->IPv6DnsServerOffset)-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]);
+        mbim_debug("%s dns1 = %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", \
+            direction, dns1[0], dns1[1], dns1[2], dns1[3], dns1[4], dns1[5], dns1[6], dns1[7], \
+            dns1[8], dns1[9], dns1[10], dns1[11], dns1[12], dns1[13], dns1[14], dns1[15]);
+        if (LE32TOH(pInfo->IPv6DnsServerCount) == 2) {
+            dns2 = dns1 + 16;
+            mbim_debug("%s dns2 = %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", \
+                direction, dns2[0], dns2[1], dns2[2], dns2[3], dns1[4], dns1[5], dns1[6], dns1[7],
+                dns2[8], dns2[9], dns2[10], dns2[11], dns2[12], dns2[13], dns2[14], dns2[15]);
+        }
+    }
+    if (pInfo->IPv6Mtu) mbim_debug("%s ipv6 mtu = %u", direction, pInfo->IPv6Mtu);
+}
+
+static void mbim_dump(MBIM_MESSAGE_HEADER *pMsg, int mbim_verbose) {
+    unsigned char *data = (unsigned char *)pMsg;
+    const char *direction = (pMsg->MessageType & 0x80000000) ? "<" : ">";
+
+    if (!mbim_verbose)
+        return;
+
+    if (mbim_verbose) {
+        unsigned i;
+        static char _tmp[4096] = {'\0'};
+        _tmp[0] = (pMsg->MessageType & 0x80000000) ? '<' : '>';
+        _tmp[1] = '\0';
+        for (i = 0; i < pMsg->MessageLength && i < 4096; i++)
+            snprintf(_tmp + strlen(_tmp), 4096 - strlen(_tmp), "%02X:", data[i]);
+        mbim_debug("%s", _tmp);
+    }
+
+    mbim_dump_header(pMsg, direction);
+    if (pMsg->MessageType == MBIM_OPEN_MSG) {
+        MBIM_OPEN_MSG_T *pOpenMsg = (MBIM_OPEN_MSG_T *)pMsg;
+        mbim_debug("%s MaxControlTransfer = %u", direction, LE32TOH(pOpenMsg->MaxControlTransfer));
+    }
+    else if (pMsg->MessageType == MBIM_OPEN_DONE) {
+        MBIM_OPEN_DONE_T *pOpenDone = (MBIM_OPEN_DONE_T *)pMsg;
+        mbim_debug("%s Status = %u", direction, LE32TOH(pOpenDone->Status));
+    }
+    else if (pMsg->MessageType == MBIM_CLOSE_MSG) {
+
+    }
+    else if (pMsg->MessageType == MBIM_CLOSE_DONE) {
+        MBIM_CLOSE_DONE_T *pCloseDone = (MBIM_CLOSE_DONE_T *)pMsg;
+        mbim_debug("%s Status = %u", direction, LE32TOH(pCloseDone->Status));
+    }
+    else if (pMsg->MessageType == MBIM_COMMAND_MSG) {
+        MBIM_COMMAND_MSG_T *pCmdMsg = (MBIM_COMMAND_MSG_T *)pMsg;
+
+        mbim_dump_command_msg(pCmdMsg, direction);
+        if (!memcmp(pCmdMsg->DeviceServiceId.uuid, str2uuid(UUID_BASIC_CONNECT), 16)) {
+            
+            if (pCmdMsg->CID == MBIM_CID_CONNECT) {
+                MBIM_SET_CONNECT_T *pInfo = (MBIM_SET_CONNECT_T *)pCmdMsg->InformationBuffer;
+                mbim_debug("%s SessionId = %u", direction, LE32TOH(pInfo->SessionId));
+            }
+            else if (pCmdMsg->CID == MBIM_CID_IP_CONFIGURATION) {
+                MBIM_IP_CONFIGURATION_INFO_T *pInfo = (MBIM_IP_CONFIGURATION_INFO_T *)pCmdMsg->InformationBuffer;
+                mbim_debug("%s SessionId = %u", direction, LE32TOH(pInfo->SessionId));
+            }
+        }
+    }
+    else if (pMsg->MessageType == MBIM_COMMAND_DONE) {
+        MBIM_COMMAND_DONE_T *pCmdDone = (MBIM_COMMAND_DONE_T *)pMsg;
+
+        mbim_dump_command_done(pCmdDone, direction);
+        if (!memcmp(pCmdDone->DeviceServiceId.uuid, str2uuid(UUID_BASIC_CONNECT), 16)) {
+            if (pCmdDone->CID == MBIM_CID_CONNECT) {
+                MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pCmdDone->InformationBuffer;
+                mbim_dump_connect(pInfo, direction);
+            }
+            else if (pCmdDone->CID == MBIM_CID_IP_CONFIGURATION) {
+                MBIM_IP_CONFIGURATION_INFO_T *pInfo = (MBIM_IP_CONFIGURATION_INFO_T *)pCmdDone->InformationBuffer;
+                mbim_dump_ipconfig(pInfo, direction);
+            }
+            else if (pCmdDone->CID == MBIM_CID_PACKET_SERVICE) {
+                MBIM_PACKET_SERVICE_INFO_T *pInfo = (MBIM_PACKET_SERVICE_INFO_T *)pCmdDone->InformationBuffer;
+                mbim_dump_packet_service(pInfo, direction);
+            }
+            else if (pCmdDone->CID == MBIM_CID_SUBSCRIBER_READY_STATUS) {
+                MBIM_SUBSCRIBER_READY_STATUS_T *pInfo = (MBIM_SUBSCRIBER_READY_STATUS_T *)pCmdDone->InformationBuffer;
+                mbim_dump_subscriber_status(pInfo, direction);    
+            }
+            else if (pCmdDone->CID == MBIM_CID_REGISTER_STATE) {
+                MBIM_REGISTRATION_STATE_INFO_T *pInfo = (MBIM_REGISTRATION_STATE_INFO_T *)pCmdDone->InformationBuffer;
+                mbim_dump_regiester_status(pInfo, direction);
+            }
+        }
+    }
+    else if (pMsg->MessageType == MBIM_INDICATE_STATUS_MSG) {
+        MBIM_INDICATE_STATUS_MSG_T *pIndMsg = (MBIM_INDICATE_STATUS_MSG_T *)pMsg;
+        
+        mbim_dump_indicate_msg(pIndMsg, direction);
+        if (!memcmp(pIndMsg->DeviceServiceId.uuid, str2uuid(UUID_BASIC_CONNECT), 16)) {
+            if (pIndMsg->CID == MBIM_CID_CONNECT) {
+                MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pIndMsg->InformationBuffer;
+                mbim_dump_connect(pInfo, direction);
+            }
+            else if (pIndMsg->CID == MBIM_CID_SIGNAL_STATE) {
+                MBIM_SIGNAL_STATE_INFO_T *pInfo = (MBIM_SIGNAL_STATE_INFO_T *)pIndMsg->InformationBuffer;
+                mbim_dump_signal_state(pInfo, direction);
+            }
+            else if (pIndMsg->CID == MBIM_CID_SUBSCRIBER_READY_STATUS) {
+                MBIM_SUBSCRIBER_READY_STATUS_T *pInfo = (MBIM_SUBSCRIBER_READY_STATUS_T *)pIndMsg->InformationBuffer;
+                mbim_dump_subscriber_status(pInfo, direction);    
+            }
+            else if (pIndMsg->CID == MBIM_CID_REGISTER_STATE) {
+                MBIM_REGISTRATION_STATE_INFO_T *pInfo = (MBIM_REGISTRATION_STATE_INFO_T *)pIndMsg->InformationBuffer;
+                mbim_dump_regiester_status(pInfo, direction);
+            }
+            else if (pIndMsg->CID == MBIM_CID_PACKET_SERVICE) {
+                MBIM_PACKET_SERVICE_INFO_T *pInfo = (MBIM_PACKET_SERVICE_INFO_T *)pIndMsg->InformationBuffer;
+                mbim_dump_packet_service(pInfo, direction);
+            }
+        }
+    }
+    else if (pMsg->MessageType == MBIM_FUNCTION_ERROR_MSG) {
+        MBIM_FUNCTION_ERROR_MSG_T *pErrMsg = (MBIM_FUNCTION_ERROR_MSG_T*)pMsg;
+        mbim_debug("%s ErrorStatusCode = %u", direction, pErrMsg->ErrorStatusCode);
+    }
+}
+
+static void mbim_recv_command(MBIM_MESSAGE_HEADER *pResponse, unsigned size)
+{    
+    pthread_mutex_lock(&mbim_command_mutex);
+
+    if (pResponse)
+        mbim_dump(pResponse, mbim_verbose);
+    
+    if (pResponse == NULL) {
+        pthread_cond_signal(&mbim_command_cond);
+    }
+    else if (mbim_pRequest && mbim_pRequest->TransactionId == pResponse->TransactionId) {
+        mbim_pResponse = mbim_alloc(pResponse->MessageLength);
+        if (mbim_pResponse)
+            memcpy(mbim_pResponse, pResponse, pResponse->MessageLength);
+        pthread_cond_signal(&mbim_command_cond);
+    }
+    else if (pResponse->MessageType ==  MBIM_INDICATE_STATUS_MSG) {
+        MBIM_INDICATE_STATUS_MSG_T *pIndMsg = (MBIM_INDICATE_STATUS_MSG_T *)pResponse;
+
+        if (!memcmp(pIndMsg->DeviceServiceId.uuid, str2uuid(UUID_BASIC_CONNECT), 16)) {
+            if (pIndMsg->CID == MBIM_CID_CONNECT) {
+                MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pIndMsg->InformationBuffer;
+                notify_state_chage(ActivationState, pInfo->ActivationState);
+            }
+            else if (pIndMsg->CID == MBIM_CID_SUBSCRIBER_READY_STATUS) {
+                MBIM_SUBSCRIBER_READY_STATUS_T *pInfo = (MBIM_SUBSCRIBER_READY_STATUS_T *)pIndMsg->InformationBuffer;
+                notify_state_chage(ReadyState, pInfo->ReadyState);
+            }
+            else if (pIndMsg->CID == MBIM_CID_REGISTER_STATE) {
+                MBIM_REGISTRATION_STATE_INFO_T *pInfo = (MBIM_REGISTRATION_STATE_INFO_T *)pIndMsg->InformationBuffer;
+                notify_state_chage(RegisterState, pInfo->RegisterState);
+            }
+            else if (pIndMsg->CID == MBIM_CID_PACKET_SERVICE) {
+                MBIM_PACKET_SERVICE_INFO_T *pInfo = (MBIM_PACKET_SERVICE_INFO_T *)pIndMsg->InformationBuffer;
+                notify_state_chage(PacketServiceState, pInfo->PacketServiceState);
+            }
+            else if (pIndMsg->CID == MBIM_CID_CONNECT) {
+                MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pIndMsg->InformationBuffer;
+                if (pInfo->ActivationState == MBIMActivationStateDeactivated || pInfo->ActivationState == MBIMActivationStateDeactivating)
+                    mbim_ifconfig(4, mbim_netcard, NULL, NULL, NULL, NULL, 0, 0);
+            }
+        }
+   }
+
+    pthread_mutex_unlock(&mbim_command_mutex);
+}
+
+static int mbim_send_command(MBIM_MESSAGE_HEADER *pRequest, MBIM_COMMAND_DONE_T **ppCmdDone, unsigned msecs) {
+    int ret;
+
+    if (ppCmdDone)
+         *ppCmdDone = NULL;
+
+    if (mbim_fd <= 0)
+        return -ENODEV;
+
+    if (!pRequest)
+        return -ENOMEM;
+
+    pthread_mutex_lock(&mbim_command_mutex);
+
+    if (pRequest)
+        mbim_dump(pRequest, mbim_verbose);
+
+    mbim_pRequest = pRequest;
+    mbim_pResponse = NULL;
+
+    ret = write(mbim_fd, pRequest, pRequest->MessageLength);
+
+    if (ret == pRequest->MessageLength) {
+        ret = pthread_cond_timeout_np(&mbim_command_cond, &mbim_command_mutex, msecs);
+        if (!ret) {
+            if (mbim_pResponse && ppCmdDone) {
+                *ppCmdDone = (MBIM_COMMAND_DONE_T *)mbim_pResponse;
+            }
+        }
+    } else {
+        mbim_debug("%s pthread_cond_timeout_np=%d", __func__, ret);
+    }
+
+    mbim_pRequest = mbim_pResponse = NULL;
+
+    pthread_mutex_unlock(&mbim_command_mutex);
+
+    return ret;
+}
+
+static UINT32 mbim_recv_buf[1024];
+static void * mbim_read_thread(void *param) {
+    mbim_debug("%s is created", __func__); 
+
+    while (mbim_fd > 0) {
+        struct pollfd pollfds[] = {{mbim_fd, POLLIN, 0}};
+        int ne, ret, nevents = 1;
+
+        ret = poll(pollfds, nevents, -1);
+
+        if (ret <= 0) {
+            if (mbim_quit == 0) mbim_debug("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
+            break;
+        }
+
+        for (ne = 0; ne < nevents; ne++) {
+            int fd = pollfds[ne].fd;
+            short revents = pollfds[ne].revents;
+
+            if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
+                mbim_debug("%s poll err/hup/inval", __func__);
+                mbim_debug("epoll fd = %d, events = 0x%04x", fd, revents);
+                if (revents & (POLLERR | POLLHUP | POLLNVAL))
+                goto __quit;
+            }
+
+            if ((revents & POLLIN) == 0)
+                continue;
+
+            if (mbim_fd == fd) {
+                ssize_t nreads;
+                MBIM_MESSAGE_HEADER *pResponse = (MBIM_MESSAGE_HEADER *) mbim_recv_buf;
+
+                nreads = read(fd, pResponse, sizeof(mbim_recv_buf));
+                if (nreads <= 0) {
+                    mbim_debug("%s read=%d errno: %d (%s)",  __func__, (int)nreads, errno, strerror(errno));
+                    break;
+                }
+
+                mbim_recv_command(pResponse, nreads);
+            }
+        }
+    }
+
+__quit:
+    read_tid = 0;
+    mbim_quit++;
+    mbim_recv_command(NULL, 0);
+    mbim_debug("%s exit", __func__);
+    _notify_state_chage();
+
+    return NULL;
+}
+
+static int mbim_status_code(MBIM_MESSAGE_HEADER *pMsgHdr) {
+    int status = 0;
+
+    if (!pMsgHdr)
+        return 0;
+
+    if (pMsgHdr->MessageType == MBIM_OPEN_DONE) {
+        MBIM_OPEN_DONE_T *pOpenDone = (MBIM_OPEN_DONE_T *)pMsgHdr;
+        status = pOpenDone->Status;
+    }
+    else if (pMsgHdr->MessageType == MBIM_CLOSE_DONE) {
+        MBIM_CLOSE_DONE_T *pCloseDone = (MBIM_CLOSE_DONE_T *)pMsgHdr;
+        status = pCloseDone->Status;
+    }
+    else  if (pMsgHdr->MessageType == MBIM_COMMAND_DONE) {
+        MBIM_COMMAND_DONE_T *pCmdDone = (MBIM_COMMAND_DONE_T *)pMsgHdr;
+        status = pCmdDone->Status;
+    }
+    else  if (pMsgHdr->MessageType == MBIM_FUNCTION_ERROR_MSG) {
+        MBIM_FUNCTION_ERROR_MSG_T *pErrMsg = (MBIM_FUNCTION_ERROR_MSG_T *)pMsgHdr;
+        status = pErrMsg->ErrorStatusCode;
+        if (pErrMsg->ErrorStatusCode == MBIM_ERROR_NOT_OPENED)
+            mbim_open_state = 0; //EM06ELAR03A05M4G when suspend/resume, may get this error
+    }
+
+    return status;
+}
+
+#define mbim_check_err(err, pRequest, pCmdDone) do { \
+    int _status = mbim_status_code(&pCmdDone->MessageHeader); \
+    if ((err || _status) && pCmdDone) { mbim_dump(&pCmdDone->MessageHeader, (mbim_verbose == 0)); } \
+    if (err || _status) {mbim_debug("%s:%d err=%d, Status=%d", __func__, __LINE__, err, _status); } \
+    if (err || pCmdDone == NULL) { mbim_free(pRequest); mbim_free(pCmdDone); return (err ? err : 8888);} \
+} while(0)
+
+/* 
+ * MBIM device can be open repeatly without error
+ * So, we can call the function, no matter it have been opened or not
+ */
+static int mbim_open_device(uint32_t MaxControlTransfer) {
+    MBIM_MESSAGE_HEADER *pRequest = NULL;
+    MBIM_OPEN_DONE_T *pOpenDone = NULL;
+    int err;
+
+    mbim_debug("%s()", __func__);
+    pRequest = compose_open_command(MaxControlTransfer);
+    err = mbim_send_command(pRequest, (MBIM_COMMAND_DONE_T **)&pOpenDone, mbim_default_timeout);
+    mbim_check_err(err, pRequest, pOpenDone);
+
+    err = pOpenDone->Status;
+    mbim_free(pRequest); mbim_free(pOpenDone);
+
+    return err;
+}
+
+static int mbim_close_device(void) {
+    MBIM_MESSAGE_HEADER *pRequest = NULL;
+    MBIM_CLOSE_DONE_T *pCloseDone = NULL;
+    int err = 0;
+
+    mbim_debug("%s()", __func__);
+    pRequest = compose_close_command();
+    err = mbim_send_command(pRequest, (MBIM_COMMAND_DONE_T **)&pCloseDone, mbim_default_timeout);
+    mbim_check_err(err, pRequest, pCloseDone);
+    
+    err = pCloseDone->Status;
+    mbim_free(pRequest); mbim_free(pCloseDone);
+
+    return err;
+}
+
+static int mbim_query_connect(int sessionID) {
+    MBIM_MESSAGE_HEADER *pRequest = NULL;
+    MBIM_COMMAND_DONE_T *pCmdDone = NULL;
+    MBIM_SET_CONNECT_T set_connect;
+    int err;
+
+    mbim_debug("%s(sessionID=%d)", __func__, sessionID);
+    set_connect.SessionId = htole32(sessionID);
+    pRequest = compose_basic_connect_command(MBIM_CID_CONNECT, MBIM_CID_CMD_TYPE_QUERY, &set_connect, sizeof(set_connect));
+    err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout);
+    mbim_check_err(err, pRequest, pCmdDone);
+    
+    if (pCmdDone->InformationBufferLength)
+    {
+        MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pCmdDone->InformationBuffer;
+        ActivationState = pInfo->ActivationState;
+    }
+    mbim_free(pRequest); mbim_free(pCmdDone);
+    return err;
+}
+
+static int mbim_device_caps_query(void) {
+    MBIM_MESSAGE_HEADER *pRequest = NULL;
+    MBIM_COMMAND_DONE_T *pCmdDone = NULL;
+    int err;
+
+    mbim_debug("%s()", __func__);
+    pRequest = compose_basic_connect_command(MBIM_CID_DEVICE_CAPS, MBIM_CID_CMD_TYPE_QUERY, NULL, 0);
+    err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout);
+    mbim_check_err(err, pRequest, pCmdDone);
+
+    if (pCmdDone->InformationBufferLength) {
+         MBIM_DEVICE_CAPS_INFO_T *pInfo = (MBIM_DEVICE_CAPS_INFO_T *)pCmdDone->InformationBuffer;
+         char tmp[32];
+         UINT32 i;
+
+         if (pInfo->DeviceIdOffset && pInfo->DeviceIdSize) {
+            for (i = 0; i < 32 && i < (pInfo->DeviceIdSize/2); i++)
+                tmp[i] = pInfo->DataBuffer[pInfo->DeviceIdOffset - sizeof(MBIM_DEVICE_CAPS_INFO_T) + i*2];
+             tmp[i] = '\0';
+            mbim_debug("DeviceId:     %s", tmp);
+         }
+         if (pInfo->FirmwareInfoOffset && pInfo->FirmwareInfoSize) {
+            for (i = 0; i < 32 && i < (pInfo->FirmwareInfoSize/2); i++)
+                tmp[i] = pInfo->DataBuffer[pInfo->FirmwareInfoOffset - sizeof(MBIM_DEVICE_CAPS_INFO_T) + i*2];
+             tmp[i] = '\0';
+            mbim_debug("FirmwareInfo: %s", tmp);
+         }
+         if (pInfo->HardwareInfoOffset && pInfo->HardwareInfoSize) {
+            for (i = 0; i < 32 && i < (pInfo->HardwareInfoSize/2); i++)
+                tmp[i] = pInfo->DataBuffer[pInfo->HardwareInfoOffset - sizeof(MBIM_DEVICE_CAPS_INFO_T) + i*2];
+             tmp[i] = '\0';
+            mbim_debug("HardwareInfo: %s", tmp);
+         }
+    }    
+    mbim_free(pRequest); mbim_free(pCmdDone);
+    return err;
+}
+
+static int mbim_subscriber_status_query(void) {
+    MBIM_MESSAGE_HEADER *pRequest = NULL;
+    MBIM_COMMAND_DONE_T *pCmdDone = NULL;
+    int err;
+
+    mbim_debug("%s()", __func__);
+    pRequest = compose_basic_connect_command(MBIM_CID_SUBSCRIBER_READY_STATUS, MBIM_CID_CMD_TYPE_QUERY, NULL, 0);
+    err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout);
+    mbim_check_err(err, pRequest, pCmdDone);
+
+    if (pCmdDone->InformationBufferLength) {
+         MBIM_SUBSCRIBER_READY_STATUS_T *pInfo = (MBIM_SUBSCRIBER_READY_STATUS_T *)pCmdDone->InformationBuffer;
+        ReadyState = pInfo->ReadyState;
+    }    
+    mbim_free(pRequest); mbim_free(pCmdDone);
+    return err;
+}
+
+static int mbim_register_state_query(void) {
+    MBIM_MESSAGE_HEADER *pRequest = NULL;
+    MBIM_COMMAND_DONE_T *pCmdDone = NULL;
+    int err;
+
+    mbim_debug("%s()", __func__);
+    pRequest = compose_basic_connect_command(MBIM_CID_REGISTER_STATE, MBIM_CID_CMD_TYPE_QUERY, NULL, 0);
+    err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout);
+    mbim_check_err(err, pRequest, pCmdDone);
+
+    if (pCmdDone->InformationBufferLength) {
+        MBIM_REGISTRATION_STATE_INFO_T *pInfo = (MBIM_REGISTRATION_STATE_INFO_T *)pCmdDone->InformationBuffer;;
+        RegisterState = pInfo->RegisterState;
+    }    
+    mbim_free(pRequest); mbim_free(pCmdDone);
+    return err;
+}
+
+static int mbim_packet_service_query(void) {
+    MBIM_MESSAGE_HEADER *pRequest = NULL;
+    MBIM_COMMAND_DONE_T *pCmdDone = NULL;
+    int err;
+
+    mbim_debug("%s()", __func__);
+    pRequest = compose_basic_connect_command(MBIM_CID_PACKET_SERVICE, MBIM_CID_CMD_TYPE_QUERY, NULL, 0);
+    err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout);
+    mbim_check_err(err, pRequest, pCmdDone);
+
+    if (pCmdDone->InformationBufferLength) {
+        MBIM_PACKET_SERVICE_INFO_T *pInfo = (MBIM_PACKET_SERVICE_INFO_T *)pCmdDone->InformationBuffer;
+        PacketServiceState = pInfo->PacketServiceState;
+    }
+    mbim_free(pRequest); mbim_free(pCmdDone);
+    return err;
+}
+
+static int mbim_packet_service_set(void) {
+    MBIM_MESSAGE_HEADER *pRequest = NULL;
+    MBIM_COMMAND_DONE_T *pCmdDone = NULL;
+    int err;
+    
+    mbim_debug("%s()", __func__);
+    UINT32 PacketServiceAction =  MBIMPacketServiceActionAttach;
+    pRequest = compose_basic_connect_command(MBIM_CID_PACKET_SERVICE, MBIM_CID_CMD_TYPE_SET, &PacketServiceAction, sizeof(PacketServiceAction));
+    err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout);
+    mbim_check_err(err, pRequest, pCmdDone);
+
+    if (pCmdDone->InformationBufferLength) {
+        MBIM_PACKET_SERVICE_INFO_T *pInfo = (MBIM_PACKET_SERVICE_INFO_T *)pCmdDone->InformationBuffer;
+        PacketServiceState = pInfo->PacketServiceState;
+    }
+    mbim_free(pRequest); mbim_free(pCmdDone);
+    return err;
+}
+
+static int mbim_populate_connect_data(MBIM_SET_CONNECT_T **connect_req_ptr) {
+    int offset;
+    int buflen = 0;
+    int i;
+
+    if (mbim_apn) buflen += 2*strlen(mbim_apn);
+    if (mbim_user) buflen += 2*strlen(mbim_user);
+    if (mbim_passwd) buflen += 2*strlen(mbim_passwd);
+
+    *connect_req_ptr = (MBIM_SET_CONNECT_T*)malloc(sizeof(MBIM_SET_CONNECT_T) + buflen);
+    if (! *connect_req_ptr) {
+        mbim_debug("not enough memory\n");
+        return -1;
+    }
+    memset(*connect_req_ptr, 0, sizeof(MBIM_SET_CONNECT_T) + buflen);
+
+    offset = 0;
+    if (mbim_apn && strlen(mbim_apn) > 0) {
+        (*connect_req_ptr)->AccessStringSize = htole32(2*strlen(mbim_apn));
+        (*connect_req_ptr)->AccessStringOffset = htole32(offset + sizeof(MBIM_SET_CONNECT_T));
+        for (i = 0; i < strlen(mbim_apn); i++) {
+            (*connect_req_ptr)->DataBuffer[offset + i*2] = mbim_apn[i];
+            (*connect_req_ptr)->DataBuffer[offset + i*2 + 1] = 0;
+        }
+    }
+
+    offset += (*connect_req_ptr)->AccessStringSize;
+    if (mbim_user && strlen(mbim_user) > 0) {
+        (*connect_req_ptr)->UserNameSize = htole32(2*strlen(mbim_user));
+        (*connect_req_ptr)->UserNameOffset = htole32(offset + sizeof(MBIM_SET_CONNECT_T));
+        for (i = 0; i < strlen(mbim_user); i++) {
+            (*connect_req_ptr)->DataBuffer[offset + i*2] = mbim_user[i];
+            (*connect_req_ptr)->DataBuffer[offset + i*2 + 1] = 0;
+        }
+    }
+
+    offset += (*connect_req_ptr)->UserNameSize;
+    if (mbim_passwd && strlen(mbim_passwd) > 0) {
+        (*connect_req_ptr)->PasswordSize = htole32(2*strlen(mbim_passwd));
+        (*connect_req_ptr)->PasswordOffset = htole32(offset + sizeof(MBIM_SET_CONNECT_T));
+        for (i = 0; i < strlen(mbim_passwd); i++) {
+            (*connect_req_ptr)->DataBuffer[offset + i*2] = mbim_passwd[i];
+            (*connect_req_ptr)->DataBuffer[offset + i*2 + 1] = 0;
+        }
+    }
+
+    return buflen;
+}
+
+static int mbim_set_connect(int onoff, int sessionID) {
+    MBIM_MESSAGE_HEADER *pRequest = NULL;
+    MBIM_COMMAND_DONE_T *pCmdDone = NULL;
+    MBIM_SET_CONNECT_T *set_connect = NULL;
+    int err;
+    
+    mbim_debug("%s(onoff=%d, sessionID=%d)", __func__, onoff, sessionID);
+    /* alloc memory then populate APN USERNAME PASSWORD */
+    int buflen = mbim_populate_connect_data(&set_connect);
+    if (buflen < 0) {
+        return ENOMEM;
+     }
+
+    set_connect->SessionId = htole32(sessionID);
+    if (onoff == 0)
+        set_connect->ActivationCommand = htole32(MBIMActivationCommandDeactivate);
+    else
+        set_connect->ActivationCommand = htole32(MBIMActivationCommandActivate);
+        
+    set_connect->Compression = htole32(MBIMCompressionNone);
+    set_connect->AuthProtocol = htole32(mbim_auth);
+    set_connect->IPType = htole32(mbim_iptype);
+    memcpy(set_connect->ContextType.uuid, str2uuid(UUID_MBIMContextTypeInternet), 16);
+    
+    pRequest = compose_basic_connect_command(MBIM_CID_CONNECT, MBIM_CID_CMD_TYPE_SET, set_connect, sizeof(MBIM_SET_CONNECT_T) + buflen);        
+    mbim_free(set_connect);
+    err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout*10);
+    mbim_check_err(err, pRequest, pCmdDone);
+
+    if (pCmdDone->InformationBufferLength) {
+        MBIM_CONNECT_T *pInfo = (MBIM_CONNECT_T *)pCmdDone->InformationBuffer;
+        ActivationState = pInfo->ActivationState;
+    }
+
+    mbim_free(pRequest); mbim_free(pCmdDone);
+    return err;
+}
+
+static int mbim_ip_config(int sessionID) {
+    MBIM_MESSAGE_HEADER *pRequest = NULL;
+    MBIM_COMMAND_DONE_T *pCmdDone = NULL;
+    MBIM_IP_CONFIGURATION_INFO_T ip_info;
+    int err;
+
+    mbim_debug("%s(sessionID=%d)", __func__, sessionID);
+    ip_info.SessionId = htole32(sessionID);
+    pRequest = compose_basic_connect_command(MBIM_CID_IP_CONFIGURATION, MBIM_CID_CMD_TYPE_QUERY, &ip_info, sizeof(ip_info));
+    err = mbim_send_command(pRequest, &pCmdDone, mbim_default_timeout);
+    mbim_check_err(err, pRequest, pCmdDone);
+
+    if (pCmdDone->InformationBufferLength) {
+        UINT8 prefix, *ipv4=NULL, *ipv6=NULL, *gw=NULL, *dns1=NULL, *dns2=NULL;
+        UINT32 mtu = 1500;
+        MBIM_IP_CONFIGURATION_INFO_T *pInfo = (MBIM_IP_CONFIGURATION_INFO_T *)pCmdDone->InformationBuffer;
+
+        if (mbim_verbose == 0) mbim_dump_ipconfig(pInfo, "<");
+
+        /* IPv4 network configration */
+        if (pInfo->IPv4ConfigurationAvailable&0x1) {
+            MBIM_IPV4_ELEMENT_T *pAddress = (MBIM_IPV4_ELEMENT_T *)(&pInfo->DataBuffer[pInfo->IPv4AddressOffset-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]);
+            prefix = pAddress->OnLinkPrefixLength;
+            ipv4 = pAddress->IPv4Address;
+
+            if (pInfo->IPv4ConfigurationAvailable&0x2)
+                gw = (UINT8 *)(&pInfo->DataBuffer[pInfo->IPv4GatewayOffset-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]);
+
+            if (pInfo->IPv4ConfigurationAvailable&0x4) {
+                dns1 = (UINT8 *)(&pInfo->DataBuffer[pInfo->IPv4DnsServerOffset-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]);
+                if (pInfo->IPv4DnsServerCount == 2)
+                    dns2 = dns1 + 4;
+            }
+
+            if (pInfo->IPv4ConfigurationAvailable&0x8)
+                mtu =  pInfo->IPv4Mtu;
+
+            mbim_ifconfig(4, mbim_netcard, ipv4, gw, dns1, dns2, prefix, mtu);
+        }
+
+        /* IPv6 network configration */
+        if (pInfo->IPv6ConfigurationAvailable&0x1) {
+            MBIM_IPV6_ELEMENT_T *pAddress = (MBIM_IPV6_ELEMENT_T *)(&pInfo->DataBuffer[pInfo->IPv6AddressOffset-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]);
+            prefix = pAddress->OnLinkPrefixLength;
+            ipv6 = pAddress->IPv6Address;
+
+            if (pInfo->IPv6ConfigurationAvailable&0x2)
+                gw = (UINT8 *)(&pInfo->DataBuffer[pInfo->IPv6GatewayOffset-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]);
+
+            if (pInfo->IPv6ConfigurationAvailable&0x4) {
+                dns1 = (UINT8 *)(&pInfo->DataBuffer[pInfo->IPv6DnsServerOffset-sizeof(MBIM_IP_CONFIGURATION_INFO_T)]);
+                if (pInfo->IPv6DnsServerCount == 2)
+                    dns2 = dns1 + 16;
+            }
+
+            if (pInfo->IPv6ConfigurationAvailable&0x8)
+                mtu =  pInfo->IPv6Mtu;
+
+            mbim_ifconfig(6, mbim_netcard, ipv6, gw, dns1, dns2, prefix, mtu);
+        }
+    }
+    return err;
+}
+
+static void meig_sigaction(int signo) {
+    mbim_debug("MBIM catch signo %d", signo);
+    if (SIGTERM == signo || SIGHUP == signo || SIGINT == signo) {
+        mbim_quit++;
+        _notify_state_chage();
+    }
+}
+
+static void mbim_reset_state(void) {
+    ReadyState = oldReadyState = MBIMSubscriberReadyStateNotInitialized;
+    RegisterState = oldRegisterState = MBIMRegisterStateUnknown;
+    PacketServiceState = oldPacketServiceState = MBIMPacketServiceStateUnknown;
+    ActivationState = oldActivationState = MBIMActivationStateUnknown;
+}
+
+static int mbim_update_state(void) {
+    int chages = 0;
+    
+    if (oldReadyState != ReadyState) {
+        mbim_debug("SubscriberReadyState %s -> %s ", MBIMSubscriberReadyStateStr(oldReadyState), MBIMSubscriberReadyStateStr(ReadyState));
+        oldReadyState = ReadyState; chages++;
+    }
+    if (oldRegisterState != RegisterState) {
+        mbim_debug("RegisterState %s -> %s ", MBIMRegisterStateStr(oldRegisterState), MBIMRegisterStateStr(RegisterState));
+        oldRegisterState = RegisterState; chages++;
+    }
+    if (oldPacketServiceState != PacketServiceState) {
+        mbim_debug("PacketServiceState %s -> %s ", MBIMPacketServiceStateStr(oldPacketServiceState), MBIMPacketServiceStateStr(PacketServiceState));
+        oldPacketServiceState = PacketServiceState; chages++;
+    } 
+    if (ActivationState != oldActivationState) {
+        mbim_debug("ActivationState %s -> %s ", MBIMActivationStateStr(oldActivationState), MBIMActivationStateStr(ActivationState));
+        oldActivationState = ActivationState; chages++;
+    }
+
+    return chages;
+}
+
+int mbim_main(PROFILE_T *profile) 
+{
+    int retval;
+    int sessionID = 0;
+
+    signal(SIGINT, meig_sigaction);
+    signal(SIGTERM, meig_sigaction);
+    signal(SIGHUP, meig_sigaction);
+    if (profile->qmichannel)
+        mbim_dev = profile->qmichannel;
+    if (profile->usbnet_adapter)
+        mbim_netcard = profile->usbnet_adapter;
+    if (profile->apn)
+        mbim_apn = profile->apn;
+    if (profile->user)
+        mbim_user = profile->user;
+    if (profile->password)
+        mbim_passwd = profile->password;
+    if (profile->auth)
+        mbim_auth = profile->auth;
+    if (profile->ipv4_flag)
+        mbim_iptype = MBIMContextIPTypeIPv4;
+    if (profile->ipv6_flag)
+        mbim_iptype = MBIMContextIPTypeIPv6;
+    if (profile->ipv4_flag && profile->ipv6_flag)
+        mbim_iptype = MBIMContextIPTypeIPv4AndIPv6;
+    mbim_verbose = debug_qmi;
+    mbim_debug("apn %s, user %s, passwd %s, auth %d", mbim_apn, mbim_user, mbim_passwd, mbim_auth);
+    mbim_debug("IP Proto %s", MBIMContextIPTypeStr(mbim_iptype))
+
+    /* set relative time, for pthread_cond_timedwait */
+    cond_setclock_attr(&mbim_state_cond, CLOCK_MONOTONIC);
+    cond_setclock_attr(&mbim_command_cond, CLOCK_MONOTONIC);
+
+    mbim_fd = open(mbim_dev, O_RDWR | O_NONBLOCK | O_NOCTTY);
+    if (mbim_fd  <= 0) {
+        mbim_debug("fail to open (%s), errno: %d (%s)", mbim_dev, errno, strerror(errno));
+        goto exit;
+    }
+    pthread_create(&read_tid, NULL, mbim_read_thread, (void *)mbim_dev);
+    mbim_open_state = 0;
+    
+    while (mbim_quit == 0 && read_tid != 0) {
+        uint32_t wait_time = 24*60*60;
+        
+        if (mbim_open_state == 0) {
+            mbim_ifconfig(4, mbim_netcard, NULL, NULL, NULL, NULL, 0, 0);
+            TransactionId = 1;
+            retval = mbim_open_device(4096);
+            if (retval) goto exit;
+            mbim_open_state = 1;
+            mbim_reset_state();
+            retval = mbim_device_caps_query();
+            if (retval) goto exit;
+        }
+        
+        if (ReadyState != MBIMSubscriberReadyStateInitialized) {
+            retval = mbim_subscriber_status_query();
+            if (retval) goto exit;
+            mbim_update_state();
+        }
+        if (ReadyState != MBIMSubscriberReadyStateInitialized) goto _wait_state_change;
+
+        if (RegisterState != MBIMRegisterStateHome && RegisterState != MBIMRegisterStateRoaming) {
+            retval = mbim_register_state_query();
+            if (retval) goto exit;
+            mbim_update_state();
+        }
+        if (RegisterState != MBIMRegisterStateHome && RegisterState != MBIMRegisterStateRoaming)  goto _wait_state_change;
+            
+        if (PacketServiceState != MBIMPacketServiceStateAttached) {
+            retval = mbim_packet_service_query();
+            if (retval) goto exit;
+            mbim_update_state();
+            if (PacketServiceState == MBIMPacketServiceStateDetached) {
+                retval = mbim_packet_service_set(); //at+cgatt=0/1
+                if (retval) goto exit;
+            }
+            mbim_update_state();
+        }
+        if (PacketServiceState != MBIMPacketServiceStateAttached) goto _wait_state_change;
+
+        if (ActivationState == MBIMActivationStateUnknown) {
+            retval = mbim_query_connect(sessionID);
+            if (retval) goto exit;
+            mbim_update_state();
+        }
+
+        if (ActivationState != MBIMActivationStateActivated && ActivationState != MBIMActivationStateActivating) {
+            retval = mbim_set_connect(1, sessionID);
+            if (retval) goto exit;
+            mbim_update_state();
+            if (ActivationState == MBIMActivationStateActivated) {
+                retval = mbim_ip_config(sessionID);
+                if (retval) goto exit;
+                mbim_update_state();
+            }
+            else {
+                wait_time = 30; //retry call mbim_set_connect 30 seconds later
+            }
+        }   
+        if (ActivationState != MBIMActivationStateActivated) goto _wait_state_change;
+    
+_wait_state_change:
+        wait_state_change(wait_time);
+        do {
+            mbim_update_state();
+        } while (mbim_quit == 0 && read_tid != 0 && wait_state_change(1) != ETIMEDOUT);
+    }
+
+exit:
+    if (read_tid > 0) {
+        if (ActivationState == MBIMActivationStateActivated || ActivationState == MBIMActivationStateActivating)
+            mbim_set_connect(0, sessionID);
+        mbim_close_device();
+        pthread_kill(read_tid, SIGTERM);
+        pthread_join(read_tid, NULL);
+        mbim_ifconfig(4, mbim_netcard, NULL, NULL, NULL, NULL, 0, 0);
+    }
+
+    if (mbim_fd > 0) {
+        close(mbim_fd);
+    }
+    pthread_mutex_destroy(&mbim_command_mutex);
+    pthread_mutex_destroy(&mbim_state_mutex);
+    pthread_cond_destroy(&mbim_command_cond);
+    pthread_cond_destroy(&mbim_state_cond);
+
+    mbim_debug("MBIM CM exit...\n");
+    return 0;    
+}
diff --git a/package/wwan/meig-cm/src/meig-cm b/package/wwan/meig-cm/src/meig-cm
new file mode 100644
index 000000000..0239c0c6b
Binary files /dev/null and b/package/wwan/meig-cm/src/meig-cm differ
diff --git a/package/wwan/meig-cm/src/meig-qmi-proxy b/package/wwan/meig-cm/src/meig-qmi-proxy
new file mode 100644
index 000000000..4d3626ae5
Binary files /dev/null and b/package/wwan/meig-cm/src/meig-qmi-proxy differ
diff --git a/package/wwan/meig-cm/src/meig-qmi-proxy.c b/package/wwan/meig-cm/src/meig-qmi-proxy.c
new file mode 100644
index 000000000..688933e4a
--- /dev/null
+++ b/package/wwan/meig-cm/src/meig-qmi-proxy.c
@@ -0,0 +1,1277 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/epoll.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <linux/un.h>
+#include <dirent.h>
+#include <signal.h>
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+
+#define cpu_to_le16(x) (x)
+#define cpu_to_le32(x) (x)
+#define le16_to_cpu(x) (x)
+#define le32_to_cpu(x) (x)
+
+#define dprintf(fmt, arg...) do { printf(fmt, ##arg); } while(0)
+#define SYSCHECK(c) do{if((c)<0) {dprintf("%s %d error: '%s' (code: %d)\n", __func__, __LINE__, strerror(errno), errno); return -1;}}while(0)
+#define cfmakenoblock(fd) do{fcntl(fd, F_SETFL, fcntl(fd,F_GETFL) | O_NONBLOCK);}while(0)
+
+typedef struct _QCQMI_HDR
+{
+   uint8_t  IFType;
+   uint16_t Length;
+   uint8_t  CtlFlags;  // reserved
+   uint8_t  QMIType;
+   uint8_t  ClientId;
+} __attribute__ ((packed)) QCQMI_HDR, *PQCQMI_HDR;
+
+typedef struct _QMICTL_SYNC_REQ_MSG
+{
+   uint8_t  CtlFlags;        // QMICTL_FLAG_REQUEST
+   uint8_t  TransactionId;
+   uint16_t QMICTLType;      // QMICTL_CTL_SYNC_REQ
+   uint16_t Length;          // 0
+} __attribute__ ((packed)) QMICTL_SYNC_REQ_MSG, *PQMICTL_SYNC_REQ_MSG;
+
+typedef struct _QMICTL_SYNC_RESP_MSG
+{
+   uint8_t  CtlFlags;        // QMICTL_FLAG_RESPONSE
+   uint8_t  TransactionId;
+   uint16_t QMICTLType;      // QMICTL_CTL_SYNC_RESP
+   uint16_t Length;
+   uint8_t  TLVType;         // QCTLV_TYPE_RESULT_CODE
+   uint16_t TLVLength;       // 0x0004
+   uint16_t QMIResult;
+   uint16_t QMIError;
+} __attribute__ ((packed)) QMICTL_SYNC_RESP_MSG, *PQMICTL_SYNC_RESP_MSG;
+
+typedef struct _QMICTL_SYNC_IND_MSG
+{
+   uint8_t  CtlFlags;        // QMICTL_FLAG_INDICATION
+   uint8_t  TransactionId;
+   uint16_t QMICTLType;      // QMICTL_REVOKE_CLIENT_ID_IND
+   uint16_t Length;
+} __attribute__ ((packed)) QMICTL_SYNC_IND_MSG, *PQMICTL_SYNC_IND_MSG;
+
+typedef struct _QMICTL_GET_CLIENT_ID_REQ_MSG
+{
+   uint8_t  CtlFlags;        // QMICTL_FLAG_REQUEST
+   uint8_t  TransactionId;
+   uint16_t QMICTLType;      // QMICTL_GET_CLIENT_ID_REQ
+   uint16_t Length;
+   uint8_t  TLVType;         // QCTLV_TYPE_REQUIRED_PARAMETER
+   uint16_t TLVLength;       // 1
+   uint8_t  QMIType;         // QMUX type
+} __attribute__ ((packed)) QMICTL_GET_CLIENT_ID_REQ_MSG, *PQMICTL_GET_CLIENT_ID_REQ_MSG;
+
+typedef struct _QMICTL_GET_CLIENT_ID_RESP_MSG
+{
+   uint8_t  CtlFlags;        // QMICTL_FLAG_RESPONSE
+   uint8_t  TransactionId;
+   uint16_t QMICTLType;      // QMICTL_GET_CLIENT_ID_RESP
+   uint16_t Length;
+   uint8_t  TLVType;         // QCTLV_TYPE_RESULT_CODE
+   uint16_t TLVLength;       // 0x0004
+   uint16_t QMIResult;       // result code
+   uint16_t QMIError;        // error code
+   uint8_t  TLV2Type;        // QCTLV_TYPE_REQUIRED_PARAMETER
+   uint16_t TLV2Length;      // 2
+   uint8_t  QMIType;
+   uint8_t  ClientId;
+} __attribute__ ((packed)) QMICTL_GET_CLIENT_ID_RESP_MSG, *PQMICTL_GET_CLIENT_ID_RESP_MSG;
+
+typedef struct _QMICTL_RELEASE_CLIENT_ID_REQ_MSG
+{
+   uint8_t  CtlFlags;        // QMICTL_FLAG_REQUEST
+   uint8_t  TransactionId;
+   uint16_t QMICTLType;      // QMICTL_RELEASE_CLIENT_ID_REQ
+   uint16_t Length;
+   uint8_t  TLVType;         // QCTLV_TYPE_REQUIRED_PARAMETER
+   uint16_t TLVLength;       // 0x0002
+   uint8_t  QMIType;
+   uint8_t  ClientId;
+} __attribute__ ((packed)) QMICTL_RELEASE_CLIENT_ID_REQ_MSG, *PQMICTL_RELEASE_CLIENT_ID_REQ_MSG;
+
+typedef struct _QMICTL_RELEASE_CLIENT_ID_RESP_MSG
+{
+   uint8_t  CtlFlags;        // QMICTL_FLAG_RESPONSE
+   uint8_t  TransactionId;
+   uint16_t QMICTLType;      // QMICTL_RELEASE_CLIENT_ID_RESP
+   uint16_t Length;
+   uint8_t  TLVType;         // QCTLV_TYPE_RESULT_CODE
+   uint16_t TLVLength;       // 0x0004
+   uint16_t QMIResult;       // result code
+   uint16_t QMIError;        // error code
+   uint8_t  TLV2Type;        // QCTLV_TYPE_REQUIRED_PARAMETER
+   uint16_t TLV2Length;      // 2
+   uint8_t  QMIType;
+   uint8_t  ClientId;
+} __attribute__ ((packed)) QMICTL_RELEASE_CLIENT_ID_RESP_MSG, *PQMICTL_RELEASE_CLIENT_ID_RESP_MSG;
+
+// QMICTL Control Flags
+#define QMICTL_CTL_FLAG_CMD     0x00
+#define QMICTL_CTL_FLAG_RSP     0x01
+#define QMICTL_CTL_FLAG_IND     0x02
+
+typedef struct _QCQMICTL_MSG_HDR
+{
+   uint8_t  CtlFlags;  // 00-cmd, 01-rsp, 10-ind
+   uint8_t  TransactionId;
+   uint16_t QMICTLType;
+   uint16_t Length;
+} __attribute__ ((packed)) QCQMICTL_MSG_HDR, *PQCQMICTL_MSG_HDR;
+
+#define QCQMICTL_MSG_HDR_SIZE sizeof(QCQMICTL_MSG_HDR)
+
+typedef struct _QCQMICTL_MSG_HDR_RESP
+{
+   uint8_t  CtlFlags;  // 00-cmd, 01-rsp, 10-ind
+   uint8_t  TransactionId;
+   uint16_t QMICTLType;
+   uint16_t Length;
+   uint8_t  TLVType;          // 0x02 - result code
+   uint16_t TLVLength;        // 4
+   uint16_t QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   uint16_t QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+} __attribute__ ((packed)) QCQMICTL_MSG_HDR_RESP, *PQCQMICTL_MSG_HDR_RESP;
+
+typedef struct _QMICTL_GET_VERSION_REQ_MSG
+{
+   uint8_t  CtlFlags;        // QMICTL_FLAG_REQUEST
+   uint8_t  TransactionId;
+   uint16_t QMICTLType;      // QMICTL_GET_VERSION_REQ
+   uint16_t Length;          // 0
+   uint8_t  TLVType;         // QCTLV_TYPE_REQUIRED_PARAMETER
+   uint16_t TLVLength;       // var
+   uint8_t  QMUXTypes;       // List of one byte QMUX_TYPE values
+                           // 0xFF returns a list of versions for all
+                           // QMUX_TYPEs implemented on the device
+} __attribute__ ((packed)) QMICTL_GET_VERSION_REQ_MSG, *PQMICTL_GET_VERSION_REQ_MSG;
+
+typedef struct _QMUX_TYPE_VERSION_STRUCT
+{
+   uint8_t  QMUXType;
+   uint16_t MajorVersion;
+   uint16_t MinorVersion;
+} __attribute__ ((packed)) QMUX_TYPE_VERSION_STRUCT, *PQMUX_TYPE_VERSION_STRUCT;
+
+typedef struct _QMICTL_GET_VERSION_RESP_MSG
+{
+   uint8_t  CtlFlags;        // QMICTL_FLAG_RESPONSE
+   uint8_t  TransactionId;
+   uint16_t QMICTLType;      // QMICTL_GET_VERSION_RESP
+   uint16_t Length;
+   uint8_t  TLVType;         // QCTLV_TYPE_RESULT_CODE
+   uint16_t TLVLength;       // 0x0004
+   uint16_t QMIResult;
+   uint16_t QMIError;
+   uint8_t  TLV2Type;        // QCTLV_TYPE_REQUIRED_PARAMETER
+   uint16_t TLV2Length;      // var
+   uint8_t  NumElements;     // Num of QMUX_TYPE_VERSION_STRUCT
+   QMUX_TYPE_VERSION_STRUCT TypeVersion[0];
+} __attribute__ ((packed)) QMICTL_GET_VERSION_RESP_MSG, *PQMICTL_GET_VERSION_RESP_MSG;
+
+
+typedef struct _QMICTL_MSG
+{
+   union
+   {
+      // Message Header
+      QCQMICTL_MSG_HDR                             QMICTLMsgHdr;
+      QCQMICTL_MSG_HDR_RESP                             QMICTLMsgHdrRsp;
+
+      // QMICTL Message
+      //QMICTL_SET_INSTANCE_ID_REQ_MSG               SetInstanceIdReq;
+      //QMICTL_SET_INSTANCE_ID_RESP_MSG              SetInstanceIdRsp;
+      QMICTL_GET_VERSION_REQ_MSG                   GetVersionReq;
+      QMICTL_GET_VERSION_RESP_MSG                  GetVersionRsp;
+      QMICTL_GET_CLIENT_ID_REQ_MSG                 GetClientIdReq;
+      QMICTL_GET_CLIENT_ID_RESP_MSG                GetClientIdRsp;
+      //QMICTL_RELEASE_CLIENT_ID_REQ_MSG             ReleaseClientIdReq;
+      QMICTL_RELEASE_CLIENT_ID_RESP_MSG            ReleaseClientIdRsp;
+      //QMICTL_REVOKE_CLIENT_ID_IND_MSG              RevokeClientIdInd;
+      //QMICTL_INVALID_CLIENT_ID_IND_MSG             InvalidClientIdInd;
+      //QMICTL_SET_DATA_FORMAT_REQ_MSG               SetDataFormatReq;
+      //QMICTL_SET_DATA_FORMAT_RESP_MSG              SetDataFormatRsp;
+      QMICTL_SYNC_REQ_MSG                          SyncReq;
+      QMICTL_SYNC_RESP_MSG                         SyncRsp;
+      QMICTL_SYNC_IND_MSG                          SyncInd;
+   };
+} __attribute__ ((packed)) QMICTL_MSG, *PQMICTL_MSG;
+
+typedef struct _QCQMUX_MSG_HDR
+{
+   uint8_t  CtlFlags;      // 0: single QMUX Msg; 1:
+   uint16_t TransactionId;
+   uint16_t Type;
+   uint16_t Length;
+   uint8_t payload[0];
+} __attribute__ ((packed)) QCQMUX_MSG_HDR, *PQCQMUX_MSG_HDR;
+
+typedef struct _QCQMUX_MSG_HDR_RESP
+{
+   uint8_t  CtlFlags;      // 0: single QMUX Msg; 1:
+   uint16_t TransactionId;
+   uint16_t Type;
+   uint16_t Length;
+   uint8_t  TLVType;          // 0x02 - result code
+   uint16_t TLVLength;        // 4
+   uint16_t QMUXResult;       // QMI_RESULT_SUCCESS
+                            // QMI_RESULT_FAILURE
+   uint16_t QMUXError;        // QMI_ERR_INVALID_ARG
+                            // QMI_ERR_NO_MEMORY
+                            // QMI_ERR_INTERNAL
+                            // QMI_ERR_FAULT
+} __attribute__ ((packed)) QCQMUX_MSG_HDR_RESP, *PQCQMUX_MSG_HDR_RESP;
+
+typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT
+{
+   uint16_t Type;             // QMUX type 0x0000
+   uint16_t Length;
+} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT, *PQMIWDS_ADMIN_SET_DATA_FORMAT;
+
+typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS
+{
+   uint8_t  TLVType;
+   uint16_t TLVLength;
+   uint8_t  QOSSetting;
+} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS, *PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS;
+
+typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_TLV
+{
+   uint8_t  TLVType;
+   uint16_t TLVLength;
+   uint32_t  Value;
+} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_TLV, *PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV;
+
+typedef struct _QMIWDS_ENDPOINT_TLV
+{
+   uint8_t  TLVType;
+   uint16_t TLVLength;
+   uint32_t  ep_type;
+   uint32_t  iface_id;
+} __attribute__ ((packed)) QMIWDS_ENDPOINT_TLV, *PQMIWDS_ENDPOINT_TLV;
+
+typedef struct _QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG
+{
+    uint8_t  CtlFlags;      // 0: single QMUX Msg; 1:
+    uint16_t TransactionId;
+    uint16_t Type;
+    uint16_t Length;
+    QMIWDS_ADMIN_SET_DATA_FORMAT_TLV_QOS QosDataFormatTlv;
+    QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UnderlyingLinkLayerProtocolTlv;
+    QMIWDS_ADMIN_SET_DATA_FORMAT_TLV UplinkDataAggregationProtocolTlv;
+    QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationProtocolTlv;
+    QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationMaxDatagramsTlv;
+    QMIWDS_ADMIN_SET_DATA_FORMAT_TLV DownlinkDataAggregationMaxSizeTlv;
+    QMIWDS_ENDPOINT_TLV epTlv;
+} __attribute__ ((packed)) QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG, *PQMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG;
+
+typedef struct _QCQMUX_TLV
+{
+   uint8_t Type;
+   uint16_t Length;
+   uint8_t  Value[0];
+} __attribute__ ((packed)) QCQMUX_TLV, *PQCQMUX_TLV;
+
+typedef struct _QMUX_MSG
+{
+   union
+   {
+      // Message Header
+      QCQMUX_MSG_HDR                           QMUXMsgHdr;
+      QCQMUX_MSG_HDR_RESP                      QMUXMsgHdrResp;
+      QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG      SetDataFormatReq;
+    };
+} __attribute__ ((packed)) QMUX_MSG, *PQMUX_MSG;
+
+typedef struct _QCQMIMSG {
+    QCQMI_HDR QMIHdr;
+    union {
+        QMICTL_MSG CTLMsg;
+        QMUX_MSG MUXMsg;
+    };
+} __attribute__ ((packed)) QCQMIMSG, *PQCQMIMSG;
+
+
+// QMUX Message Definitions -- QMI SDU
+#define QMUX_CTL_FLAG_SINGLE_MSG    0x00
+#define QMUX_CTL_FLAG_COMPOUND_MSG  0x01
+#define QMUX_CTL_FLAG_TYPE_CMD      0x00
+#define QMUX_CTL_FLAG_TYPE_RSP      0x02
+#define QMUX_CTL_FLAG_TYPE_IND      0x04
+#define QMUX_CTL_FLAG_MASK_COMPOUND 0x01
+#define QMUX_CTL_FLAG_MASK_TYPE     0x06 // 00-cmd, 01-rsp, 10-ind
+
+#define USB_CTL_MSG_TYPE_QMI 0x01
+
+#define QMICTL_FLAG_REQUEST    0x00
+#define QMICTL_FLAG_RESPONSE   0x01
+#define QMICTL_FLAG_INDICATION 0x02
+
+// QMICTL Type
+#define QMICTL_SET_INSTANCE_ID_REQ    0x0020
+#define QMICTL_SET_INSTANCE_ID_RESP   0x0020
+#define QMICTL_GET_VERSION_REQ        0x0021
+#define QMICTL_GET_VERSION_RESP       0x0021
+#define QMICTL_GET_CLIENT_ID_REQ      0x0022
+#define QMICTL_GET_CLIENT_ID_RESP     0x0022
+#define QMICTL_RELEASE_CLIENT_ID_REQ  0x0023
+#define QMICTL_RELEASE_CLIENT_ID_RESP 0x0023
+#define QMICTL_REVOKE_CLIENT_ID_IND   0x0024
+#define QMICTL_INVALID_CLIENT_ID_IND  0x0025
+#define QMICTL_SET_DATA_FORMAT_REQ    0x0026
+#define QMICTL_SET_DATA_FORMAT_RESP   0x0026
+#define QMICTL_SYNC_REQ               0x0027
+#define QMICTL_SYNC_RESP              0x0027
+#define QMICTL_SYNC_IND               0x0027
+    
+#define QCTLV_TYPE_REQUIRED_PARAMETER 0x01
+
+// Define QMI Type
+typedef enum _QMI_SERVICE_TYPE
+{
+   QMUX_TYPE_CTL  = 0x00,
+   QMUX_TYPE_WDS  = 0x01,
+   QMUX_TYPE_DMS  = 0x02,
+   QMUX_TYPE_NAS  = 0x03,
+   QMUX_TYPE_QOS  = 0x04,
+   QMUX_TYPE_WMS  = 0x05,
+   QMUX_TYPE_PDS  = 0x06,
+   QMUX_TYPE_UIM  = 0x0B,
+   QMUX_TYPE_WDS_IPV6  = 0x11,
+   QMUX_TYPE_WDS_ADMIN  = 0x1A,
+   QMUX_TYPE_MAX  = 0xFF,
+   QMUX_TYPE_ALL  = 0xFF
+} QMI_SERVICE_TYPE;
+
+#define QMIWDS_ADMIN_SET_DATA_FORMAT_REQ      0x0020
+#define QMIWDS_ADMIN_SET_DATA_FORMAT_RESP     0x0020
+
+struct qlistnode
+{
+    struct qlistnode *next;
+    struct qlistnode *prev;
+};
+
+#define qnode_to_item(node, container, member) \
+    (container *) (((char*) (node)) - offsetof(container, member))
+
+#define qlist_for_each(node, list) \
+    for (node = (list)->next; node != (list); node = node->next)
+
+#define qlist_empty(list) ((list) == (list)->next)
+#define qlist_head(list) ((list)->next)
+#define qlist_tail(list) ((list)->prev)
+
+typedef struct {
+    struct qlistnode qnode;
+    uint8_t ClientFd;
+    QCQMIMSG qmi[0];
+} QMI_PROXY_MSG;
+
+typedef struct {
+    struct qlistnode qnode;
+    uint8_t QMIType;
+    uint8_t ClientId;
+    unsigned AccessTime;
+} QMI_PROXY_CLINET;
+
+typedef struct {
+    struct qlistnode qnode;
+    struct qlistnode client_qnode;
+    uint8_t ClientFd;
+    unsigned AccessTime;
+} QMI_PROXY_CONNECTION;
+
+static void qlist_init(struct qlistnode *node)
+{
+    node->next = node;
+    node->prev = node;
+}
+
+static void qlist_add_tail(struct qlistnode *head, struct qlistnode *item)
+{
+    item->next = head;
+    item->prev = head->prev;
+    head->prev->next = item;
+    head->prev = item;
+}
+
+static void qlist_remove(struct qlistnode *item)
+{
+    item->next->prev = item->prev;
+    item->prev->next = item->next;
+}
+
+static int qmi_proxy_quit = 0;
+static pthread_t thread_id = 0;
+static int cdc_wdm_fd = -1;
+static int qmi_proxy_server_fd = -1;
+static struct qlistnode qmi_proxy_connection;
+static struct qlistnode qmi_proxy_ctl_msg;
+static PQCQMIMSG s_pCtlReq;
+static PQCQMIMSG s_pCtlRsq;
+static pthread_mutex_t s_ctlmutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t s_ctlcond = PTHREAD_COND_INITIALIZER;
+
+static void setTimespecRelative(struct timespec *p_ts, long long msec)
+{
+    struct timeval tv;
+
+    gettimeofday(&tv, (struct timezone *) NULL);
+
+    /* what's really funny about this is that I know
+       pthread_cond_timedwait just turns around and makes this
+       a relative time again */
+    p_ts->tv_sec = tv.tv_sec + (msec / 1000);
+    p_ts->tv_nsec = (tv.tv_usec + (msec % 1000) * 1000L ) * 1000L;
+}
+
+static int pthread_cond_timeout_np(pthread_cond_t *cond, pthread_mutex_t * mutex, unsigned msecs) {
+    if (msecs != 0) {
+        struct timespec ts;
+        setTimespecRelative(&ts, msecs);
+        return pthread_cond_timedwait(cond, mutex, &ts);
+    } else {
+        return pthread_cond_wait(cond, mutex);
+    }
+}
+
+static int create_local_server(const char *name) {
+    int sockfd = -1;
+    int reuse_addr = 1;
+    struct sockaddr_un sockaddr;
+    socklen_t alen;
+
+    /*Create server socket*/
+    SYSCHECK(sockfd = socket(AF_LOCAL, SOCK_STREAM, 0));
+
+    memset(&sockaddr, 0, sizeof(sockaddr));
+    sockaddr.sun_family = AF_LOCAL;
+    sockaddr.sun_path[0] = 0;
+    memcpy(sockaddr.sun_path + 1, name, strlen(name) );
+
+    alen = strlen(name) + offsetof(struct sockaddr_un, sun_path) + 1;
+    SYSCHECK(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,sizeof(reuse_addr)));
+    if(bind(sockfd, (struct sockaddr *)&sockaddr, alen) < 0) {
+        close(sockfd);
+        dprintf("%s bind %s errno: %d (%s)\n", __func__, name, errno, strerror(errno));
+        return -1;
+    }
+
+    dprintf("local server: %s sockfd = %d\n", name, sockfd);
+    cfmakenoblock(sockfd);
+    listen(sockfd, 1);    
+
+    return sockfd;
+}
+
+static void accept_qmi_connection(int serverfd) {
+    int clientfd = -1;
+    unsigned char addr[128];
+    socklen_t alen = sizeof(addr);
+    QMI_PROXY_CONNECTION *qmi_con;
+
+    clientfd = accept(serverfd, (struct sockaddr *)addr, &alen);
+
+    qmi_con = (QMI_PROXY_CONNECTION *)malloc(sizeof(QMI_PROXY_CONNECTION));
+    if (qmi_con) {
+        qlist_init(&qmi_con->qnode);
+        qlist_init(&qmi_con->client_qnode);
+        qmi_con->ClientFd= clientfd;
+        qmi_con->AccessTime = 0;
+        dprintf("+++ ClientFd=%d\n", qmi_con->ClientFd);
+        qlist_add_tail(&qmi_proxy_connection, &qmi_con->qnode);
+    }
+
+    cfmakenoblock(clientfd);
+}
+
+static void cleanup_qmi_connection(int clientfd) {
+    struct qlistnode *con_node, *qmi_node;
+    
+    qlist_for_each(con_node, &qmi_proxy_connection) {
+        QMI_PROXY_CONNECTION *qmi_con = qnode_to_item(con_node, QMI_PROXY_CONNECTION, qnode);
+
+        if (qmi_con->ClientFd == clientfd) {
+            while (!qlist_empty(&qmi_con->client_qnode)) {
+                QMI_PROXY_CLINET *qmi_client = qnode_to_item(qlist_head(&qmi_con->client_qnode), QMI_PROXY_CLINET, qnode);
+
+                dprintf("xxx ClientFd=%d QMIType=%d ClientId=%d\n", qmi_con->ClientFd, qmi_client->QMIType, qmi_client->ClientId);
+
+                qlist_remove(&qmi_client->qnode);
+                free(qmi_client);
+            }
+           
+            qlist_for_each(qmi_node, &qmi_proxy_ctl_msg) {
+                QMI_PROXY_MSG *qmi_msg = qnode_to_item(qmi_node, QMI_PROXY_MSG, qnode);
+
+                if (qmi_msg->ClientFd == qmi_con->ClientFd) {
+                    qlist_remove(&qmi_msg->qnode);
+                    free(qmi_msg);
+                    break;
+                 }
+            }
+
+            dprintf("--- ClientFd=%d\n", qmi_con->ClientFd);    
+            close(qmi_con->ClientFd);
+            qlist_remove(&qmi_con->qnode);
+            free(qmi_con);
+            break;
+        }
+    }
+}
+
+static void get_client_id(QMI_PROXY_CONNECTION *qmi_con, PQMICTL_GET_CLIENT_ID_RESP_MSG pClient) {
+    if (pClient->QMIResult == 0 && pClient->QMIError == 0) {
+        QMI_PROXY_CLINET *qmi_client = (QMI_PROXY_CLINET *)malloc(sizeof(QMI_PROXY_CLINET));
+
+        qlist_init(&qmi_client->qnode);
+        qmi_client->QMIType = pClient->QMIType;
+        qmi_client->ClientId = pClient->ClientId;
+        qmi_client->AccessTime = 0;
+
+        dprintf("+++ ClientFd=%d QMIType=%d ClientId=%d\n", qmi_con->ClientFd, qmi_client->QMIType, qmi_client->ClientId);
+        qlist_add_tail(&qmi_con->client_qnode, &qmi_client->qnode);
+    }
+}
+
+static void release_client_id(QMI_PROXY_CONNECTION *qmi_con, PQMICTL_RELEASE_CLIENT_ID_RESP_MSG pClient) {
+    struct qlistnode *client_node;
+    
+    if (pClient->QMIResult == 0 && pClient->QMIError == 0) {
+        qlist_for_each (client_node, &qmi_con->client_qnode) {
+            QMI_PROXY_CLINET *qmi_client = qnode_to_item(client_node, QMI_PROXY_CLINET, qnode);
+            
+            if (pClient->QMIType == qmi_client->QMIType && pClient->ClientId == qmi_client->ClientId) {
+                dprintf("--- ClientFd=%d QMIType=%d ClientId=%d\n", qmi_con->ClientFd, qmi_client->QMIType, qmi_client->ClientId);
+                qlist_remove(&qmi_client->qnode);
+                free(qmi_client);
+                break;
+            }
+        }
+    }
+}
+
+static int verbose_debug = 0;
+static int send_qmi_to_cdc_wdm(PQCQMIMSG pQMI) {
+    struct pollfd pollfds[]= {{cdc_wdm_fd, POLLOUT, 0}};
+    ssize_t ret = 0;
+
+    do {
+        ret = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), 5000);
+    } while ((ret < 0) && (errno == EINTR));
+
+    if (pollfds[0].revents & POLLOUT) {
+        ssize_t size = le16_to_cpu(pQMI->QMIHdr.Length) + 1;
+        ret = write(cdc_wdm_fd, pQMI, size);
+        if (verbose_debug)
+        {
+            ssize_t i;
+            printf("w %d %zd: ", cdc_wdm_fd, ret);
+            for (i = 0; i < 16; i++)
+                printf("%02x ", ((uint8_t *)pQMI)[i]);
+            printf("\n");
+        }
+    }
+
+    return ret;
+}
+
+static int send_qmi_to_client(PQCQMIMSG pQMI, int clientFd) {
+    struct pollfd pollfds[]= {{clientFd, POLLOUT, 0}};
+    ssize_t ret = 0;
+
+    do {
+        ret = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), 5000);
+    } while ((ret < 0) && (errno == EINTR));
+
+    if (pollfds[0].revents & POLLOUT) {
+        ssize_t size = le16_to_cpu(pQMI->QMIHdr.Length) + 1;
+        ret = write(clientFd, pQMI, size);
+        if (verbose_debug)
+        {
+            ssize_t i;
+            printf("w %d %zd: ", clientFd, ret);
+            for (i = 0; i < 16; i++)
+                printf("%02x ", ((uint8_t *)pQMI)[i]);
+            printf("\n");
+        }
+    }
+
+    return ret;
+}
+
+
+static void recv_qmi(PQCQMIMSG pQMI, unsigned size) {
+    struct qlistnode *con_node, *client_node;
+
+    if (qmi_proxy_server_fd <= 0) {
+        pthread_mutex_lock(&s_ctlmutex);
+
+        if (s_pCtlReq != NULL) {
+            if (pQMI->QMIHdr.QMIType == QMUX_TYPE_CTL
+                && s_pCtlReq->CTLMsg.QMICTLMsgHdrRsp.TransactionId == pQMI->CTLMsg.QMICTLMsgHdrRsp.TransactionId) {
+                s_pCtlRsq = malloc(size);
+                memcpy(s_pCtlRsq, pQMI, size);
+                pthread_cond_signal(&s_ctlcond);
+            }
+            else if (pQMI->QMIHdr.QMIType != QMUX_TYPE_CTL
+                && s_pCtlReq->MUXMsg.QMUXMsgHdr.TransactionId == pQMI->MUXMsg.QMUXMsgHdr.TransactionId) {
+                s_pCtlRsq = malloc(size);
+                memcpy(s_pCtlRsq, pQMI, size);
+                pthread_cond_signal(&s_ctlcond);
+            }
+        }
+
+        pthread_mutex_unlock(&s_ctlmutex);
+    }
+    else if (pQMI->QMIHdr.QMIType == QMUX_TYPE_CTL) {
+        if (pQMI->CTLMsg.QMICTLMsgHdr.CtlFlags == QMICTL_CTL_FLAG_RSP) {            
+            if (!qlist_empty(&qmi_proxy_ctl_msg)) {
+                QMI_PROXY_MSG *qmi_msg = qnode_to_item(qlist_head(&qmi_proxy_ctl_msg), QMI_PROXY_MSG, qnode);
+
+                qlist_for_each(con_node, &qmi_proxy_connection) {
+                    QMI_PROXY_CONNECTION *qmi_con = qnode_to_item(con_node, QMI_PROXY_CONNECTION, qnode);
+
+                    if (qmi_con->ClientFd == qmi_msg->ClientFd) {
+                        send_qmi_to_client(pQMI, qmi_msg->ClientFd);
+
+                        if (pQMI->CTLMsg.QMICTLMsgHdrRsp.QMICTLType == QMICTL_GET_CLIENT_ID_RESP)
+                            get_client_id(qmi_con, &pQMI->CTLMsg.GetClientIdRsp);                                                        
+                        else if (pQMI->CTLMsg.QMICTLMsgHdrRsp.QMICTLType == QMICTL_RELEASE_CLIENT_ID_RESP)
+                            release_client_id(qmi_con, &pQMI->CTLMsg.ReleaseClientIdRsp);
+                        else {
+                        }
+                    }
+                }
+
+                qlist_remove(&qmi_msg->qnode);
+                free(qmi_msg);
+            }
+        }
+
+        if (!qlist_empty(&qmi_proxy_ctl_msg)) {
+            QMI_PROXY_MSG *qmi_msg = qnode_to_item(qlist_head(&qmi_proxy_ctl_msg), QMI_PROXY_MSG, qnode);
+
+            qlist_for_each(con_node, &qmi_proxy_connection) {
+                QMI_PROXY_CONNECTION *qmi_con = qnode_to_item(con_node, QMI_PROXY_CONNECTION, qnode);
+
+                if (qmi_con->ClientFd == qmi_msg->ClientFd) {
+                    send_qmi_to_cdc_wdm(qmi_msg->qmi);
+                }
+            }
+        }
+    }
+    else  {
+        qlist_for_each(con_node, &qmi_proxy_connection) {
+            QMI_PROXY_CONNECTION *qmi_con = qnode_to_item(con_node, QMI_PROXY_CONNECTION, qnode);
+            
+            qlist_for_each(client_node, &qmi_con->client_qnode) {
+                QMI_PROXY_CLINET *qmi_client = qnode_to_item(client_node, QMI_PROXY_CLINET, qnode);
+                if (pQMI->QMIHdr.QMIType == qmi_client->QMIType) {
+                    if (pQMI->QMIHdr.ClientId == 0 || pQMI->QMIHdr.ClientId == qmi_client->ClientId) {
+                        send_qmi_to_client(pQMI, qmi_con->ClientFd);
+                    }
+                }
+            }
+        }
+    }
+}
+
+static int send_qmi(PQCQMIMSG pQMI, unsigned size, int clientfd) {
+    if (qmi_proxy_server_fd <= 0) {
+        send_qmi_to_cdc_wdm(pQMI);
+    }
+    else if (pQMI->QMIHdr.QMIType == QMUX_TYPE_CTL) {  
+        QMI_PROXY_MSG *qmi_msg;
+
+        if (qlist_empty(&qmi_proxy_ctl_msg))
+            send_qmi_to_cdc_wdm(pQMI);
+
+        qmi_msg = malloc(sizeof(QMI_PROXY_MSG) + size);
+        qlist_init(&qmi_msg->qnode);
+        qmi_msg->ClientFd = clientfd;
+        memcpy(qmi_msg->qmi, pQMI, size);
+        qlist_add_tail(&qmi_proxy_ctl_msg, &qmi_msg->qnode);
+    }
+    else {
+        send_qmi_to_cdc_wdm(pQMI);
+    }
+
+    return 0;
+}
+
+static int send_qmi_timeout(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse, unsigned mseconds) {
+    int ret;
+
+    pthread_mutex_lock(&s_ctlmutex);
+
+    s_pCtlReq = pRequest;
+    s_pCtlRsq = NULL;
+    if (ppResponse) *ppResponse = NULL;
+
+    send_qmi_to_cdc_wdm(pRequest);
+    ret = pthread_cond_timeout_np(&s_ctlcond, &s_ctlmutex, mseconds);
+    if (!ret) {
+        if (s_pCtlRsq && ppResponse) {
+            *ppResponse = s_pCtlRsq;
+        } else if (s_pCtlRsq) {
+            free(s_pCtlRsq);
+        }
+    } else {
+        dprintf("%s ret=%d\n", __func__, ret);
+    }
+
+    s_pCtlReq = NULL;
+    pthread_mutex_unlock(&s_ctlmutex);
+
+    return ret;
+}
+
+static PQCQMUX_TLV qmi_find_tlv (PQCQMIMSG pQMI, uint8_t TLVType) {
+    int Length = 0;
+
+    while (Length < le16_to_cpu(pQMI->MUXMsg.QMUXMsgHdr.Length)) {
+        PQCQMUX_TLV pTLV = (PQCQMUX_TLV)(&pQMI->MUXMsg.QMUXMsgHdr.payload[Length]);
+
+        //dprintf("TLV {%02x, %04x}\n", pTLV->Type, pTLV->Length);
+        if (pTLV->Type == TLVType) {
+            return pTLV;
+        }
+
+        Length += (le16_to_cpu((pTLV->Length)) + sizeof(QCQMUX_TLV));
+    }
+
+   return NULL;
+}
+
+static int qmi_proxy_init(void) {
+    unsigned i;
+    int ret;
+    QCQMIMSG _QMI;
+    PQCQMIMSG pQMI = &_QMI;
+    PQCQMIMSG pRsp;
+    uint8_t TransactionId = 0xC1;
+    uint8_t WDAClientId = 0;
+    unsigned rx_urb_size = 0;
+    unsigned ep_type, iface_id;
+
+    dprintf("%s enter\n", __func__);
+
+    pQMI->QMIHdr.IFType   = USB_CTL_MSG_TYPE_QMI;
+    pQMI->QMIHdr.CtlFlags = 0x00;
+    pQMI->QMIHdr.QMIType  = QMUX_TYPE_CTL;
+    pQMI->QMIHdr.ClientId= 0x00;
+    
+    pQMI->CTLMsg.QMICTLMsgHdr.CtlFlags = QMICTL_FLAG_REQUEST;
+
+    for (i = 0; i < 10; i++) {
+        pQMI->CTLMsg.SyncReq.TransactionId = TransactionId++;    
+        pQMI->CTLMsg.SyncReq.QMICTLType = QMICTL_SYNC_REQ;
+        pQMI->CTLMsg.SyncReq.Length = 0;
+
+        pQMI->QMIHdr.Length = pQMI->CTLMsg.QMICTLMsgHdr.Length + sizeof(QCQMI_HDR) + sizeof(QCQMICTL_MSG_HDR) - 1;
+
+        ret = send_qmi_timeout(pQMI, NULL, 1000);
+        if (!ret)
+            break;
+    }
+
+    if (ret)
+        goto qmi_proxy_init_fail;
+
+    pQMI->CTLMsg.GetVersionReq.TransactionId = TransactionId++;    
+    pQMI->CTLMsg.GetVersionReq.QMICTLType = QMICTL_GET_VERSION_REQ;
+    pQMI->CTLMsg.GetVersionReq.Length = 0x0004;
+    pQMI->CTLMsg.GetVersionReq.TLVType= QCTLV_TYPE_REQUIRED_PARAMETER;
+    pQMI->CTLMsg.GetVersionReq.TLVLength = 0x0001;
+    pQMI->CTLMsg.GetVersionReq.QMUXTypes = QMUX_TYPE_ALL;
+
+    pQMI->QMIHdr.Length = pQMI->CTLMsg.QMICTLMsgHdr.Length + sizeof(QCQMI_HDR) + sizeof(QCQMICTL_MSG_HDR) - 1;
+
+    ret = send_qmi_timeout(pQMI, &pRsp, 3000);
+    if (ret || (pRsp == NULL))
+        goto qmi_proxy_init_fail;
+
+    if (pRsp) {
+        uint8_t  NumElements = 0;
+        if (pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXResult ||pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXError) {
+            dprintf("QMICTL_GET_VERSION_REQ QMUXResult=%d, QMUXError=%d\n",
+                pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXResult, pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXError);	
+            goto qmi_proxy_init_fail;
+        }
+
+        for (NumElements = 0; NumElements < pRsp->CTLMsg.GetVersionRsp.NumElements; NumElements++) {
+            if (verbose_debug)
+            dprintf("QMUXType = %02x Version = %d.%d\n",
+                pRsp->CTLMsg.GetVersionRsp.TypeVersion[NumElements].QMUXType,
+                pRsp->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MajorVersion,
+                pRsp->CTLMsg.GetVersionRsp.TypeVersion[NumElements].MinorVersion);
+        }
+    }
+    free(pRsp);
+
+    pQMI->CTLMsg.GetClientIdReq.TransactionId = TransactionId++;    
+    pQMI->CTLMsg.GetClientIdReq.QMICTLType = QMICTL_GET_CLIENT_ID_REQ;
+    pQMI->CTLMsg.GetClientIdReq.Length = 0x0004;
+    pQMI->CTLMsg.GetClientIdReq.TLVType= QCTLV_TYPE_REQUIRED_PARAMETER;
+    pQMI->CTLMsg.GetClientIdReq.TLVLength = 0x0001;
+    pQMI->CTLMsg.GetClientIdReq.QMIType = QMUX_TYPE_WDS_ADMIN;
+
+    pQMI->QMIHdr.Length = pQMI->CTLMsg.QMICTLMsgHdr.Length + sizeof(QCQMI_HDR) + sizeof(QCQMICTL_MSG_HDR) - 1;
+
+    ret = send_qmi_timeout(pQMI, &pRsp, 3000);
+    if (ret || (pRsp == NULL))
+        goto qmi_proxy_init_fail;
+
+    if (pRsp) {
+        if (pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXResult ||pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXError) {
+            dprintf("QMICTL_GET_CLIENT_ID_REQ QMUXResult=%d, QMUXError=%d\n",
+                pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXResult, pRsp->CTLMsg.QMICTLMsgHdrRsp.QMUXError);
+            goto qmi_proxy_init_fail;
+        }
+
+        WDAClientId = pRsp->CTLMsg.GetClientIdRsp.ClientId;
+        if (verbose_debug) dprintf("WDAClientId = %d\n", WDAClientId);
+    }
+    free(pRsp);
+
+    rx_urb_size = 32*1024; //must same as rx_urb_size defined in GobiNet&qmi_wwan driver //SDX24&SDX55 support 32KB
+    ep_type = 0x02;
+    iface_id = 0x04;
+
+    pQMI->QMIHdr.IFType   = USB_CTL_MSG_TYPE_QMI;
+    pQMI->QMIHdr.CtlFlags = 0x00;
+    pQMI->QMIHdr.QMIType  = QMUX_TYPE_WDS_ADMIN;
+    pQMI->QMIHdr.ClientId= WDAClientId;
+    
+    pQMI->MUXMsg.QMUXMsgHdr.CtlFlags = QMICTL_FLAG_REQUEST;
+    pQMI->MUXMsg.QMUXMsgHdr.TransactionId = TransactionId++;
+
+    pQMI->MUXMsg.SetDataFormatReq.Type = QMIWDS_ADMIN_SET_DATA_FORMAT_REQ;
+    pQMI->MUXMsg.SetDataFormatReq.Length = sizeof(QMIWDS_ADMIN_SET_DATA_FORMAT_REQ_MSG) - sizeof(QCQMUX_MSG_HDR);
+
+//Indicates whether the Quality of Service(QOS) data format is used by the client.
+    pQMI->MUXMsg.SetDataFormatReq.QosDataFormatTlv.TLVType = 0x10;
+    pQMI->MUXMsg.SetDataFormatReq.QosDataFormatTlv.TLVLength = cpu_to_le16(0x0001);
+    pQMI->MUXMsg.SetDataFormatReq.QosDataFormatTlv.QOSSetting = 0; /* no-QOS header */
+//Underlying Link Layer Protocol
+    pQMI->MUXMsg.SetDataFormatReq.UnderlyingLinkLayerProtocolTlv.TLVType = 0x11; 
+    pQMI->MUXMsg.SetDataFormatReq.UnderlyingLinkLayerProtocolTlv.TLVLength = cpu_to_le16(4);
+    pQMI->MUXMsg.SetDataFormatReq.UnderlyingLinkLayerProtocolTlv.Value = cpu_to_le32(0x02);     /* Set IP mode */
+//Uplink (UL) data aggregation protocol to be used for uplink data transfer.
+    pQMI->MUXMsg.SetDataFormatReq.UplinkDataAggregationProtocolTlv.TLVType = 0x12; 
+    pQMI->MUXMsg.SetDataFormatReq.UplinkDataAggregationProtocolTlv.TLVLength = cpu_to_le16(4);
+    pQMI->MUXMsg.SetDataFormatReq.UplinkDataAggregationProtocolTlv.Value = cpu_to_le32(0x05); //UL QMAP is enabled
+//Downlink (DL) data aggregation protocol to be used for downlink data transfer
+    pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationProtocolTlv.TLVType = 0x13; 
+    pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationProtocolTlv.TLVLength = cpu_to_le16(4);
+    pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationProtocolTlv.Value = cpu_to_le32(0x05); //UL QMAP is enabled
+//Maximum number of datagrams in a single aggregated packet on downlink
+    pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.TLVType = 0x15; 
+    pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.TLVLength = cpu_to_le16(4);
+    pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxDatagramsTlv.Value = cpu_to_le32((rx_urb_size>2048)?(rx_urb_size/1024):1);
+//Maximum size in bytes of a single aggregated packet allowed on downlink
+    pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.TLVType = 0x16; 
+    pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.TLVLength = cpu_to_le16(4);
+    pQMI->MUXMsg.SetDataFormatReq.DownlinkDataAggregationMaxSizeTlv.Value = cpu_to_le32(rx_urb_size);
+//Peripheral End Point ID
+    pQMI->MUXMsg.SetDataFormatReq.epTlv.TLVType = 0x17; 
+    pQMI->MUXMsg.SetDataFormatReq.epTlv.TLVLength = cpu_to_le16(8);
+    pQMI->MUXMsg.SetDataFormatReq.epTlv.ep_type = cpu_to_le32(ep_type); // DATA_EP_TYPE_BAM_DMUX
+    pQMI->MUXMsg.SetDataFormatReq.epTlv.iface_id = cpu_to_le32(iface_id); 
+
+    pQMI->QMIHdr.Length = pQMI->MUXMsg.QMUXMsgHdr.Length + sizeof(QCQMI_HDR) + sizeof(QCQMUX_MSG_HDR) - 1;
+
+    ret = send_qmi_timeout(pQMI, &pRsp, 3000);
+    if (ret || (pRsp == NULL))
+        goto qmi_proxy_init_fail;
+
+    if (pRsp) {
+        PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV pFormat;
+        
+        if (pRsp->MUXMsg.QMUXMsgHdrResp.QMUXResult || pRsp->MUXMsg.QMUXMsgHdrResp.QMUXError) {
+            dprintf("QMIWDS_ADMIN_SET_DATA_FORMAT_REQ QMUXResult=%d, QMUXError=%d\n",
+                pRsp->MUXMsg.QMUXMsgHdrResp.QMUXResult, pRsp->MUXMsg.QMUXMsgHdrResp.QMUXError);	     
+            goto qmi_proxy_init_fail;
+        }
+
+        pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x11);
+        if (pFormat)
+            dprintf("link_prot %d\n", le32_to_cpu(pFormat->Value));
+        pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x12);
+        if (pFormat)
+            dprintf("ul_data_aggregation_protocol %d\n", le32_to_cpu(pFormat->Value));
+        pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x13);
+        if (pFormat)
+            dprintf("dl_data_aggregation_protocol %d\n", le32_to_cpu(pFormat->Value));
+        pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x15);
+        if (pFormat)
+            dprintf("dl_data_aggregation_max_datagrams %d\n", le32_to_cpu(pFormat->Value));
+        pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x16);
+        if (pFormat) {
+            dprintf("dl_data_aggregation_max_size %d\n", le32_to_cpu(pFormat->Value));
+            rx_urb_size = le32_to_cpu(pFormat->Value);
+        }
+        pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x17);
+        if (pFormat)
+            dprintf("ul_data_aggregation_max_datagrams %d\n", le32_to_cpu(pFormat->Value));
+        pFormat = (PQMIWDS_ADMIN_SET_DATA_FORMAT_TLV)qmi_find_tlv(pRsp, 0x18);
+        if (pFormat)
+            dprintf("ul_data_aggregation_max_size %d\n", le32_to_cpu(pFormat->Value));
+    }
+    free(pRsp);
+
+    dprintf("%s finished, rx_urb_size is %u\n", __func__, rx_urb_size);
+    return 0;
+
+qmi_proxy_init_fail:
+    dprintf("%s failed\n", __func__);
+    return -1;
+}
+
+static void qmi_start_server(void) {
+    qmi_proxy_server_fd = create_local_server("meig-qmi-proxy");
+    printf("%s: qmi_proxy_server_fd = %d\n", __func__, qmi_proxy_server_fd);
+    if (qmi_proxy_server_fd == -1) {
+        dprintf("%s Failed to create %s, errno: %d (%s)\n", __func__, "meig-qmi-proxy", errno, strerror(errno));
+    }
+}
+
+static void qmi_close_server(void) {
+    if (qmi_proxy_server_fd != -1) {
+        dprintf("%s %s close server\n", __func__, "meig-qmi-proxy");
+        close(qmi_proxy_server_fd);
+        qmi_proxy_server_fd = -1;
+    }
+}
+
+static void *qmi_proxy_loop(void *param)
+{
+    static uint8_t qmi_buf[2048];
+    PQCQMIMSG pQMI = (PQCQMIMSG)qmi_buf;
+    struct qlistnode *con_node;
+    QMI_PROXY_CONNECTION *qmi_con;
+
+    dprintf("%s enter thread_id %ld\n", __func__, pthread_self());
+
+    qlist_init(&qmi_proxy_connection);
+    qlist_init(&qmi_proxy_ctl_msg);
+
+    while (cdc_wdm_fd > 0 && qmi_proxy_quit == 0) {
+        struct pollfd pollfds[2+64];
+        int ne, ret, nevents = 0;
+        ssize_t nreads;
+
+        pollfds[nevents].fd = cdc_wdm_fd;
+        pollfds[nevents].events = POLLIN;
+        pollfds[nevents].revents= 0;
+        nevents++;
+        
+        if (qmi_proxy_server_fd > 0) {
+            pollfds[nevents].fd = qmi_proxy_server_fd;
+            pollfds[nevents].events = POLLIN;
+            pollfds[nevents].revents= 0;
+            nevents++;
+        }
+
+        qlist_for_each(con_node, &qmi_proxy_connection) {
+            qmi_con = qnode_to_item(con_node, QMI_PROXY_CONNECTION, qnode);
+            
+            pollfds[nevents].fd = qmi_con->ClientFd;
+            pollfds[nevents].events = POLLIN;
+            pollfds[nevents].revents= 0;
+            nevents++;
+
+            if (nevents == (sizeof(pollfds)/sizeof(pollfds[0])))
+                break;
+        }
+
+#if 0
+        dprintf("poll ");
+        for (ne = 0; ne < nevents; ne++) {
+            dprintf("%d ", pollfds[ne].fd);
+        }
+        dprintf("\n");
+#endif
+
+        do {
+            //ret = poll(pollfds, nevents, -1);
+            ret = poll(pollfds, nevents, (qmi_proxy_server_fd > 0) ? -1 : 200);
+         } while (ret < 0 && errno == EINTR && qmi_proxy_quit == 0);
+         
+        if (ret < 0) {
+            dprintf("%s poll=%d, errno: %d (%s)\n", __func__, ret, errno, strerror(errno));
+            goto qmi_proxy_loop_exit;
+        }
+
+        for (ne = 0; ne < nevents; ne++) {
+            int fd = pollfds[ne].fd;
+            short revents = pollfds[ne].revents;
+
+            if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
+                dprintf("%s poll fd = %d, revents = %04x\n", __func__, fd, revents);
+                if (fd == cdc_wdm_fd) {
+                    goto qmi_proxy_loop_exit;
+                } else if(fd == qmi_proxy_server_fd) {
+                
+                } else {
+                    cleanup_qmi_connection(fd);
+                }
+
+                continue;
+            }
+
+            if (!(pollfds[ne].revents & POLLIN)) {
+                continue;
+            }
+
+            if (fd == qmi_proxy_server_fd) {
+                accept_qmi_connection(fd);
+            }
+            else if (fd == cdc_wdm_fd) {
+                nreads = read(fd, pQMI, sizeof(qmi_buf));
+                if (verbose_debug)
+                {
+                    ssize_t i;
+                    printf("r %d %zd: ", fd, nreads);
+                    for (i = 0; i < 16; i++)
+                        printf("%02x ", ((uint8_t *)pQMI)[i]);
+                    printf("\n");
+                }
+                if (nreads <= 0) {
+                    dprintf("%s read=%d errno: %d (%s)\n",  __func__, (int)nreads, errno, strerror(errno));
+                    goto qmi_proxy_loop_exit;
+                }
+
+                if (nreads != ((pQMI->QMIHdr.Length) + 1)) {
+                    dprintf("%s nreads=%d,  pQCQMI->QMIHdr.Length = %d\n",  __func__, (int)nreads, (pQMI->QMIHdr.Length));
+                    continue;
+                }
+
+                recv_qmi(pQMI, nreads);
+            }
+            else {
+                nreads = read(fd, pQMI, sizeof(qmi_buf));
+                if (verbose_debug)
+                {
+                    ssize_t i;
+                    printf("r %d %zd: ", fd, nreads);
+                    for (i = 0; i < 16; i++)
+                        printf("%02x ", ((uint8_t *)pQMI)[i]);
+                    printf("\n");
+                }
+                if (nreads <= 0) {
+                    dprintf("%s read=%d errno: %d (%s)",  __func__, (int)nreads, errno, strerror(errno));
+                    cleanup_qmi_connection(fd);
+                    break;
+                }
+
+                if (nreads != ((pQMI->QMIHdr.Length) + 1)) {
+                    dprintf("%s nreads=%d,  pQCQMI->QMIHdr.Length = %d\n",  __func__, (int)nreads, (pQMI->QMIHdr.Length));
+                    continue;
+                }
+
+                send_qmi(pQMI, nreads, fd);
+            }
+        }
+    }
+
+qmi_proxy_loop_exit:
+    while (!qlist_empty(&qmi_proxy_connection)) {
+        QMI_PROXY_CONNECTION *qmi_con = qnode_to_item(qlist_head(&qmi_proxy_connection), QMI_PROXY_CONNECTION, qnode);
+
+        cleanup_qmi_connection(qmi_con->ClientFd);
+    }
+    
+    dprintf("%s exit, thread_id %ld\n", __func__, pthread_self());
+
+    return NULL;
+}
+
+static void qmidevice_detect(char **device_name) {
+    struct dirent* ent = NULL;
+    DIR *pDir;
+
+    char dir[255] = "/sys/bus/usb/devices";
+    pDir = opendir(dir);
+    if (pDir)  {
+        struct {
+           char subdir[255 * 3];
+            char qmifile[255 * 2];
+        } *pl;
+        pl = (typeof(pl)) malloc(sizeof(*pl));
+        memset(pl, 0x00, sizeof(*pl));
+
+        while ((ent = readdir(pDir)) != NULL)  {
+            struct dirent* subent = NULL;
+            DIR *psubDir;
+            char idVendor[4+1] = {0};
+            char idProduct[4+1] = {0};
+            int fd = 0;
+
+            memset(pl, 0x00, sizeof(*pl));
+            snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s/idVendor", dir, ent->d_name);
+            fd = open(pl->subdir, O_RDONLY);
+            if (fd > 0) {
+                read(fd, idVendor, 4);
+                close(fd);
+             }
+
+            snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s/idProduct", dir, ent->d_name);
+            fd  = open(pl->subdir, O_RDONLY);
+            if (fd > 0) {
+                read(fd, idProduct, 4);
+                close(fd);
+            }
+
+            if (strncasecmp(idVendor, "05c6", 4) && strncasecmp(idVendor, "2c7c", 4) && strncasecmp(idVendor, "2dee", 4))
+                continue;
+
+            dprintf("Find %s/%s idVendor=%s idProduct=%s\n", dir, ent->d_name, idVendor, idProduct);
+            snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s:1.4/usbmisc", dir, ent->d_name);
+            if (access(pl->subdir, R_OK)) {
+                snprintf(pl->subdir, sizeof(pl->subdir), "%s/%s:1.4/usb", dir, ent->d_name);
+                if (access(pl->subdir, R_OK)) {
+                    dprintf("no GobiQMI/usbmic/usb found in %s/%s:1.4\n", dir, ent->d_name);
+                    continue;
+                }
+            }
+            
+            psubDir = opendir(pl->subdir);
+            if (pDir == NULL)  {
+                dprintf("Cannot open directory: %s, errno: %d (%s)\n", dir, errno, strerror(errno));
+                continue;
+            }
+
+            while ((subent = readdir(psubDir)) != NULL)  {
+                if (subent->d_name[0] == '.')
+                    continue;
+                dprintf("Find %s/%s\n", pl->subdir, subent->d_name);
+                snprintf(pl->qmifile, sizeof(pl->qmifile), "/dev/%s", subent->d_name);
+                break;
+            }
+            *device_name = strdup(pl->qmifile);
+            closedir(psubDir);
+        }
+        closedir(pDir);
+        free(pl);
+    }
+}
+
+static void usage(void) {
+    dprintf(" -d <device_name>                      A valid qmi device\n"
+            "                                       default /dev/cdc-wdm0, but cdc-wdm0 may be invalid\n"
+            " -v                                    Will show all details\n");
+}
+
+static void sig_action(int sig) {
+    if (qmi_proxy_quit == 0) {
+        qmi_proxy_quit = 1;
+        if (thread_id)
+            pthread_kill(thread_id, sig);
+    }
+}
+
+int main(int argc, char *argv[]) {
+    int opt;
+    char *cdc_wdm = NULL;
+    int retry_times = 0;
+
+    optind = 1;
+
+    signal(SIGINT, sig_action);
+
+    while ( -1 != (opt = getopt(argc, argv, "d:v"))) {
+        switch (opt) {
+            case 'd':
+                cdc_wdm = strdup(optarg);
+                break;
+            case 'v':
+                verbose_debug = 1;
+                break;
+            default:
+                usage();
+                break;
+        }
+    }
+    
+    if (cdc_wdm == NULL)
+        qmidevice_detect(&cdc_wdm);
+
+    if (cdc_wdm == NULL) {
+        dprintf("Fail to find any /dev/cdc-wdm device. break\n");
+        return -1;
+    }
+
+    if (access(cdc_wdm, R_OK | W_OK)) {
+        dprintf("Fail to access %s, errno: %d (%s). break\n", cdc_wdm, errno, strerror(errno));
+        free(cdc_wdm);
+        return -1;
+    }
+
+    while (qmi_proxy_quit == 0) {
+        if (access(cdc_wdm, R_OK | W_OK)) {
+            dprintf("Fail to access %s, errno: %d (%s). continue\n", cdc_wdm, errno, strerror(errno));
+            // wait device
+            sleep(3);
+            continue;
+        }
+
+        dprintf("Will use cdc-wdm %s\n", cdc_wdm);
+        
+        cdc_wdm_fd = open(cdc_wdm, O_RDWR | O_NONBLOCK | O_NOCTTY);
+        if (cdc_wdm_fd == -1) {
+            dprintf("Failed to open %s, errno: %d (%s). break\n", cdc_wdm, errno, strerror(errno));
+            return -1;
+        }
+        cfmakenoblock(cdc_wdm_fd);
+        
+        /* no qmi_proxy_loop lives, create one */
+        pthread_create(&thread_id, NULL, qmi_proxy_loop, NULL);
+        /* try to redo init if failed, init function must be successfully */
+        while (qmi_proxy_init() != 0) {
+            if (retry_times < 5) {
+                dprintf("fail to init proxy, try again in 2 seconds.\n");
+                sleep(2);
+                retry_times++;
+            } else {
+                dprintf("has failed too much times, restart the modem and have a try...\n");
+                break;
+            }
+            /* break loop if modem is detached */
+            if (access(cdc_wdm, F_OK|R_OK|W_OK))
+                break;
+        }
+        retry_times = 0;
+        qmi_start_server();
+        pthread_join(thread_id, NULL);
+
+        /* close local server at last */
+        qmi_close_server();
+        close(cdc_wdm_fd);
+    }
+
+    if (cdc_wdm) {
+        free(cdc_wdm);
+    }
+    return 0;
+}
diff --git a/package/wwan/meig-cm/src/ql-ifconfig.c b/package/wwan/meig-cm/src/ql-ifconfig.c
new file mode 100644
index 000000000..a9def8920
--- /dev/null
+++ b/package/wwan/meig-cm/src/ql-ifconfig.c
@@ -0,0 +1,494 @@
+/* ifconfig
+ *
+ * Similar to the standard Unix ifconfig, but with only the necessary
+ * parts for AF_INET, and without any printing of if info (for now).
+ *
+ * Bjorn Wesen, Axis Communications AB
+ *
+ *
+ * Authors of the original ifconfig was:      
+ *              Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *
+ * This program is free software; you can redistribute it
+ * and/or  modify it under  the terms of  the GNU General
+ * Public  License as  published  by  the  Free  Software
+ * Foundation;  either  version 2 of the License, or  (at
+ * your option) any later version.
+ *
+ * $Id: ifconfig.c,v 1.12 2001/08/10 06:02:23 mjn3 Exp $
+ *
+ */
+
+/*
+ * Heavily modified by Manuel Novoa III       Mar 6, 2001
+ *
+ * From initial port to busybox, removed most of the redundancy by
+ * converting to a table-driven approach.  Added several (optional)
+ * args missing from initial port.
+ *
+ * Still missing:  media, tunnel.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>   // strcmp and friends
+#include <ctype.h>    // isdigit and friends
+#include <stddef.h>				/* offsetof */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <linux/if.h>
+#include <net/if_arp.h>
+#include <linux/if_ether.h>
+
+#include "QMIThread.h"
+
+#ifdef BB_FEATURE_IFCONFIG_SLIP
+#include <linux/if_slip.h>
+#endif
+#define LOG_TAG "IFCONFIG"
+/* I don't know if this is needed for busybox or not.  Anyone? */
+#define QUESTIONABLE_ALIAS_CASE
+
+
+/* Defines for glibc2.0 users. */
+#ifndef SIOCSIFTXQLEN
+#define SIOCSIFTXQLEN      0x8943
+#define SIOCGIFTXQLEN      0x8942
+#endif
+
+/* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */
+#ifndef ifr_qlen
+#define ifr_qlen        ifr_ifru.ifru_mtu
+#endif
+
+#ifndef IFF_DYNAMIC
+#define IFF_DYNAMIC     0x8000  /* dialup device with changing addresses */
+#endif
+
+/*
+ * Here are the bit masks for the "flags" member of struct options below.
+ * N_ signifies no arg prefix; M_ signifies arg prefixed by '-'.
+ * CLR clears the flag; SET sets the flag; ARG signifies (optional) arg.
+ */
+#define N_CLR            0x01
+#define M_CLR            0x02
+#define N_SET            0x04
+#define M_SET            0x08
+#define N_ARG            0x10
+#define M_ARG            0x20
+
+#define M_MASK           (M_CLR | M_SET | M_ARG)
+#define N_MASK           (N_CLR | N_SET | N_ARG)
+#define SET_MASK         (N_SET | M_SET)
+#define CLR_MASK         (N_CLR | M_CLR)
+#define SET_CLR_MASK     (SET_MASK | CLR_MASK)
+#define ARG_MASK         (M_ARG | N_ARG)
+
+/*
+ * Here are the bit masks for the "arg_flags" member of struct options below.
+ */
+
+/*
+ * cast type:
+ *   00 int
+ *   01 char *
+ *   02 HOST_COPY in_ether
+ *   03 HOST_COPY INET_resolve
+ */
+#define A_CAST_TYPE      0x03
+/*
+ * map type:
+ *   00 not a map type (mem_start, io_addr, irq)
+ *   04 memstart (unsigned long)
+ *   08 io_addr  (unsigned short)
+ *   0C irq      (unsigned char)
+ */
+#define A_MAP_TYPE       0x0C
+#define A_ARG_REQ        0x10	/* Set if an arg is required. */
+#define A_NETMASK        0x20	/* Set if netmask (check for multiple sets). */
+#define A_SET_AFTER      0x40	/* Set a flag at the end. */
+#define A_COLON_CHK      0x80	/* Is this needed?  See below. */
+
+/*
+ * These defines are for dealing with the A_CAST_TYPE field.
+ */
+#define A_CAST_CHAR_PTR  0x01
+#define A_CAST_RESOLVE   0x01
+#define A_CAST_HOST_COPY 0x02
+#define A_CAST_HOST_COPY_IN_ETHER    A_CAST_HOST_COPY
+#define A_CAST_HOST_COPY_RESOLVE     (A_CAST_HOST_COPY | A_CAST_RESOLVE)
+
+/*
+ * These defines are for dealing with the A_MAP_TYPE field.
+ */
+#define A_MAP_ULONG      0x04	/* memstart */
+#define A_MAP_USHORT     0x08	/* io_addr */
+#define A_MAP_UCHAR      0x0C	/* irq */
+
+/*
+ * Define the bit masks signifying which operations to perform for each arg.
+ */
+
+#define ARG_METRIC       (A_ARG_REQ /*| A_CAST_INT*/)
+#define ARG_MTU          (A_ARG_REQ /*| A_CAST_INT*/)
+#define ARG_TXQUEUELEN   (A_ARG_REQ /*| A_CAST_INT*/)
+#define ARG_MEM_START    (A_ARG_REQ | A_MAP_ULONG)
+#define ARG_IO_ADDR      (A_ARG_REQ | A_MAP_USHORT)
+#define ARG_IRQ          (A_ARG_REQ | A_MAP_UCHAR)
+#define ARG_DSTADDR      (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE)
+#define ARG_NETMASK      (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_NETMASK)
+#define ARG_BROADCAST    (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
+#define ARG_HW           (A_ARG_REQ | A_CAST_HOST_COPY_IN_ETHER)
+#define ARG_POINTOPOINT  (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
+#define ARG_KEEPALIVE    (A_ARG_REQ | A_CAST_CHAR_PTR)
+#define ARG_OUTFILL      (A_ARG_REQ | A_CAST_CHAR_PTR)
+#define ARG_HOSTNAME     (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_COLON_CHK)
+
+
+/*
+ * Set up the tables.  Warning!  They must have corresponding order!
+ */
+
+struct arg1opt {
+	const char *name;
+	unsigned short selector;
+	unsigned short ifr_offset;
+};
+
+struct options {
+	const char *name;
+	const unsigned char flags;
+	const unsigned char arg_flags;
+	const unsigned short selector;
+};
+
+#define ifreq_offsetof(x)  offsetof(struct ifreq, x)
+
+static const struct arg1opt Arg1Opt[] = {
+	{"SIOCSIFMETRIC",  SIOCSIFMETRIC,  ifreq_offsetof(ifr_metric)},
+	{"SIOCSIFMTU",     SIOCSIFMTU,     ifreq_offsetof(ifr_mtu)},
+	{"SIOCSIFTXQLEN",  SIOCSIFTXQLEN,  ifreq_offsetof(ifr_qlen)},
+	{"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)},
+	{"SIOCSIFNETMASK", SIOCSIFNETMASK, ifreq_offsetof(ifr_netmask)},
+	{"SIOCSIFBRDADDR", SIOCSIFBRDADDR, ifreq_offsetof(ifr_broadaddr)},
+#ifdef BB_FEATURE_IFCONFIG_HW
+	{"SIOCSIFHWADDR",  SIOCSIFHWADDR,  ifreq_offsetof(ifr_hwaddr)},
+#endif
+	{"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)},
+#ifdef SIOCSKEEPALIVE
+	{"SIOCSKEEPALIVE", SIOCSKEEPALIVE, ifreq_offsetof(ifr_data)},
+#endif
+#ifdef SIOCSOUTFILL
+	{"SIOCSOUTFILL",   SIOCSOUTFILL,   ifreq_offsetof(ifr_data)},
+#endif
+#ifdef BB_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+	{"SIOCSIFMAP",     SIOCSIFMAP,     ifreq_offsetof(ifr_map.mem_start)},
+	{"SIOCSIFMAP",     SIOCSIFMAP,     ifreq_offsetof(ifr_map.base_addr)},
+	{"SIOCSIFMAP",     SIOCSIFMAP,     ifreq_offsetof(ifr_map.irq)},
+#endif
+	/* Last entry if for unmatched (possibly hostname) arg. */
+	{"SIOCSIFADDR",    SIOCSIFADDR,    ifreq_offsetof(ifr_addr)},
+};
+
+static const struct options OptArray[] = {
+	{"metric",       N_ARG,         ARG_METRIC,      0},
+    {"mtu",          N_ARG,         ARG_MTU,         0},
+	{"txqueuelen",   N_ARG,         ARG_TXQUEUELEN,  0},
+	{"dstaddr",      N_ARG,         ARG_DSTADDR,     0},
+	{"netmask",      N_ARG,         ARG_NETMASK,     0},
+	{"broadcast",    N_ARG | M_CLR, ARG_BROADCAST,   IFF_BROADCAST},
+#ifdef BB_FEATURE_IFCONFIG_HW
+	{"hw",           N_ARG,         ARG_HW,          0},
+#endif
+	{"pointopoint",  N_ARG | M_CLR, ARG_POINTOPOINT, IFF_POINTOPOINT},
+#ifdef SIOCSKEEPALIVE
+	{"keepalive",    N_ARG,         ARG_KEEPALIVE,   0},
+#endif
+#ifdef SIOCSOUTFILL
+	{"outfill",      N_ARG,         ARG_OUTFILL,     0},
+#endif
+#ifdef BB_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+	{"mem_start",    N_ARG,         ARG_MEM_START,   0},
+	{"io_addr",      N_ARG,         ARG_IO_ADDR,     0},
+	{"irq",          N_ARG,         ARG_IRQ,         0},
+#endif
+	{"arp",          N_CLR | M_SET, 0,               IFF_NOARP},
+	{"trailers",     N_CLR | M_SET, 0,               IFF_NOTRAILERS},
+	{"promisc",      N_SET | M_CLR, 0,               IFF_PROMISC},
+	{"multicast",    N_SET | M_CLR, 0,               IFF_MULTICAST},
+	{"allmulti",     N_SET | M_CLR, 0,               IFF_ALLMULTI},
+	{"dynamic",      N_SET | M_CLR, 0,               IFF_DYNAMIC},
+	{"up",           N_SET        , 0,               (IFF_UP | IFF_RUNNING)},
+	{"down",         N_CLR        , 0,               IFF_UP},
+	{ NULL,          0,             ARG_HOSTNAME,    (IFF_UP | IFF_RUNNING)}
+};
+
+/*
+ * A couple of prototypes.
+ */
+
+#ifdef BB_FEATURE_IFCONFIG_HW
+static int in_ether(char *bufp, struct sockaddr *sap);
+#endif
+
+#ifdef BB_FEATURE_IFCONFIG_STATUS
+extern int interface_opt_a;
+extern int display_interfaces(char *ifname);
+#endif
+
+/*
+ * Our main function.
+ */
+
+int meig_ifconfig(int argc, char **argv)
+{
+	struct ifreq ifr;
+	struct sockaddr_in sai;
+#ifdef BB_FEATURE_IFCONFIG_HW
+	struct sockaddr sa;
+#endif
+	const struct arg1opt *a1op;
+	const struct options *op;
+	int sockfd;  /* socket fd we use to manipulate stuff with */
+	int goterr;
+	int selector;
+	char *p;
+	char host[128];
+	unsigned char mask;
+	unsigned char did_flags;
+
+
+	goterr = 0;
+	did_flags = 0;
+
+#ifdef BB_FEATURE_IFCONFIG_STATUS
+	if ((argc > 0) && (strcmp(*argv,"-a") == 0)) {
+		interface_opt_a = 1;
+		--argc;
+		++argv;
+	}
+#endif
+
+	if(argc <= 1) {
+#ifdef BB_FEATURE_IFCONFIG_STATUS
+		return display_interfaces(argc ? *argv : NULL);
+#else
+		//error_msg_and_die( "ifconfig was not compiled with interface status display support.");
+#endif
+	}
+
+	/* Create a channel to the NET kernel. */
+	if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+		//perror_msg_and_die("socket");
+	}
+
+	/* get interface name */
+	strncpy(ifr.ifr_name, *argv, IFNAMSIZ);
+
+	/* Process the remaining arguments. */
+	while (*++argv != (char *) NULL) {
+		p = *argv;
+		mask = N_MASK;
+
+		if (*p == '-') {		/* If the arg starts with '-'... */
+			++p;				/*    advance past it and */
+			mask = M_MASK;		/*    set the appropriate mask. */
+		}
+
+		for (op = OptArray ; op->name ; op++) {	/* Find table entry. */
+			if (strcmp(p,op->name) == 0) { /* If name matches... */
+				if ((mask &= op->flags)) { /* set the mask and go. */
+				    goto FOUND_ARG;;
+				}
+				/* If we get here, there was a valid arg with an */
+				/* invalid '-' prefix. */
+				++goterr;
+				goto __LOOP;
+			}
+		}
+		/* We fell through, so treat as possible hostname. */
+		a1op = Arg1Opt + (sizeof(Arg1Opt) / sizeof(Arg1Opt[0])) - 1;
+		mask = op->arg_flags;
+		goto HOSTNAME;
+
+	FOUND_ARG:
+		if (mask & ARG_MASK) {
+			mask = op->arg_flags;
+			a1op = Arg1Opt + (op - OptArray);
+			if (mask & A_NETMASK & did_flags) {
+				//show_usage();
+			}
+			if (*++argv == NULL) {
+				if (mask & A_ARG_REQ) {
+					//show_usage();
+				} else {
+					--argv;
+					mask &= A_SET_AFTER; /* just for broadcast */
+				}
+			} else {			/* got an arg so process it */
+			HOSTNAME:
+				did_flags |= (mask & A_NETMASK);
+				if (mask & A_CAST_HOST_COPY) {
+#ifdef BB_FEATURE_IFCONFIG_HW
+					if (mask & A_CAST_RESOLVE) {
+#endif
+						strncpy(host, *argv, (sizeof host));
+						sai.sin_family = AF_INET;
+						sai.sin_port = 0;
+						if (!strcmp(host, "default")) {
+							/* Default is special, meaning 0.0.0.0. */
+							sai.sin_addr.s_addr = INADDR_ANY;
+						} else if (inet_aton(host, &sai.sin_addr) == 0) {
+							/* It's not a dotted quad. */
+							++goterr;
+							continue;
+						}
+						p = (char *) &sai;
+#ifdef BB_FEATURE_IFCONFIG_HW
+					} else { /* A_CAST_HOST_COPY_IN_ETHER */
+						/* This is the "hw" arg case. */
+						if (strcmp("ether", *argv) || (*++argv == NULL)) {
+							//show_usage();
+						}
+						strncpy(host, *argv, (sizeof host));
+						if (in_ether(host, &sa)) {
+							fprintf(stderr, "invalid hw-addr %s\n", host);
+							++goterr;
+							continue;
+						}
+						p = (char *) &sa;
+					}
+#endif
+					memcpy((((char *)(&ifr)) + a1op->ifr_offset),
+						   p, sizeof(struct sockaddr));
+
+				} else {
+					unsigned int i = strtoul(*argv,NULL,0);
+					p = ((char *)(&ifr)) + a1op->ifr_offset;
+#ifdef BB_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+					if (mask & A_MAP_TYPE) {
+						if (ioctl(sockfd, SIOCGIFMAP, &ifr) < 0) {
+							++goterr;
+							continue;
+						}
+						if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR) {
+							*((unsigned char *) p) = i;
+						} else if (mask & A_MAP_USHORT) {
+							*((unsigned short *) p) = i;
+						} else {
+							*((unsigned long *) p) = i;
+						}
+					} else
+#endif
+					if (mask & A_CAST_CHAR_PTR) {
+						*((caddr_t *) p) = (caddr_t) i;
+					} else { /* A_CAST_INT */
+						*((int *) p) = i;
+					}
+				}
+				if (ioctl(sockfd, a1op->selector, &ifr) < 0) {
+					++goterr;
+					continue;
+				}
+
+#ifdef QUESTIONABLE_ALIAS_CASE
+				if (mask & A_COLON_CHK) {
+					/*
+					 * Don't do the set_flag() if the address is an alias with
+					 * a - at the end, since it's deleted already! - Roman
+					 *
+					 * Should really use regex.h here, not sure though how well
+					 * it'll go with the cross-platform support etc. 
+					 */
+					char *ptr;
+					short int found_colon = 0;
+					for (ptr = ifr.ifr_name; *ptr; ptr++ ) {
+						if (*ptr == ':') {
+							found_colon++;
+						}
+					}
+					if (found_colon && *(ptr - 1) == '-') {
+						continue;
+					}
+				}
+#endif
+			}
+			if (!(mask & A_SET_AFTER)) {
+				continue;
+			}
+			mask = N_SET;
+		}
+
+		if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
+			printf("SIOCGIFFLAGS"); 
+			++goterr;
+		} else {
+			selector = op->selector;
+			if (mask & SET_MASK) {
+				ifr.ifr_flags |= selector;
+			} else {
+				ifr.ifr_flags &= ~selector;
+			}
+			if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
+				printf("SIOCSIFFLAGS"); 
+				++goterr;
+			}
+		}
+	__LOOP:
+		dbg_time("func = %s, line = %d", __func__, __LINE__);
+	} /* end of while-loop */
+	dbg_time("func = %s, line = %d, goterr = %d", __func__, __LINE__, goterr);
+
+	return goterr;
+}
+
+#ifdef BB_FEATURE_IFCONFIG_HW
+/* Input an Ethernet address and convert to binary. */
+static int
+in_ether(char *bufp, struct sockaddr *sap)
+{
+	unsigned char *ptr;
+	int i, j;
+	unsigned char val;
+	unsigned char c;
+	
+	sap->sa_family = ARPHRD_ETHER;
+	ptr = sap->sa_data;
+	
+	for (i = 0 ; i < ETH_ALEN ; i++) {
+		val = 0;
+
+		/* We might get a semicolon here - not required. */
+		if (i && (*bufp == ':')) {
+			bufp++;
+		}
+
+		for (j=0 ; j<2 ; j++) {
+			c = *bufp;
+			if (c >= '0' && c <= '9') {
+				c -= '0';
+			} else if (c >= 'a' && c <= 'f') {
+				c -= ('a' - 10);
+			} else if (c >= 'A' && c <= 'F') {
+				c -= ('A' - 10);
+			} else if (j && (c == ':' || c == 0)) {
+				break;
+			} else {
+				return -1;
+			}
+			++bufp;
+			val <<= 4;
+			val += c;
+		}
+		*ptr++ = val;
+	}
+
+	return (int) (*bufp);		/* Error if we don't end at end of string. */
+}
+#endif
+
+
diff --git a/package/wwan/meig-cm/src/qmap_bridge_mode.c b/package/wwan/meig-cm/src/qmap_bridge_mode.c
new file mode 100644
index 000000000..009d819ea
--- /dev/null
+++ b/package/wwan/meig-cm/src/qmap_bridge_mode.c
@@ -0,0 +1,264 @@
+#include "QMIThread.h"
+
+static size_t meig_fread(const char *filename, void *buf, size_t size) {
+    FILE *fp = fopen(filename , "r");
+    size_t n = 0;
+
+    memset(buf, 0x00, size);
+
+    if (fp) {
+        n = fread(buf, 1, size, fp);
+        if (n <= 0 || n == size) {
+            dbg_time("warnning: fail to fread(%s), fread=%zu, buf_size=%zu: (%s)", filename, n, size, strerror(errno));
+        }
+        fclose(fp);
+    }
+
+    return n > 0 ? n : 0;
+}
+
+static size_t meig_fwrite(const char *filename, const void *buf, size_t size) {
+    FILE *fp = fopen(filename , "w");
+    size_t n = 0;
+
+    if (fp) {
+        n = fwrite(buf, 1, size, fp);
+        if (n != size) {
+            dbg_time("warnning: fail to fwrite(%s), fwrite=%zu, buf_size=%zu: (%s)", filename, n, size, strerror(errno));
+        }
+        fclose(fp);
+    }
+
+    return n > 0 ? n : 0;
+}
+
+static int meig_iface_is_in_bridge(const char *iface) {
+    char filename[256];
+
+    snprintf(filename, sizeof(filename), "/sys/class/net/%s/brport", iface);
+    return (access(filename, F_OK) == 0 || errno != ENOENT);    
+}
+
+int meig_bridge_mode_detect(PROFILE_T *profile) {
+    const char *ifname = profile->qmapnet_adapter ? profile->qmapnet_adapter : profile->usbnet_adapter;
+    const char *driver;
+    char bridge_mode[128];
+    char bridge_ipv4[128];
+    char ipv4[128];
+    char buf[64];
+    size_t n;
+    int in_bridge;
+
+    driver = profile->driver_name;
+    snprintf(bridge_mode, sizeof(bridge_mode), "/sys/class/net/%s/bridge_mode", ifname);
+    snprintf(bridge_ipv4, sizeof(bridge_ipv4), "/sys/class/net/%s/bridge_ipv4", ifname);
+    
+    if (access(bridge_mode, F_OK) && errno == ENOENT) {
+        snprintf(bridge_mode, sizeof(bridge_mode), "/sys/module/%s/parameters/bridge_mode", driver);
+        snprintf(bridge_ipv4, sizeof(bridge_ipv4), "/sys/module/%s/parameters/bridge_ipv4", driver);
+        
+       if (access(bridge_mode, F_OK) && errno == ENOENT) {
+            bridge_mode[0] = '\0';
+        }
+    }
+
+    in_bridge = meig_iface_is_in_bridge(ifname);
+    if (in_bridge) {
+        dbg_time("notice: iface %s had add to bridge\n", ifname);   
+    } else {
+        return 0;
+    }
+
+    if (in_bridge && bridge_mode[0] == '\0') {
+        dbg_time("warnning: can not find bride_mode file for %s\n", ifname);
+        return 1;
+    }
+
+    n = meig_fread(bridge_mode, buf, sizeof(buf));
+    
+    if (in_bridge) {
+        if (n <= 0 || buf[0] == '0') {
+            dbg_time("warnning: should set 1 to bride_mode file for %s\n", ifname);
+            return 1;
+        }
+    }
+    else {
+        if (buf[0] == '0') {
+            return 0;
+        }
+    }
+    
+    memset(ipv4, 0, sizeof(ipv4));
+
+    if (strstr(bridge_ipv4, "/sys/class/net/") || profile->qmap_mode == 0 || profile->qmap_mode == 1) {
+        snprintf(ipv4, sizeof(ipv4), "0x%x", profile->ipv4.Address);
+        dbg_time("echo '%s' > %s", ipv4, bridge_ipv4);
+        meig_fwrite(bridge_ipv4, ipv4, strlen(ipv4));
+    }
+    else {
+        snprintf(ipv4, sizeof(ipv4), "0x%x:%d", profile->ipv4.Address, profile->muxid);
+        dbg_time("echo '%s' > %s", ipv4, bridge_ipv4);
+        meig_fwrite(bridge_ipv4, ipv4, strlen(ipv4));
+    }
+
+    return 1;
+}
+
+int meig_enable_qmi_wwan_rawip_mode(PROFILE_T *profile) {
+    char filename[256];
+    char buf[4];
+    size_t n;
+    FILE *fp;
+
+    if (!qmidev_is_qmiwwan(profile->qmichannel))
+        return 0;
+
+    snprintf(filename, sizeof(filename), "/sys/class/net/%s/qmi/rawip", profile->usbnet_adapter);
+    n = meig_fread(filename, buf, sizeof(buf));
+
+    if (n == 0)
+        return 0;
+
+    if (buf[0] == '1' || buf[0] == 'Y')
+        return 0;
+
+    fp = fopen(filename , "w");
+    if (fp == NULL) {
+        dbg_time("Fail to fopen(%s, \"w\"), errno: %d (%s)", filename, errno, strerror(errno));
+        return 1;
+    }
+
+    buf[0] = 'Y';
+    n = fwrite(buf, 1, 1, fp);
+    if (n != 1) {
+        dbg_time("Fail to fwrite(%s), errno: %d (%s)", filename, errno, strerror(errno));
+        fclose(fp);
+        return 1;
+    }
+    fclose(fp);
+
+    return 0;
+}
+
+int meig_driver_type_detect(PROFILE_T *profile) {
+    if (qmidev_is_gobinet(profile->qmichannel)) {
+        profile->qmi_ops = &gobi_qmidev_ops;
+    }
+    else {
+        profile->qmi_ops = &qmiwwan_qmidev_ops;
+    }
+    qmidev_send = profile->qmi_ops->send;
+
+    return 0;
+}
+
+int meig_qmap_mode_detect(PROFILE_T *profile) {
+    char buf[128];
+    int n;
+    char qmap_netcard[128];
+    struct {
+        char filename[255 * 2];
+        char linkname[255 * 2];
+    } *pl;
+    
+    pl = (typeof(pl)) malloc(sizeof(*pl));
+
+    snprintf(pl->linkname, sizeof(pl->linkname), "/sys/class/net/%s/device/driver", profile->usbnet_adapter);
+    n = readlink(pl->linkname, pl->filename, sizeof(pl->filename));
+    pl->filename[n] = '\0';
+    while (pl->filename[n] != '/')
+        n--;
+    strset(profile->driver_name, &pl->filename[n+1]);
+
+    if (qmidev_is_gobinet(profile->qmichannel))
+    {
+        snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_mode", profile->usbnet_adapter);
+
+        n = meig_fread(pl->filename, buf, sizeof(buf));
+        if (n > 0) {
+            profile->qmap_mode = atoi(buf);
+            
+            if (profile->qmap_mode > 1) {
+                profile->muxid = profile->pdp + 0x80; //muxis is 0x8X for PDN-X
+                sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter, profile->pdp);
+                profile->qmapnet_adapter = strdup(qmap_netcard);
+           } if (profile->qmap_mode == 1) {
+                profile->muxid = 0x81;
+                profile->qmapnet_adapter = strdup(profile->usbnet_adapter);
+           }
+        }
+    }
+    else if(qmidev_is_qmiwwan(profile->qmichannel))
+    {
+        snprintf(pl->filename, sizeof(pl->filename), "/sys/module/%s/parameters/qmap_mode", profile->driver_name);
+
+         if (access(pl->filename, R_OK) == 0) {
+            //Meig Style QMAP qmi_wwan.c
+
+            if (meig_fread(pl->filename, buf, sizeof(buf))) {
+                profile->qmap_mode = atoi(buf);
+
+                if (profile->qmap_mode > 1) {
+                    profile->muxid = profile->pdp + 0x80; //muxis is 0x8X for PDN-X
+                    sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter, profile->pdp);
+                    profile->qmapnet_adapter = strdup(qmap_netcard);
+#if 1 //TODO Ubuntu qmi_wwan 1-1.3:1.4 wwp0s26u1u3i4: renamed from wwan0
+                    if (access(qmap_netcard, R_OK) && errno == ENOENT) {
+                        sprintf(qmap_netcard, "%s.%d", "wwan0", profile->pdp);
+                        free(profile->qmapnet_adapter);
+                        profile->qmapnet_adapter = strdup(qmap_netcard);                        
+                    }
+#endif
+                }
+                else if (profile->qmap_mode == 1) {
+                    profile->muxid = 0x81;
+                    profile->qmapnet_adapter = strdup(profile->usbnet_adapter);
+                }
+            }
+            else if (errno != ENOENT) {
+                dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno, strerror(errno));
+            }
+            else {
+                snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/qmimux%d", profile->pdp - 1);
+
+                if (access(pl->filename, R_OK) == 0) {
+                    //upstream Kernel Style QMAP qmi_wwan.c
+
+                    snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmi/add_mux", profile->usbnet_adapter);
+
+                    n = meig_fread(pl->filename, buf, sizeof(buf));
+                    if (n >= 3) {
+                        profile->qmap_mode = n/3;
+                        if (profile->qmap_mode > 1) {
+                            //PDN-X map to qmimux-X
+                            profile->muxid = (buf[3*(profile->pdp - 1) + 0] - '0')*16 + (buf[3*(profile->pdp - 1) + 1] - '0');
+                            sprintf(qmap_netcard, "qmimux%d", profile->pdp - 1);
+                            profile->qmapnet_adapter = strdup(qmap_netcard);
+                        } else if (profile->qmap_mode == 1){
+                            profile->muxid = (buf[3*0 + 0] - '0')*16 + (buf[3*0 + 1] - '0');
+                            sprintf(qmap_netcard, "qmimux%d", 0);
+                            profile->qmapnet_adapter = strdup(qmap_netcard);
+                        }
+                    }
+                }
+            }
+        }
+        else if (errno != ENOENT) {
+            dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno, strerror(errno));
+        }
+    } 
+    else if (qmidev_is_pciemhi(profile->qmichannel)) {
+        profile->qmap_mode = 1;
+        profile->muxid = 0x81;
+        profile->qmapnet_adapter = strdup(profile->usbnet_adapter);
+    }
+
+    if (profile->qmap_mode) {
+        dbg_time("qmap_mode = %d, muxid = 0x%02x, qmap_netcard = %s",
+            profile->qmap_mode, profile->muxid, profile->qmapnet_adapter);
+    }
+
+    free(pl);
+
+    return 0;
+}
diff --git a/package/wwan/meig-cm/src/udhcpc.c b/package/wwan/meig-cm/src/udhcpc.c
new file mode 100644
index 000000000..acb81b50a
--- /dev/null
+++ b/package/wwan/meig-cm/src/udhcpc.c
@@ -0,0 +1,306 @@
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <endian.h>
+
+#include "util.h"
+#include "QMIThread.h"
+
+static int meig_system(const char *shell_cmd) {
+    dbg_time("%s", shell_cmd);
+    return system(shell_cmd);
+}
+
+static void meig_set_mtu(const char *ifname, int ifru_mtu) {
+    int inet_sock;
+    struct ifreq ifr;
+
+    inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
+
+    if (inet_sock > 0) {
+        strcpy(ifr.ifr_name, ifname);
+
+        if (!ioctl(inet_sock, SIOCGIFMTU, &ifr)) {
+            if (ifr.ifr_ifru.ifru_mtu != ifru_mtu) {
+                dbg_time("change mtu %d -> %d", ifr.ifr_ifru.ifru_mtu , ifru_mtu);
+                ifr.ifr_ifru.ifru_mtu = ifru_mtu;
+                ioctl(inet_sock, SIOCSIFMTU, &ifr);
+            }
+        }
+
+        close(inet_sock);
+    }
+}
+
+static void* udhcpc_thread_function(void* arg) {
+    FILE * udhcpc_fp;
+    char *udhcpc_cmd = (char *)arg;
+
+    if (udhcpc_cmd == NULL)
+        return NULL;
+
+    dbg_time("%s", udhcpc_cmd);
+    udhcpc_fp = popen(udhcpc_cmd, "r");
+    free(udhcpc_cmd);
+    if (udhcpc_fp) {
+        char buf[0xff];
+
+        buf[sizeof(buf)-1] = '\0';
+        while((fgets(buf, sizeof(buf)-1, udhcpc_fp)) != NULL) {
+            if ((strlen(buf) > 1) && (buf[strlen(buf) - 1] == '\n'))
+                buf[strlen(buf) - 1] = '\0';
+            dbg_time("%s", buf);
+        }
+
+        pclose(udhcpc_fp);
+    }
+
+    return NULL;
+}
+
+//#define USE_DHCLIENT
+#ifdef USE_DHCLIENT
+static int dhclient_alive = 0;
+#endif
+static int dibbler_client_alive = 0;
+
+void udhcpc_start(PROFILE_T *profile) {
+    char *ifname = profile->usbnet_adapter;
+    char shell_cmd[128];
+
+    if (profile->qmapnet_adapter) {
+        ifname = profile->qmapnet_adapter;
+    }
+
+    if (profile->rawIP && profile->ipv4.Address && profile->ipv4.Mtu) {
+        meig_set_mtu(ifname, (profile->ipv4.Mtu));
+    }
+
+    if (strcmp(ifname, profile->usbnet_adapter)) {
+        snprintf(shell_cmd, sizeof(shell_cmd), "ifconfig %s up", profile->usbnet_adapter);
+        meig_system(shell_cmd);
+    }
+
+    snprintf(shell_cmd, sizeof(shell_cmd), "ifconfig %s up", ifname);
+    meig_system(shell_cmd);
+
+#if 1 //for bridge mode, only one public IP, so do udhcpc manually
+    if (meig_bridge_mode_detect(profile)) {
+        return;
+    }
+#endif
+
+/* Do DHCP using Routing Discovery Protocol */
+/* This is needed for IPv6 */
+//because must use udhcpc to obtain IP when working on ETH mode,
+//so it is better also use udhcpc to obtain IP when working on IP mode.
+//use the same policy for all modules
+//zpf
+#if 1
+    if (profile->rawIP != 0) //mdm9x07/ec25,ec20 R2.0
+    {
+        if (profile->ipv4.Address) {
+            unsigned char *ip = (unsigned char *)&profile->ipv4.Address;
+            unsigned char *gw = (unsigned char *)&profile->ipv4.Gateway;
+            unsigned char *netmask = (unsigned char *)&profile->ipv4.SubnetMask;
+            unsigned char *dns1 = (unsigned char *)&profile->ipv4.DnsPrimary;
+            unsigned char *dns2 = (unsigned char *)&profile->ipv4.DnsSecondary;
+
+            snprintf(shell_cmd, sizeof(shell_cmd), "ifconfig %s %d.%d.%d.%d netmask %d.%d.%d.%d",ifname,
+                ip[3], ip[2], ip[1], ip[0], netmask[3], netmask[2], netmask[1], netmask[0]);
+            meig_system(shell_cmd);
+
+            //Resetting default routes
+            snprintf(shell_cmd, sizeof(shell_cmd), "route del default gw 0.0.0.0 dev %s", ifname);
+            while(!system(shell_cmd));
+
+            snprintf(shell_cmd, sizeof(shell_cmd), "route add default gw %d.%d.%d.%d dev %s metric 0", gw[3], gw[2], gw[1], gw[0], ifname);
+            meig_system(shell_cmd);
+
+            //Adding DNS
+            if (profile->ipv4.DnsSecondary == 0)
+                profile->ipv4.DnsSecondary = profile->ipv4.DnsPrimary;
+
+            if (dns1[0]) {
+                dbg_time("Adding DNS %d.%d.%d.%d %d.%d.%d.%d", dns1[3], dns1[2], dns1[1], dns1[0], dns2[3], dns2[2], dns2[1], dns2[0]);
+                snprintf(shell_cmd, sizeof(shell_cmd), "echo -n \"nameserver %d.%d.%d.%d\nnameserver %d.%d.%d.%d\n\" > /etc/resolv.conf",
+                    dns1[3], dns1[2], dns1[1], dns1[0], dns2[3], dns2[2], dns2[1], dns2[0]);
+                system(shell_cmd);
+            }
+        }
+
+/*
+        if (profile->ipv6.Address[0] && profile->ipv6.PrefixLengthIPAddr) {
+            unsigned char *ip = (unsigned char *)&profile->ipv4.Address;
+#if 1
+            snprintf(shell_cmd, sizeof(shell_cmd), "ifconfig %s inet6 add %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x/%d",
+                ifname, ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15], profile->ipv6.PrefixLengthIPAddr);
+#else
+            snprintf(shell_cmd, sizeof(shell_cmd), "ip -6 addr add %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x/%d dev %s",
+                ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15], profile->ipv6.PrefixLengthIPAddr, ifname);
+#endif
+            meig_system(shell_cmd);
+            snprintf(shell_cmd, sizeof(shell_cmd), "route -A inet6 add default dev %s", ifname);
+            meig_system(shell_cmd);
+        }
+*/
+        return;
+    }
+#endif
+
+/* Do DHCP using busybox tools */
+    {
+        char udhcpc_cmd[128];
+        pthread_attr_t udhcpc_thread_attr;
+        pthread_t udhcpc_thread_id;
+
+        pthread_attr_init(&udhcpc_thread_attr);
+        pthread_attr_setdetachstate(&udhcpc_thread_attr, PTHREAD_CREATE_DETACHED);
+
+        if (profile->ipv4.Address) {
+#ifdef USE_DHCLIENT
+            snprintf(udhcpc_cmd, sizeof(udhcpc_cmd), "dhclient -4 -d --no-pid %s", ifname);
+            dhclient_alive++;
+#else
+            if (access("/usr/share/udhcpc/default.script", X_OK)) {
+                dbg_time("Fail to access /usr/share/udhcpc/default.script, errno: %d (%s)", errno, strerror(errno));
+            }
+
+            //-f,--foreground    Run in foreground
+            //-b,--background    Background if lease is not obtained
+            //-n,--now        Exit if lease is not obtained
+            //-q,--quit        Exit after obtaining lease
+            //-t,--retries N        Send up to N discover packets (default 3)
+            snprintf(udhcpc_cmd, sizeof(udhcpc_cmd), "busybox udhcpc -f -n -q -t 5 -i %s", ifname);
+#endif
+
+#if 1 //for OpenWrt
+            if (!access("/lib/netifd/dhcp.script", X_OK) && !access("/sbin/ifup", X_OK) && !access("/sbin/ifstatus", X_OK)) {
+                dbg_time("you are use OpenWrt?");
+                dbg_time("should not calling udhcpc manually?");
+                dbg_time("should modify /etc/config/network as below?");
+                dbg_time("config interface wan");
+                dbg_time("\toption ifname	%s", ifname);
+                dbg_time("\toption proto	dhcp");
+                dbg_time("should use \"/sbin/ifstaus wan\" to check %s 's status?", ifname);
+            }
+#endif
+
+#ifdef USE_DHCLIENT            
+            pthread_create(&udhcpc_thread_id, &udhcpc_thread_attr, udhcpc_thread_function, (void*)strdup(udhcpc_cmd));
+            sleep(1);
+#else
+            pthread_create(&udhcpc_thread_id, NULL, udhcpc_thread_function, (void*)strdup(udhcpc_cmd));
+            pthread_join(udhcpc_thread_id, NULL);
+#endif
+        }
+
+        if (profile->ipv6.Address[0] && profile->ipv6.PrefixLengthIPAddr) {
+            const unsigned char *d = (unsigned char *)&profile->ipv6.Address;
+#if 1
+		//module do not support DHCPv6, only support 'Router Solicit'
+		//and it seem if enable /proc/sys/net/ipv6/conf/all/forwarding, Kernel do not send RS
+		const char *forward_file = "/proc/sys/net/ipv6/conf/all/forwarding";
+		int forward_fd = open(forward_file, O_RDONLY);
+		if (forward_fd > 0) {
+			char forward_state[2];
+			read(forward_fd, forward_state, 2);
+			if (forward_state[0] == '1') {
+				//dbg_time("%s enabled, kernel maybe donot send 'Router Solicit'", forward_file);
+			}
+			close(forward_fd);
+            }
+
+            snprintf(shell_cmd, sizeof(shell_cmd),
+                "ip -%d address add %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x/%u dev %s",
+                6, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
+                profile->ipv6.PrefixLengthIPAddr, ifname);
+            meig_system(shell_cmd);
+
+            //ping6 www.qq.com
+            d = (unsigned char *)&profile->ipv6.Gateway;
+            snprintf(shell_cmd, sizeof(shell_cmd),
+                "ip -%d route add default via %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x dev %s",
+                6, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15],
+                ifname);
+            meig_system(shell_cmd);
+
+            if (profile->ipv6.DnsPrimary[0] || profile->ipv6.DnsSecondary[0]) {
+                char dns1str[128], dns2str[128];
+
+                if (profile->ipv6.DnsPrimary[0]) {
+                    d = profile->ipv6.DnsPrimary;
+                    snprintf(dns1str, sizeof(dns1str), "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+                        d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+                }
+
+                if (profile->ipv6.DnsSecondary[0]) {
+                    d = profile->ipv6.DnsSecondary;
+                    snprintf(dns2str, sizeof(dns2str), "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+                        d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+                }
+                
+                update_resolv_conf(6, ifname, profile->ipv6.DnsPrimary[0] ? dns1str : NULL, profile->ipv6.DnsSecondary ? dns2str : NULL);
+            }
+#else
+#ifdef USE_DHCLIENT
+            snprintf(udhcpc_cmd, sizeof(udhcpc_cmd), "dhclient -6 -d --no-pid %s",  ifname);
+            dhclient_alive++;
+#else
+            /*
+                DHCPv6: Dibbler - a portable DHCPv6
+                1. download from http://klub.com.pl/dhcpv6/
+                2. cross-compile
+                    2.1 ./configure --host=arm-linux-gnueabihf
+                    2.2 copy dibbler-client to your board
+                3. mkdir -p /var/log/dibbler/ /var/lib/ on your board
+                4. create /etc/dibbler/client.conf on your board, the content is
+                    log-mode short
+                    log-level 7
+                    iface wwan0 {
+                        ia
+                        option dns-server
+                    }
+                 5. run "dibbler-client start" to get ipV6 address
+                 6. run "route -A inet6 add default dev wwan0" to add default route
+            */
+            snprintf(shell_cmd, sizeof(shell_cmd), "route -A inet6 add default %s", ifname);
+            meig_system(shell_cmd);
+            snprintf(udhcpc_cmd, sizeof(udhcpc_cmd), "dibbler-client run");
+            dibbler_client_alive++;
+#endif
+
+            pthread_create(&udhcpc_thread_id, &udhcpc_thread_attr, udhcpc_thread_function, (void*)strdup(udhcpc_cmd));
+#endif
+        }
+    }
+}
+
+void udhcpc_stop(PROFILE_T *profile) {
+    char *ifname = profile->usbnet_adapter;
+    char shell_cmd[128];
+
+    if (profile->qmapnet_adapter) {
+        ifname = profile->qmapnet_adapter;
+    }
+
+#ifdef USE_DHCLIENT
+    if (dhclient_alive) {
+        system("killall dhclient");
+        dhclient_alive = 0;
+    }
+#endif
+    if (dibbler_client_alive) {
+        system("killall dibbler-client");
+        dibbler_client_alive = 0;
+    }
+
+    snprintf(shell_cmd, sizeof(shell_cmd), "ifconfig %s down", ifname);
+    meig_system(shell_cmd);
+    snprintf(shell_cmd, sizeof(shell_cmd), "ifconfig %s 0.0.0.0", ifname);
+    meig_system(shell_cmd);
+}
diff --git a/package/wwan/meig-cm/src/util.c b/package/wwan/meig-cm/src/util.c
new file mode 100644
index 000000000..0d55a70b6
--- /dev/null
+++ b/package/wwan/meig-cm/src/util.c
@@ -0,0 +1,208 @@
+#include <sys/time.h>
+#if defined(__STDC__)
+#include <stdarg.h>
+#define __V(x)	x
+#else
+#include <varargs.h>
+#define __V(x)	(va_alist) va_dcl
+#define const
+#define volatile
+#endif
+
+#include <syslog.h>
+
+#include "QMIThread.h"
+/*
+ * Use clock_gettime instead of gettimeofday
+ * -- Warnning:
+ * There is a risk that, the system may sync time at anytime.
+ * if gettimeofday may returns just before the sync time point,
+ * the funtion pthread_cond_timedwait will failed.
+ * -- use clock_gettime to get relative time will avoid the risk.
+ */
+
+void setTimespecRelative(struct timespec *p_ts, long long msec)
+{
+    struct timespec ts;
+
+    /* 
+       CLOCK_REALTIME              system time from 1970-1-1
+       CLOCK_MONOTONIC             system start time, cannot be changed
+       CLOCK_PROCESS_CPUTIME_ID    the process running time
+       CLOCK_THREAD_CPUTIME_ID     the thread running time
+     */
+
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    p_ts->tv_sec = ts.tv_sec + (msec / 1000);
+    p_ts->tv_nsec = ts.tv_nsec + (msec % 1000) * 1000L * 1000L;
+}
+
+int pthread_cond_timeout_np(pthread_cond_t *cond, pthread_mutex_t * mutex, unsigned msecs) {
+    if (msecs != 0) {
+        struct timespec ts;
+        setTimespecRelative(&ts, msecs);
+        return pthread_cond_timedwait(cond, mutex, &ts);
+    } else {
+        return pthread_cond_wait(cond, mutex);
+    }
+}
+
+void cond_setclock_attr(pthread_cond_t *cond, clockid_t clock)
+{
+    /* set relative time, for pthread_cond_timedwait */
+    pthread_condattr_t attr;
+    pthread_condattr_init (&attr);
+    pthread_condattr_setclock(&attr, clock);
+    pthread_cond_init(cond, &attr);
+    pthread_condattr_destroy (&attr);
+}
+
+const char * get_time(void) {
+    static char time_buf[50];
+    struct timeval  tv;
+    time_t time;
+    suseconds_t millitm;
+    struct tm *ti;
+
+    gettimeofday (&tv, NULL);
+
+    time= tv.tv_sec;
+    millitm = (tv.tv_usec + 500) / 1000;
+
+    if (millitm == 1000) {
+        ++time;
+        millitm = 0;
+    }
+
+    ti = localtime(&time);
+    sprintf(time_buf, "%02d-%02d_%02d:%02d:%02d:%03d", ti->tm_mon+1, ti->tm_mday, ti->tm_hour, ti->tm_min, ti->tm_sec, (int)millitm);
+    return time_buf;
+}
+
+FILE *logfilefp = NULL;
+
+const int i = 1;
+#define is_bigendian() ( (*(char*)&i) == 0 )
+
+USHORT le16_to_cpu(USHORT v16) {
+    USHORT tmp = v16;
+    if (is_bigendian()) {
+        unsigned char *s = (unsigned char *)(&v16);
+        unsigned char *d = (unsigned char *)(&tmp);
+        d[0] = s[1];
+        d[1] = s[0];
+    }
+    return tmp;
+}
+
+UINT  le32_to_cpu (UINT v32) {
+    UINT tmp = v32;
+    if (is_bigendian()) {
+        unsigned char *s = (unsigned char *)(&v32);
+        unsigned char *d = (unsigned char *)(&tmp);
+        d[0] = s[3];
+        d[1] = s[2];
+        d[2] = s[1];
+        d[3] = s[0];
+    }
+    return tmp;
+}
+
+UINT meig_swap32(UINT v32) {
+    UINT tmp = v32;
+    {
+        unsigned char *s = (unsigned char *)(&v32);
+        unsigned char *d = (unsigned char *)(&tmp);
+        d[0] = s[3];
+        d[1] = s[2];
+        d[2] = s[1];
+        d[3] = s[0];
+    }
+    return tmp;
+}
+
+USHORT cpu_to_le16(USHORT v16) {
+    USHORT tmp = v16;
+    if (is_bigendian()) {
+        unsigned char *s = (unsigned char *)(&v16);
+        unsigned char *d = (unsigned char *)(&tmp);
+        d[0] = s[1];
+        d[1] = s[0];
+    }
+    return tmp;
+}
+
+UINT cpu_to_le32 (UINT v32) {
+    UINT tmp = v32;
+    if (is_bigendian()) {
+        unsigned char *s = (unsigned char *)(&v32);
+        unsigned char *d = (unsigned char *)(&tmp);
+        d[0] = s[3];
+        d[1] = s[2];
+        d[2] = s[1];
+        d[3] = s[0];
+    }
+    return tmp;
+}
+
+void update_resolv_conf(int iptype, const char *ifname, const char *dns1, const char *dns2) {
+    const char *dns_file = "/etc/resolv.conf";
+    FILE *dns_fp;
+    char dns_line[256];
+    #define MAX_DNS 16
+    char *dns_info[MAX_DNS];
+    char dns_tag[64];
+    int dns_match = 0;
+    int i;
+
+    snprintf(dns_tag, sizeof(dns_tag), "# IPV%d %s", iptype, ifname);
+
+    for (i = 0; i < MAX_DNS; i++)
+        dns_info[i] = NULL;
+    
+    dns_fp = fopen(dns_file, "r");
+    if (dns_fp) {
+        i = 0;    
+        dns_line[sizeof(dns_line)-1] = '\0';
+        
+        while((fgets(dns_line, sizeof(dns_line)-1, dns_fp)) != NULL) {
+            if ((strlen(dns_line) > 1) && (dns_line[strlen(dns_line) - 1] == '\n'))
+                dns_line[strlen(dns_line) - 1] = '\0';
+            //dbg_time("%s", dns_line);
+            if (strstr(dns_line, dns_tag)) {
+                dns_match++;
+                continue;
+            }
+            dns_info[i++] = strdup(dns_line);
+            if (i == MAX_DNS)
+                break;
+        }
+
+        fclose(dns_fp);
+    }
+    else if (errno != ENOENT) {
+        dbg_time("fopen %s fail, errno:%d (%s)", dns_file, errno, strerror(errno));
+        return;
+    }
+    
+    if (dns1 == NULL && dns_match == 0)
+        return;
+
+    dns_fp = fopen(dns_file, "w");
+    if (dns_fp) {
+        if (dns1)
+            fprintf(dns_fp, "nameserver %s %s\n", dns1, dns_tag);
+        if (dns2)
+            fprintf(dns_fp, "nameserver %s %s\n", dns2, dns_tag);
+        
+        for (i = 0; i < MAX_DNS && dns_info[i]; i++)
+            fprintf(dns_fp, "%s\n", dns_info[i]);
+        fclose(dns_fp);
+    }
+    else {
+        dbg_time("fopen %s fail, errno:%d (%s)", dns_file, errno, strerror(errno));
+    }
+
+    for (i = 0; i < MAX_DNS && dns_info[i]; i++)
+        free(dns_info[i]);
+}
diff --git a/package/wwan/meig-cm/src/util.h b/package/wwan/meig-cm/src/util.h
new file mode 100644
index 000000000..17802fabc
--- /dev/null
+++ b/package/wwan/meig-cm/src/util.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UTILS_H_
+#define _UTILS_H_
+
+#include <stddef.h>
+
+struct listnode
+{
+    struct listnode *next;
+    struct listnode *prev;
+};
+
+#define node_to_item(node, container, member) \
+    (container *) (((char*) (node)) - offsetof(container, member))
+
+#define list_declare(name) \
+    struct listnode name = { \
+        .next = &name, \
+        .prev = &name, \
+    }
+
+#define list_for_each(node, list) \
+    for (node = (list)->next; node != (list); node = node->next)
+
+#define list_for_each_reverse(node, list) \
+    for (node = (list)->prev; node != (list); node = node->prev)
+
+void list_init(struct listnode *list);
+void list_add_tail(struct listnode *list, struct listnode *item);
+void list_add_head(struct listnode *head, struct listnode *item);
+void list_remove(struct listnode *item);
+
+#define list_empty(list) ((list) == (list)->next)
+#define list_head(list) ((list)->next)
+#define list_tail(list) ((list)->prev)
+
+int epoll_register(int  epoll_fd, int  fd, unsigned int events);
+int epoll_deregister(int  epoll_fd, int  fd);
+const char * get_time(void);
+#endif
diff --git a/package/wwan/quectel_MHI/Makefile b/package/wwan/quectel_MHI/Makefile
index 6322519d6..7716efbba 100644
--- a/package/wwan/quectel_MHI/Makefile
+++ b/package/wwan/quectel_MHI/Makefile
@@ -8,7 +8,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=pcie_mhi
-PKG_VERSION:=1.3.6
+PKG_VERSION:=1.3.8
 PKG_RELEASE:=1
 
 include $(INCLUDE_DIR)/kernel.mk
diff --git a/package/wwan/quectel_MHI/src/ReleaseNote.txt b/package/wwan/quectel_MHI/src/ReleaseNote.txt
index 877d24cb8..8a55788d1 100644
--- a/package/wwan/quectel_MHI/src/ReleaseNote.txt
+++ b/package/wwan/quectel_MHI/src/ReleaseNote.txt
@@ -1,126 +1,132 @@
-Release Notes
-[V1.3.7]
-Date:   27/03/2024
-enhancement:
-  1. support SDX35's PID/VID
-  2. support IPQ QSDK MHI used with rmnetdata driver
-
-Release Notes
-[V1.3.6]
-Date:   01/08/2023
-enhancement:
-  1. support Linux Kernel V6.4
-  2. support change mtu
-fix:
-  1. fix compile error on ipq's spf12.x
-
-Release Notes
-[V1.3.5]
-Date:   25/02/2023
-enhancement:
-  1. support efuse SDX sleep
-  2. support IPQ9574 SFE
-fix:
-  1. fix cannot find the node when dialing. Nodes in the /sys/bus/mhi_q/devices directory named hex
-
-[V1.3.4]
-Date:   12/8/2022
-enhancement:
-  1. only allow to enable autosuspend when module is in MHI_EE_AMSS
-  2. show pcie link speed and width when driver probe
-  3. check pcie link status by read pcie vid and pid when driver probe,
-     if pcie link is down, return -EIO
-  4. support RM520 (1eac:1004)
-  5. support qmap command packet
-fix:
-  1. fix tx queue is wrong stop when do uplink TPUT
-  2. fix after QFirehose, module fail to bootup at very small probability
-  3. mhi uci add mutex lock for concurrent reads/writes
-
-[V1.3.3]
-Date:   30/6/2022
-enhancement:
-  1. remove one un-necessary kmalloc when do qfirehose
-  2. support mhi monitor (like usbmon), usage: cat /sys/kernel/debug/mhi_q/0306_00\:01.00/mhimon
-  3. set ring size of event 0 to 256 (from 1024), required by x6x
-  4. support PCIE local network card mhi_swip0 (chan 46/47), default disabled
-  5. porting IPQ5018 mhi rate controll code from spf11.5 
-  6. set pcie rmnet download max qmap packet size to 15KB (same to IPQ MHI Driver)
-  7. support set different mac address for rmnet net card
-  8. when mhi netdev fail to malloc, use delay_work instead work
-  9. optimize code for 'when driver load, modem is still in MHI_EE_PTHRU' 
-fix:
- 1. Fix not synchronize access rp/wp when mhi_queue_xxx and mhi_process_xxx_ring run on different CPU
- 2. set dma mask when driver probe, some SOC like rpi_4 need it
-
-[V1.3.2]
-Date:   12/16/2021
-enhancement:
-  1. support Linux Kernel V5.14
-  2. mhi_netdev_quectel.c do not print log in softirq context
-
-[V1.3.1]
-Date:   9/26/2021
-enhancement:
-fix:
-
-[V1.3.0.19]
-Date:   9/18/2021
-enhancement:
-  1. support sdx62 (17cb:0308)
-	2. support IPQ5018's NSS
-	3. use 'qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c' instead myself rmnet_nss.c
-	   and pcie_mhi.ko must load after then rmnet_nss.ko
-	4. allow bhi irq is not 0 (for ipq5018)
-fix:
-
-[V1.3.0.18]
-Date:   4/14/2021
-enhancement:
-  1. support mbim multiple call, usage:
-     # insmod pcie_mhi.ko mhi_mbim_enabeld=1 qmap_mode=4
-     # quectel-mbim-proxy -d /dev/mhi_MBIM &
-     # quectel-CM -n X
-fix:
-
-[V1.3.0.17]
-Date:   3/11/2021
-enhancement:
-fix:
-  1. fix CPU loading very high when TPUT test when only one MSI interrupt
-  2. fix error on latest X24 modem
-
-[V1.3.0.16]
-Date:   11/18/2020
-enhancement:
-fix:
-  1. add ring size to 32, for in-bound chan, if one ring is full, modem will not generate MSI interrupt for all chan
- 
-[V1.3.0.15]
-Date:   10/30/2020
-enhancement:
-	1. support multi-modems, named as /dev/mhi_<chan_name>X
-fix:
-  1. fix compile error on kernel v5.8
- 
-[V1.3.0.14]
-Date:   10/9/2020
-enhancement:
-  1. suppport EM120&EM160
-fix:
-  1. fix compile error on kernel v5.6
-  2. support runtime suspend
-
-[V1.3.0.13]
-Date:   9/7/2020 
-enhancement:
-  1. suppport EM120&EM160
-fix:
-  1. fix error on X55 + PCIE2.0(e.g IPQ4019)
-  2. support runtime suspend
-
-[V1.3.0.12]
-Date:   7/7/2020 
-enhancement:
-  1. suppport create only none netcard (enabled by marco MHI_NETDEV_ONE_CARD_MODE),
+Release Notes
+[V1.3.8]
+Date:   27/06/2024
+enhancement:
+  1. support SDX7x's PID/VID
+
+Release Notes
+[V1.3.7]
+Date:   27/03/2024
+enhancement:
+  1. support SDX35's PID/VID
+  2. support IPQ QSDK MHI used with rmnetdata driver
+
+Release Notes
+[V1.3.6]
+Date:   01/08/2023
+enhancement:
+  1. support Linux Kernel V6.4
+  2. support change mtu
+fix:
+  1. fix compile error on ipq's spf12.x
+
+Release Notes
+[V1.3.5]
+Date:   25/02/2023
+enhancement:
+  1. support efuse SDX sleep
+  2. support IPQ9574 SFE
+fix:
+  1. fix cannot find the node when dialing. Nodes in the /sys/bus/mhi_q/devices directory named hex
+
+[V1.3.4]
+Date:   12/8/2022
+enhancement:
+  1. only allow to enable autosuspend when module is in MHI_EE_AMSS
+  2. show pcie link speed and width when driver probe
+  3. check pcie link status by read pcie vid and pid when driver probe,
+     if pcie link is down, return -EIO
+  4. support RM520 (1eac:1004)
+  5. support qmap command packet
+fix:
+  1. fix tx queue is wrong stop when do uplink TPUT
+  2. fix after QFirehose, module fail to bootup at very small probability
+  3. mhi uci add mutex lock for concurrent reads/writes
+
+[V1.3.3]
+Date:   30/6/2022
+enhancement:
+  1. remove one un-necessary kmalloc when do qfirehose
+  2. support mhi monitor (like usbmon), usage: cat /sys/kernel/debug/mhi_q/0306_00\:01.00/mhimon
+  3. set ring size of event 0 to 256 (from 1024), required by x6x
+  4. support PCIE local network card mhi_swip0 (chan 46/47), default disabled
+  5. porting IPQ5018 mhi rate controll code from spf11.5 
+  6. set pcie rmnet download max qmap packet size to 15KB (same to IPQ MHI Driver)
+  7. support set different mac address for rmnet net card
+  8. when mhi netdev fail to malloc, use delay_work instead work
+  9. optimize code for 'when driver load, modem is still in MHI_EE_PTHRU' 
+fix:
+ 1. Fix not synchronize access rp/wp when mhi_queue_xxx and mhi_process_xxx_ring run on different CPU
+ 2. set dma mask when driver probe, some SOC like rpi_4 need it
+
+[V1.3.2]
+Date:   12/16/2021
+enhancement:
+  1. support Linux Kernel V5.14
+  2. mhi_netdev_quectel.c do not print log in softirq context
+
+[V1.3.1]
+Date:   9/26/2021
+enhancement:
+fix:
+
+[V1.3.0.19]
+Date:   9/18/2021
+enhancement:
+  1. support sdx62 (17cb:0308)
+	2. support IPQ5018's NSS
+	3. use 'qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c' instead myself rmnet_nss.c
+	   and pcie_mhi.ko must load after then rmnet_nss.ko
+	4. allow bhi irq is not 0 (for ipq5018)
+fix:
+
+[V1.3.0.18]
+Date:   4/14/2021
+enhancement:
+  1. support mbim multiple call, usage:
+     # insmod pcie_mhi.ko mhi_mbim_enabeld=1 qmap_mode=4
+     # quectel-mbim-proxy -d /dev/mhi_MBIM &
+     # quectel-CM -n X
+fix:
+
+[V1.3.0.17]
+Date:   3/11/2021
+enhancement:
+fix:
+  1. fix CPU loading very high when TPUT test when only one MSI interrupt
+  2. fix error on latest X24 modem
+
+[V1.3.0.16]
+Date:   11/18/2020
+enhancement:
+fix:
+  1. add ring size to 32, for in-bound chan, if one ring is full, modem will not generate MSI interrupt for all chan
+ 
+[V1.3.0.15]
+Date:   10/30/2020
+enhancement:
+	1. support multi-modems, named as /dev/mhi_<chan_name>X
+fix:
+  1. fix compile error on kernel v5.8
+ 
+[V1.3.0.14]
+Date:   10/9/2020
+enhancement:
+  1. suppport EM120&EM160
+fix:
+  1. fix compile error on kernel v5.6
+  2. support runtime suspend
+
+[V1.3.0.13]
+Date:   9/7/2020 
+enhancement:
+  1. suppport EM120&EM160
+fix:
+  1. fix error on X55 + PCIE2.0(e.g IPQ4019)
+  2. support runtime suspend
+
+[V1.3.0.12]
+Date:   7/7/2020 
+enhancement:
+  1. suppport create only none netcard (enabled by marco MHI_NETDEV_ONE_CARD_MODE),
 fix:
\ No newline at end of file
diff --git a/package/wwan/quectel_MHI/src/controllers/mhi_qcom.c b/package/wwan/quectel_MHI/src/controllers/mhi_qcom.c
index df6ce193c..70dd9172b 100644
--- a/package/wwan/quectel_MHI/src/controllers/mhi_qcom.c
+++ b/package/wwan/quectel_MHI/src/controllers/mhi_qcom.c
@@ -1,715 +1,715 @@
-/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/debugfs.h>
-#include <linux/device.h>
-#include <linux/dma-direction.h>
-#include <linux/list.h>
-#include <linux/of.h>
-#include <linux/memblock.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/pm_runtime.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/interrupt.h>
-#include <linux/version.h>
-#include "../core/mhi.h"
-#include "mhi_qcom.h"
-
-#if 1
-#ifndef PCI_IRQ_MSI
-#define PCI_IRQ_MSI		(1 << 1) /* Allow MSI interrupts */
-
-#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,53 ))
-int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
-{
-	int nvec = maxvec;
-	int rc;
-
-	if (maxvec < minvec)
-		return -ERANGE;
-
-	do {
-		rc = pci_enable_msi_block(dev, nvec);
-		if (rc < 0) {
-			return rc;
-		} else if (rc > 0) {
-			if (rc < minvec)
-				return -ENOSPC;
-			nvec = rc;
-		}
-	} while (rc);
-
-	return nvec;
-}
-#endif
-
-static int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
-		      unsigned int max_vecs, unsigned int flags)
-{
-	return pci_enable_msi_range(dev, min_vecs, max_vecs);
-}
-
-static void pci_free_irq_vectors(struct pci_dev *dev)
-{
-	pci_disable_msi(dev);
-}
-
-static int pci_irq_vector(struct pci_dev *dev, unsigned int nr)
-{
-    return dev->irq + nr;
-}
-#endif
-#endif
-
-static struct pci_device_id mhi_pcie_device_id[] = {
-	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0303)}, //SDX20
-	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0304)}, //SDX24
-	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0305)},
-	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0306)}, //SDX55
-	{PCI_DEVICE(0x2C7C, 0x0512)},
-	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, MHI_PCIE_DEBUG_ID)},
-	{0},
-};
-
-MODULE_DEVICE_TABLE(pci, mhi_pcie_device_id);
-
-static struct pci_driver mhi_pcie_driver;
-
-void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl)
-{
-	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
-	struct pci_dev *pci_dev = mhi_dev->pci_dev;
-
-	pci_free_irq_vectors(pci_dev);
-	iounmap(mhi_cntrl->regs);
-	mhi_cntrl->regs = NULL;
-	pci_clear_master(pci_dev);
-	pci_release_region(pci_dev, mhi_dev->resn);
-	pci_disable_device(pci_dev);
-}
-
-static int mhi_init_pci_dev(struct mhi_controller *mhi_cntrl)
-{
-	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
-	struct pci_dev *pci_dev = mhi_dev->pci_dev;
-	int ret;
-	resource_size_t start, len;
-	int i;
-
-	mhi_dev->resn = MHI_PCI_BAR_NUM;
-	ret = pci_assign_resource(pci_dev, mhi_dev->resn);
-	if (ret) {
-		MHI_ERR("Error assign pci resources, ret:%d\n", ret);
-		return ret;
-	}
-
-	ret = pci_enable_device(pci_dev);
-	if (ret) {
-		MHI_ERR("Error enabling device, ret:%d\n", ret);
-		goto error_enable_device;
-	}
-
-	ret = pci_request_region(pci_dev, mhi_dev->resn, "mhi");
-	if (ret) {
-		MHI_ERR("Error pci_request_region, ret:%d\n", ret);
-		goto error_request_region;
-	}
-
-	pci_set_master(pci_dev);
-
-	start = pci_resource_start(pci_dev, mhi_dev->resn);
-	len = pci_resource_len(pci_dev, mhi_dev->resn);
-	mhi_cntrl->regs = ioremap_nocache(start, len);
-	MHI_LOG("mhi_cntrl->regs = %p\n", mhi_cntrl->regs);
-	if (!mhi_cntrl->regs) {
-		MHI_ERR("Error ioremap region\n");
-		goto error_ioremap;
-	}
-
-	ret = pci_alloc_irq_vectors(pci_dev, 1, mhi_cntrl->msi_required, PCI_IRQ_MSI);
-	if (IS_ERR_VALUE((ulong)ret) || ret < mhi_cntrl->msi_required) {
-		if (ret == -ENOSPC) {
-			/* imx_3.14.52_1.1.0_ga
-			diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
-			index f06e8f0..6a9614f 100644
-			--- a/drivers/pci/host/pcie-designware.c
-			+++ b/drivers/pci/host/pcie-designware.c
-			@@ -376,6 +376,13 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev,
-				if (msgvec > 5)
-					msgvec = 0;
-
-			+#if 1 //Add by Quectel 20190419
-			+       if (msgvec > 0 && pdev->vendor == 0x17cb) {
-			+               dev_info(&pdev->dev, "%s quectel fixup pos=%d, msg_ctr=%04x, msgvec=%d\n",  __func__, desc->msi_attrib.pos, msg_ctr, msgvec);
-			+               msgvec = 0;
-			+       }
-			+#endif
-			+
-				irq = assign_irq((1 << msgvec), desc, &pos);
-				if (irq < 0)
-					return irq;
-			*/
-		}
-		//imx_4.1.15_2.0.0_ga & DELL_OPTIPLEX_7010 only alloc one msi interrupt for one pcie device
-		if (ret != 1) {
-			MHI_ERR("Failed to enable MSI, ret=%d, msi_required=%d\n", ret, mhi_cntrl->msi_required);
-			goto error_req_msi;
-		}
-	}
-
-	mhi_cntrl->msi_allocated = ret;
-	MHI_LOG("msi_required = %d, msi_allocated = %d, msi_irq = %u\n", mhi_cntrl->msi_required, mhi_cntrl->msi_allocated, pci_dev->irq);
-
-	for (i = 0; i < mhi_cntrl->msi_allocated; i++) {
-		mhi_cntrl->irq[i] = pci_irq_vector(pci_dev, i);
-		if (mhi_cntrl->irq[i] < 0) {
-			ret = mhi_cntrl->irq[i];
-			goto error_get_irq_vec;
-		}
-	}
-
-#if 0
-	/* configure runtime pm */
-	pm_runtime_set_autosuspend_delay(&pci_dev->dev, MHI_RPM_SUSPEND_TMR_MS);
-	pm_runtime_dont_use_autosuspend(&pci_dev->dev);
-	pm_suspend_ignore_children(&pci_dev->dev, true);
-
-	/*
-	 * pci framework will increment usage count (twice) before
-	 * calling local device driver probe function.
-	 * 1st pci.c pci_pm_init() calls pm_runtime_forbid
-	 * 2nd pci-driver.c local_pci_probe calls pm_runtime_get_sync
-	 * Framework expect pci device driver to call
-	 * pm_runtime_put_noidle to decrement usage count after
-	 * successful probe and and call pm_runtime_allow to enable
-	 * runtime suspend.
-	 */
-	pm_runtime_mark_last_busy(&pci_dev->dev);
-	pm_runtime_put_noidle(&pci_dev->dev);
-#endif
-
-	return 0;
-
-error_get_irq_vec:
-	pci_free_irq_vectors(pci_dev);
-
-error_req_msi:
-	iounmap(mhi_cntrl->regs);
-
-error_ioremap:
-	pci_clear_master(pci_dev);
-
-error_request_region:
-	pci_disable_device(pci_dev);
-
-error_enable_device:
-	pci_release_region(pci_dev, mhi_dev->resn);
-
-	return ret;
-}
-
-#ifdef CONFIG_PM
-static int mhi_runtime_idle(struct device *dev)
-{
-	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
-
-	MHI_LOG("Entered returning -EBUSY\n");
-
-	/*
-	 * RPM framework during runtime resume always calls
-	 * rpm_idle to see if device ready to suspend.
-	 * If dev.power usage_count count is 0, rpm fw will call
-	 * rpm_idle cb to see if device is ready to suspend.
-	 * if cb return 0, or cb not defined the framework will
-	 * assume device driver is ready to suspend;
-	 * therefore, fw will schedule runtime suspend.
-	 * In MHI power management, MHI host shall go to
-	 * runtime suspend only after entering MHI State M2, even if
-	 * usage count is 0.  Return -EBUSY to disable automatic suspend.
-	 */
-	return -EBUSY;
-}
-
-static int mhi_runtime_suspend(struct device *dev)
-{
-	int ret = 0;
-	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
-
-	MHI_LOG("Enter\n");
-
-	mutex_lock(&mhi_cntrl->pm_mutex);
-
-	ret = mhi_pm_suspend(mhi_cntrl);
-	if (ret) {
-		MHI_LOG("Abort due to ret:%d\n", ret);
-		goto exit_runtime_suspend;
-	}
-
-	ret = mhi_arch_link_off(mhi_cntrl, true);
-	if (ret)
-		MHI_ERR("Failed to Turn off link ret:%d\n", ret);
-
-exit_runtime_suspend:
-	mutex_unlock(&mhi_cntrl->pm_mutex);
-	MHI_LOG("Exited with ret:%d\n", ret);
-
-	return ret;
-}
-
-static int mhi_runtime_resume(struct device *dev)
-{
-	int ret = 0;
-	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
-	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
-
-	MHI_LOG("Enter\n");
-
-	mutex_lock(&mhi_cntrl->pm_mutex);
-
-	if (!mhi_dev->powered_on) {
-		MHI_LOG("Not fully powered, return success\n");
-		mutex_unlock(&mhi_cntrl->pm_mutex);
-		return 0;
-	}
-
-	/* turn on link */
-	ret = mhi_arch_link_on(mhi_cntrl);
-	if (ret)
-		goto rpm_resume_exit;
-
-	/* enter M0 state */
-	ret = mhi_pm_resume(mhi_cntrl);
-
-rpm_resume_exit:
-	mutex_unlock(&mhi_cntrl->pm_mutex);
-	MHI_LOG("Exited with :%d\n", ret);
-
-	return ret;
-}
-
-static int mhi_system_resume(struct device *dev)
-{
-	int ret = 0;
-	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
-
-	ret = mhi_runtime_resume(dev);
-	if (ret) {
-		MHI_ERR("Failed to resume link\n");
-	} else {
-		pm_runtime_set_active(dev);
-		pm_runtime_enable(dev);
-	}
-
-	return ret;
-}
-
-int mhi_system_suspend(struct device *dev)
-{
-	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
-
-	MHI_LOG("Entered\n");
-
-	/* if rpm status still active then force suspend */
-	if (!pm_runtime_status_suspended(dev))
-		return mhi_runtime_suspend(dev);
-
-	pm_runtime_set_suspended(dev);
-	pm_runtime_disable(dev);
-
-	MHI_LOG("Exit\n");
-	return 0;
-}
-#endif
-
-/* checks if link is down */
-static int mhi_link_status(struct mhi_controller *mhi_cntrl, void *priv)
-{
-	struct mhi_dev *mhi_dev = priv;
-	u16 dev_id;
-	int ret;
-
-	/* try reading device id, if dev id don't match, link is down */
-	ret = pci_read_config_word(mhi_dev->pci_dev, PCI_DEVICE_ID, &dev_id);
-
-	return (ret || dev_id != mhi_cntrl->dev_id) ? -EIO : 0;
-}
-
-static int mhi_runtime_get(struct mhi_controller *mhi_cntrl, void *priv)
-{
-	struct mhi_dev *mhi_dev = priv;
-	struct device *dev = &mhi_dev->pci_dev->dev;
-
-	return pm_runtime_get(dev);
-}
-
-static void mhi_runtime_put(struct mhi_controller *mhi_cntrl, void *priv)
-{
-	struct mhi_dev *mhi_dev = priv;
-	struct device *dev = &mhi_dev->pci_dev->dev;
-
-	pm_runtime_put_noidle(dev);
-}
-
-static void mhi_status_cb(struct mhi_controller *mhi_cntrl,
-			  void *priv,
-			  enum MHI_CB reason)
-{
-	struct mhi_dev *mhi_dev = priv;
-	struct device *dev = &mhi_dev->pci_dev->dev;
-
-	if (reason == MHI_CB_IDLE) {
-		MHI_LOG("Schedule runtime suspend 1\n");
-		pm_runtime_mark_last_busy(dev);
-		pm_request_autosuspend(dev);
-	}
-}
-
-int mhi_debugfs_trigger_m0(void *data, u64 val)
-{
-	struct mhi_controller *mhi_cntrl = data;
-	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
-
-	MHI_LOG("Trigger M3 Exit\n");
-	pm_runtime_get(&mhi_dev->pci_dev->dev);
-	pm_runtime_put(&mhi_dev->pci_dev->dev);
-
-	return 0;
-}
-
-int mhi_debugfs_trigger_m3(void *data, u64 val)
-{
-	struct mhi_controller *mhi_cntrl = data;
-	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
-
-	MHI_LOG("Trigger M3 Entry\n");
-	pm_runtime_mark_last_busy(&mhi_dev->pci_dev->dev);
-	pm_request_autosuspend(&mhi_dev->pci_dev->dev);
-
-	return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m0_fops, NULL,
-			mhi_debugfs_trigger_m0, "%llu\n");
-
-DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m3_fops, NULL,
-			mhi_debugfs_trigger_m3, "%llu\n");
-
-static int mhi_init_debugfs_trigger_go(void *data, u64 val)
-{
-	struct mhi_controller *mhi_cntrl = data;
-
-	MHI_LOG("Trigger power up sequence\n");
-
-	mhi_async_power_up(mhi_cntrl);
-
-	return 0;
-}
-DEFINE_SIMPLE_ATTRIBUTE(mhi_init_debugfs_trigger_go_fops, NULL,
-			mhi_init_debugfs_trigger_go, "%llu\n");
-
-
-int mhi_init_debugfs_debug_show(struct seq_file *m, void *d)
-{
-	seq_puts(m, "Enable debug mode to debug  external soc\n");
-	seq_puts(m,
-		 "Usage:  echo 'devid,timeout,domain,smmu_cfg' > debug_mode\n");
-	seq_puts(m, "No spaces between parameters\n");
-	seq_puts(m, "\t1.  devid : 0 or pci device id to register\n");
-	seq_puts(m, "\t2.  timeout: mhi cmd/state transition timeout\n");
-	seq_puts(m, "\t3.  domain: Rootcomplex\n");
-	seq_puts(m, "\t4.  smmu_cfg: smmu configuration mask:\n");
-	seq_puts(m, "\t\t- BIT0: ATTACH\n");
-	seq_puts(m, "\t\t- BIT1: S1 BYPASS\n");
-	seq_puts(m, "\t\t-BIT2: FAST_MAP\n");
-	seq_puts(m, "\t\t-BIT3: ATOMIC\n");
-	seq_puts(m, "\t\t-BIT4: FORCE_COHERENT\n");
-	seq_puts(m, "\t\t-BIT5: GEOMETRY\n");
-	seq_puts(m, "\tAll timeout are in ms, enter 0 to keep default\n");
-	seq_puts(m, "Examples inputs: '0x307,10000'\n");
-	seq_puts(m, "\techo '0,10000,1'\n");
-	seq_puts(m, "\techo '0x307,10000,0,0x3d'\n");
-	seq_puts(m, "firmware image name will be changed to debug.mbn\n");
-
-	return 0;
-}
-
-static int mhi_init_debugfs_debug_open(struct inode *node, struct file *file)
-{
-	return single_open(file, mhi_init_debugfs_debug_show, NULL);
-}
-
-static ssize_t mhi_init_debugfs_debug_write(struct file *fp,
-					    const char __user *ubuf,
-					    size_t count,
-					    loff_t *pos)
-{
-	char *buf = kmalloc(count + 1, GFP_KERNEL);
-	/* #,devid,timeout,domain,smmu-cfg */
-	int args[5] = {0};
-	static char const *dbg_fw = "debug.mbn";
-	int ret;
-	struct mhi_controller *mhi_cntrl = fp->f_inode->i_private;
-	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
-	struct pci_device_id *id;
-
-	if (!buf)
-		return -ENOMEM;
-
-	ret = copy_from_user(buf, ubuf, count);
-	if (ret)
-		goto error_read;
-	buf[count] = 0;
-	get_options(buf, ARRAY_SIZE(args), args);
-	kfree(buf);
-
-	/* override default parameters */
-	mhi_cntrl->fw_image = dbg_fw;
-	mhi_cntrl->edl_image = dbg_fw;
-
-	if (args[0] >= 2 && args[2])
-		mhi_cntrl->timeout_ms = args[2];
-
-	if (args[0] >= 3 && args[3])
-		mhi_cntrl->domain = args[3];
-
-	if (args[0] >= 4 && args[4])
-		mhi_dev->smmu_cfg = args[4];
-
-	/* If it's a new device id register it */
-	if (args[0] && args[1]) {
-		/* find the debug_id  and overwrite it */
-		for (id = mhi_pcie_device_id; id->vendor; id++)
-			if (id->device == MHI_PCIE_DEBUG_ID) {
-				id->device = args[1];
-				pci_unregister_driver(&mhi_pcie_driver);
-				ret = pci_register_driver(&mhi_pcie_driver);
-			}
-	}
-
-	mhi_dev->debug_mode = true;
-	debugfs_create_file("go", 0444, mhi_cntrl->parent, mhi_cntrl,
-			    &mhi_init_debugfs_trigger_go_fops);
-	pr_info(
-		"%s: ret:%d pcidev:0x%x smm_cfg:%u timeout:%u\n",
-		__func__, ret, args[1], mhi_dev->smmu_cfg,
-		mhi_cntrl->timeout_ms);
-	return count;
-
-error_read:
-	kfree(buf);
-	return ret;
-}
-
-static const struct file_operations debugfs_debug_ops = {
-	.open = mhi_init_debugfs_debug_open,
-	.release = single_release,
-	.read = seq_read,
-	.write = mhi_init_debugfs_debug_write,
-};
-
-static struct mhi_controller * mhi_platform_probe(struct pci_dev *pci_dev)
-{
-	struct mhi_controller *mhi_cntrl;
-	struct mhi_dev *mhi_dev;
-	u64 addr_win[2];
-	int ret;
-
-	mhi_cntrl = mhi_alloc_controller(sizeof(*mhi_dev));
-	if (!mhi_cntrl) {
-		pr_err("mhi_alloc_controller fail\n");
-		return NULL;
-	}
-
-	mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
-
-	mhi_cntrl->dev_id = pci_dev->device;
-	mhi_cntrl->domain = pci_domain_nr(pci_dev->bus);
-	mhi_cntrl->bus = pci_dev->bus->number;
-	mhi_cntrl->slot = PCI_SLOT(pci_dev->devfn);
-	mhi_dev->smmu_cfg = 0;
-	#if 0 //def CONFIG_HAVE_MEMBLOCK
-	addr_win[0] = memblock_start_of_DRAM();
-	addr_win[1] = memblock_end_of_DRAM();
-	#else
-#define MHI_MEM_BASE_DEFAULT         0x000000000
-#define MHI_MEM_SIZE_DEFAULT         0x2000000000
-	addr_win[0] = MHI_MEM_BASE_DEFAULT;
-	addr_win[1] = MHI_MEM_SIZE_DEFAULT;
-	if (sizeof(dma_addr_t) == 4) {
-		addr_win[1] = 0xFFFFFFFF;
-	}
-	#endif
-
-	mhi_cntrl->iova_start = addr_win[0];
-	mhi_cntrl->iova_stop = addr_win[1];
-
-	mhi_dev->pci_dev = pci_dev;
-	mhi_cntrl->pci_dev = pci_dev;
-
-	/* setup power management apis */
-	mhi_cntrl->status_cb = mhi_status_cb;
-	mhi_cntrl->runtime_get = mhi_runtime_get;
-	mhi_cntrl->runtime_put = mhi_runtime_put;
-	mhi_cntrl->link_status = mhi_link_status;
-
-	ret = mhi_arch_platform_init(mhi_dev);
-	if (ret)
-		goto error_probe;
-
-	ret = mhi_register_mhi_controller(mhi_cntrl);
-	if (ret)
-		goto error_register;
-
-	if (mhi_cntrl->parent)
-		debugfs_create_file("debug_mode", 0444, mhi_cntrl->parent,
-				    mhi_cntrl, &debugfs_debug_ops);
-
-	return mhi_cntrl;
-
-error_register:
-	mhi_arch_platform_deinit(mhi_dev);
-
-error_probe:
-	mhi_free_controller(mhi_cntrl);
-
-	return NULL;
-}	
-
-int mhi_pci_probe(struct pci_dev *pci_dev,
-		  const struct pci_device_id *device_id)
-{
-	struct mhi_controller *mhi_cntrl = NULL;
-	u32 domain = pci_domain_nr(pci_dev->bus);
-	u32 bus = pci_dev->bus->number;
-	u32 slot = PCI_SLOT(pci_dev->devfn);
-	struct mhi_dev *mhi_dev;
-	int ret;
-
-	pr_info("%s pci_dev->name = %s, domain=%d, bus=%d, slot=%d, vendor=%04X, device=%04X\n",
-		__func__, dev_name(&pci_dev->dev), domain, bus, slot, pci_dev->vendor, pci_dev->device);
-	
-	mhi_cntrl = mhi_platform_probe(pci_dev);
-	if (!mhi_cntrl) {
-		pr_err("mhi_platform_probe fail\n");
-		return -EPROBE_DEFER;
-	}
-	
-	mhi_cntrl->dev_id = pci_dev->device;
-	mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
-	mhi_dev->pci_dev = pci_dev;
-	mhi_dev->powered_on = true;
-
-	ret = mhi_arch_pcie_init(mhi_cntrl);
-	if (ret) {
-		MHI_ERR("Error mhi_arch_pcie_init, ret:%d\n", ret);
-		return ret;
-	}
-
-	ret = mhi_arch_iommu_init(mhi_cntrl);
-	if (ret) {
-		MHI_ERR("Error mhi_arch_iommu_init, ret:%d\n", ret);
-		goto error_iommu_init;
-	}
-
-	ret = mhi_init_pci_dev(mhi_cntrl);
-	if (ret) {
-		MHI_ERR("Error mhi_init_pci_dev, ret:%d\n", ret);
-		goto error_init_pci;
-	}
-
-	/* start power up sequence if not in debug mode */
-	if (!mhi_dev->debug_mode) {
-		ret = mhi_async_power_up(mhi_cntrl);
-		if (ret) {
-			MHI_ERR("Error mhi_async_power_up, ret:%d\n", ret);
-			goto error_power_up;
-		}
-	}
-
-#if 0
-	pm_runtime_mark_last_busy(&pci_dev->dev);
-	pm_runtime_allow(&pci_dev->dev);
-	pm_runtime_disable(&pci_dev->dev);
-#endif
-
-	if (mhi_cntrl->dentry) {
-		debugfs_create_file("m0", 0444, mhi_cntrl->dentry, mhi_cntrl,
-				    &debugfs_trigger_m0_fops);
-		debugfs_create_file("m3", 0444, mhi_cntrl->dentry, mhi_cntrl,
-				    &debugfs_trigger_m3_fops);
-	}
-
-	dev_set_drvdata(&pci_dev->dev, mhi_cntrl);
-	MHI_LOG("Return successful\n");
-
-	return 0;
-
-error_power_up:
-	mhi_deinit_pci_dev(mhi_cntrl);
-
-error_init_pci:
-	mhi_arch_iommu_deinit(mhi_cntrl);
-
-error_iommu_init:
-	mhi_arch_pcie_deinit(mhi_cntrl);
-
-	return ret;
-}
-
-static void mhi_pci_remove(struct pci_dev *pci_dev)
-{
-	struct mhi_controller *mhi_cntrl = (struct mhi_controller *)dev_get_drvdata(&pci_dev->dev);
-
-	if (mhi_cntrl && mhi_cntrl->pci_dev == pci_dev) {
-		struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
-		MHI_LOG("%s\n", dev_name(&pci_dev->dev));
-		if (!mhi_dev->debug_mode) {
-			mhi_power_down(mhi_cntrl, 1);
-		}
-		mhi_deinit_pci_dev(mhi_cntrl);
-		mhi_arch_iommu_deinit(mhi_cntrl);
-		mhi_arch_pcie_deinit(mhi_cntrl);
-		mhi_unregister_mhi_controller(mhi_cntrl);
-	}
-}
-
-static const struct dev_pm_ops pm_ops = {
-	SET_RUNTIME_PM_OPS(mhi_runtime_suspend,
-			   mhi_runtime_resume,
-			   mhi_runtime_idle)
-	SET_SYSTEM_SLEEP_PM_OPS(mhi_system_suspend, mhi_system_resume)
-};
-
-static struct pci_driver mhi_pcie_driver = {
-	.name = "mhi",
-	.id_table = mhi_pcie_device_id,
-	.probe = mhi_pci_probe,
-	.remove = mhi_pci_remove,
-	.driver = {
-		.pm = &pm_ops
-	}
-};
-
-int __init mhi_controller_qcom_init(void)
-{	
-	return pci_register_driver(&mhi_pcie_driver);
-};
-
-void mhi_controller_qcom_exit(void)
-{
-	pr_info("%s enter\n", __func__);
-	pci_unregister_driver(&mhi_pcie_driver);
-	pr_info("%s exit\n", __func__);
-}
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/dma-direction.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/memblock.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
+#include <linux/version.h>
+#include "../core/mhi.h"
+#include "mhi_qcom.h"
+
+#if 1
+#ifndef PCI_IRQ_MSI
+#define PCI_IRQ_MSI		(1 << 1) /* Allow MSI interrupts */
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,53 ))
+int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
+{
+	int nvec = maxvec;
+	int rc;
+
+	if (maxvec < minvec)
+		return -ERANGE;
+
+	do {
+		rc = pci_enable_msi_block(dev, nvec);
+		if (rc < 0) {
+			return rc;
+		} else if (rc > 0) {
+			if (rc < minvec)
+				return -ENOSPC;
+			nvec = rc;
+		}
+	} while (rc);
+
+	return nvec;
+}
+#endif
+
+static int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
+		      unsigned int max_vecs, unsigned int flags)
+{
+	return pci_enable_msi_range(dev, min_vecs, max_vecs);
+}
+
+static void pci_free_irq_vectors(struct pci_dev *dev)
+{
+	pci_disable_msi(dev);
+}
+
+static int pci_irq_vector(struct pci_dev *dev, unsigned int nr)
+{
+    return dev->irq + nr;
+}
+#endif
+#endif
+
+static struct pci_device_id mhi_pcie_device_id[] = {
+	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0303)}, //SDX20
+	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0304)}, //SDX24
+	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0305)},
+	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0306)}, //SDX55
+	{PCI_DEVICE(0x2C7C, 0x0512)},
+	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, MHI_PCIE_DEBUG_ID)},
+	{0},
+};
+
+MODULE_DEVICE_TABLE(pci, mhi_pcie_device_id);
+
+static struct pci_driver mhi_pcie_driver;
+
+void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl)
+{
+	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+	struct pci_dev *pci_dev = mhi_dev->pci_dev;
+
+	pci_free_irq_vectors(pci_dev);
+	iounmap(mhi_cntrl->regs);
+	mhi_cntrl->regs = NULL;
+	pci_clear_master(pci_dev);
+	pci_release_region(pci_dev, mhi_dev->resn);
+	pci_disable_device(pci_dev);
+}
+
+static int mhi_init_pci_dev(struct mhi_controller *mhi_cntrl)
+{
+	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+	struct pci_dev *pci_dev = mhi_dev->pci_dev;
+	int ret;
+	resource_size_t start, len;
+	int i;
+
+	mhi_dev->resn = MHI_PCI_BAR_NUM;
+	ret = pci_assign_resource(pci_dev, mhi_dev->resn);
+	if (ret) {
+		MHI_ERR("Error assign pci resources, ret:%d\n", ret);
+		return ret;
+	}
+
+	ret = pci_enable_device(pci_dev);
+	if (ret) {
+		MHI_ERR("Error enabling device, ret:%d\n", ret);
+		goto error_enable_device;
+	}
+
+	ret = pci_request_region(pci_dev, mhi_dev->resn, "mhi");
+	if (ret) {
+		MHI_ERR("Error pci_request_region, ret:%d\n", ret);
+		goto error_request_region;
+	}
+
+	pci_set_master(pci_dev);
+
+	start = pci_resource_start(pci_dev, mhi_dev->resn);
+	len = pci_resource_len(pci_dev, mhi_dev->resn);
+	mhi_cntrl->regs = ioremap_nocache(start, len);
+	MHI_LOG("mhi_cntrl->regs = %p\n", mhi_cntrl->regs);
+	if (!mhi_cntrl->regs) {
+		MHI_ERR("Error ioremap region\n");
+		goto error_ioremap;
+	}
+
+	ret = pci_alloc_irq_vectors(pci_dev, 1, mhi_cntrl->msi_required, PCI_IRQ_MSI);
+	if (IS_ERR_VALUE((ulong)ret) || ret < mhi_cntrl->msi_required) {
+		if (ret == -ENOSPC) {
+			/* imx_3.14.52_1.1.0_ga
+			diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
+			index f06e8f0..6a9614f 100644
+			--- a/drivers/pci/host/pcie-designware.c
+			+++ b/drivers/pci/host/pcie-designware.c
+			@@ -376,6 +376,13 @@ static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev,
+				if (msgvec > 5)
+					msgvec = 0;
+
+			+#if 1 //Add by Quectel 20190419
+			+       if (msgvec > 0 && pdev->vendor == 0x17cb) {
+			+               dev_info(&pdev->dev, "%s quectel fixup pos=%d, msg_ctr=%04x, msgvec=%d\n",  __func__, desc->msi_attrib.pos, msg_ctr, msgvec);
+			+               msgvec = 0;
+			+       }
+			+#endif
+			+
+				irq = assign_irq((1 << msgvec), desc, &pos);
+				if (irq < 0)
+					return irq;
+			*/
+		}
+		//imx_4.1.15_2.0.0_ga & DELL_OPTIPLEX_7010 only alloc one msi interrupt for one pcie device
+		if (ret != 1) {
+			MHI_ERR("Failed to enable MSI, ret=%d, msi_required=%d\n", ret, mhi_cntrl->msi_required);
+			goto error_req_msi;
+		}
+	}
+
+	mhi_cntrl->msi_allocated = ret;
+	MHI_LOG("msi_required = %d, msi_allocated = %d, msi_irq = %u\n", mhi_cntrl->msi_required, mhi_cntrl->msi_allocated, pci_dev->irq);
+
+	for (i = 0; i < mhi_cntrl->msi_allocated; i++) {
+		mhi_cntrl->irq[i] = pci_irq_vector(pci_dev, i);
+		if (mhi_cntrl->irq[i] < 0) {
+			ret = mhi_cntrl->irq[i];
+			goto error_get_irq_vec;
+		}
+	}
+
+#if 0
+	/* configure runtime pm */
+	pm_runtime_set_autosuspend_delay(&pci_dev->dev, MHI_RPM_SUSPEND_TMR_MS);
+	pm_runtime_dont_use_autosuspend(&pci_dev->dev);
+	pm_suspend_ignore_children(&pci_dev->dev, true);
+
+	/*
+	 * pci framework will increment usage count (twice) before
+	 * calling local device driver probe function.
+	 * 1st pci.c pci_pm_init() calls pm_runtime_forbid
+	 * 2nd pci-driver.c local_pci_probe calls pm_runtime_get_sync
+	 * Framework expect pci device driver to call
+	 * pm_runtime_put_noidle to decrement usage count after
+	 * successful probe and and call pm_runtime_allow to enable
+	 * runtime suspend.
+	 */
+	pm_runtime_mark_last_busy(&pci_dev->dev);
+	pm_runtime_put_noidle(&pci_dev->dev);
+#endif
+
+	return 0;
+
+error_get_irq_vec:
+	pci_free_irq_vectors(pci_dev);
+
+error_req_msi:
+	iounmap(mhi_cntrl->regs);
+
+error_ioremap:
+	pci_clear_master(pci_dev);
+
+error_request_region:
+	pci_disable_device(pci_dev);
+
+error_enable_device:
+	pci_release_region(pci_dev, mhi_dev->resn);
+
+	return ret;
+}
+
+#ifdef CONFIG_PM
+static int mhi_runtime_idle(struct device *dev)
+{
+	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
+
+	MHI_LOG("Entered returning -EBUSY\n");
+
+	/*
+	 * RPM framework during runtime resume always calls
+	 * rpm_idle to see if device ready to suspend.
+	 * If dev.power usage_count count is 0, rpm fw will call
+	 * rpm_idle cb to see if device is ready to suspend.
+	 * if cb return 0, or cb not defined the framework will
+	 * assume device driver is ready to suspend;
+	 * therefore, fw will schedule runtime suspend.
+	 * In MHI power management, MHI host shall go to
+	 * runtime suspend only after entering MHI State M2, even if
+	 * usage count is 0.  Return -EBUSY to disable automatic suspend.
+	 */
+	return -EBUSY;
+}
+
+static int mhi_runtime_suspend(struct device *dev)
+{
+	int ret = 0;
+	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
+
+	MHI_LOG("Enter\n");
+
+	mutex_lock(&mhi_cntrl->pm_mutex);
+
+	ret = mhi_pm_suspend(mhi_cntrl);
+	if (ret) {
+		MHI_LOG("Abort due to ret:%d\n", ret);
+		goto exit_runtime_suspend;
+	}
+
+	ret = mhi_arch_link_off(mhi_cntrl, true);
+	if (ret)
+		MHI_ERR("Failed to Turn off link ret:%d\n", ret);
+
+exit_runtime_suspend:
+	mutex_unlock(&mhi_cntrl->pm_mutex);
+	MHI_LOG("Exited with ret:%d\n", ret);
+
+	return ret;
+}
+
+static int mhi_runtime_resume(struct device *dev)
+{
+	int ret = 0;
+	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
+	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+
+	MHI_LOG("Enter\n");
+
+	mutex_lock(&mhi_cntrl->pm_mutex);
+
+	if (!mhi_dev->powered_on) {
+		MHI_LOG("Not fully powered, return success\n");
+		mutex_unlock(&mhi_cntrl->pm_mutex);
+		return 0;
+	}
+
+	/* turn on link */
+	ret = mhi_arch_link_on(mhi_cntrl);
+	if (ret)
+		goto rpm_resume_exit;
+
+	/* enter M0 state */
+	ret = mhi_pm_resume(mhi_cntrl);
+
+rpm_resume_exit:
+	mutex_unlock(&mhi_cntrl->pm_mutex);
+	MHI_LOG("Exited with :%d\n", ret);
+
+	return ret;
+}
+
+static int mhi_system_resume(struct device *dev)
+{
+	int ret = 0;
+	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
+
+	ret = mhi_runtime_resume(dev);
+	if (ret) {
+		MHI_ERR("Failed to resume link\n");
+	} else {
+		pm_runtime_set_active(dev);
+		pm_runtime_enable(dev);
+	}
+
+	return ret;
+}
+
+int mhi_system_suspend(struct device *dev)
+{
+	struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
+
+	MHI_LOG("Entered\n");
+
+	/* if rpm status still active then force suspend */
+	if (!pm_runtime_status_suspended(dev))
+		return mhi_runtime_suspend(dev);
+
+	pm_runtime_set_suspended(dev);
+	pm_runtime_disable(dev);
+
+	MHI_LOG("Exit\n");
+	return 0;
+}
+#endif
+
+/* checks if link is down */
+static int mhi_link_status(struct mhi_controller *mhi_cntrl, void *priv)
+{
+	struct mhi_dev *mhi_dev = priv;
+	u16 dev_id;
+	int ret;
+
+	/* try reading device id, if dev id don't match, link is down */
+	ret = pci_read_config_word(mhi_dev->pci_dev, PCI_DEVICE_ID, &dev_id);
+
+	return (ret || dev_id != mhi_cntrl->dev_id) ? -EIO : 0;
+}
+
+static int mhi_runtime_get(struct mhi_controller *mhi_cntrl, void *priv)
+{
+	struct mhi_dev *mhi_dev = priv;
+	struct device *dev = &mhi_dev->pci_dev->dev;
+
+	return pm_runtime_get(dev);
+}
+
+static void mhi_runtime_put(struct mhi_controller *mhi_cntrl, void *priv)
+{
+	struct mhi_dev *mhi_dev = priv;
+	struct device *dev = &mhi_dev->pci_dev->dev;
+
+	pm_runtime_put_noidle(dev);
+}
+
+static void mhi_status_cb(struct mhi_controller *mhi_cntrl,
+			  void *priv,
+			  enum MHI_CB reason)
+{
+	struct mhi_dev *mhi_dev = priv;
+	struct device *dev = &mhi_dev->pci_dev->dev;
+
+	if (reason == MHI_CB_IDLE) {
+		MHI_LOG("Schedule runtime suspend 1\n");
+		pm_runtime_mark_last_busy(dev);
+		pm_request_autosuspend(dev);
+	}
+}
+
+int mhi_debugfs_trigger_m0(void *data, u64 val)
+{
+	struct mhi_controller *mhi_cntrl = data;
+	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+
+	MHI_LOG("Trigger M3 Exit\n");
+	pm_runtime_get(&mhi_dev->pci_dev->dev);
+	pm_runtime_put(&mhi_dev->pci_dev->dev);
+
+	return 0;
+}
+
+int mhi_debugfs_trigger_m3(void *data, u64 val)
+{
+	struct mhi_controller *mhi_cntrl = data;
+	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+
+	MHI_LOG("Trigger M3 Entry\n");
+	pm_runtime_mark_last_busy(&mhi_dev->pci_dev->dev);
+	pm_request_autosuspend(&mhi_dev->pci_dev->dev);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m0_fops, NULL,
+			mhi_debugfs_trigger_m0, "%llu\n");
+
+DEFINE_SIMPLE_ATTRIBUTE(debugfs_trigger_m3_fops, NULL,
+			mhi_debugfs_trigger_m3, "%llu\n");
+
+static int mhi_init_debugfs_trigger_go(void *data, u64 val)
+{
+	struct mhi_controller *mhi_cntrl = data;
+
+	MHI_LOG("Trigger power up sequence\n");
+
+	mhi_async_power_up(mhi_cntrl);
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(mhi_init_debugfs_trigger_go_fops, NULL,
+			mhi_init_debugfs_trigger_go, "%llu\n");
+
+
+int mhi_init_debugfs_debug_show(struct seq_file *m, void *d)
+{
+	seq_puts(m, "Enable debug mode to debug  external soc\n");
+	seq_puts(m,
+		 "Usage:  echo 'devid,timeout,domain,smmu_cfg' > debug_mode\n");
+	seq_puts(m, "No spaces between parameters\n");
+	seq_puts(m, "\t1.  devid : 0 or pci device id to register\n");
+	seq_puts(m, "\t2.  timeout: mhi cmd/state transition timeout\n");
+	seq_puts(m, "\t3.  domain: Rootcomplex\n");
+	seq_puts(m, "\t4.  smmu_cfg: smmu configuration mask:\n");
+	seq_puts(m, "\t\t- BIT0: ATTACH\n");
+	seq_puts(m, "\t\t- BIT1: S1 BYPASS\n");
+	seq_puts(m, "\t\t-BIT2: FAST_MAP\n");
+	seq_puts(m, "\t\t-BIT3: ATOMIC\n");
+	seq_puts(m, "\t\t-BIT4: FORCE_COHERENT\n");
+	seq_puts(m, "\t\t-BIT5: GEOMETRY\n");
+	seq_puts(m, "\tAll timeout are in ms, enter 0 to keep default\n");
+	seq_puts(m, "Examples inputs: '0x307,10000'\n");
+	seq_puts(m, "\techo '0,10000,1'\n");
+	seq_puts(m, "\techo '0x307,10000,0,0x3d'\n");
+	seq_puts(m, "firmware image name will be changed to debug.mbn\n");
+
+	return 0;
+}
+
+static int mhi_init_debugfs_debug_open(struct inode *node, struct file *file)
+{
+	return single_open(file, mhi_init_debugfs_debug_show, NULL);
+}
+
+static ssize_t mhi_init_debugfs_debug_write(struct file *fp,
+					    const char __user *ubuf,
+					    size_t count,
+					    loff_t *pos)
+{
+	char *buf = kmalloc(count + 1, GFP_KERNEL);
+	/* #,devid,timeout,domain,smmu-cfg */
+	int args[5] = {0};
+	static char const *dbg_fw = "debug.mbn";
+	int ret;
+	struct mhi_controller *mhi_cntrl = fp->f_inode->i_private;
+	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+	struct pci_device_id *id;
+
+	if (!buf)
+		return -ENOMEM;
+
+	ret = copy_from_user(buf, ubuf, count);
+	if (ret)
+		goto error_read;
+	buf[count] = 0;
+	get_options(buf, ARRAY_SIZE(args), args);
+	kfree(buf);
+
+	/* override default parameters */
+	mhi_cntrl->fw_image = dbg_fw;
+	mhi_cntrl->edl_image = dbg_fw;
+
+	if (args[0] >= 2 && args[2])
+		mhi_cntrl->timeout_ms = args[2];
+
+	if (args[0] >= 3 && args[3])
+		mhi_cntrl->domain = args[3];
+
+	if (args[0] >= 4 && args[4])
+		mhi_dev->smmu_cfg = args[4];
+
+	/* If it's a new device id register it */
+	if (args[0] && args[1]) {
+		/* find the debug_id  and overwrite it */
+		for (id = mhi_pcie_device_id; id->vendor; id++)
+			if (id->device == MHI_PCIE_DEBUG_ID) {
+				id->device = args[1];
+				pci_unregister_driver(&mhi_pcie_driver);
+				ret = pci_register_driver(&mhi_pcie_driver);
+			}
+	}
+
+	mhi_dev->debug_mode = true;
+	debugfs_create_file("go", 0444, mhi_cntrl->parent, mhi_cntrl,
+			    &mhi_init_debugfs_trigger_go_fops);
+	pr_info(
+		"%s: ret:%d pcidev:0x%x smm_cfg:%u timeout:%u\n",
+		__func__, ret, args[1], mhi_dev->smmu_cfg,
+		mhi_cntrl->timeout_ms);
+	return count;
+
+error_read:
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations debugfs_debug_ops = {
+	.open = mhi_init_debugfs_debug_open,
+	.release = single_release,
+	.read = seq_read,
+	.write = mhi_init_debugfs_debug_write,
+};
+
+static struct mhi_controller * mhi_platform_probe(struct pci_dev *pci_dev)
+{
+	struct mhi_controller *mhi_cntrl;
+	struct mhi_dev *mhi_dev;
+	u64 addr_win[2];
+	int ret;
+
+	mhi_cntrl = mhi_alloc_controller(sizeof(*mhi_dev));
+	if (!mhi_cntrl) {
+		pr_err("mhi_alloc_controller fail\n");
+		return NULL;
+	}
+
+	mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+
+	mhi_cntrl->dev_id = pci_dev->device;
+	mhi_cntrl->domain = pci_domain_nr(pci_dev->bus);
+	mhi_cntrl->bus = pci_dev->bus->number;
+	mhi_cntrl->slot = PCI_SLOT(pci_dev->devfn);
+	mhi_dev->smmu_cfg = 0;
+	#if 0 //def CONFIG_HAVE_MEMBLOCK
+	addr_win[0] = memblock_start_of_DRAM();
+	addr_win[1] = memblock_end_of_DRAM();
+	#else
+#define MHI_MEM_BASE_DEFAULT         0x000000000
+#define MHI_MEM_SIZE_DEFAULT         0x2000000000
+	addr_win[0] = MHI_MEM_BASE_DEFAULT;
+	addr_win[1] = MHI_MEM_SIZE_DEFAULT;
+	if (sizeof(dma_addr_t) == 4) {
+		addr_win[1] = 0xFFFFFFFF;
+	}
+	#endif
+
+	mhi_cntrl->iova_start = addr_win[0];
+	mhi_cntrl->iova_stop = addr_win[1];
+
+	mhi_dev->pci_dev = pci_dev;
+	mhi_cntrl->pci_dev = pci_dev;
+
+	/* setup power management apis */
+	mhi_cntrl->status_cb = mhi_status_cb;
+	mhi_cntrl->runtime_get = mhi_runtime_get;
+	mhi_cntrl->runtime_put = mhi_runtime_put;
+	mhi_cntrl->link_status = mhi_link_status;
+
+	ret = mhi_arch_platform_init(mhi_dev);
+	if (ret)
+		goto error_probe;
+
+	ret = mhi_register_mhi_controller(mhi_cntrl);
+	if (ret)
+		goto error_register;
+
+	if (mhi_cntrl->parent)
+		debugfs_create_file("debug_mode", 0444, mhi_cntrl->parent,
+				    mhi_cntrl, &debugfs_debug_ops);
+
+	return mhi_cntrl;
+
+error_register:
+	mhi_arch_platform_deinit(mhi_dev);
+
+error_probe:
+	mhi_free_controller(mhi_cntrl);
+
+	return NULL;
+}	
+
+int mhi_pci_probe(struct pci_dev *pci_dev,
+		  const struct pci_device_id *device_id)
+{
+	struct mhi_controller *mhi_cntrl = NULL;
+	u32 domain = pci_domain_nr(pci_dev->bus);
+	u32 bus = pci_dev->bus->number;
+	u32 slot = PCI_SLOT(pci_dev->devfn);
+	struct mhi_dev *mhi_dev;
+	int ret;
+
+	pr_info("%s pci_dev->name = %s, domain=%d, bus=%d, slot=%d, vendor=%04X, device=%04X\n",
+		__func__, dev_name(&pci_dev->dev), domain, bus, slot, pci_dev->vendor, pci_dev->device);
+	
+	mhi_cntrl = mhi_platform_probe(pci_dev);
+	if (!mhi_cntrl) {
+		pr_err("mhi_platform_probe fail\n");
+		return -EPROBE_DEFER;
+	}
+	
+	mhi_cntrl->dev_id = pci_dev->device;
+	mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+	mhi_dev->pci_dev = pci_dev;
+	mhi_dev->powered_on = true;
+
+	ret = mhi_arch_pcie_init(mhi_cntrl);
+	if (ret) {
+		MHI_ERR("Error mhi_arch_pcie_init, ret:%d\n", ret);
+		return ret;
+	}
+
+	ret = mhi_arch_iommu_init(mhi_cntrl);
+	if (ret) {
+		MHI_ERR("Error mhi_arch_iommu_init, ret:%d\n", ret);
+		goto error_iommu_init;
+	}
+
+	ret = mhi_init_pci_dev(mhi_cntrl);
+	if (ret) {
+		MHI_ERR("Error mhi_init_pci_dev, ret:%d\n", ret);
+		goto error_init_pci;
+	}
+
+	/* start power up sequence if not in debug mode */
+	if (!mhi_dev->debug_mode) {
+		ret = mhi_async_power_up(mhi_cntrl);
+		if (ret) {
+			MHI_ERR("Error mhi_async_power_up, ret:%d\n", ret);
+			goto error_power_up;
+		}
+	}
+
+#if 0
+	pm_runtime_mark_last_busy(&pci_dev->dev);
+	pm_runtime_allow(&pci_dev->dev);
+	pm_runtime_disable(&pci_dev->dev);
+#endif
+
+	if (mhi_cntrl->dentry) {
+		debugfs_create_file("m0", 0444, mhi_cntrl->dentry, mhi_cntrl,
+				    &debugfs_trigger_m0_fops);
+		debugfs_create_file("m3", 0444, mhi_cntrl->dentry, mhi_cntrl,
+				    &debugfs_trigger_m3_fops);
+	}
+
+	dev_set_drvdata(&pci_dev->dev, mhi_cntrl);
+	MHI_LOG("Return successful\n");
+
+	return 0;
+
+error_power_up:
+	mhi_deinit_pci_dev(mhi_cntrl);
+
+error_init_pci:
+	mhi_arch_iommu_deinit(mhi_cntrl);
+
+error_iommu_init:
+	mhi_arch_pcie_deinit(mhi_cntrl);
+
+	return ret;
+}
+
+static void mhi_pci_remove(struct pci_dev *pci_dev)
+{
+	struct mhi_controller *mhi_cntrl = (struct mhi_controller *)dev_get_drvdata(&pci_dev->dev);
+
+	if (mhi_cntrl && mhi_cntrl->pci_dev == pci_dev) {
+		struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+		MHI_LOG("%s\n", dev_name(&pci_dev->dev));
+		if (!mhi_dev->debug_mode) {
+			mhi_power_down(mhi_cntrl, 1);
+		}
+		mhi_deinit_pci_dev(mhi_cntrl);
+		mhi_arch_iommu_deinit(mhi_cntrl);
+		mhi_arch_pcie_deinit(mhi_cntrl);
+		mhi_unregister_mhi_controller(mhi_cntrl);
+	}
+}
+
+static const struct dev_pm_ops pm_ops = {
+	SET_RUNTIME_PM_OPS(mhi_runtime_suspend,
+			   mhi_runtime_resume,
+			   mhi_runtime_idle)
+	SET_SYSTEM_SLEEP_PM_OPS(mhi_system_suspend, mhi_system_resume)
+};
+
+static struct pci_driver mhi_pcie_driver = {
+	.name = "mhi",
+	.id_table = mhi_pcie_device_id,
+	.probe = mhi_pci_probe,
+	.remove = mhi_pci_remove,
+	.driver = {
+		.pm = &pm_ops
+	}
+};
+
+int __init mhi_controller_qcom_init(void)
+{	
+	return pci_register_driver(&mhi_pcie_driver);
+};
+
+void mhi_controller_qcom_exit(void)
+{
+	pr_info("%s enter\n", __func__);
+	pci_unregister_driver(&mhi_pcie_driver);
+	pr_info("%s exit\n", __func__);
+}
diff --git a/package/wwan/quectel_MHI/src/controllers/mhi_qcom.h b/package/wwan/quectel_MHI/src/controllers/mhi_qcom.h
index bced45b38..65ec5b8d2 100644
--- a/package/wwan/quectel_MHI/src/controllers/mhi_qcom.h
+++ b/package/wwan/quectel_MHI/src/controllers/mhi_qcom.h
@@ -1,92 +1,92 @@
-/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#ifndef _MHI_QCOM_
-#define _MHI_QCOM_
-
-/* iova cfg bitmask */
-#define MHI_SMMU_ATTACH BIT(0)
-#define MHI_SMMU_S1_BYPASS BIT(1)
-#define MHI_SMMU_FAST BIT(2)
-#define MHI_SMMU_ATOMIC BIT(3)
-#define MHI_SMMU_FORCE_COHERENT BIT(4)
-
-#define MHI_PCIE_VENDOR_ID (0x17cb)
-#define MHI_PCIE_DEBUG_ID (0xffff)
-#define MHI_RPM_SUSPEND_TMR_MS (3000)
-#define MHI_PCI_BAR_NUM (0)
-
-struct mhi_dev {
-	struct pci_dev *pci_dev;
-	u32 smmu_cfg;
-	int resn;
-	void *arch_info;
-	bool powered_on;
-	bool debug_mode;
-};
-
-void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl);
-int mhi_pci_probe(struct pci_dev *pci_dev,
-		  const struct pci_device_id *device_id);
-
-#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,65 ))
-static inline int dma_set_mask_and_coherent(struct device *dev, u64 mask)
-{
-	int rc = dma_set_mask(dev, mask);
-	if (rc == 0)
-		dma_set_coherent_mask(dev, mask);
-	return rc;
-}
-#endif
-
-static inline int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl)
-{
-	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
-
-	mhi_cntrl->dev = &mhi_dev->pci_dev->dev;
-
-	return dma_set_mask_and_coherent(mhi_cntrl->dev, DMA_BIT_MASK(64));
-}
-
-static inline void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl)
-{
-}
-
-static inline int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl)
-{
-	return 0;
-}
-
-static inline void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl)
-{
-}
-
-static inline int mhi_arch_platform_init(struct mhi_dev *mhi_dev)
-{
-	return 0;
-}
-
-static inline void mhi_arch_platform_deinit(struct mhi_dev *mhi_dev)
-{
-}
-
-static inline int mhi_arch_link_off(struct mhi_controller *mhi_cntrl,
-				    bool graceful)
-{
-	return 0;
-}
-
-static inline int mhi_arch_link_on(struct mhi_controller *mhi_cntrl)
-{
-	return 0;
-}
-
-#endif /* _MHI_QCOM_ */
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MHI_QCOM_
+#define _MHI_QCOM_
+
+/* iova cfg bitmask */
+#define MHI_SMMU_ATTACH BIT(0)
+#define MHI_SMMU_S1_BYPASS BIT(1)
+#define MHI_SMMU_FAST BIT(2)
+#define MHI_SMMU_ATOMIC BIT(3)
+#define MHI_SMMU_FORCE_COHERENT BIT(4)
+
+#define MHI_PCIE_VENDOR_ID (0x17cb)
+#define MHI_PCIE_DEBUG_ID (0xffff)
+#define MHI_RPM_SUSPEND_TMR_MS (3000)
+#define MHI_PCI_BAR_NUM (0)
+
+struct mhi_dev {
+	struct pci_dev *pci_dev;
+	u32 smmu_cfg;
+	int resn;
+	void *arch_info;
+	bool powered_on;
+	bool debug_mode;
+};
+
+void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl);
+int mhi_pci_probe(struct pci_dev *pci_dev,
+		  const struct pci_device_id *device_id);
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,65 ))
+static inline int dma_set_mask_and_coherent(struct device *dev, u64 mask)
+{
+	int rc = dma_set_mask(dev, mask);
+	if (rc == 0)
+		dma_set_coherent_mask(dev, mask);
+	return rc;
+}
+#endif
+
+static inline int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl)
+{
+	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
+
+	mhi_cntrl->dev = &mhi_dev->pci_dev->dev;
+
+	return dma_set_mask_and_coherent(mhi_cntrl->dev, DMA_BIT_MASK(64));
+}
+
+static inline void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl)
+{
+}
+
+static inline int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl)
+{
+	return 0;
+}
+
+static inline void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl)
+{
+}
+
+static inline int mhi_arch_platform_init(struct mhi_dev *mhi_dev)
+{
+	return 0;
+}
+
+static inline void mhi_arch_platform_deinit(struct mhi_dev *mhi_dev)
+{
+}
+
+static inline int mhi_arch_link_off(struct mhi_controller *mhi_cntrl,
+				    bool graceful)
+{
+	return 0;
+}
+
+static inline int mhi_arch_link_on(struct mhi_controller *mhi_cntrl)
+{
+	return 0;
+}
+
+#endif /* _MHI_QCOM_ */
diff --git a/package/wwan/quectel_MHI/src/controllers/mhi_qti.c b/package/wwan/quectel_MHI/src/controllers/mhi_qti.c
index 981b46fa4..21bcd04bc 100644
--- a/package/wwan/quectel_MHI/src/controllers/mhi_qti.c
+++ b/package/wwan/quectel_MHI/src/controllers/mhi_qti.c
@@ -1067,6 +1067,7 @@ static struct pci_device_id mhi_pcie_device_id[] = {
 	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0306)}, //SDX55
 	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0308)}, //SDX62
 	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x011a)}, //SDX35
+	{PCI_DEVICE(MHI_PCIE_VENDOR_ID, 0x0309)}, //SDX7X
 	{PCI_DEVICE(0x1eac, 0x1001)}, //EM120
 	{PCI_DEVICE(0x1eac, 0x1002)}, //EM160
 	{PCI_DEVICE(0x1eac, 0x1004)}, //RM520
diff --git a/package/wwan/quectel_MHI/src/core/mhi.h b/package/wwan/quectel_MHI/src/core/mhi.h
index fe330d59d..f9671af4a 100644
--- a/package/wwan/quectel_MHI/src/core/mhi.h
+++ b/package/wwan/quectel_MHI/src/core/mhi.h
@@ -4,7 +4,7 @@
 #ifndef _MHI_H_
 #define _MHI_H_
 
-#define PCIE_MHI_DRIVER_VERSION "V1.3.7"
+#define PCIE_MHI_DRIVER_VERSION "V1.3.8"
 #define ENABLE_MHI_MON
 //#define ENABLE_IP_SW0
 
diff --git a/package/wwan/quectel_MHI/src/core/mhi_sdx20.h b/package/wwan/quectel_MHI/src/core/mhi_sdx20.h
index 5a92efa4c..2bc5dc7b3 100644
--- a/package/wwan/quectel_MHI/src/core/mhi_sdx20.h
+++ b/package/wwan/quectel_MHI/src/core/mhi_sdx20.h
@@ -1,362 +1,362 @@
-#ifndef __SDX20_MHI_H
-#define __SDX20_MHI_H
-
-#include <linux/types.h>
-
-/* MHI control data structures alloted by the host, including
- * channel context array, event context array, command context and rings */
-
-/* Channel context state */
-enum mhi_dev_ch_ctx_state {
-	MHI_DEV_CH_STATE_DISABLED,
-	MHI_DEV_CH_STATE_ENABLED,
-	MHI_DEV_CH_STATE_RUNNING,
-	MHI_DEV_CH_STATE_SUSPENDED,
-	MHI_DEV_CH_STATE_STOP,
-	MHI_DEV_CH_STATE_ERROR,
-	MHI_DEV_CH_STATE_RESERVED,
-	MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF
-};
-
-/* Channel type */
-enum mhi_dev_ch_ctx_type {
-	MHI_DEV_CH_TYPE_NONE,
-	MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL,
-	MHI_DEV_CH_TYPE_INBOUND_CHANNEL,
-	MHI_DEV_CH_RESERVED
-};
-
-/* Channel context type */
-struct mhi_dev_ch_ctx {
-	enum mhi_dev_ch_ctx_state	ch_state;
-	enum mhi_dev_ch_ctx_type	ch_type;
-	uint32_t			err_indx;
-	uint64_t			rbase;
-	uint64_t			rlen;
-	uint64_t			rp;
-	uint64_t			wp;
-} __packed;
-
-enum mhi_dev_ring_element_type_id {
-	MHI_DEV_RING_EL_INVALID = 0,
-	MHI_DEV_RING_EL_NOOP = 1,
-	MHI_DEV_RING_EL_TRANSFER = 2,
-	MHI_DEV_RING_EL_RESET = 16,
-	MHI_DEV_RING_EL_STOP = 17,
-	MHI_DEV_RING_EL_START = 18,
-	MHI_DEV_RING_EL_MHI_STATE_CHG = 32,
-	MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33,
-	MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34,
-	MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64,
-	MHI_DEV_RING_EL_UNDEF
-};
-
-enum mhi_dev_ring_state {
-	RING_STATE_UINT = 0,
-	RING_STATE_IDLE,
-	RING_STATE_PENDING,
-};
-
-enum mhi_dev_ring_type {
-	RING_TYPE_CMD = 0,
-	RING_TYPE_ER,
-	RING_TYPE_CH,
-	RING_TYPE_INVAL
-};
-
-/* Event context interrupt moderation */
-enum mhi_dev_evt_ctx_int_mod_timer {
-	MHI_DEV_EVT_INT_MODERATION_DISABLED
-};
-
-/* Event ring type */
-enum mhi_dev_evt_ctx_event_ring_type {
-	MHI_DEV_EVT_TYPE_DEFAULT,
-	MHI_DEV_EVT_TYPE_VALID,
-	MHI_DEV_EVT_RESERVED
-};
-
-/* Event ring context type */
-struct mhi_dev_ev_ctx {
-	uint32_t				res1:16;
-	enum mhi_dev_evt_ctx_int_mod_timer	intmodt:16;
-	enum mhi_dev_evt_ctx_event_ring_type	ertype;
-	uint32_t				msivec;
-	uint64_t				rbase;
-	uint64_t				rlen;
-	uint64_t				rp;
-	uint64_t				wp;
-} __packed;
-
-/* Command context */
-struct mhi_dev_cmd_ctx {
-	uint32_t				res1;
-	uint32_t				res2;
-	uint32_t				res3;
-	uint64_t				rbase;
-	uint64_t				rlen;
-	uint64_t				rp;
-	uint64_t				wp;
-} __packed;
-
-/* generic context */
-struct mhi_dev_gen_ctx {
-	uint32_t				res1;
-	uint32_t				res2;
-	uint32_t				res3;
-	uint64_t				rbase;
-	uint64_t				rlen;
-	uint64_t				rp;
-	uint64_t				wp;
-} __packed;
-
-/* Transfer ring element */
-struct mhi_dev_transfer_ring_element {
-	uint64_t				data_buf_ptr;
-	uint32_t				len:16;
-	uint32_t				res1:16;
-	uint32_t				chain:1;
-	uint32_t				res2:7;
-	uint32_t				ieob:1;
-	uint32_t				ieot:1;
-	uint32_t				bei:1;
-	uint32_t				res3:5;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				res4:8;
-} __packed;
-
-/* Command ring element */
-/* Command ring No op command */
-struct mhi_dev_cmd_ring_op {
-	uint64_t				res1;
-	uint32_t				res2;
-	uint32_t				res3:16;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				chid:8;
-} __packed;
-
-/* Command ring reset channel command */
-struct mhi_dev_cmd_ring_reset_channel_cmd {
-	uint64_t				res1;
-	uint32_t				res2;
-	uint32_t				res3:16;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				chid:8;
-} __packed;
-
-/* Command ring stop channel command */
-struct mhi_dev_cmd_ring_stop_channel_cmd {
-	uint64_t				res1;
-	uint32_t				res2;
-	uint32_t				res3:16;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				chid:8;
-} __packed;
-
-/* Command ring start channel command */
-struct mhi_dev_cmd_ring_start_channel_cmd {
-	uint64_t				res1;
-	uint32_t				seqnum;
-	uint32_t				reliable:1;
-	uint32_t				res2:15;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				chid:8;
-} __packed;
-
-enum mhi_dev_cmd_completion_code {
-	MHI_CMD_COMPL_CODE_INVALID = 0,
-	MHI_CMD_COMPL_CODE_SUCCESS = 1,
-	MHI_CMD_COMPL_CODE_EOT = 2,
-	MHI_CMD_COMPL_CODE_OVERFLOW = 3,
-	MHI_CMD_COMPL_CODE_EOB = 4,
-	MHI_CMD_COMPL_CODE_UNDEFINED = 16,
-	MHI_CMD_COMPL_CODE_RING_EL = 17,
-	MHI_CMD_COMPL_CODE_RES
-};
-
-/* Event ring elements */
-/* Transfer completion event */
-struct mhi_dev_event_ring_transfer_completion {
-	uint64_t				ptr;
-	uint32_t				len:16;
-	uint32_t				res1:8;
-	enum mhi_dev_cmd_completion_code	code:8;
-	uint32_t				res2:16;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				chid:8;
-} __packed;
-
-/* Command completion event */
-struct mhi_dev_event_ring_cmd_completion {
-	uint64_t				ptr;
-	uint32_t				res1:24;
-	enum mhi_dev_cmd_completion_code	code:8;
-	uint32_t				res2:16;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				res3:8;
-} __packed;
-
-enum mhi_dev_state {
-	MHI_DEV_RESET_STATE = 0,
-	MHI_DEV_READY_STATE,
-	MHI_DEV_M0_STATE,
-	MHI_DEV_M1_STATE,
-	MHI_DEV_M2_STATE,
-	MHI_DEV_M3_STATE,
-	MHI_DEV_MAX_STATE,
-	MHI_DEV_SYSERR_STATE = 0xff
-};
-
-/* MHI state change event */
-struct mhi_dev_event_ring_state_change {
-	uint64_t				ptr;
-	uint32_t				res1:24;
-	enum mhi_dev_state			mhistate:8;
-	uint32_t				res2:16;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				res3:8;
-} __packed;
-
-enum mhi_dev_execenv {
-	MHI_DEV_SBL_EE = 1,
-	MHI_DEV_AMSS_EE = 2,
-	MHI_DEV_UNRESERVED
-};
-
-/* EE state change event */
-struct mhi_dev_event_ring_ee_state_change {
-	uint64_t				ptr;
-	uint32_t				res1:24;
-	enum mhi_dev_execenv			execenv:8;
-	uint32_t				res2:16;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				res3:8;
-} __packed;
-
-/* Generic cmd to parse common details like type and channel id */
-struct mhi_dev_ring_generic {
-	uint64_t				ptr;
-	uint32_t				res1:24;
-	enum mhi_dev_state			mhistate:8;
-	uint32_t				res2:16;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				chid:8;
-} __packed;
-
-struct mhi_config {
-	uint32_t	mhi_reg_len;
-	uint32_t	version;
-	uint32_t	event_rings;
-	uint32_t	channels;
-	uint32_t	chdb_offset;
-	uint32_t	erdb_offset;
-};
-
-#define NUM_CHANNELS			128
-#define HW_CHANNEL_BASE			100
-#define HW_CHANNEL_END			107
-#define MHI_ENV_VALUE			2
-#define MHI_MASK_ROWS_CH_EV_DB		4
-#define TRB_MAX_DATA_SIZE		8192
-#define MHI_CTRL_STATE			25
-#define IPA_DMA_SYNC                    1
-#define IPA_DMA_ASYNC                   0
-
-/*maximum trasnfer completion events buffer*/
-#define MAX_TR_EVENTS			50
-/*maximum event requests */
-#define MHI_MAX_EVT_REQ			50
-
-/* Possible ring element types */
-union mhi_dev_ring_element_type {
-	struct mhi_dev_cmd_ring_op			cmd_no_op;
-	struct mhi_dev_cmd_ring_reset_channel_cmd	cmd_reset;
-	struct mhi_dev_cmd_ring_stop_channel_cmd	cmd_stop;
-	struct mhi_dev_cmd_ring_start_channel_cmd	cmd_start;
-	struct mhi_dev_transfer_ring_element		cmd_transfer;
-	struct mhi_dev_event_ring_transfer_completion	evt_tr_comp;
-	struct mhi_dev_event_ring_cmd_completion	evt_cmd_comp;
-	struct mhi_dev_event_ring_state_change		evt_state_change;
-	struct mhi_dev_event_ring_ee_state_change	evt_ee_state;
-	struct mhi_dev_ring_generic			generic;
-};
-
-/* Transfer ring element type */
-union mhi_dev_ring_ctx {
-	struct mhi_dev_cmd_ctx		cmd;
-	struct mhi_dev_ev_ctx		ev;
-	struct mhi_dev_ch_ctx		ch;
-	struct mhi_dev_gen_ctx		generic;
-};
-
-/* MHI host Control and data address region */
-struct mhi_host_addr {
-	uint32_t	ctrl_base_lsb;
-	uint32_t	ctrl_base_msb;
-	uint32_t	ctrl_limit_lsb;
-	uint32_t	ctrl_limit_msb;
-	uint32_t	data_base_lsb;
-	uint32_t	data_base_msb;
-	uint32_t	data_limit_lsb;
-	uint32_t	data_limit_msb;
-};
-
-/* MHI physical and virtual address region */
-struct mhi_meminfo {
-	struct device	*dev;
-	uintptr_t	pa_aligned;
-	uintptr_t	pa_unaligned;
-	uintptr_t	va_aligned;
-	uintptr_t	va_unaligned;
-	uintptr_t	size;
-};
-
-struct mhi_addr {
-	uint64_t	host_pa;
-	uintptr_t	device_pa;
-	uintptr_t	device_va;
-	size_t		size;
-	dma_addr_t	phy_addr;
-	void		*virt_addr;
-	bool		use_ipa_dma;
-};
-
-struct mhi_interrupt_state {
-	uint32_t	mask;
-	uint32_t	status;
-};
-
-enum mhi_dev_channel_state {
-	MHI_DEV_CH_UNINT,
-	MHI_DEV_CH_STARTED,
-	MHI_DEV_CH_PENDING_START,
-	MHI_DEV_CH_PENDING_STOP,
-	MHI_DEV_CH_STOPPED,
-	MHI_DEV_CH_CLOSED,
-};
-
-enum mhi_dev_ch_operation {
-	MHI_DEV_OPEN_CH,
-	MHI_DEV_CLOSE_CH,
-	MHI_DEV_READ_CH,
-	MHI_DEV_READ_WR,
-	MHI_DEV_POLL,
-};
-
-enum mhi_ctrl_info {
-	MHI_STATE_CONFIGURED = 0,
-	MHI_STATE_CONNECTED = 1,
-	MHI_STATE_DISCONNECTED = 2,
-	MHI_STATE_INVAL,
-};
-
-enum mhi_dev_tr_compl_evt_type {
-	SEND_EVENT_BUFFER,
-	SEND_EVENT_RD_OFFSET,
-};
-
-enum mhi_dev_transfer_type {
-	MHI_DEV_DMA_SYNC,
-	MHI_DEV_DMA_ASYNC,
-};
-#endif /* _SDX20_MHI_H_ */
+#ifndef __SDX20_MHI_H
+#define __SDX20_MHI_H
+
+#include <linux/types.h>
+
+/* MHI control data structures alloted by the host, including
+ * channel context array, event context array, command context and rings */
+
+/* Channel context state */
+enum mhi_dev_ch_ctx_state {
+	MHI_DEV_CH_STATE_DISABLED,
+	MHI_DEV_CH_STATE_ENABLED,
+	MHI_DEV_CH_STATE_RUNNING,
+	MHI_DEV_CH_STATE_SUSPENDED,
+	MHI_DEV_CH_STATE_STOP,
+	MHI_DEV_CH_STATE_ERROR,
+	MHI_DEV_CH_STATE_RESERVED,
+	MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF
+};
+
+/* Channel type */
+enum mhi_dev_ch_ctx_type {
+	MHI_DEV_CH_TYPE_NONE,
+	MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL,
+	MHI_DEV_CH_TYPE_INBOUND_CHANNEL,
+	MHI_DEV_CH_RESERVED
+};
+
+/* Channel context type */
+struct mhi_dev_ch_ctx {
+	enum mhi_dev_ch_ctx_state	ch_state;
+	enum mhi_dev_ch_ctx_type	ch_type;
+	uint32_t			err_indx;
+	uint64_t			rbase;
+	uint64_t			rlen;
+	uint64_t			rp;
+	uint64_t			wp;
+} __packed;
+
+enum mhi_dev_ring_element_type_id {
+	MHI_DEV_RING_EL_INVALID = 0,
+	MHI_DEV_RING_EL_NOOP = 1,
+	MHI_DEV_RING_EL_TRANSFER = 2,
+	MHI_DEV_RING_EL_RESET = 16,
+	MHI_DEV_RING_EL_STOP = 17,
+	MHI_DEV_RING_EL_START = 18,
+	MHI_DEV_RING_EL_MHI_STATE_CHG = 32,
+	MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33,
+	MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34,
+	MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64,
+	MHI_DEV_RING_EL_UNDEF
+};
+
+enum mhi_dev_ring_state {
+	RING_STATE_UINT = 0,
+	RING_STATE_IDLE,
+	RING_STATE_PENDING,
+};
+
+enum mhi_dev_ring_type {
+	RING_TYPE_CMD = 0,
+	RING_TYPE_ER,
+	RING_TYPE_CH,
+	RING_TYPE_INVAL
+};
+
+/* Event context interrupt moderation */
+enum mhi_dev_evt_ctx_int_mod_timer {
+	MHI_DEV_EVT_INT_MODERATION_DISABLED
+};
+
+/* Event ring type */
+enum mhi_dev_evt_ctx_event_ring_type {
+	MHI_DEV_EVT_TYPE_DEFAULT,
+	MHI_DEV_EVT_TYPE_VALID,
+	MHI_DEV_EVT_RESERVED
+};
+
+/* Event ring context type */
+struct mhi_dev_ev_ctx {
+	uint32_t				res1:16;
+	enum mhi_dev_evt_ctx_int_mod_timer	intmodt:16;
+	enum mhi_dev_evt_ctx_event_ring_type	ertype;
+	uint32_t				msivec;
+	uint64_t				rbase;
+	uint64_t				rlen;
+	uint64_t				rp;
+	uint64_t				wp;
+} __packed;
+
+/* Command context */
+struct mhi_dev_cmd_ctx {
+	uint32_t				res1;
+	uint32_t				res2;
+	uint32_t				res3;
+	uint64_t				rbase;
+	uint64_t				rlen;
+	uint64_t				rp;
+	uint64_t				wp;
+} __packed;
+
+/* generic context */
+struct mhi_dev_gen_ctx {
+	uint32_t				res1;
+	uint32_t				res2;
+	uint32_t				res3;
+	uint64_t				rbase;
+	uint64_t				rlen;
+	uint64_t				rp;
+	uint64_t				wp;
+} __packed;
+
+/* Transfer ring element */
+struct mhi_dev_transfer_ring_element {
+	uint64_t				data_buf_ptr;
+	uint32_t				len:16;
+	uint32_t				res1:16;
+	uint32_t				chain:1;
+	uint32_t				res2:7;
+	uint32_t				ieob:1;
+	uint32_t				ieot:1;
+	uint32_t				bei:1;
+	uint32_t				res3:5;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				res4:8;
+} __packed;
+
+/* Command ring element */
+/* Command ring No op command */
+struct mhi_dev_cmd_ring_op {
+	uint64_t				res1;
+	uint32_t				res2;
+	uint32_t				res3:16;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				chid:8;
+} __packed;
+
+/* Command ring reset channel command */
+struct mhi_dev_cmd_ring_reset_channel_cmd {
+	uint64_t				res1;
+	uint32_t				res2;
+	uint32_t				res3:16;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				chid:8;
+} __packed;
+
+/* Command ring stop channel command */
+struct mhi_dev_cmd_ring_stop_channel_cmd {
+	uint64_t				res1;
+	uint32_t				res2;
+	uint32_t				res3:16;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				chid:8;
+} __packed;
+
+/* Command ring start channel command */
+struct mhi_dev_cmd_ring_start_channel_cmd {
+	uint64_t				res1;
+	uint32_t				seqnum;
+	uint32_t				reliable:1;
+	uint32_t				res2:15;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				chid:8;
+} __packed;
+
+enum mhi_dev_cmd_completion_code {
+	MHI_CMD_COMPL_CODE_INVALID = 0,
+	MHI_CMD_COMPL_CODE_SUCCESS = 1,
+	MHI_CMD_COMPL_CODE_EOT = 2,
+	MHI_CMD_COMPL_CODE_OVERFLOW = 3,
+	MHI_CMD_COMPL_CODE_EOB = 4,
+	MHI_CMD_COMPL_CODE_UNDEFINED = 16,
+	MHI_CMD_COMPL_CODE_RING_EL = 17,
+	MHI_CMD_COMPL_CODE_RES
+};
+
+/* Event ring elements */
+/* Transfer completion event */
+struct mhi_dev_event_ring_transfer_completion {
+	uint64_t				ptr;
+	uint32_t				len:16;
+	uint32_t				res1:8;
+	enum mhi_dev_cmd_completion_code	code:8;
+	uint32_t				res2:16;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				chid:8;
+} __packed;
+
+/* Command completion event */
+struct mhi_dev_event_ring_cmd_completion {
+	uint64_t				ptr;
+	uint32_t				res1:24;
+	enum mhi_dev_cmd_completion_code	code:8;
+	uint32_t				res2:16;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				res3:8;
+} __packed;
+
+enum mhi_dev_state {
+	MHI_DEV_RESET_STATE = 0,
+	MHI_DEV_READY_STATE,
+	MHI_DEV_M0_STATE,
+	MHI_DEV_M1_STATE,
+	MHI_DEV_M2_STATE,
+	MHI_DEV_M3_STATE,
+	MHI_DEV_MAX_STATE,
+	MHI_DEV_SYSERR_STATE = 0xff
+};
+
+/* MHI state change event */
+struct mhi_dev_event_ring_state_change {
+	uint64_t				ptr;
+	uint32_t				res1:24;
+	enum mhi_dev_state			mhistate:8;
+	uint32_t				res2:16;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				res3:8;
+} __packed;
+
+enum mhi_dev_execenv {
+	MHI_DEV_SBL_EE = 1,
+	MHI_DEV_AMSS_EE = 2,
+	MHI_DEV_UNRESERVED
+};
+
+/* EE state change event */
+struct mhi_dev_event_ring_ee_state_change {
+	uint64_t				ptr;
+	uint32_t				res1:24;
+	enum mhi_dev_execenv			execenv:8;
+	uint32_t				res2:16;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				res3:8;
+} __packed;
+
+/* Generic cmd to parse common details like type and channel id */
+struct mhi_dev_ring_generic {
+	uint64_t				ptr;
+	uint32_t				res1:24;
+	enum mhi_dev_state			mhistate:8;
+	uint32_t				res2:16;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				chid:8;
+} __packed;
+
+struct mhi_config {
+	uint32_t	mhi_reg_len;
+	uint32_t	version;
+	uint32_t	event_rings;
+	uint32_t	channels;
+	uint32_t	chdb_offset;
+	uint32_t	erdb_offset;
+};
+
+#define NUM_CHANNELS			128
+#define HW_CHANNEL_BASE			100
+#define HW_CHANNEL_END			107
+#define MHI_ENV_VALUE			2
+#define MHI_MASK_ROWS_CH_EV_DB		4
+#define TRB_MAX_DATA_SIZE		8192
+#define MHI_CTRL_STATE			25
+#define IPA_DMA_SYNC                    1
+#define IPA_DMA_ASYNC                   0
+
+/*maximum trasnfer completion events buffer*/
+#define MAX_TR_EVENTS			50
+/*maximum event requests */
+#define MHI_MAX_EVT_REQ			50
+
+/* Possible ring element types */
+union mhi_dev_ring_element_type {
+	struct mhi_dev_cmd_ring_op			cmd_no_op;
+	struct mhi_dev_cmd_ring_reset_channel_cmd	cmd_reset;
+	struct mhi_dev_cmd_ring_stop_channel_cmd	cmd_stop;
+	struct mhi_dev_cmd_ring_start_channel_cmd	cmd_start;
+	struct mhi_dev_transfer_ring_element		cmd_transfer;
+	struct mhi_dev_event_ring_transfer_completion	evt_tr_comp;
+	struct mhi_dev_event_ring_cmd_completion	evt_cmd_comp;
+	struct mhi_dev_event_ring_state_change		evt_state_change;
+	struct mhi_dev_event_ring_ee_state_change	evt_ee_state;
+	struct mhi_dev_ring_generic			generic;
+};
+
+/* Transfer ring element type */
+union mhi_dev_ring_ctx {
+	struct mhi_dev_cmd_ctx		cmd;
+	struct mhi_dev_ev_ctx		ev;
+	struct mhi_dev_ch_ctx		ch;
+	struct mhi_dev_gen_ctx		generic;
+};
+
+/* MHI host Control and data address region */
+struct mhi_host_addr {
+	uint32_t	ctrl_base_lsb;
+	uint32_t	ctrl_base_msb;
+	uint32_t	ctrl_limit_lsb;
+	uint32_t	ctrl_limit_msb;
+	uint32_t	data_base_lsb;
+	uint32_t	data_base_msb;
+	uint32_t	data_limit_lsb;
+	uint32_t	data_limit_msb;
+};
+
+/* MHI physical and virtual address region */
+struct mhi_meminfo {
+	struct device	*dev;
+	uintptr_t	pa_aligned;
+	uintptr_t	pa_unaligned;
+	uintptr_t	va_aligned;
+	uintptr_t	va_unaligned;
+	uintptr_t	size;
+};
+
+struct mhi_addr {
+	uint64_t	host_pa;
+	uintptr_t	device_pa;
+	uintptr_t	device_va;
+	size_t		size;
+	dma_addr_t	phy_addr;
+	void		*virt_addr;
+	bool		use_ipa_dma;
+};
+
+struct mhi_interrupt_state {
+	uint32_t	mask;
+	uint32_t	status;
+};
+
+enum mhi_dev_channel_state {
+	MHI_DEV_CH_UNINT,
+	MHI_DEV_CH_STARTED,
+	MHI_DEV_CH_PENDING_START,
+	MHI_DEV_CH_PENDING_STOP,
+	MHI_DEV_CH_STOPPED,
+	MHI_DEV_CH_CLOSED,
+};
+
+enum mhi_dev_ch_operation {
+	MHI_DEV_OPEN_CH,
+	MHI_DEV_CLOSE_CH,
+	MHI_DEV_READ_CH,
+	MHI_DEV_READ_WR,
+	MHI_DEV_POLL,
+};
+
+enum mhi_ctrl_info {
+	MHI_STATE_CONFIGURED = 0,
+	MHI_STATE_CONNECTED = 1,
+	MHI_STATE_DISCONNECTED = 2,
+	MHI_STATE_INVAL,
+};
+
+enum mhi_dev_tr_compl_evt_type {
+	SEND_EVENT_BUFFER,
+	SEND_EVENT_RD_OFFSET,
+};
+
+enum mhi_dev_transfer_type {
+	MHI_DEV_DMA_SYNC,
+	MHI_DEV_DMA_ASYNC,
+};
+#endif /* _SDX20_MHI_H_ */
diff --git a/package/wwan/quectel_MHI/src/core/sdx20_mhi.h b/package/wwan/quectel_MHI/src/core/sdx20_mhi.h
index a7d37839f..29c4a3a25 100644
--- a/package/wwan/quectel_MHI/src/core/sdx20_mhi.h
+++ b/package/wwan/quectel_MHI/src/core/sdx20_mhi.h
@@ -1,426 +1,426 @@
-#ifndef __SDX20_MHI_H
-#define __SDX20_MHI_H
-
-#include <linux/types.h>
-
-/* MHI control data structures alloted by the host, including
- * channel context array, event context array, command context and rings */
-
-/* Channel context state */
-enum mhi_dev_ch_ctx_state {
-	MHI_DEV_CH_STATE_DISABLED,
-	MHI_DEV_CH_STATE_ENABLED,
-	MHI_DEV_CH_STATE_RUNNING,
-	MHI_DEV_CH_STATE_SUSPENDED,
-	MHI_DEV_CH_STATE_STOP,
-	MHI_DEV_CH_STATE_ERROR,
-	MHI_DEV_CH_STATE_RESERVED,
-	MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF
-};
-
-/* Channel type */
-enum mhi_dev_ch_ctx_type {
-	MHI_DEV_CH_TYPE_NONE,
-	MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL,
-	MHI_DEV_CH_TYPE_INBOUND_CHANNEL,
-	MHI_DEV_CH_RESERVED
-};
-
-/* Channel context type */
-struct mhi_dev_ch_ctx {
-	enum mhi_dev_ch_ctx_state	ch_state;
-	enum mhi_dev_ch_ctx_type	ch_type;
-	uint32_t			err_indx;
-	uint64_t			rbase;
-	uint64_t			rlen;
-	uint64_t			rp;
-	uint64_t			wp;
-} __packed;
-
-enum mhi_dev_ring_element_type_id {
-	MHI_DEV_RING_EL_INVALID = 0,
-	MHI_DEV_RING_EL_NOOP = 1,
-	MHI_DEV_RING_EL_TRANSFER = 2,
-	MHI_DEV_RING_EL_RESET = 16,
-	MHI_DEV_RING_EL_STOP = 17,
-	MHI_DEV_RING_EL_START = 18,
-	MHI_DEV_RING_EL_MHI_STATE_CHG = 32,
-	MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33,
-	MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34,
-	MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64,
-	MHI_DEV_RING_EL_UNDEF
-};
-
-enum mhi_dev_ring_state {
-	RING_STATE_UINT = 0,
-	RING_STATE_IDLE,
-	RING_STATE_PENDING,
-};
-
-enum mhi_dev_ring_type {
-	RING_TYPE_CMD = 0,
-	RING_TYPE_ER,
-	RING_TYPE_CH,
-	RING_TYPE_INVAL
-};
-
-/* Event context interrupt moderation */
-enum mhi_dev_evt_ctx_int_mod_timer {
-	MHI_DEV_EVT_INT_MODERATION_DISABLED
-};
-
-/* Event ring type */
-enum mhi_dev_evt_ctx_event_ring_type {
-	MHI_DEV_EVT_TYPE_DEFAULT,
-	MHI_DEV_EVT_TYPE_VALID,
-	MHI_DEV_EVT_RESERVED
-};
-
-/* Event ring context type */
-struct mhi_dev_ev_ctx {
-	uint32_t				res1:16;
-	enum mhi_dev_evt_ctx_int_mod_timer	intmodt:16;
-	enum mhi_dev_evt_ctx_event_ring_type	ertype;
-	uint32_t				msivec;
-	uint64_t				rbase;
-	uint64_t				rlen;
-	uint64_t				rp;
-	uint64_t				wp;
-} __packed;
-
-/* Command context */
-struct mhi_dev_cmd_ctx {
-	uint32_t				res1;
-	uint32_t				res2;
-	uint32_t				res3;
-	uint64_t				rbase;
-	uint64_t				rlen;
-	uint64_t				rp;
-	uint64_t				wp;
-} __packed;
-
-/* generic context */
-struct mhi_dev_gen_ctx {
-	uint32_t				res1;
-	uint32_t				res2;
-	uint32_t				res3;
-	uint64_t				rbase;
-	uint64_t				rlen;
-	uint64_t				rp;
-	uint64_t				wp;
-} __packed;
-
-/* Transfer ring element */
-struct mhi_dev_transfer_ring_element {
-	uint64_t				data_buf_ptr;
-	uint32_t				len:16;
-	uint32_t				res1:16;
-	uint32_t				chain:1;
-	uint32_t				res2:7;
-	uint32_t				ieob:1;
-	uint32_t				ieot:1;
-	uint32_t				bei:1;
-	uint32_t				res3:5;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				res4:8;
-} __packed;
-
-/* Command ring element */
-/* Command ring No op command */
-struct mhi_dev_cmd_ring_op {
-	uint64_t				res1;
-	uint32_t				res2;
-	uint32_t				res3:16;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				chid:8;
-} __packed;
-
-/* Command ring reset channel command */
-struct mhi_dev_cmd_ring_reset_channel_cmd {
-	uint64_t				res1;
-	uint32_t				res2;
-	uint32_t				res3:16;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				chid:8;
-} __packed;
-
-/* Command ring stop channel command */
-struct mhi_dev_cmd_ring_stop_channel_cmd {
-	uint64_t				res1;
-	uint32_t				res2;
-	uint32_t				res3:16;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				chid:8;
-} __packed;
-
-/* Command ring start channel command */
-struct mhi_dev_cmd_ring_start_channel_cmd {
-	uint64_t				res1;
-	uint32_t				seqnum;
-	uint32_t				reliable:1;
-	uint32_t				res2:15;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				chid:8;
-} __packed;
-
-enum mhi_dev_cmd_completion_code {
-	MHI_CMD_COMPL_CODE_INVALID = 0,
-	MHI_CMD_COMPL_CODE_SUCCESS = 1,
-	MHI_CMD_COMPL_CODE_EOT = 2,
-	MHI_CMD_COMPL_CODE_OVERFLOW = 3,
-	MHI_CMD_COMPL_CODE_EOB = 4,
-	MHI_CMD_COMPL_CODE_UNDEFINED = 16,
-	MHI_CMD_COMPL_CODE_RING_EL = 17,
-	MHI_CMD_COMPL_CODE_RES
-};
-
-/* Event ring elements */
-/* Transfer completion event */
-struct mhi_dev_event_ring_transfer_completion {
-	uint64_t				ptr;
-	uint32_t				len:16;
-	uint32_t				res1:8;
-	enum mhi_dev_cmd_completion_code	code:8;
-	uint32_t				res2:16;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				chid:8;
-} __packed;
-
-/* Command completion event */
-struct mhi_dev_event_ring_cmd_completion {
-	uint64_t				ptr;
-	uint32_t				res1:24;
-	enum mhi_dev_cmd_completion_code	code:8;
-	uint32_t				res2:16;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				res3:8;
-} __packed;
-
-enum mhi_dev_state {
-	MHI_DEV_RESET_STATE = 0,
-	MHI_DEV_READY_STATE,
-	MHI_DEV_M0_STATE,
-	MHI_DEV_M1_STATE,
-	MHI_DEV_M2_STATE,
-	MHI_DEV_M3_STATE,
-	MHI_DEV_MAX_STATE,
-	MHI_DEV_SYSERR_STATE = 0xff
-};
-
-/* MHI state change event */
-struct mhi_dev_event_ring_state_change {
-	uint64_t				ptr;
-	uint32_t				res1:24;
-	enum mhi_dev_state			mhistate:8;
-	uint32_t				res2:16;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				res3:8;
-} __packed;
-
-enum mhi_dev_execenv {
-	MHI_DEV_SBL_EE = 1,
-	MHI_DEV_AMSS_EE = 2,
-	MHI_DEV_UNRESERVED
-};
-
-/* EE state change event */
-struct mhi_dev_event_ring_ee_state_change {
-	uint64_t				ptr;
-	uint32_t				res1:24;
-	enum mhi_dev_execenv			execenv:8;
-	uint32_t				res2:16;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				res3:8;
-} __packed;
-
-/* Generic cmd to parse common details like type and channel id */
-struct mhi_dev_ring_generic {
-	uint64_t				ptr;
-	uint32_t				res1:24;
-	enum mhi_dev_state			mhistate:8;
-	uint32_t				res2:16;
-	enum mhi_dev_ring_element_type_id	type:8;
-	uint32_t				chid:8;
-} __packed;
-
-struct mhi_config {
-	uint32_t	mhi_reg_len;
-	uint32_t	version;
-	uint32_t	event_rings;
-	uint32_t	channels;
-	uint32_t	chdb_offset;
-	uint32_t	erdb_offset;
-};
-
-#define NUM_CHANNELS			128
-#define HW_CHANNEL_BASE			100
-#define HW_CHANNEL_END			107
-#define MHI_ENV_VALUE			2
-#define MHI_MASK_ROWS_CH_EV_DB		4
-#define TRB_MAX_DATA_SIZE		8192
-#define MHI_CTRL_STATE			25
-#define IPA_DMA_SYNC                    1
-#define IPA_DMA_ASYNC                   0
-
-/*maximum trasnfer completion events buffer*/
-#define MAX_TR_EVENTS			50
-/*maximum event requests */
-#define MHI_MAX_EVT_REQ			50
-
-/* Possible ring element types */
-union mhi_dev_ring_element_type {
-	struct mhi_dev_cmd_ring_op			cmd_no_op;
-	struct mhi_dev_cmd_ring_reset_channel_cmd	cmd_reset;
-	struct mhi_dev_cmd_ring_stop_channel_cmd	cmd_stop;
-	struct mhi_dev_cmd_ring_start_channel_cmd	cmd_start;
-	struct mhi_dev_transfer_ring_element		tre;
-	struct mhi_dev_event_ring_transfer_completion	evt_tr_comp;
-	struct mhi_dev_event_ring_cmd_completion	evt_cmd_comp;
-	struct mhi_dev_event_ring_state_change		evt_state_change;
-	struct mhi_dev_event_ring_ee_state_change	evt_ee_state;
-	struct mhi_dev_ring_generic			generic;
-};
-
-/* Transfer ring element type */
-union mhi_dev_ring_ctx {
-	struct mhi_dev_cmd_ctx		cmd;
-	struct mhi_dev_ev_ctx		ev;
-	struct mhi_dev_ch_ctx		ch;
-	struct mhi_dev_gen_ctx		generic;
-};
-
-/* MHI host Control and data address region */
-struct mhi_host_addr {
-	uint32_t	ctrl_base_lsb;
-	uint32_t	ctrl_base_msb;
-	uint32_t	ctrl_limit_lsb;
-	uint32_t	ctrl_limit_msb;
-	uint32_t	data_base_lsb;
-	uint32_t	data_base_msb;
-	uint32_t	data_limit_lsb;
-	uint32_t	data_limit_msb;
-};
-
-/* MHI physical and virtual address region */
-struct mhi_meminfo {
-	struct device	*dev;
-	uintptr_t	pa_aligned;
-	uintptr_t	pa_unaligned;
-	uintptr_t	va_aligned;
-	uintptr_t	va_unaligned;
-	uintptr_t	size;
-};
-
-struct mhi_addr {
-	uint64_t	host_pa;
-	uintptr_t	device_pa;
-	uintptr_t	device_va;
-	size_t		size;
-	dma_addr_t	phy_addr;
-	void		*virt_addr;
-	bool		use_ipa_dma;
-};
-
-struct mhi_interrupt_state {
-	uint32_t	mask;
-	uint32_t	status;
-};
-
-enum mhi_dev_channel_state {
-	MHI_DEV_CH_UNINT,
-	MHI_DEV_CH_STARTED,
-	MHI_DEV_CH_PENDING_START,
-	MHI_DEV_CH_PENDING_STOP,
-	MHI_DEV_CH_STOPPED,
-	MHI_DEV_CH_CLOSED,
-};
-
-enum mhi_dev_ch_operation {
-	MHI_DEV_OPEN_CH,
-	MHI_DEV_CLOSE_CH,
-	MHI_DEV_READ_CH,
-	MHI_DEV_READ_WR,
-	MHI_DEV_POLL,
-};
-
-enum mhi_ctrl_info {
-	MHI_STATE_CONFIGURED = 0,
-	MHI_STATE_CONNECTED = 1,
-	MHI_STATE_DISCONNECTED = 2,
-	MHI_STATE_INVAL,
-};
-
-enum mhi_dev_tr_compl_evt_type {
-	SEND_EVENT_BUFFER,
-	SEND_EVENT_RD_OFFSET,
-};
-
-enum mhi_dev_transfer_type {
-	MHI_DEV_DMA_SYNC,
-	MHI_DEV_DMA_ASYNC,
-};
-
-#if 0
-/* SW channel client list */
-enum mhi_client_channel {
-	MHI_CLIENT_LOOPBACK_OUT = 0,
-	MHI_CLIENT_LOOPBACK_IN = 1,
-	MHI_CLIENT_SAHARA_OUT = 2,
-	MHI_CLIENT_SAHARA_IN = 3,
-	MHI_CLIENT_DIAG_OUT = 4,
-	MHI_CLIENT_DIAG_IN = 5,
-	MHI_CLIENT_SSR_OUT = 6,
-	MHI_CLIENT_SSR_IN = 7,
-	MHI_CLIENT_QDSS_OUT = 8,
-	MHI_CLIENT_QDSS_IN = 9,
-	MHI_CLIENT_EFS_OUT = 10,
-	MHI_CLIENT_EFS_IN = 11,
-	MHI_CLIENT_MBIM_OUT = 12,
-	MHI_CLIENT_MBIM_IN = 13,
-	MHI_CLIENT_QMI_OUT = 14,
-	MHI_CLIENT_QMI_IN = 15,
-	MHI_CLIENT_IP_CTRL_0_OUT = 16,
-	MHI_CLIENT_IP_CTRL_0_IN = 17,
-	MHI_CLIENT_IP_CTRL_1_OUT = 18,
-	MHI_CLIENT_IP_CTRL_1_IN = 19,
-	MHI_CLIENT_DCI_OUT = 20,
-	MHI_CLIENT_DCI_IN = 21,
-	MHI_CLIENT_IP_CTRL_3_OUT = 22,
-	MHI_CLIENT_IP_CTRL_3_IN = 23,
-	MHI_CLIENT_IP_CTRL_4_OUT = 24,
-	MHI_CLIENT_IP_CTRL_4_IN = 25,
-	MHI_CLIENT_IP_CTRL_5_OUT = 26,
-	MHI_CLIENT_IP_CTRL_5_IN = 27,
-	MHI_CLIENT_IP_CTRL_6_OUT = 28,
-	MHI_CLIENT_IP_CTRL_6_IN = 29,
-	MHI_CLIENT_IP_CTRL_7_OUT = 30,
-	MHI_CLIENT_IP_CTRL_7_IN = 31,
-	MHI_CLIENT_DUN_OUT = 32,
-	MHI_CLIENT_DUN_IN = 33,
-	MHI_CLIENT_IP_SW_0_OUT = 34,
-	MHI_CLIENT_IP_SW_0_IN = 35,
-	MHI_CLIENT_IP_SW_1_OUT = 36,
-	MHI_CLIENT_IP_SW_1_IN = 37,
-	MHI_CLIENT_IP_SW_2_OUT = 38,
-	MHI_CLIENT_IP_SW_2_IN = 39,
-	MHI_CLIENT_IP_SW_3_OUT = 40,
-	MHI_CLIENT_IP_SW_3_IN = 41,
-	MHI_CLIENT_CSVT_OUT = 42,
-	MHI_CLIENT_CSVT_IN = 43,
-	MHI_CLIENT_SMCT_OUT = 44,
-	MHI_CLIENT_SMCT_IN = 45,
-	MHI_CLIENT_IP_SW_4_OUT  = 46,
-	MHI_CLIENT_IP_SW_4_IN  = 47,
-	MHI_MAX_SOFTWARE_CHANNELS = 48,
-	MHI_CLIENT_TEST_OUT = 60,
-	MHI_CLIENT_TEST_IN = 61,
-	MHI_CLIENT_RESERVED_1_LOWER = 62,
-	MHI_CLIENT_RESERVED_1_UPPER = 99,
-	MHI_CLIENT_IP_HW_0_OUT = 100,
-	MHI_CLIENT_IP_HW_0_IN = 101,
-	MHI_CLIENT_RESERVED_2_LOWER = 102,
-	MHI_CLIENT_RESERVED_2_UPPER = 127,
-	MHI_MAX_CHANNELS = 102,
-};
-#endif
-#endif /* _SDX20_MHI_H_ */
+#ifndef __SDX20_MHI_H
+#define __SDX20_MHI_H
+
+#include <linux/types.h>
+
+/* MHI control data structures alloted by the host, including
+ * channel context array, event context array, command context and rings */
+
+/* Channel context state */
+enum mhi_dev_ch_ctx_state {
+	MHI_DEV_CH_STATE_DISABLED,
+	MHI_DEV_CH_STATE_ENABLED,
+	MHI_DEV_CH_STATE_RUNNING,
+	MHI_DEV_CH_STATE_SUSPENDED,
+	MHI_DEV_CH_STATE_STOP,
+	MHI_DEV_CH_STATE_ERROR,
+	MHI_DEV_CH_STATE_RESERVED,
+	MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF
+};
+
+/* Channel type */
+enum mhi_dev_ch_ctx_type {
+	MHI_DEV_CH_TYPE_NONE,
+	MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL,
+	MHI_DEV_CH_TYPE_INBOUND_CHANNEL,
+	MHI_DEV_CH_RESERVED
+};
+
+/* Channel context type */
+struct mhi_dev_ch_ctx {
+	enum mhi_dev_ch_ctx_state	ch_state;
+	enum mhi_dev_ch_ctx_type	ch_type;
+	uint32_t			err_indx;
+	uint64_t			rbase;
+	uint64_t			rlen;
+	uint64_t			rp;
+	uint64_t			wp;
+} __packed;
+
+enum mhi_dev_ring_element_type_id {
+	MHI_DEV_RING_EL_INVALID = 0,
+	MHI_DEV_RING_EL_NOOP = 1,
+	MHI_DEV_RING_EL_TRANSFER = 2,
+	MHI_DEV_RING_EL_RESET = 16,
+	MHI_DEV_RING_EL_STOP = 17,
+	MHI_DEV_RING_EL_START = 18,
+	MHI_DEV_RING_EL_MHI_STATE_CHG = 32,
+	MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33,
+	MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34,
+	MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64,
+	MHI_DEV_RING_EL_UNDEF
+};
+
+enum mhi_dev_ring_state {
+	RING_STATE_UINT = 0,
+	RING_STATE_IDLE,
+	RING_STATE_PENDING,
+};
+
+enum mhi_dev_ring_type {
+	RING_TYPE_CMD = 0,
+	RING_TYPE_ER,
+	RING_TYPE_CH,
+	RING_TYPE_INVAL
+};
+
+/* Event context interrupt moderation */
+enum mhi_dev_evt_ctx_int_mod_timer {
+	MHI_DEV_EVT_INT_MODERATION_DISABLED
+};
+
+/* Event ring type */
+enum mhi_dev_evt_ctx_event_ring_type {
+	MHI_DEV_EVT_TYPE_DEFAULT,
+	MHI_DEV_EVT_TYPE_VALID,
+	MHI_DEV_EVT_RESERVED
+};
+
+/* Event ring context type */
+struct mhi_dev_ev_ctx {
+	uint32_t				res1:16;
+	enum mhi_dev_evt_ctx_int_mod_timer	intmodt:16;
+	enum mhi_dev_evt_ctx_event_ring_type	ertype;
+	uint32_t				msivec;
+	uint64_t				rbase;
+	uint64_t				rlen;
+	uint64_t				rp;
+	uint64_t				wp;
+} __packed;
+
+/* Command context */
+struct mhi_dev_cmd_ctx {
+	uint32_t				res1;
+	uint32_t				res2;
+	uint32_t				res3;
+	uint64_t				rbase;
+	uint64_t				rlen;
+	uint64_t				rp;
+	uint64_t				wp;
+} __packed;
+
+/* generic context */
+struct mhi_dev_gen_ctx {
+	uint32_t				res1;
+	uint32_t				res2;
+	uint32_t				res3;
+	uint64_t				rbase;
+	uint64_t				rlen;
+	uint64_t				rp;
+	uint64_t				wp;
+} __packed;
+
+/* Transfer ring element */
+struct mhi_dev_transfer_ring_element {
+	uint64_t				data_buf_ptr;
+	uint32_t				len:16;
+	uint32_t				res1:16;
+	uint32_t				chain:1;
+	uint32_t				res2:7;
+	uint32_t				ieob:1;
+	uint32_t				ieot:1;
+	uint32_t				bei:1;
+	uint32_t				res3:5;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				res4:8;
+} __packed;
+
+/* Command ring element */
+/* Command ring No op command */
+struct mhi_dev_cmd_ring_op {
+	uint64_t				res1;
+	uint32_t				res2;
+	uint32_t				res3:16;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				chid:8;
+} __packed;
+
+/* Command ring reset channel command */
+struct mhi_dev_cmd_ring_reset_channel_cmd {
+	uint64_t				res1;
+	uint32_t				res2;
+	uint32_t				res3:16;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				chid:8;
+} __packed;
+
+/* Command ring stop channel command */
+struct mhi_dev_cmd_ring_stop_channel_cmd {
+	uint64_t				res1;
+	uint32_t				res2;
+	uint32_t				res3:16;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				chid:8;
+} __packed;
+
+/* Command ring start channel command */
+struct mhi_dev_cmd_ring_start_channel_cmd {
+	uint64_t				res1;
+	uint32_t				seqnum;
+	uint32_t				reliable:1;
+	uint32_t				res2:15;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				chid:8;
+} __packed;
+
+enum mhi_dev_cmd_completion_code {
+	MHI_CMD_COMPL_CODE_INVALID = 0,
+	MHI_CMD_COMPL_CODE_SUCCESS = 1,
+	MHI_CMD_COMPL_CODE_EOT = 2,
+	MHI_CMD_COMPL_CODE_OVERFLOW = 3,
+	MHI_CMD_COMPL_CODE_EOB = 4,
+	MHI_CMD_COMPL_CODE_UNDEFINED = 16,
+	MHI_CMD_COMPL_CODE_RING_EL = 17,
+	MHI_CMD_COMPL_CODE_RES
+};
+
+/* Event ring elements */
+/* Transfer completion event */
+struct mhi_dev_event_ring_transfer_completion {
+	uint64_t				ptr;
+	uint32_t				len:16;
+	uint32_t				res1:8;
+	enum mhi_dev_cmd_completion_code	code:8;
+	uint32_t				res2:16;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				chid:8;
+} __packed;
+
+/* Command completion event */
+struct mhi_dev_event_ring_cmd_completion {
+	uint64_t				ptr;
+	uint32_t				res1:24;
+	enum mhi_dev_cmd_completion_code	code:8;
+	uint32_t				res2:16;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				res3:8;
+} __packed;
+
+enum mhi_dev_state {
+	MHI_DEV_RESET_STATE = 0,
+	MHI_DEV_READY_STATE,
+	MHI_DEV_M0_STATE,
+	MHI_DEV_M1_STATE,
+	MHI_DEV_M2_STATE,
+	MHI_DEV_M3_STATE,
+	MHI_DEV_MAX_STATE,
+	MHI_DEV_SYSERR_STATE = 0xff
+};
+
+/* MHI state change event */
+struct mhi_dev_event_ring_state_change {
+	uint64_t				ptr;
+	uint32_t				res1:24;
+	enum mhi_dev_state			mhistate:8;
+	uint32_t				res2:16;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				res3:8;
+} __packed;
+
+enum mhi_dev_execenv {
+	MHI_DEV_SBL_EE = 1,
+	MHI_DEV_AMSS_EE = 2,
+	MHI_DEV_UNRESERVED
+};
+
+/* EE state change event */
+struct mhi_dev_event_ring_ee_state_change {
+	uint64_t				ptr;
+	uint32_t				res1:24;
+	enum mhi_dev_execenv			execenv:8;
+	uint32_t				res2:16;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				res3:8;
+} __packed;
+
+/* Generic cmd to parse common details like type and channel id */
+struct mhi_dev_ring_generic {
+	uint64_t				ptr;
+	uint32_t				res1:24;
+	enum mhi_dev_state			mhistate:8;
+	uint32_t				res2:16;
+	enum mhi_dev_ring_element_type_id	type:8;
+	uint32_t				chid:8;
+} __packed;
+
+struct mhi_config {
+	uint32_t	mhi_reg_len;
+	uint32_t	version;
+	uint32_t	event_rings;
+	uint32_t	channels;
+	uint32_t	chdb_offset;
+	uint32_t	erdb_offset;
+};
+
+#define NUM_CHANNELS			128
+#define HW_CHANNEL_BASE			100
+#define HW_CHANNEL_END			107
+#define MHI_ENV_VALUE			2
+#define MHI_MASK_ROWS_CH_EV_DB		4
+#define TRB_MAX_DATA_SIZE		8192
+#define MHI_CTRL_STATE			25
+#define IPA_DMA_SYNC                    1
+#define IPA_DMA_ASYNC                   0
+
+/*maximum trasnfer completion events buffer*/
+#define MAX_TR_EVENTS			50
+/*maximum event requests */
+#define MHI_MAX_EVT_REQ			50
+
+/* Possible ring element types */
+union mhi_dev_ring_element_type {
+	struct mhi_dev_cmd_ring_op			cmd_no_op;
+	struct mhi_dev_cmd_ring_reset_channel_cmd	cmd_reset;
+	struct mhi_dev_cmd_ring_stop_channel_cmd	cmd_stop;
+	struct mhi_dev_cmd_ring_start_channel_cmd	cmd_start;
+	struct mhi_dev_transfer_ring_element		tre;
+	struct mhi_dev_event_ring_transfer_completion	evt_tr_comp;
+	struct mhi_dev_event_ring_cmd_completion	evt_cmd_comp;
+	struct mhi_dev_event_ring_state_change		evt_state_change;
+	struct mhi_dev_event_ring_ee_state_change	evt_ee_state;
+	struct mhi_dev_ring_generic			generic;
+};
+
+/* Transfer ring element type */
+union mhi_dev_ring_ctx {
+	struct mhi_dev_cmd_ctx		cmd;
+	struct mhi_dev_ev_ctx		ev;
+	struct mhi_dev_ch_ctx		ch;
+	struct mhi_dev_gen_ctx		generic;
+};
+
+/* MHI host Control and data address region */
+struct mhi_host_addr {
+	uint32_t	ctrl_base_lsb;
+	uint32_t	ctrl_base_msb;
+	uint32_t	ctrl_limit_lsb;
+	uint32_t	ctrl_limit_msb;
+	uint32_t	data_base_lsb;
+	uint32_t	data_base_msb;
+	uint32_t	data_limit_lsb;
+	uint32_t	data_limit_msb;
+};
+
+/* MHI physical and virtual address region */
+struct mhi_meminfo {
+	struct device	*dev;
+	uintptr_t	pa_aligned;
+	uintptr_t	pa_unaligned;
+	uintptr_t	va_aligned;
+	uintptr_t	va_unaligned;
+	uintptr_t	size;
+};
+
+struct mhi_addr {
+	uint64_t	host_pa;
+	uintptr_t	device_pa;
+	uintptr_t	device_va;
+	size_t		size;
+	dma_addr_t	phy_addr;
+	void		*virt_addr;
+	bool		use_ipa_dma;
+};
+
+struct mhi_interrupt_state {
+	uint32_t	mask;
+	uint32_t	status;
+};
+
+enum mhi_dev_channel_state {
+	MHI_DEV_CH_UNINT,
+	MHI_DEV_CH_STARTED,
+	MHI_DEV_CH_PENDING_START,
+	MHI_DEV_CH_PENDING_STOP,
+	MHI_DEV_CH_STOPPED,
+	MHI_DEV_CH_CLOSED,
+};
+
+enum mhi_dev_ch_operation {
+	MHI_DEV_OPEN_CH,
+	MHI_DEV_CLOSE_CH,
+	MHI_DEV_READ_CH,
+	MHI_DEV_READ_WR,
+	MHI_DEV_POLL,
+};
+
+enum mhi_ctrl_info {
+	MHI_STATE_CONFIGURED = 0,
+	MHI_STATE_CONNECTED = 1,
+	MHI_STATE_DISCONNECTED = 2,
+	MHI_STATE_INVAL,
+};
+
+enum mhi_dev_tr_compl_evt_type {
+	SEND_EVENT_BUFFER,
+	SEND_EVENT_RD_OFFSET,
+};
+
+enum mhi_dev_transfer_type {
+	MHI_DEV_DMA_SYNC,
+	MHI_DEV_DMA_ASYNC,
+};
+
+#if 0
+/* SW channel client list */
+enum mhi_client_channel {
+	MHI_CLIENT_LOOPBACK_OUT = 0,
+	MHI_CLIENT_LOOPBACK_IN = 1,
+	MHI_CLIENT_SAHARA_OUT = 2,
+	MHI_CLIENT_SAHARA_IN = 3,
+	MHI_CLIENT_DIAG_OUT = 4,
+	MHI_CLIENT_DIAG_IN = 5,
+	MHI_CLIENT_SSR_OUT = 6,
+	MHI_CLIENT_SSR_IN = 7,
+	MHI_CLIENT_QDSS_OUT = 8,
+	MHI_CLIENT_QDSS_IN = 9,
+	MHI_CLIENT_EFS_OUT = 10,
+	MHI_CLIENT_EFS_IN = 11,
+	MHI_CLIENT_MBIM_OUT = 12,
+	MHI_CLIENT_MBIM_IN = 13,
+	MHI_CLIENT_QMI_OUT = 14,
+	MHI_CLIENT_QMI_IN = 15,
+	MHI_CLIENT_IP_CTRL_0_OUT = 16,
+	MHI_CLIENT_IP_CTRL_0_IN = 17,
+	MHI_CLIENT_IP_CTRL_1_OUT = 18,
+	MHI_CLIENT_IP_CTRL_1_IN = 19,
+	MHI_CLIENT_DCI_OUT = 20,
+	MHI_CLIENT_DCI_IN = 21,
+	MHI_CLIENT_IP_CTRL_3_OUT = 22,
+	MHI_CLIENT_IP_CTRL_3_IN = 23,
+	MHI_CLIENT_IP_CTRL_4_OUT = 24,
+	MHI_CLIENT_IP_CTRL_4_IN = 25,
+	MHI_CLIENT_IP_CTRL_5_OUT = 26,
+	MHI_CLIENT_IP_CTRL_5_IN = 27,
+	MHI_CLIENT_IP_CTRL_6_OUT = 28,
+	MHI_CLIENT_IP_CTRL_6_IN = 29,
+	MHI_CLIENT_IP_CTRL_7_OUT = 30,
+	MHI_CLIENT_IP_CTRL_7_IN = 31,
+	MHI_CLIENT_DUN_OUT = 32,
+	MHI_CLIENT_DUN_IN = 33,
+	MHI_CLIENT_IP_SW_0_OUT = 34,
+	MHI_CLIENT_IP_SW_0_IN = 35,
+	MHI_CLIENT_IP_SW_1_OUT = 36,
+	MHI_CLIENT_IP_SW_1_IN = 37,
+	MHI_CLIENT_IP_SW_2_OUT = 38,
+	MHI_CLIENT_IP_SW_2_IN = 39,
+	MHI_CLIENT_IP_SW_3_OUT = 40,
+	MHI_CLIENT_IP_SW_3_IN = 41,
+	MHI_CLIENT_CSVT_OUT = 42,
+	MHI_CLIENT_CSVT_IN = 43,
+	MHI_CLIENT_SMCT_OUT = 44,
+	MHI_CLIENT_SMCT_IN = 45,
+	MHI_CLIENT_IP_SW_4_OUT  = 46,
+	MHI_CLIENT_IP_SW_4_IN  = 47,
+	MHI_MAX_SOFTWARE_CHANNELS = 48,
+	MHI_CLIENT_TEST_OUT = 60,
+	MHI_CLIENT_TEST_IN = 61,
+	MHI_CLIENT_RESERVED_1_LOWER = 62,
+	MHI_CLIENT_RESERVED_1_UPPER = 99,
+	MHI_CLIENT_IP_HW_0_OUT = 100,
+	MHI_CLIENT_IP_HW_0_IN = 101,
+	MHI_CLIENT_RESERVED_2_LOWER = 102,
+	MHI_CLIENT_RESERVED_2_UPPER = 127,
+	MHI_MAX_CHANNELS = 102,
+};
+#endif
+#endif /* _SDX20_MHI_H_ */
diff --git a/package/wwan/quectel_MHI/src/devices/mhi_netdev_quectel.c b/package/wwan/quectel_MHI/src/devices/mhi_netdev_quectel.c
index 34c8befee..f38742ffb 100644
--- a/package/wwan/quectel_MHI/src/devices/mhi_netdev_quectel.c
+++ b/package/wwan/quectel_MHI/src/devices/mhi_netdev_quectel.c
@@ -206,6 +206,8 @@ static void qmap_hex_dump(const char *tag, unsigned char *data, unsigned len) {
 }
 #endif
 
+#define MBIM_MUX_ID_SDX7X	112	//sdx7x is 112-126, others is 0-14
+
 static uint __read_mostly mhi_mbim_enabled = 0;
 module_param(mhi_mbim_enabled, uint, S_IRUGO);
 int mhi_netdev_mbin_enabled(void) { return mhi_mbim_enabled; }
@@ -341,6 +343,7 @@ struct mhi_netdev {
 #endif
 
 	MHI_MBIM_CTX mbim_ctx;
+	u32 mbim_mux_id;
 
 	u32 mru;
 	u32 max_mtu;
@@ -652,7 +655,7 @@ static struct sk_buff * add_mbim_hdr(struct sk_buff *skb, u8 mux_id) {
 	struct mhi_mbim_hdr *mhdr;
 	__le32 sign;
 	u8 *c;
-	u16 tci = mux_id - QUECTEL_QMAP_MUX_ID;
+	u16 tci = mux_id;
 	unsigned int skb_len = skb->len;
 
 	if (qmap_mode > 1)
@@ -1305,12 +1308,12 @@ static void rmnet_mbim_rx_handler(void *dev, struct sk_buff *skb_in)
 			goto error;
 		}
 
-		if ((qmap_mode == 1 && tci != 0) || (qmap_mode > 1 && tci > qmap_mode)) {
+		if ((qmap_mode == 1 && tci != mhi_netdev->mbim_mux_id) || (qmap_mode > 1 && (tci - mhi_netdev->mbim_mux_id) > qmap_mode)){
 			MSG_ERR("unsupported tci %d by now\n", tci);
 			goto error;
 		}
 		tci = abs(tci);
-		qmap_net = pQmapDev->mpQmapNetDev[qmap_mode == 1 ? 0 : tci - 1];
+		qmap_net = pQmapDev->mpQmapNetDev[qmap_mode == 1 ? 0 : tci - 1 - mhi_netdev->mbim_mux_id];
 
 		dpe16 = ndp16->dpe16;
 
@@ -2389,7 +2392,7 @@ static netdev_tx_t mhi_netdev_xmit(struct sk_buff *skb, struct net_device *dev)
 	}
 
 	if (mhi_netdev->net_type == MHI_NET_MBIM) {
-		if (add_mbim_hdr(skb, QUECTEL_QMAP_MUX_ID) == NULL) {
+		if (add_mbim_hdr(skb, mhi_netdev->mbim_mux_id) == NULL) {
 			dev_kfree_skb_any (skb);
 			return NETDEV_TX_OK;
 		}
@@ -2587,8 +2590,8 @@ static void mhi_netdev_get_drvinfo (struct net_device *ndev, struct ethtool_drvi
 {
 	//struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev);
 
-	strlcpy (info->driver, "pcie_mhi", sizeof info->driver);
-	strlcpy (info->version, PCIE_MHI_DRIVER_VERSION, sizeof info->version);
+	strscpy (info->driver, "pcie_mhi", sizeof info->driver);
+	strscpy (info->version, PCIE_MHI_DRIVER_VERSION, sizeof info->version);
 }
 
 static const struct ethtool_ops mhi_netdev_ethtool_ops = {
@@ -3162,10 +3165,12 @@ static void mhi_netdev_remove(struct mhi_device *mhi_dev)
 {
 	struct mhi_netdev *mhi_netdev = mhi_device_get_devdata(mhi_dev);
 	struct sk_buff *skb;
-	unsigned i;
 
 	MSG_LOG("Remove notification received\n");
+#ifndef MHI_NETDEV_ONE_CARD_MODE
+#ifndef	CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY
 
+	unsigned i;
 	write_lock_irq(&mhi_netdev->pm_lock);
 	mhi_netdev->enabled = false;
 	write_unlock_irq(&mhi_netdev->pm_lock);
@@ -3183,7 +3188,8 @@ static void mhi_netdev_remove(struct mhi_device *mhi_dev)
 		&& rtnl_dereference(mhi_netdev->ndev->rx_handler) == rmnet_rx_handler)
 		netdev_rx_handler_unregister(mhi_netdev->ndev);
 	rtnl_unlock();
-
+#endif
+#endif
 	while ((skb = skb_dequeue (&mhi_netdev->skb_chain)))
 		dev_kfree_skb_any(skb);
 	while ((skb = skb_dequeue (&mhi_netdev->qmap_chain)))
@@ -3274,6 +3280,7 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev,
 		|| (mhi_dev->vendor == 0x1eac && mhi_dev->dev_id == 0x1004)
 		|| (mhi_dev->vendor == 0x17cb && mhi_dev->dev_id == 0x011a)
 		|| (mhi_dev->vendor == 0x1eac && mhi_dev->dev_id == 0x100b)
+		|| (mhi_dev->vendor == 0x17cb && mhi_dev->dev_id == 0x0309)
 	) {
 		mhi_netdev->qmap_version = 9;
 	}
@@ -3282,6 +3289,11 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev,
 		mhi_netdev->qmap_version = 0; 
 		mhi_netdev->use_rmnet_usb = 0;
 	}
+
+	mhi_netdev->mbim_mux_id = 0;
+	if (mhi_dev->vendor == 0x17cb && mhi_dev->dev_id == 0x0309) {
+		mhi_netdev->mbim_mux_id = MBIM_MUX_ID_SDX7X;
+	}
 	rmnet_info_set(mhi_netdev, &mhi_netdev->rmnet_info);
 
 	mhi_netdev->rx_queue = mhi_netdev_alloc_skb;
@@ -3314,8 +3326,12 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev,
 		mhi_netdev->mpQmapNetDev[0] = mhi_netdev->ndev;
 		strcpy(mhi_netdev->rmnet_info.ifname[0], mhi_netdev->mpQmapNetDev[0]->name);
 		mhi_netdev->rmnet_info.mux_id[0] = QUECTEL_QMAP_MUX_ID;
+		if (mhi_mbim_enabled) {
+			mhi_netdev->rmnet_info.mux_id[0] = mhi_netdev->mbim_mux_id;
+		}
 	}
-#endif
+#else
+
 #ifdef CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY
 	else if (1) {
 		BUG_ON(mhi_netdev->net_type != MHI_NET_RMNET);
@@ -3327,7 +3343,10 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev,
 #endif
 	else if (mhi_netdev->use_rmnet_usb) {
 		for (i = 0; i < mhi_netdev->qmap_mode; i++) {
-			u8 mux_id = QUECTEL_QMAP_MUX_ID+i;
+			u8 mux_id = QUECTEL_QMAP_MUX_ID + i;
+			if (mhi_mbim_enabled) {
+				mux_id = mhi_netdev->mbim_mux_id + i;
+			}			
 			mhi_netdev->mpQmapNetDev[i] = rmnet_vnd_register_device(mhi_netdev, i, mux_id);
 			if (mhi_netdev->mpQmapNetDev[i]) {
 				strcpy(mhi_netdev->rmnet_info.ifname[i], mhi_netdev->mpQmapNetDev[i]->name);
@@ -3344,6 +3363,7 @@ static int mhi_netdev_probe(struct mhi_device *mhi_dev,
 
 #if defined(CONFIG_IPQ5018_RATE_CONTROL)
 	mhi_netdev->mhi_rate_control = 1;
+#endif
 #endif
 
 	return 0;
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/Kconfig b/package/wwan/quectel_MHI/src/devices/rmnet/Kconfig
deleted file mode 100644
index 9bb06d284..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet/Kconfig
+++ /dev/null
@@ -1,13 +0,0 @@
-#
-# RMNET MAP driver
-#
-
-menuconfig RMNET
-	tristate "RmNet MAP driver"
-	default n
-	select GRO_CELLS
-	---help---
-	  If you select this, you will enable the RMNET module which is used
-	  for handling data in the multiplexing and aggregation protocol (MAP)
-	  format in the embedded data path. RMNET devices can be attached to
-	  any IP mode physical device.
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/Makefile b/package/wwan/quectel_MHI/src/devices/rmnet/Makefile
deleted file mode 100644
index b175fbb7f..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-#
-# Makefile for the RMNET module
-#
-
-rmnet-y		 := rmnet_config.o
-rmnet-y		 += rmnet_vnd.o
-rmnet-y		 += rmnet_handlers.o
-rmnet-y		 += rmnet_map_data.o
-rmnet-y		 += rmnet_map_command.o
-rmnet-y		 += rmnet_descriptor.o
-obj-$(CONFIG_RMNET) += rmnet.o
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_config.c b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_config.c
deleted file mode 100644
index c5ec0c892..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_config.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * RMNET configuration engine
- *
- */
-
-#include <net/sock.h>
-#include <linux/module.h>
-#include <linux/netlink.h>
-#include <linux/netdevice.h>
-#include <linux/hashtable.h>
-#include "rmnet_config.h"
-#include "rmnet_handlers.h"
-#include "rmnet_vnd.h"
-#include "rmnet_private.h"
-#include "rmnet_map.h"
-#include "rmnet_descriptor.h"
-
-/* Locking scheme -
- * The shared resource which needs to be protected is realdev->rx_handler_data.
- * For the writer path, this is using rtnl_lock(). The writer paths are
- * rmnet_newlink(), rmnet_dellink() and rmnet_force_unassociate_device(). These
- * paths are already called with rtnl_lock() acquired in. There is also an
- * ASSERT_RTNL() to ensure that we are calling with rtnl acquired. For
- * dereference here, we will need to use rtnl_dereference(). Dev list writing
- * needs to happen with rtnl_lock() acquired for netdev_master_upper_dev_link().
- * For the reader path, the real_dev->rx_handler_data is called in the TX / RX
- * path. We only need rcu_read_lock() for these scenarios. In these cases,
- * the rcu_read_lock() is held in __dev_queue_xmit() and
- * netif_receive_skb_internal(), so readers need to use rcu_dereference_rtnl()
- * to get the relevant information. For dev list reading, we again acquire
- * rcu_read_lock() in rmnet_dellink() for netdev_master_upper_dev_get_rcu().
- * We also use unregister_netdevice_many() to free all rmnet devices in
- * rmnet_force_unassociate_device() so we dont lose the rtnl_lock() and free in
- * same context.
- */
-
-/* Local Definitions and Declarations */
-
-static int rmnet_is_real_dev_registered(const struct net_device *real_dev)
-{
-	return rcu_access_pointer(real_dev->rx_handler) == rmnet_rx_handler;
-}
-
-/* Needs rtnl lock */
-static struct rmnet_port*
-rmnet_get_port_rtnl(const struct net_device *real_dev)
-{
-	return rtnl_dereference(real_dev->rx_handler_data);
-}
-
-static int rmnet_unregister_real_device(struct net_device *real_dev,
-					struct rmnet_port *port)
-{
-	if (port->nr_rmnet_devs)
-		return -EINVAL;
-
-	rmnet_map_cmd_exit(port);
-	rmnet_map_tx_aggregate_exit(port);
-
-	rmnet_descriptor_deinit(port);
-
-	kfree(port);
-
-	netdev_rx_handler_unregister(real_dev);
-
-	/* release reference on real_dev */
-	dev_put(real_dev);
-
-	netdev_dbg(real_dev, "Removed from rmnet\n");
-	return 0;
-}
-
-static int rmnet_register_real_device(struct net_device *real_dev)
-{
-	struct rmnet_port *port;
-	int rc, entry;
-
-	ASSERT_RTNL();
-
-	if (rmnet_is_real_dev_registered(real_dev))
-		return 0;
-
-	port = kzalloc(sizeof(*port), GFP_ATOMIC);
-	if (!port)
-		return -ENOMEM;
-
-	port->dev = real_dev;
-	rc = netdev_rx_handler_register(real_dev, rmnet_rx_handler, port);
-	if (rc) {
-		kfree(port);
-		return -EBUSY;
-	}
-	/* hold on to real dev for MAP data */
-	dev_hold(real_dev);
-
-	for (entry = 0; entry < RMNET_MAX_LOGICAL_EP; entry++)
-		INIT_HLIST_HEAD(&port->muxed_ep[entry]);
-
-	rc = rmnet_descriptor_init(port);
-	if (rc) {
-		rmnet_descriptor_deinit(port);
-		return rc;
-	}
-
-	rmnet_map_tx_aggregate_init(port);
-	rmnet_map_cmd_init(port);
-
-	netdev_dbg(real_dev, "registered with rmnet\n");
-	return 0;
-}
-
-/* Needs either rcu_read_lock() or rtnl lock */
-static struct rmnet_port *rmnet_get_port(struct net_device *real_dev)
-{
-	if (rmnet_is_real_dev_registered(real_dev))
-		return rcu_dereference_rtnl(real_dev->rx_handler_data);
-	else
-		return NULL;
-}
-
-static struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id)
-{
-	struct rmnet_endpoint *ep;
-
-	hlist_for_each_entry_rcu(ep, &port->muxed_ep[mux_id], hlnode) {
-		if (ep->mux_id == mux_id)
-			return ep;
-	}
-
-	return NULL;
-}
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_config.h b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_config.h
deleted file mode 100644
index c74fcdf21..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_config.h
+++ /dev/null
@@ -1,174 +0,0 @@
-/* Copyright (c) 2013-2017, 2019 The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * RMNET Data configuration engine
- *
- */
-
-#include <linux/skbuff.h>
-#include <net/gro_cells.h>
-
-#ifndef _RMNET_CONFIG_H_
-#define _RMNET_CONFIG_H_
-
-#define RMNET_MAX_LOGICAL_EP 255
-#define RMNET_MAX_VEID 4
-
-struct rmnet_endpoint {
-	u8 mux_id;
-	struct net_device *egress_dev;
-	struct hlist_node hlnode;
-};
-
-struct rmnet_port_priv_stats {
-	u64 dl_hdr_last_qmap_vers;
-	u64 dl_hdr_last_ep_id;
-	u64 dl_hdr_last_trans_id;
-	u64 dl_hdr_last_seq;
-	u64 dl_hdr_last_bytes;
-	u64 dl_hdr_last_pkts;
-	u64 dl_hdr_last_flows;
-	u64 dl_hdr_count;
-	u64 dl_hdr_total_bytes;
-	u64 dl_hdr_total_pkts;
-	u64 dl_trl_last_seq;
-	u64 dl_trl_count;
-};
-
-struct rmnet_egress_agg_params {
-	u16 agg_size;
-	u16 agg_count;
-	u32 agg_time;
-};
-
-/* One instance of this structure is instantiated for each real_dev associated
- * with rmnet.
- */
-struct rmnet_port {
-	struct net_device *dev;
-	u32 data_format;
-	u8 nr_rmnet_devs;
-	u8 rmnet_mode;
-	struct hlist_head muxed_ep[RMNET_MAX_LOGICAL_EP];
-	struct net_device *bridge_ep;
-	void *rmnet_perf;
-
-	struct rmnet_egress_agg_params egress_agg_params;
-
-	/* Protect aggregation related elements */
-	spinlock_t agg_lock;
-
-	struct sk_buff *agg_skb;
-	int agg_state;
-	u8 agg_count;
-	struct timespec agg_time;
-	struct timespec agg_last;
-	struct hrtimer hrtimer;
-	struct work_struct agg_wq;
-
-	/* dl marker elements */
-	struct list_head dl_list;
-	struct rmnet_port_priv_stats stats;
-	int dl_marker_flush;
-
-	/* Descriptor pool */
-	spinlock_t desc_pool_lock;
-	struct rmnet_frag_descriptor_pool *frag_desc_pool;
-	struct sk_buff *chain_head;
-	struct sk_buff *chain_tail;
-};
-
-extern struct rtnl_link_ops rmnet_link_ops;
-
-struct rmnet_vnd_stats {
-	u64 rx_pkts;
-	u64 rx_bytes;
-	u64 tx_pkts;
-	u64 tx_bytes;
-	u32 tx_drops;
-};
-
-struct rmnet_pcpu_stats {
-	struct rmnet_vnd_stats stats;
-	struct u64_stats_sync syncp;
-};
-
-struct rmnet_coal_close_stats {
-	u64 non_coal;
-	u64 ip_miss;
-	u64 trans_miss;
-	u64 hw_nl;
-	u64 hw_pkt;
-	u64 hw_byte;
-	u64 hw_time;
-	u64 hw_evict;
-	u64 coal;
-};
-
-struct rmnet_coal_stats {
-	u64 coal_rx;
-	u64 coal_pkts;
-	u64 coal_hdr_nlo_err;
-	u64 coal_hdr_pkt_err;
-	u64 coal_csum_err;
-	u64 coal_reconstruct;
-	u64 coal_ip_invalid;
-	u64 coal_trans_invalid;
-	struct rmnet_coal_close_stats close;
-	u64 coal_veid[RMNET_MAX_VEID];
-};
-
-struct rmnet_priv_stats {
-	u64 csum_ok;
-	u64 csum_valid_unset;
-	u64 csum_validation_failed;
-	u64 csum_err_bad_buffer;
-	u64 csum_err_invalid_ip_version;
-	u64 csum_err_invalid_transport;
-	u64 csum_fragmented_pkt;
-	u64 csum_skipped;
-	u64 csum_sw;
-	u64 csum_hw;
-	struct rmnet_coal_stats coal;
-};
-
-struct rmnet_priv {
-	u8 mux_id;
-	struct net_device *real_dev;
-	struct rmnet_pcpu_stats __percpu *pcpu_stats;
-	struct gro_cells gro_cells;
-	struct rmnet_priv_stats stats;
-};
-
-enum rmnet_dl_marker_prio {
-	RMNET_PERF,
-	RMNET_SHS,
-};
-
-enum rmnet_trace_func {
-	RMNET_MODULE,
-	NW_STACK_MODULE,
-};
-
-enum rmnet_trace_evt {
-	RMNET_DLVR_SKB,
-	RMNET_RCV_FROM_PND,
-	RMNET_TX_UL_PKT,
-	NW_STACK_DEV_Q_XMIT,
-	NW_STACK_NAPI_GRO_FLUSH,
-	NW_STACK_RX,
-	NW_STACK_TX,
-};
-
-static int rmnet_is_real_dev_registered(const struct net_device *real_dev);
-static struct rmnet_port *rmnet_get_port(struct net_device *real_dev);
-static struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id);
-#endif /* _RMNET_CONFIG_H_ */
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_data.c b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_data.c
deleted file mode 100644
index ad8953c30..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_data.c
+++ /dev/null
@@ -1,1150 +0,0 @@
-#if 0
-
-#define RMNET_MAX_PACKET_SIZE      16384
-#define RMNET_DFLT_PACKET_SIZE     1500
-#define RMNET_NEEDED_HEADROOM      16
-#define RMNET_TX_QUEUE_LEN         1000
-
-#define RMNET_MAX_LOGICAL_EP 255
-#define RMNET_MAP_DESC_HEADROOM   128
-#define RMNET_FRAG_DESCRIPTOR_POOL_SIZE 64
-
-/* Pass the frame up the stack with no modifications to skb->dev */
-#define RMNET_EPMODE_NONE (0)
-/* Replace skb->dev to a virtual rmnet device and pass up the stack */
-#define RMNET_EPMODE_VND (1)
-/* Pass the frame directly to another device with dev_queue_xmit() */
-#define RMNET_EPMODE_BRIDGE (2)
-
-/* rmnet section */
-
-#define RMNET_FLAGS_INGRESS_DEAGGREGATION         (1U << 0)
-#define RMNET_FLAGS_INGRESS_MAP_COMMANDS          (1U << 1)
-#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4           (1U << 2)
-#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4            (1U << 3)
-#define RMNET_FLAGS_INGRESS_COALESCE              (1U << 4)
-#define RMNET_FLAGS_INGRESS_MAP_CKSUMV5           (1U << 5)
-#define RMNET_FLAGS_EGRESS_MAP_CKSUMV5            (1U << 6)
-
-enum rmnet_map_v5_header_type {
-	RMNET_MAP_HEADER_TYPE_UNKNOWN,
-	RMNET_MAP_HEADER_TYPE_COALESCING = 0x1,
-	RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD = 0x2,
-	RMNET_MAP_HEADER_TYPE_ENUM_LENGTH
-};
-
-/* Main QMAP header */
-struct rmnet_map_header {
-	u8  pad_len:6;
-	u8  next_hdr:1;
-	u8  cd_bit:1;
-	u8  mux_id;
-	__be16 pkt_len;
-}  __aligned(1);
-
-/* QMAP v5 headers */
-struct rmnet_map_v5_csum_header {
-	u8  next_hdr:1;
-	u8  header_type:7;
-	u8  hw_reserved:7;
-	u8  csum_valid_required:1;
-	__be16 reserved;
-} __aligned(1);
-
-struct rmnet_map_v5_nl_pair {
-	__be16 pkt_len;
-	u8  csum_error_bitmap;
-	u8  num_packets;
-} __aligned(1);
-
-/* NLO: Number-length object */
-#define RMNET_MAP_V5_MAX_NLOS         (6)
-#define RMNET_MAP_V5_MAX_PACKETS      (48)
-
-struct rmnet_map_v5_coal_header {
-	u8  next_hdr:1;
-	u8  header_type:7;
-	u8  reserved1:4;
-	u8  num_nlos:3;
-	u8  csum_valid:1;
-	u8  close_type:4;
-	u8  close_value:4;
-	u8  reserved2:4;
-	u8  virtual_channel_id:4;
-
-	struct rmnet_map_v5_nl_pair nl_pairs[RMNET_MAP_V5_MAX_NLOS];
-} __aligned(1);
-
-/* QMAP v4 headers */
-struct rmnet_map_dl_csum_trailer {
-	u8  reserved1;
-	u8  valid:1;
-	u8  reserved2:7;
-	u16 csum_start_offset;
-	u16 csum_length;
-	__be16 csum_value;
-} __aligned(1);
-
-struct rmnet_frag_descriptor_pool {
-	struct list_head free_list;
-	u32 pool_size;
-};
-
-struct rmnet_frag_descriptor {
-	struct list_head list;
-	struct list_head sub_frags;
-	skb_frag_t frag;
-	u8 *hdr_ptr;
-	struct net_device *dev;
-	u32 hash;
-	__be32 tcp_seq;
-	__be16 ip_id;
-	u16 data_offset;
-	u16 gso_size;
-	u16 gso_segs;
-	u16 ip_len;
-	u16 trans_len;
-	u8 ip_proto;
-	u8 trans_proto;
-	u8 pkt_id;
-	u8 csum_valid:1,
-	   hdrs_valid:1,
-	   ip_id_set:1,
-	   tcp_seq_set:1,
-	   flush_shs:1,
-	   reserved:3;
-};
-
-struct rmnet_endpoint {
-	u8 rmnet_mode;
-	u8 mux_id;
-	struct net_device *rmnet_dev;
-};
-
-/* One instance of this structure is instantiated for each real_dev associated
- * with rmnet.
- */
-struct rmnet_port {
-	struct net_device *dev;
-	u8 rmnet_mode;
-	u32 data_format;
-	u32 nr_rmnet_devs;
-	struct rmnet_endpoint muxed_ep[16];
-
-	/* Descriptor pool */
-	spinlock_t desc_pool_lock;
-	struct rmnet_frag_descriptor_pool *frag_desc_pool;
-	struct sk_buff *chain_head;
-	struct sk_buff *chain_tail;
-};
-
-static struct sk_buff * add_qhdr_v5(struct sk_buff *skb, u8 mux_id)
-{
-	struct rmnet_map_header *map_header;
-	struct rmnet_map_v5_csum_header *ul_header;
-	u32 padding, map_datalen;
-
-	map_datalen = skb->len;
-	padding = map_datalen%4;
-	if (padding) {
-		padding = 4 - padding;
-		if (skb_tailroom(skb) < padding) {
-			printk("skb_tailroom small!\n");
-			padding = 0;
-		}
-		if (padding)
-			__skb_put(skb, padding);
-	}
-					
-	map_header = (struct rmnet_map_header *)skb_push(skb, (sizeof(struct rmnet_map_header) + sizeof(struct rmnet_map_v5_csum_header)));
-
-	BUILD_BUG_ON((sizeof(struct rmnet_map_header) + sizeof(struct rmnet_map_v5_csum_header)) != 8);
-
-	map_header->cd_bit = 0;
-	map_header->next_hdr = 1;
-	map_header->pad_len = padding;
-	map_header->mux_id = mux_id;
-	map_header->pkt_len = htons(map_datalen + padding);
-
-	ul_header = (struct rmnet_map_v5_csum_header *)(map_header + 1);
-	memset(ul_header, 0, sizeof(*ul_header));
-	ul_header->header_type = RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD;
-
-	return skb;
-}
-
-struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id)
-{
-	return &port->muxed_ep[0];
-}
-
-static void
-rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_port *port)
-{
-	struct rmnet_nss_cb *nss_cb;
-
-	//rmnet_vnd_rx_fixup(skb->dev, skb->len);
-
-	/* Pass off the packet to NSS driver if we can */
-	nss_cb = rcu_dereference(rmnet_nss_callbacks);
-	if (nss_cb) {
-		if (!port->chain_head)
-			port->chain_head = skb;
-		else
-			skb_shinfo(port->chain_tail)->frag_list = skb;
-
-		port->chain_tail = skb;
-		return;
-	}
-
-	skb_reset_transport_header(skb);
-	skb_reset_network_header(skb);
-
-	skb->pkt_type = PACKET_HOST;
-	skb_set_mac_header(skb, 0);
-
-	//if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) {
-	//} else {
-		//if (!rmnet_check_skb_can_gro(skb))
-		//	gro_cells_receive(&priv->gro_cells, skb);
-		//else
-			netif_receive_skb(skb);
-	//}
-}
-
-static inline unsigned char *rmnet_map_data_ptr(struct sk_buff *skb)
-{
-	/* Nonlinear packets we receive are entirely within frag 0 */
-	if (skb_is_nonlinear(skb) && skb->len == skb->data_len)
-		return skb_frag_address(skb_shinfo(skb)->frags);
-
-	return skb->data;
-}
-
-static inline void *rmnet_frag_data_ptr(struct rmnet_frag_descriptor *frag_desc)
-{
-	return skb_frag_address(&frag_desc->frag);
-}
-
-static struct rmnet_frag_descriptor *
-rmnet_get_frag_descriptor(struct rmnet_port *port)
-{
-	struct rmnet_frag_descriptor_pool *pool = port->frag_desc_pool;
-	struct rmnet_frag_descriptor *frag_desc;
-
-	spin_lock(&port->desc_pool_lock);
-	if (!list_empty(&pool->free_list)) {
-		frag_desc = list_first_entry(&pool->free_list,
-					     struct rmnet_frag_descriptor,
-					     list);
-		list_del_init(&frag_desc->list);
-	} else {
-		frag_desc = kzalloc(sizeof(*frag_desc), GFP_ATOMIC);
-		if (!frag_desc)
-			goto out;
-
-		INIT_LIST_HEAD(&frag_desc->list);
-		INIT_LIST_HEAD(&frag_desc->sub_frags);
-		pool->pool_size++;
-	}
-
-out:
-	spin_unlock(&port->desc_pool_lock);
-	return frag_desc;
-}
-
-static void rmnet_recycle_frag_descriptor(struct rmnet_frag_descriptor *frag_desc,
-				   struct rmnet_port *port)
-{
-	struct rmnet_frag_descriptor_pool *pool = port->frag_desc_pool;
-	struct page *page = skb_frag_page(&frag_desc->frag);
-
-	list_del(&frag_desc->list);
-	if (page)
-		put_page(page);
-
-	memset(frag_desc, 0, sizeof(*frag_desc));
-	INIT_LIST_HEAD(&frag_desc->list);
-	INIT_LIST_HEAD(&frag_desc->sub_frags);
-	spin_lock(&port->desc_pool_lock);
-	list_add_tail(&frag_desc->list, &pool->free_list);
-	spin_unlock(&port->desc_pool_lock);
-}
-
-static inline void rmnet_frag_fill(struct rmnet_frag_descriptor *frag_desc,
-				   struct page *p, u32 page_offset, u32 len)
-{
-	get_page(p);
-	__skb_frag_set_page(&frag_desc->frag, p);
-	skb_frag_size_set(&frag_desc->frag, len);
-	frag_desc->frag.page_offset = page_offset;
-}
-
-static inline void *rmnet_frag_pull(struct rmnet_frag_descriptor *frag_desc,
-				    struct rmnet_port *port,
-				    unsigned int size)
-{
-	if (size >= skb_frag_size(&frag_desc->frag)) {
-		pr_info("%s(): Pulling %u bytes from %u byte pkt. Dropping\n",
-			__func__, size, skb_frag_size(&frag_desc->frag));
-		rmnet_recycle_frag_descriptor(frag_desc, port);
-		return NULL;
-	}
-
-	frag_desc->frag.page_offset += size;
-	skb_frag_size_sub(&frag_desc->frag, size);
-
-	return rmnet_frag_data_ptr(frag_desc);
-}
-
-static inline void *rmnet_frag_trim(struct rmnet_frag_descriptor *frag_desc,
-				    struct rmnet_port *port,
-				    unsigned int size)
-{
-	if (!size) {
-		pr_info("%s(): Trimming %u byte pkt to 0. Dropping\n",
-			__func__, skb_frag_size(&frag_desc->frag));
-		rmnet_recycle_frag_descriptor(frag_desc, port);
-		return NULL;
-	}
-
-	if (size < skb_frag_size(&frag_desc->frag))
-		skb_frag_size_set(&frag_desc->frag, size);
-
-	return rmnet_frag_data_ptr(frag_desc);
-}
-
-static inline u8
-rmnet_frag_get_next_hdr_type(struct rmnet_frag_descriptor *frag_desc)
-{
-	unsigned char *data = rmnet_frag_data_ptr(frag_desc);
-
-	data += sizeof(struct rmnet_map_header);
-	return ((struct rmnet_map_v5_coal_header *)data)->header_type;
-}
-
-static inline bool
-rmnet_frag_get_csum_valid(struct rmnet_frag_descriptor *frag_desc)
-{
-	unsigned char *data = rmnet_frag_data_ptr(frag_desc);
-
-	data += sizeof(struct rmnet_map_header);
-	return ((struct rmnet_map_v5_csum_header *)data)->csum_valid_required;
-}
-
-static void rmnet_descriptor_add_frag(struct rmnet_port *port, struct list_head *list,
-			       struct page *p, u32 page_offset, u32 len)
-{
-	struct rmnet_frag_descriptor *frag_desc;
-
-	frag_desc = rmnet_get_frag_descriptor(port);
-	if (!frag_desc)
-		return;
-
-	rmnet_frag_fill(frag_desc, p, page_offset, len);
-	list_add_tail(&frag_desc->list, list);
-}
-
-static void rmnet_frag_deaggregate(skb_frag_t *frag, struct rmnet_port *port,
-			    struct list_head *list)
-{
-	struct rmnet_map_header *maph;
-	u8 *data = skb_frag_address(frag);
-	u32 offset = 0;
-	u32 packet_len;
-
-	while (offset < skb_frag_size(frag)) {
-		maph = (struct rmnet_map_header *)data;
-		packet_len = ntohs(maph->pkt_len);
-
-		/* Some hardware can send us empty frames. Catch them */
-		if (packet_len == 0)
-			return;
-
-		packet_len += sizeof(*maph);
-
-		if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) {
-			packet_len += sizeof(struct rmnet_map_dl_csum_trailer);
-			WARN_ON(1);
-		} else if (port->data_format &
-			   (RMNET_FLAGS_INGRESS_MAP_CKSUMV5 |
-			    RMNET_FLAGS_INGRESS_COALESCE) && !maph->cd_bit) {
-			u32 hsize = 0;
-			u8 type;
-
-			type = ((struct rmnet_map_v5_coal_header *)
-				(data + sizeof(*maph)))->header_type;
-			switch (type) {
-			case RMNET_MAP_HEADER_TYPE_COALESCING:
-				hsize = sizeof(struct rmnet_map_v5_coal_header);
-				break;
-			case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD:
-				hsize = sizeof(struct rmnet_map_v5_csum_header);
-				break;
-			}
-
-			packet_len += hsize;
-		}
-		else {
-			qmap_hex_dump(__func__, data, 64);
-			WARN_ON(1);
-		}
-
-		if ((int)skb_frag_size(frag) - (int)packet_len < 0)
-			return;
-
-		rmnet_descriptor_add_frag(port, list, skb_frag_page(frag),
-					  frag->page_offset + offset,
-					  packet_len);
-
-		offset += packet_len;
-		data += packet_len;
-	}
-}
-
-
-#define RMNET_IP_VERSION_4 0x40
-#define RMNET_IP_VERSION_6 0x60
-
-/* Helper Functions */
-
-static void rmnet_set_skb_proto(struct sk_buff *skb)
-{
-	switch (rmnet_map_data_ptr(skb)[0] & 0xF0) {
-	case RMNET_IP_VERSION_4:
-		skb->protocol = htons(ETH_P_IP);
-		break;
-	case RMNET_IP_VERSION_6:
-		skb->protocol = htons(ETH_P_IPV6);
-		break;
-	default:
-		skb->protocol = htons(ETH_P_MAP);
-		WARN_ON(1);
-		break;
-	}
-}
-
-/* Allocate and populate an skb to contain the packet represented by the
- * frag descriptor.
- */
-static struct sk_buff *rmnet_alloc_skb(struct rmnet_frag_descriptor *frag_desc,
-				       struct rmnet_port *port)
-{
-	struct sk_buff *head_skb, *current_skb, *skb;
-	struct skb_shared_info *shinfo;
-	struct rmnet_frag_descriptor *sub_frag, *tmp;
-
-	/* Use the exact sizes if we know them (i.e. RSB/RSC, rmnet_perf) */
-	if (frag_desc->hdrs_valid) {
-		u16 hdr_len = frag_desc->ip_len + frag_desc->trans_len;
-
-		head_skb = alloc_skb(hdr_len + RMNET_MAP_DESC_HEADROOM,
-				     GFP_ATOMIC);
-		if (!head_skb)
-			return NULL;
-
-		skb_reserve(head_skb, RMNET_MAP_DESC_HEADROOM);
-		skb_put_data(head_skb, frag_desc->hdr_ptr, hdr_len);
-		skb_reset_network_header(head_skb);
-
-		if (frag_desc->trans_len)
-			skb_set_transport_header(head_skb, frag_desc->ip_len);
-
-		/* Packets that have no data portion don't need any frags */
-		if (hdr_len == skb_frag_size(&frag_desc->frag))
-			goto skip_frags;
-
-		/* If the headers we added are the start of the page,
-		 * we don't want to add them twice
-		 */
-		if (frag_desc->hdr_ptr == rmnet_frag_data_ptr(frag_desc)) {
-			if (!rmnet_frag_pull(frag_desc, port, hdr_len)) {
-				kfree_skb(head_skb);
-				return NULL;
-			}
-		}
-	} else {
-		/* Allocate enough space to avoid penalties in the stack
-		 * from __pskb_pull_tail()
-		 */
-		head_skb = alloc_skb(256 + RMNET_MAP_DESC_HEADROOM,
-				     GFP_ATOMIC);
-		if (!head_skb)
-			return NULL;
-
-		skb_reserve(head_skb, RMNET_MAP_DESC_HEADROOM);
-	}
-
-	/* Add main fragment */
-	get_page(skb_frag_page(&frag_desc->frag));
-	skb_add_rx_frag(head_skb, 0, skb_frag_page(&frag_desc->frag),
-			frag_desc->frag.page_offset,
-			skb_frag_size(&frag_desc->frag),
-			skb_frag_size(&frag_desc->frag));
-
-	shinfo = skb_shinfo(head_skb);
-	current_skb = head_skb;
-
-	/* Add in any frags from rmnet_perf */
-	list_for_each_entry_safe(sub_frag, tmp, &frag_desc->sub_frags, list) {
-		skb_frag_t *frag;
-		u32 frag_size;
-
-		frag = &sub_frag->frag;
-		frag_size = skb_frag_size(frag);
-
-add_frag:
-		if (shinfo->nr_frags < MAX_SKB_FRAGS) {
-			get_page(skb_frag_page(frag));
-			skb_add_rx_frag(current_skb, shinfo->nr_frags,
-					skb_frag_page(frag), frag->page_offset,
-					frag_size, frag_size);
-			if (current_skb != head_skb) {
-				head_skb->len += frag_size;
-				head_skb->data_len += frag_size;
-			}
-		} else {
-			/* Alloc a new skb and try again */
-			skb = alloc_skb(0, GFP_ATOMIC);
-			if (!skb)
-				break;
-
-			if (current_skb == head_skb)
-				shinfo->frag_list = skb;
-			else
-				current_skb->next = skb;
-
-			current_skb = skb;
-			shinfo = skb_shinfo(current_skb);
-			goto add_frag;
-		}
-
-		rmnet_recycle_frag_descriptor(sub_frag, port);
-	}
-
-skip_frags:
-	head_skb->dev = frag_desc->dev;
-	rmnet_set_skb_proto(head_skb);
-
-	/* Handle any header metadata that needs to be updated after RSB/RSC
-	 * segmentation
-	 */
-	if (frag_desc->ip_id_set) {
-		struct iphdr *iph;
-
-		iph = (struct iphdr *)rmnet_map_data_ptr(head_skb);
-		csum_replace2(&iph->check, iph->id, frag_desc->ip_id);
-		iph->id = frag_desc->ip_id;
-	}
-
-	if (frag_desc->tcp_seq_set) {
-		struct tcphdr *th;
-
-		th = (struct tcphdr *)
-		     (rmnet_map_data_ptr(head_skb) + frag_desc->ip_len);
-		th->seq = frag_desc->tcp_seq;
-	}
-
-	/* Handle csum offloading */
-	if (frag_desc->csum_valid && frag_desc->hdrs_valid) {
-		/* Set the partial checksum information */
-		//rmnet_frag_partial_csum(head_skb, frag_desc);
-		WARN_ON(1);
-	} else if (frag_desc->csum_valid) {
-		/* Non-RSB/RSC/perf packet. The current checksum is fine */
-		head_skb->ip_summed = CHECKSUM_UNNECESSARY;
-	} else if (frag_desc->hdrs_valid &&
-		   (frag_desc->trans_proto == IPPROTO_TCP ||
-		    frag_desc->trans_proto == IPPROTO_UDP)) {
-		/* Unfortunately, we have to fake a bad checksum here, since
-		 * the original bad value is lost by the hardware. The only
-		 * reliable way to do it is to calculate the actual checksum
-		 * and corrupt it.
-		 */
-		__sum16 *check;
-		__wsum csum;
-		unsigned int offset = skb_transport_offset(head_skb);
-		__sum16 pseudo;
-
-		WARN_ON(1);
-		/* Calculate pseudo header and update header fields */
-		if (frag_desc->ip_proto == 4) {
-			struct iphdr *iph = ip_hdr(head_skb);
-			__be16 tot_len = htons(head_skb->len);
-
-			csum_replace2(&iph->check, iph->tot_len, tot_len);
-			iph->tot_len = tot_len;
-			pseudo = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
-						    head_skb->len -
-						    frag_desc->ip_len,
-						    frag_desc->trans_proto, 0);
-		} else {
-			struct ipv6hdr *ip6h = ipv6_hdr(head_skb);
-
-			ip6h->payload_len = htons(head_skb->len -
-						  sizeof(*ip6h));
-			pseudo = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
-						  head_skb->len -
-						  frag_desc->ip_len,
-						  frag_desc->trans_proto, 0);
-		}
-
-		if (frag_desc->trans_proto == IPPROTO_TCP) {
-			check = &tcp_hdr(head_skb)->check;
-		} else {
-			udp_hdr(head_skb)->len = htons(head_skb->len -
-						       frag_desc->ip_len);
-			check = &udp_hdr(head_skb)->check;
-		}
-
-		*check = pseudo;
-		csum = skb_checksum(head_skb, offset, head_skb->len - offset,
-				    0);
-		/* Add 1 to corrupt. This cannot produce a final value of 0
-		 * since csum_fold() can't return a value of 0xFFFF
-		 */
-		*check = csum16_add(csum_fold(csum), htons(1));
-		head_skb->ip_summed = CHECKSUM_NONE;
-	}
-
-	/* Handle any rmnet_perf metadata */
-	if (frag_desc->hash) {
-		head_skb->hash = frag_desc->hash;
-		head_skb->sw_hash = 1;
-	}
-
-	if (frag_desc->flush_shs)
-		head_skb->cb[0] = 1;
-
-	/* Handle coalesced packets */
-	//if (frag_desc->gso_segs > 1)
-	//	rmnet_frag_gso_stamp(head_skb, frag_desc);
-
-	return head_skb;
-}				       
-
-/* Deliver the packets contained within a frag descriptor */
-static void rmnet_frag_deliver(struct rmnet_frag_descriptor *frag_desc,
-			struct rmnet_port *port)
-{
-	struct sk_buff *skb;
-
-	skb = rmnet_alloc_skb(frag_desc, port);
-	if (skb)
-		rmnet_deliver_skb(skb, port);
-	rmnet_recycle_frag_descriptor(frag_desc, port);
-}
-
-/* Process a QMAPv5 packet header */
-static int rmnet_frag_process_next_hdr_packet(struct rmnet_frag_descriptor *frag_desc,
-				       struct rmnet_port *port,
-				       struct list_head *list,
-				       u16 len)
-{
-	int rc = 0;
-
-	switch (rmnet_frag_get_next_hdr_type(frag_desc)) {
-	case RMNET_MAP_HEADER_TYPE_COALESCING:
-		rc = -1;
-		WARN_ON(1);
-		break;
-	case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD:
-		if (rmnet_frag_get_csum_valid(frag_desc)) {
-			frag_desc->csum_valid = true;
-		} else {
-		}
-
-		if (!rmnet_frag_pull(frag_desc, port,
-				     sizeof(struct rmnet_map_header) +
-				     sizeof(struct rmnet_map_v5_csum_header))) {
-			rc = -EINVAL;
-			break;
-		}
-
-		frag_desc->hdr_ptr = rmnet_frag_data_ptr(frag_desc);
-
-		/* Remove padding only for csum offload packets.
-		 * Coalesced packets should never have padding.
-		 */
-		if (!rmnet_frag_trim(frag_desc, port, len)) {
-			rc = -EINVAL;
-			break;
-		}
-
-		list_del_init(&frag_desc->list);
-		list_add_tail(&frag_desc->list, list);
-		break;
-	default:
-		qmap_hex_dump(__func__, rmnet_frag_data_ptr(frag_desc), 64);
-		rc = -EINVAL;
-		break;
-	}
-
-	return rc;
-}
-
-static void
-__rmnet_frag_ingress_handler(struct rmnet_frag_descriptor *frag_desc,
-			     struct rmnet_port *port)
-{
-	struct rmnet_map_header *qmap;
-	struct rmnet_endpoint *ep;
-	struct rmnet_frag_descriptor *frag, *tmp;
-	LIST_HEAD(segs);
-	u16 len, pad;
-	u8 mux_id;
-
-	qmap = (struct rmnet_map_header *)skb_frag_address(&frag_desc->frag);
-	mux_id = qmap->mux_id;
-	pad = qmap->pad_len;
-	len = ntohs(qmap->pkt_len) - pad;
-
-	if (qmap->cd_bit) {
-		goto recycle;
-	}
-
-	if (mux_id >= RMNET_MAX_LOGICAL_EP)
-		goto recycle;
-
-	ep = rmnet_get_endpoint(port, mux_id);
-	if (!ep)
-		goto recycle;
-
-	frag_desc->dev = ep->rmnet_dev;
-
-	/* Handle QMAPv5 packet */
-	if (qmap->next_hdr &&
-	    (port->data_format & (RMNET_FLAGS_INGRESS_COALESCE |
-				  RMNET_FLAGS_INGRESS_MAP_CKSUMV5))) {
-		if (rmnet_frag_process_next_hdr_packet(frag_desc, port, &segs,
-						       len))
-			goto recycle;
-	} else {
-		/* We only have the main QMAP header to worry about */
-		if (!rmnet_frag_pull(frag_desc, port, sizeof(*qmap)))
-			return;
-
-		frag_desc->hdr_ptr = rmnet_frag_data_ptr(frag_desc);
-
-		if (!rmnet_frag_trim(frag_desc, port, len))
-			return;
-
-		list_add_tail(&frag_desc->list, &segs);
-	}
-
-	list_for_each_entry_safe(frag, tmp, &segs, list) {
-		list_del_init(&frag->list);
-		rmnet_frag_deliver(frag, port);
-	}
-	return;
-
-recycle:
-	rmnet_recycle_frag_descriptor(frag_desc, port);
-}
-
-static void rmnet_frag_ingress_handler(struct sk_buff *skb,
-				struct rmnet_port *port)
-{
-	LIST_HEAD(desc_list);
-	int i = 0;
-	struct rmnet_nss_cb *nss_cb;
-
-	/* Deaggregation and freeing of HW originating
-	 * buffers is done within here
-	 */
-	while (skb) {
-		struct sk_buff *skb_frag;
-
-		port->chain_head = NULL;
-		port->chain_tail = NULL;
-
-		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
-			rmnet_frag_deaggregate(&skb_shinfo(skb)->frags[i], port,
-					       &desc_list);
-			if (!list_empty(&desc_list)) {
-				struct rmnet_frag_descriptor *frag_desc, *tmp;
-
-				list_for_each_entry_safe(frag_desc, tmp,
-							 &desc_list, list) {
-					list_del_init(&frag_desc->list);
-					__rmnet_frag_ingress_handler(frag_desc,
-								     port);
-				}
-			}
-		}
-
-		nss_cb = rcu_dereference(rmnet_nss_callbacks);
-		if (nss_cb && port->chain_head) {
-			port->chain_head->cb[0] = 0;
-			netif_receive_skb(port->chain_head);
-		}
-
-		skb_frag = skb_shinfo(skb)->frag_list;
-		skb_shinfo(skb)->frag_list = NULL;
-		consume_skb(skb);
-		skb = skb_frag;
-	}
-}
-
-static void
-rmnet_map_ingress_handler(struct sk_buff *skb,
-			  struct rmnet_port *port)
-{
-	if (port->data_format & (RMNET_FLAGS_INGRESS_COALESCE |
-				 RMNET_FLAGS_INGRESS_MAP_CKSUMV5)) {
-		if (skb_is_nonlinear(skb)) {
-			rmnet_frag_ingress_handler(skb, port);
-			return;
-		}
-	}
-
-	WARN_ON(1);
-}
-
-static rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb);
-static int rmnet_is_real_dev_registered(const struct net_device *real_dev)
-{
-	return rcu_access_pointer(real_dev->rx_handler) == rmnet_rx_handler;
-}
-
-
-/* Needs either rcu_read_lock() or rtnl lock */
-struct rmnet_port *rmnet_get_port(struct net_device *real_dev)
-{
-	if (rmnet_is_real_dev_registered(real_dev))
-		return rcu_dereference_rtnl(real_dev->rx_handler_data);
-	else
-		return NULL;
-}
-
-static rx_handler_result_t rmnet_rx_priv_handler(struct sk_buff **pskb)
-{
-	struct sk_buff *skb = *pskb;
-	struct rmnet_nss_cb *nss_cb;
-
-	if (!skb)
-		return RX_HANDLER_CONSUMED;
-	if (nss_debug) printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len);
-
-	if (skb->pkt_type == PACKET_LOOPBACK)
-		return RX_HANDLER_PASS;
-
-	/* Check this so that we dont loop around netif_receive_skb */
-	if (skb->cb[0] == 1) {
-		skb->cb[0] = 0;
-
-		skb->dev->stats.rx_packets++;
-		return RX_HANDLER_PASS;
-	}
-
-	while (skb) {
-		struct sk_buff *skb_frag = skb_shinfo(skb)->frag_list;
-
-		skb_shinfo(skb)->frag_list = NULL;
-
-		nss_cb = rcu_dereference(rmnet_nss_callbacks);
-		if (nss_cb)
-			nss_cb->nss_tx(skb);
-
-		skb = skb_frag;
-	}
-
-	return RX_HANDLER_CONSUMED;
-}
-
-/* Ingress / Egress Entry Points */
-
-/* Processes packet as per ingress data format for receiving device. Logical
- * endpoint is determined from packet inspection. Packet is then sent to the
- * egress device listed in the logical endpoint configuration.
- */
-static rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb)
-{
-	struct sk_buff *skb = *pskb;
-	struct rmnet_port *port;
-	struct net_device *dev;
-
-	if (!skb)
-		goto done;
-
-	if (nss_debug) printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len);
-
-	if (skb->pkt_type == PACKET_LOOPBACK)
-		return RX_HANDLER_PASS;
-
-	if (skb->protocol != htons(ETH_P_MAP)) {
-		WARN_ON(1);
-		return RX_HANDLER_PASS;
-	}
-
-	dev = skb->dev;
-	port = rmnet_get_port(dev);
-
-	if (port == NULL)
-		return RX_HANDLER_PASS;
-
-	port->chain_head = NULL;
-	port->chain_tail = NULL;
-
-	switch (port->rmnet_mode) {
-	case RMNET_EPMODE_VND:
-		rmnet_map_ingress_handler(skb, port);
-		break;
-	case RMNET_EPMODE_BRIDGE:
-		//rmnet_bridge_handler(skb, port->bridge_ep);
-		break;
-	}
-
-done:
-	return RX_HANDLER_CONSUMED;
-}
-
-static void rmnet_descriptor_deinit(struct rmnet_port *port)
-{
-	struct rmnet_frag_descriptor_pool *pool;
-	struct rmnet_frag_descriptor *frag_desc, *tmp;
-
-	pool = port->frag_desc_pool;
-
-	list_for_each_entry_safe(frag_desc, tmp, &pool->free_list, list) {
-		kfree(frag_desc);
-		pool->pool_size--;
-	}
-
-	kfree(pool);
-}
-
-static int rmnet_descriptor_init(struct rmnet_port *port)
-{
-	struct rmnet_frag_descriptor_pool *pool;
-	int i;
-
-	spin_lock_init(&port->desc_pool_lock);
-	pool = kzalloc(sizeof(*pool), GFP_ATOMIC);
-	if (!pool)
-		return -ENOMEM;
-
-	INIT_LIST_HEAD(&pool->free_list);
-	port->frag_desc_pool = pool;
-
-	for (i = 0; i < RMNET_FRAG_DESCRIPTOR_POOL_SIZE; i++) {
-		struct rmnet_frag_descriptor *frag_desc;
-
-		frag_desc = kzalloc(sizeof(*frag_desc), GFP_ATOMIC);
-		if (!frag_desc)
-			return -ENOMEM;
-
-		INIT_LIST_HEAD(&frag_desc->list);
-		INIT_LIST_HEAD(&frag_desc->sub_frags);
-		list_add_tail(&frag_desc->list, &pool->free_list);
-		pool->pool_size++;
-	}
-
-	return 0;
-}
-
-struct rmnet_priv {
-	//struct rmnet_endpoint local_ep;
-	struct net_device *real_dev;
-	u8 mux_id;
-};
-
-static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb,
-					struct net_device *dev)
-{
-	struct rmnet_priv *priv;
-
-	if (nss_debug) printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len);
-
-	priv = netdev_priv(dev);
-	if (priv->real_dev) {
-		add_qhdr_v5(skb, priv->mux_id);
-		skb->protocol = htons(ETH_P_MAP);
-		skb->dev = priv->real_dev;
-		dev_queue_xmit(skb);
-		dev->stats.tx_packets++;
-		//rmnet_egress_handler(skb);
-	} else {
-		//this_cpu_inc(priv->pcpu_stats->stats.tx_drops);
-		kfree_skb(skb);
-	}
-	return NETDEV_TX_OK;
-}
-
-static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu)
-{
-	if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE)
-		return -EINVAL;
-
-	rmnet_dev->mtu = new_mtu;
-	return 0;
-}
-
-static const struct net_device_ops rmnet_vnd_ops = {
-	.ndo_start_xmit = rmnet_vnd_start_xmit,
-	.ndo_change_mtu = rmnet_vnd_change_mtu,
-	//.ndo_get_iflink = rmnet_vnd_get_iflink,
-	//.ndo_add_slave  = rmnet_add_bridge,
-	//.ndo_del_slave  = rmnet_del_bridge,
-	//.ndo_init       = rmnet_vnd_init,
-	//.ndo_uninit     = rmnet_vnd_uninit,
-	//.ndo_get_stats64 = rmnet_get_stats64,
-};
-
-static void rmnet_vnd_setup(struct net_device *rmnet_dev)
-{
-	rmnet_dev->netdev_ops = &rmnet_vnd_ops;
-	rmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE;
-	rmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM;
-	random_ether_addr(rmnet_dev->dev_addr);
-	rmnet_dev->tx_queue_len = RMNET_TX_QUEUE_LEN;
-
-	/* Raw IP mode */
-	rmnet_dev->header_ops = NULL;  /* No header */
-	rmnet_dev->type = ARPHRD_RAWIP;
-	rmnet_dev->hard_header_len = 0;
-	rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
-
-	//rmnet_dev->needs_free_netdev = true;
-
-	rmnet_dev->hw_features = NETIF_F_RXCSUM;
-	rmnet_dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
-	//rmnet_dev->hw_features |= NETIF_F_SG;
-	//rmnet_dev->hw_features |= NETIF_F_GRO_HW;	
-}
-#else
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/etherdevice.h>
-
-static uint nss_debug = 0;
-module_param( nss_debug, uint, S_IRUGO | S_IWUSR);
-
-/* rmnet section */
-
-#define RMNET_FLAGS_INGRESS_DEAGGREGATION         (1U << 0)
-#define RMNET_FLAGS_INGRESS_MAP_COMMANDS          (1U << 1)
-#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4           (1U << 2)
-#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4            (1U << 3)
-#define RMNET_FLAGS_INGRESS_COALESCE              (1U << 4)
-#define RMNET_FLAGS_INGRESS_MAP_CKSUMV5           (1U << 5)
-#define RMNET_FLAGS_EGRESS_MAP_CKSUMV5            (1U << 6)
-
-#ifdef CONFIG_ARCH_IPQ807x
-#define CONFIG_QCA_NSS_DRV
-#endif
-#ifdef CONFIG_QCA_NSS_DRV
-#include "rmnet_nss.c"
-#else
-#include "rmnet_nss.h"
-#endif
-
-#include "rmnet_vnd.c"
-#include "rmnet_map_command.c"
-#include "rmnet_map_data.c"
-#include "rmnet_descriptor.c"
-#include "rmnet_config.c"
-#include "rmnet_handlers.c"
-
-struct rmnet_nss_cb *rmnet_nss_callbacks __rcu __read_mostly;
-
-void rmnet_data_init(struct net_device *real_dev, u32 nr_rmnet_devs)
-{
-	struct rmnet_port *port;
-	struct rmnet_endpoint *ep;
-	struct net_device *rmnet_dev = NULL;
-	u32 nr = 0;
-	struct rmnet_nss_cb *nss_cb;
-	int rc = 0;
-
-	nss_cb = rcu_dereference(rmnet_nss_callbacks);
-	if (!nss_cb)
-	{
-#ifdef CONFIG_QCA_NSS_DRV
-		pr_err("%s(): initializing rmnet_nss\n", __func__);
-		RCU_INIT_POINTER(rmnet_nss_callbacks, &rmnet_nss);
-#endif
-	}
-		
-	rtnl_lock();
-	rc = rmnet_register_real_device(real_dev);
-	rtnl_unlock();
-
-	if (rc) {
-		pr_err("%s rmnet_register_real_device = %d\n", __func__, rc);
-		return;
-	}
-
-	port = rmnet_get_port_rtnl(real_dev);
-	port->data_format = RMNET_FLAGS_INGRESS_DEAGGREGATION
-		| RMNET_FLAGS_INGRESS_MAP_CKSUMV5 | RMNET_FLAGS_EGRESS_MAP_CKSUMV5;
-	port->rmnet_mode = RMNET_EPMODE_VND;
-
-	for (nr = 0;  nr < nr_rmnet_devs; nr++) {
-		u8 mux_id = 0x81+nr;
-
-		ep = kzalloc(sizeof(*ep), GFP_ATOMIC);
-
-		rtnl_lock();
-		rmnet_dev = alloc_netdev(sizeof(struct rmnet_priv),
-					"rmnet_data%d", NET_NAME_PREDICTABLE,
-					rmnet_vnd_setup);
-		
-		rmnet_vnd_newlink(mux_id, rmnet_dev, port, real_dev, ep);
-		netdev_rx_handler_register(rmnet_dev, rmnet_rx_priv_handler, NULL);
-		rtnl_unlock();
-
-		hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]);
-	}
-
-	port->nr_rmnet_devs = nr_rmnet_devs;
-}
-
-void rmnet_data_deinit(struct net_device *real_dev, u32 nr_rmnet_devs)
-{
-	struct rmnet_port *port;
-	u32 nr = 0;
-	struct rmnet_nss_cb *nss_cb;
-
-	if (!real_dev || !rmnet_is_real_dev_registered(real_dev))
-		return;
-
-	port = rmnet_get_port_rtnl(real_dev);
-
-	for (nr = 0;  nr < nr_rmnet_devs; nr++) {
-		struct rmnet_endpoint *ep;
-		u8 mux_id = 0x81+nr;
-
-		ep = rmnet_get_endpoint(port, mux_id);
-		if (ep) {
-			hlist_del_init_rcu(&ep->hlnode);
-			rmnet_vnd_dellink(mux_id, port, ep);
-			synchronize_rcu();
-			kfree(ep);
-		}
-	}
-
-	rmnet_unregister_real_device(real_dev, port);
-
-	nss_cb = rcu_dereference(rmnet_nss_callbacks);
-	if (nss_cb) {
-#ifdef CONFIG_QCA_NSS_DRV
-		struct hlist_node *tmp;
-		struct rmnet_nss_ctx *ctx;
-		int bkt;
-
-		pr_err("%s(): exiting rmnet_nss\n", __func__);
-		RCU_INIT_POINTER(rmnet_nss_callbacks, NULL);
-
-		/* Tear down all NSS contexts */
-		hash_for_each_safe(rmnet_nss_ctx_hashtable, bkt, tmp, ctx, hnode)
-			rmnet_nss_free_ctx(ctx);
-#endif
-	}
-}
-#endif
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_descriptor.c b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_descriptor.c
deleted file mode 100644
index 75006d1cf..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_descriptor.c
+++ /dev/null
@@ -1,661 +0,0 @@
-/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * RMNET Packet Descriptor Framework
- *
- */
-
-#include <linux/ip.h>
-#include <linux/ipv6.h>
-#include <net/ipv6.h>
-#include <net/ip6_checksum.h>
-#include "rmnet_config.h"
-#include "rmnet_descriptor.h"
-#include "rmnet_handlers.h"
-#include "rmnet_private.h"
-#include "rmnet_vnd.h"
-
-#define RMNET_FRAG_DESCRIPTOR_POOL_SIZE 64
-#define RMNET_DL_IND_HDR_SIZE (sizeof(struct rmnet_map_dl_ind_hdr) + \
-			       sizeof(struct rmnet_map_header) + \
-			       sizeof(struct rmnet_map_control_command_header))
-#define RMNET_DL_IND_TRL_SIZE (sizeof(struct rmnet_map_dl_ind_trl) + \
-			       sizeof(struct rmnet_map_header) + \
-			       sizeof(struct rmnet_map_control_command_header))
-
-typedef void (*rmnet_perf_desc_hook_t)(struct rmnet_frag_descriptor *frag_desc,
-				       struct rmnet_port *port);
-typedef void (*rmnet_perf_chain_hook_t)(void);
-
-static struct rmnet_frag_descriptor *
-rmnet_get_frag_descriptor(struct rmnet_port *port)
-{
-	struct rmnet_frag_descriptor_pool *pool = port->frag_desc_pool;
-	struct rmnet_frag_descriptor *frag_desc;
-
-	spin_lock(&port->desc_pool_lock);
-	if (!list_empty(&pool->free_list)) {
-		frag_desc = list_first_entry(&pool->free_list,
-					     struct rmnet_frag_descriptor,
-					     list);
-		list_del_init(&frag_desc->list);
-	} else {
-		frag_desc = kzalloc(sizeof(*frag_desc), GFP_ATOMIC);
-		if (!frag_desc)
-			goto out;
-
-		INIT_LIST_HEAD(&frag_desc->list);
-		INIT_LIST_HEAD(&frag_desc->sub_frags);
-		pool->pool_size++;
-	}
-
-out:
-	spin_unlock(&port->desc_pool_lock);
-	return frag_desc;
-}
-
-static void rmnet_recycle_frag_descriptor(struct rmnet_frag_descriptor *frag_desc,
-				   struct rmnet_port *port)
-{
-	struct rmnet_frag_descriptor_pool *pool = port->frag_desc_pool;
-	struct page *page = skb_frag_page(&frag_desc->frag);
-
-	list_del(&frag_desc->list);
-	if (page)
-		put_page(page);
-
-	memset(frag_desc, 0, sizeof(*frag_desc));
-	INIT_LIST_HEAD(&frag_desc->list);
-	INIT_LIST_HEAD(&frag_desc->sub_frags);
-	spin_lock(&port->desc_pool_lock);
-	list_add_tail(&frag_desc->list, &pool->free_list);
-	spin_unlock(&port->desc_pool_lock);
-}
-
-static void rmnet_descriptor_add_frag(struct rmnet_port *port, struct list_head *list,
-			       struct page *p, u32 page_offset, u32 len)
-{
-	struct rmnet_frag_descriptor *frag_desc;
-
-	frag_desc = rmnet_get_frag_descriptor(port);
-	if (!frag_desc)
-		return;
-
-	rmnet_frag_fill(frag_desc, p, page_offset, len);
-	list_add_tail(&frag_desc->list, list);
-}
-
-static u8 rmnet_frag_do_flow_control(struct rmnet_map_header *qmap,
-				     struct rmnet_port *port,
-				     int enable)
-{
-	struct rmnet_map_control_command *cmd;
-	struct rmnet_endpoint *ep;
-	struct net_device *vnd;
-	u16 ip_family;
-	u16 fc_seq;
-	u32 qos_id;
-	u8 mux_id;
-	int r;
-
-	mux_id = qmap->mux_id;
-	cmd = (struct rmnet_map_control_command *)
-	      ((char *)qmap + sizeof(*qmap));
-
-	if (mux_id >= RMNET_MAX_LOGICAL_EP)
-		return RX_HANDLER_CONSUMED;
-
-	ep = rmnet_get_endpoint(port, mux_id);
-	if (!ep)
-		return RX_HANDLER_CONSUMED;
-
-	vnd = ep->egress_dev;
-
-	ip_family = cmd->flow_control.ip_family;
-	fc_seq = ntohs(cmd->flow_control.flow_control_seq_num);
-	qos_id = ntohl(cmd->flow_control.qos_id);
-
-	/* Ignore the ip family and pass the sequence number for both v4 and v6
-	 * sequence. User space does not support creating dedicated flows for
-	 * the 2 protocols
-	 */
-	r = rmnet_vnd_do_flow_control(vnd, enable);
-	if (r)
-		return RMNET_MAP_COMMAND_UNSUPPORTED;
-	else
-		return RMNET_MAP_COMMAND_ACK;
-}
-
-static void rmnet_frag_send_ack(struct rmnet_map_header *qmap,
-				unsigned char type,
-				struct rmnet_port *port)
-{
-	struct rmnet_map_control_command *cmd;
-	struct net_device *dev = port->dev;
-	struct sk_buff *skb;
-	u16 alloc_len = ntohs(qmap->pkt_len) + sizeof(*qmap);
-
-	skb = alloc_skb(alloc_len, GFP_ATOMIC);
-	if (!skb)
-		return;
-
-	skb->protocol = htons(ETH_P_MAP);
-	skb->dev = dev;
-
-	cmd = rmnet_map_get_cmd_start(skb);
-	cmd->cmd_type = type & 0x03;
-
-	netif_tx_lock(dev);
-	dev->netdev_ops->ndo_start_xmit(skb, dev);
-	netif_tx_unlock(dev);
-}
-
-
-/* Process MAP command frame and send N/ACK message as appropriate. Message cmd
- * name is decoded here and appropriate handler is called.
- */
-static void rmnet_frag_command(struct rmnet_map_header *qmap, struct rmnet_port *port)
-{
-	struct rmnet_map_control_command *cmd;
-	unsigned char command_name;
-	unsigned char rc = 0;
-
-	cmd = (struct rmnet_map_control_command *)
-	      ((char *)qmap + sizeof(*qmap));
-	command_name = cmd->command_name;
-
-	switch (command_name) {
-	case RMNET_MAP_COMMAND_FLOW_ENABLE:
-		rc = rmnet_frag_do_flow_control(qmap, port, 1);
-		break;
-
-	case RMNET_MAP_COMMAND_FLOW_DISABLE:
-		rc = rmnet_frag_do_flow_control(qmap, port, 0);
-		break;
-
-	default:
-		rc = RMNET_MAP_COMMAND_UNSUPPORTED;
-		break;
-	}
-	if (rc == RMNET_MAP_COMMAND_ACK)
-		rmnet_frag_send_ack(qmap, rc, port);
-}
-
-static void rmnet_frag_deaggregate(skb_frag_t *frag, struct rmnet_port *port,
-			    struct list_head *list)
-{
-	struct rmnet_map_header *maph;
-	u8 *data = skb_frag_address(frag);
-	u32 offset = 0;
-	u32 packet_len;
-
-	while (offset < skb_frag_size(frag)) {
-		maph = (struct rmnet_map_header *)data;
-		packet_len = ntohs(maph->pkt_len);
-
-		/* Some hardware can send us empty frames. Catch them */
-		if (packet_len == 0)
-			return;
-
-		packet_len += sizeof(*maph);
-
-		if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) {
-			packet_len += sizeof(struct rmnet_map_dl_csum_trailer);
-			WARN_ON(1);
-		} else if (port->data_format &
-			   (RMNET_FLAGS_INGRESS_MAP_CKSUMV5 |
-			    RMNET_FLAGS_INGRESS_COALESCE) && !maph->cd_bit) {
-			u32 hsize = 0;
-			u8 type;
-
-			type = ((struct rmnet_map_v5_coal_header *)
-				(data + sizeof(*maph)))->header_type;
-			switch (type) {
-			case RMNET_MAP_HEADER_TYPE_COALESCING:
-				hsize = sizeof(struct rmnet_map_v5_coal_header);
-				break;
-			case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD:
-				hsize = sizeof(struct rmnet_map_v5_csum_header);
-				break;
-			}
-
-			packet_len += hsize;
-		}
-		else {
-			//qmap_hex_dump(__func__, data, 64);
-			WARN_ON(1);
-		}
-
-		if ((int)skb_frag_size(frag) - (int)packet_len < 0)
-			return;
-
-		rmnet_descriptor_add_frag(port, list, skb_frag_page(frag),
-					  frag->page_offset + offset,
-					  packet_len);
-
-		offset += packet_len;
-		data += packet_len;
-	}
-}
-
-/* Allocate and populate an skb to contain the packet represented by the
- * frag descriptor.
- */
-static struct sk_buff *rmnet_alloc_skb(struct rmnet_frag_descriptor *frag_desc,
-				       struct rmnet_port *port)
-{
-	struct sk_buff *head_skb, *current_skb, *skb;
-	struct skb_shared_info *shinfo;
-	struct rmnet_frag_descriptor *sub_frag, *tmp;
-
-	/* Use the exact sizes if we know them (i.e. RSB/RSC, rmnet_perf) */
-	if (frag_desc->hdrs_valid) {
-		u16 hdr_len = frag_desc->ip_len + frag_desc->trans_len;
-
-		head_skb = alloc_skb(hdr_len + RMNET_MAP_DESC_HEADROOM,
-				     GFP_ATOMIC);
-		if (!head_skb)
-			return NULL;
-
-		skb_reserve(head_skb, RMNET_MAP_DESC_HEADROOM);
-		skb_put_data(head_skb, frag_desc->hdr_ptr, hdr_len);
-		skb_reset_network_header(head_skb);
-
-		if (frag_desc->trans_len)
-			skb_set_transport_header(head_skb, frag_desc->ip_len);
-
-		/* Packets that have no data portion don't need any frags */
-		if (hdr_len == skb_frag_size(&frag_desc->frag))
-			goto skip_frags;
-
-		/* If the headers we added are the start of the page,
-		 * we don't want to add them twice
-		 */
-		if (frag_desc->hdr_ptr == rmnet_frag_data_ptr(frag_desc)) {
-			if (!rmnet_frag_pull(frag_desc, port, hdr_len)) {
-				kfree_skb(head_skb);
-				return NULL;
-			}
-		}
-	} else {
-		/* Allocate enough space to avoid penalties in the stack
-		 * from __pskb_pull_tail()
-		 */
-		head_skb = alloc_skb(256 + RMNET_MAP_DESC_HEADROOM,
-				     GFP_ATOMIC);
-		if (!head_skb)
-			return NULL;
-
-		skb_reserve(head_skb, RMNET_MAP_DESC_HEADROOM);
-	}
-
-	/* Add main fragment */
-	get_page(skb_frag_page(&frag_desc->frag));
-	skb_add_rx_frag(head_skb, 0, skb_frag_page(&frag_desc->frag),
-			frag_desc->frag.page_offset,
-			skb_frag_size(&frag_desc->frag),
-			skb_frag_size(&frag_desc->frag));
-
-	shinfo = skb_shinfo(head_skb);
-	current_skb = head_skb;
-
-	/* Add in any frags from rmnet_perf */
-	list_for_each_entry_safe(sub_frag, tmp, &frag_desc->sub_frags, list) {
-		skb_frag_t *frag;
-		u32 frag_size;
-
-		frag = &sub_frag->frag;
-		frag_size = skb_frag_size(frag);
-
-add_frag:
-		if (shinfo->nr_frags < MAX_SKB_FRAGS) {
-			get_page(skb_frag_page(frag));
-			skb_add_rx_frag(current_skb, shinfo->nr_frags,
-					skb_frag_page(frag), frag->page_offset,
-					frag_size, frag_size);
-			if (current_skb != head_skb) {
-				head_skb->len += frag_size;
-				head_skb->data_len += frag_size;
-			}
-		} else {
-			/* Alloc a new skb and try again */
-			skb = alloc_skb(0, GFP_ATOMIC);
-			if (!skb)
-				break;
-
-			if (current_skb == head_skb)
-				shinfo->frag_list = skb;
-			else
-				current_skb->next = skb;
-
-			current_skb = skb;
-			shinfo = skb_shinfo(current_skb);
-			goto add_frag;
-		}
-
-		rmnet_recycle_frag_descriptor(sub_frag, port);
-	}
-
-skip_frags:
-	head_skb->dev = frag_desc->dev;
-	rmnet_set_skb_proto(head_skb);
-
-	/* Handle any header metadata that needs to be updated after RSB/RSC
-	 * segmentation
-	 */
-	if (frag_desc->ip_id_set) {
-		struct iphdr *iph;
-
-		iph = (struct iphdr *)rmnet_map_data_ptr(head_skb);
-		csum_replace2(&iph->check, iph->id, frag_desc->ip_id);
-		iph->id = frag_desc->ip_id;
-	}
-
-	if (frag_desc->tcp_seq_set) {
-		struct tcphdr *th;
-
-		th = (struct tcphdr *)
-		     (rmnet_map_data_ptr(head_skb) + frag_desc->ip_len);
-		th->seq = frag_desc->tcp_seq;
-	}
-
-	/* Handle csum offloading */
-	if (frag_desc->csum_valid && frag_desc->hdrs_valid) {
-		/* Set the partial checksum information */
-		//rmnet_frag_partial_csum(head_skb, frag_desc);
-		WARN_ON(1);
-	} else if (frag_desc->csum_valid) {
-		/* Non-RSB/RSC/perf packet. The current checksum is fine */
-		head_skb->ip_summed = CHECKSUM_UNNECESSARY;
-	} else if (frag_desc->hdrs_valid &&
-		   (frag_desc->trans_proto == IPPROTO_TCP ||
-		    frag_desc->trans_proto == IPPROTO_UDP)) {
-		/* Unfortunately, we have to fake a bad checksum here, since
-		 * the original bad value is lost by the hardware. The only
-		 * reliable way to do it is to calculate the actual checksum
-		 * and corrupt it.
-		 */
-		__sum16 *check;
-		__wsum csum;
-		unsigned int offset = skb_transport_offset(head_skb);
-		__sum16 pseudo;
-
-		WARN_ON(1);
-		/* Calculate pseudo header and update header fields */
-		if (frag_desc->ip_proto == 4) {
-			struct iphdr *iph = ip_hdr(head_skb);
-			__be16 tot_len = htons(head_skb->len);
-
-			csum_replace2(&iph->check, iph->tot_len, tot_len);
-			iph->tot_len = tot_len;
-			pseudo = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
-						    head_skb->len -
-						    frag_desc->ip_len,
-						    frag_desc->trans_proto, 0);
-		} else {
-			struct ipv6hdr *ip6h = ipv6_hdr(head_skb);
-
-			ip6h->payload_len = htons(head_skb->len -
-						  sizeof(*ip6h));
-			pseudo = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
-						  head_skb->len -
-						  frag_desc->ip_len,
-						  frag_desc->trans_proto, 0);
-		}
-
-		if (frag_desc->trans_proto == IPPROTO_TCP) {
-			check = &tcp_hdr(head_skb)->check;
-		} else {
-			udp_hdr(head_skb)->len = htons(head_skb->len -
-						       frag_desc->ip_len);
-			check = &udp_hdr(head_skb)->check;
-		}
-
-		*check = pseudo;
-		csum = skb_checksum(head_skb, offset, head_skb->len - offset,
-				    0);
-		/* Add 1 to corrupt. This cannot produce a final value of 0
-		 * since csum_fold() can't return a value of 0xFFFF
-		 */
-		*check = csum16_add(csum_fold(csum), htons(1));
-		head_skb->ip_summed = CHECKSUM_NONE;
-	}
-
-	/* Handle any rmnet_perf metadata */
-	if (frag_desc->hash) {
-		head_skb->hash = frag_desc->hash;
-		head_skb->sw_hash = 1;
-	}
-
-	if (frag_desc->flush_shs)
-		head_skb->cb[0] = 1;
-
-	/* Handle coalesced packets */
-	//if (frag_desc->gso_segs > 1)
-	//	rmnet_frag_gso_stamp(head_skb, frag_desc);
-
-	return head_skb;
-}
-
-/* Deliver the packets contained within a frag descriptor */
-static void rmnet_frag_deliver(struct rmnet_frag_descriptor *frag_desc,
-			struct rmnet_port *port)
-{
-	struct sk_buff *skb;
-
-	skb = rmnet_alloc_skb(frag_desc, port);
-	if (skb)
-		rmnet_deliver_skb(skb, port);
-	rmnet_recycle_frag_descriptor(frag_desc, port);
-}
-
-/* Process a QMAPv5 packet header */
-static int rmnet_frag_process_next_hdr_packet(struct rmnet_frag_descriptor *frag_desc,
-				       struct rmnet_port *port,
-				       struct list_head *list,
-				       u16 len)
-{
-	int rc = 0;
-
-	switch (rmnet_frag_get_next_hdr_type(frag_desc)) {
-	case RMNET_MAP_HEADER_TYPE_COALESCING:
-		rc = -1;
-		WARN_ON(1);
-		break;
-	case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD:
-		if (rmnet_frag_get_csum_valid(frag_desc)) {
-			frag_desc->csum_valid = true;
-		} else {
-		}
-
-		if (!rmnet_frag_pull(frag_desc, port,
-				     sizeof(struct rmnet_map_header) +
-				     sizeof(struct rmnet_map_v5_csum_header))) {
-			rc = -EINVAL;
-			break;
-		}
-
-		frag_desc->hdr_ptr = rmnet_frag_data_ptr(frag_desc);
-
-		/* Remove padding only for csum offload packets.
-		 * Coalesced packets should never have padding.
-		 */
-		if (!rmnet_frag_trim(frag_desc, port, len)) {
-			rc = -EINVAL;
-			break;
-		}
-
-		list_del_init(&frag_desc->list);
-		list_add_tail(&frag_desc->list, list);
-		break;
-	default:
-		//qmap_hex_dump(__func__, rmnet_frag_data_ptr(frag_desc), 64);
-		rc = -EINVAL;
-		break;
-	}
-
-	return rc;
-}
-
-static void
-__rmnet_frag_ingress_handler(struct rmnet_frag_descriptor *frag_desc,
-			     struct rmnet_port *port)
-{
-	struct rmnet_map_header *qmap;
-	struct rmnet_endpoint *ep;
-	struct rmnet_frag_descriptor *frag, *tmp;
-	LIST_HEAD(segs);
-	u16 len, pad;
-	u8 mux_id;
-
-	qmap = (struct rmnet_map_header *)skb_frag_address(&frag_desc->frag);
-	mux_id = qmap->mux_id;
-	pad = qmap->pad_len;
-	len = ntohs(qmap->pkt_len) - pad;
-
-	if (qmap->cd_bit) {
-		if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) {
-			//rmnet_frag_flow_command(qmap, port, len);
-			goto recycle;
-		}
-
-		if (port->data_format & RMNET_FLAGS_INGRESS_MAP_COMMANDS)
-			rmnet_frag_command(qmap, port);
-
-		goto recycle;
-	}
-
-	if (mux_id >= RMNET_MAX_LOGICAL_EP)
-		goto recycle;
-
-	ep = rmnet_get_endpoint(port, mux_id);
-	if (!ep)
-		goto recycle;
-
-	frag_desc->dev = ep->egress_dev;
-
-	/* Handle QMAPv5 packet */
-	if (qmap->next_hdr &&
-	    (port->data_format & (RMNET_FLAGS_INGRESS_COALESCE |
-				  RMNET_FLAGS_INGRESS_MAP_CKSUMV5))) {
-		if (rmnet_frag_process_next_hdr_packet(frag_desc, port, &segs,
-						       len))
-			goto recycle;
-	} else {
-		/* We only have the main QMAP header to worry about */
-		if (!rmnet_frag_pull(frag_desc, port, sizeof(*qmap)))
-			return;
-
-		frag_desc->hdr_ptr = rmnet_frag_data_ptr(frag_desc);
-
-		if (!rmnet_frag_trim(frag_desc, port, len))
-			return;
-
-		list_add_tail(&frag_desc->list, &segs);
-	}
-
-	list_for_each_entry_safe(frag, tmp, &segs, list) {
-		list_del_init(&frag->list);
-		rmnet_frag_deliver(frag, port);
-	}
-	return;
-
-recycle:
-	rmnet_recycle_frag_descriptor(frag_desc, port);
-}
-
-static void rmnet_frag_ingress_handler(struct sk_buff *skb,
-				struct rmnet_port *port)
-{
-	LIST_HEAD(desc_list);
-	int i = 0;
-	struct rmnet_nss_cb *nss_cb;
-
-	/* Deaggregation and freeing of HW originating
-	 * buffers is done within here
-	 */
-	while (skb) {
-		struct sk_buff *skb_frag;
-
-		port->chain_head = NULL;
-		port->chain_tail = NULL;
-
-		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
-			rmnet_frag_deaggregate(&skb_shinfo(skb)->frags[i], port,
-					       &desc_list);
-			if (!list_empty(&desc_list)) {
-				struct rmnet_frag_descriptor *frag_desc, *tmp;
-
-				list_for_each_entry_safe(frag_desc, tmp,
-							 &desc_list, list) {
-					list_del_init(&frag_desc->list);
-					__rmnet_frag_ingress_handler(frag_desc,
-								     port);
-				}
-			}
-		}
-
-		nss_cb = rcu_dereference(rmnet_nss_callbacks);
-		if (nss_cb && port->chain_head) {
-			port->chain_head->cb[0] = 0;
-			netif_receive_skb(port->chain_head);
-		}
-
-		skb_frag = skb_shinfo(skb)->frag_list;
-		skb_shinfo(skb)->frag_list = NULL;
-		consume_skb(skb);
-		skb = skb_frag;
-	}
-}
-
-void rmnet_descriptor_deinit(struct rmnet_port *port)
-{
-	struct rmnet_frag_descriptor_pool *pool;
-	struct rmnet_frag_descriptor *frag_desc, *tmp;
-
-	pool = port->frag_desc_pool;
-
-	list_for_each_entry_safe(frag_desc, tmp, &pool->free_list, list) {
-		kfree(frag_desc);
-		pool->pool_size--;
-	}
-
-	kfree(pool);
-}
-
-int rmnet_descriptor_init(struct rmnet_port *port)
-{
-	struct rmnet_frag_descriptor_pool *pool;
-	int i;
-
-	spin_lock_init(&port->desc_pool_lock);
-	pool = kzalloc(sizeof(*pool), GFP_ATOMIC);
-	if (!pool)
-		return -ENOMEM;
-
-	INIT_LIST_HEAD(&pool->free_list);
-	port->frag_desc_pool = pool;
-
-	for (i = 0; i < RMNET_FRAG_DESCRIPTOR_POOL_SIZE; i++) {
-		struct rmnet_frag_descriptor *frag_desc;
-
-		frag_desc = kzalloc(sizeof(*frag_desc), GFP_ATOMIC);
-		if (!frag_desc)
-			return -ENOMEM;
-
-		INIT_LIST_HEAD(&frag_desc->list);
-		INIT_LIST_HEAD(&frag_desc->sub_frags);
-		list_add_tail(&frag_desc->list, &pool->free_list);
-		pool->pool_size++;
-	}
-
-	return 0;
-}
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_descriptor.h b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_descriptor.h
deleted file mode 100644
index 962c663af..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_descriptor.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * RMNET Packet Descriptor Framework
- *
- */
-
-#ifndef _RMNET_DESCRIPTOR_H_
-#define _RMNET_DESCRIPTOR_H_
-
-#include <linux/netdevice.h>
-#include <linux/list.h>
-#include <linux/skbuff.h>
-#include "rmnet_config.h"
-#include "rmnet_map.h"
-
-struct rmnet_frag_descriptor_pool {
-	struct list_head free_list;
-	u32 pool_size;
-};
-
-struct rmnet_frag_descriptor {
-	struct list_head list;
-	struct list_head sub_frags;
-	skb_frag_t frag;
-	u8 *hdr_ptr;
-	struct net_device *dev;
-	u32 hash;
-	__be32 tcp_seq;
-	__be16 ip_id;
-	u16 data_offset;
-	u16 gso_size;
-	u16 gso_segs;
-	u16 ip_len;
-	u16 trans_len;
-	u8 ip_proto;
-	u8 trans_proto;
-	u8 pkt_id;
-	u8 csum_valid:1,
-	   hdrs_valid:1,
-	   ip_id_set:1,
-	   tcp_seq_set:1,
-	   flush_shs:1,
-	   reserved:3;
-};
-
-/* Descriptor management */
-static struct rmnet_frag_descriptor *
-rmnet_get_frag_descriptor(struct rmnet_port *port);
-static void rmnet_recycle_frag_descriptor(struct rmnet_frag_descriptor *frag_desc,
-				   struct rmnet_port *port);
-static void rmnet_descriptor_add_frag(struct rmnet_port *port, struct list_head *list,
-			       struct page *p, u32 page_offset, u32 len);
-
-/* QMAP command packets */
-
-/* Ingress data handlers */
-static void rmnet_frag_deaggregate(skb_frag_t *frag, struct rmnet_port *port,
-			    struct list_head *list);
-static void rmnet_frag_deliver(struct rmnet_frag_descriptor *frag_desc,
-			struct rmnet_port *port);
-static int rmnet_frag_process_next_hdr_packet(struct rmnet_frag_descriptor *frag_desc,
-				       struct rmnet_port *port,
-				       struct list_head *list,
-				       u16 len);
-static void rmnet_frag_ingress_handler(struct sk_buff *skb,
-				struct rmnet_port *port);
-
-static int rmnet_descriptor_init(struct rmnet_port *port);
-static void rmnet_descriptor_deinit(struct rmnet_port *port);
-
-static inline void *rmnet_frag_data_ptr(struct rmnet_frag_descriptor *frag_desc)
-{
-	return skb_frag_address(&frag_desc->frag);
-}
-
-static inline void *rmnet_frag_pull(struct rmnet_frag_descriptor *frag_desc,
-				    struct rmnet_port *port,
-				    unsigned int size)
-{
-	if (size >= skb_frag_size(&frag_desc->frag)) {
-		pr_info("%s(): Pulling %u bytes from %u byte pkt. Dropping\n",
-			__func__, size, skb_frag_size(&frag_desc->frag));
-		rmnet_recycle_frag_descriptor(frag_desc, port);
-		return NULL;
-	}
-
-	frag_desc->frag.page_offset += size;
-	skb_frag_size_sub(&frag_desc->frag, size);
-
-	return rmnet_frag_data_ptr(frag_desc);
-}
-
-static inline void *rmnet_frag_trim(struct rmnet_frag_descriptor *frag_desc,
-				    struct rmnet_port *port,
-				    unsigned int size)
-{
-	if (!size) {
-		pr_info("%s(): Trimming %u byte pkt to 0. Dropping\n",
-			__func__, skb_frag_size(&frag_desc->frag));
-		rmnet_recycle_frag_descriptor(frag_desc, port);
-		return NULL;
-	}
-
-	if (size < skb_frag_size(&frag_desc->frag))
-		skb_frag_size_set(&frag_desc->frag, size);
-
-	return rmnet_frag_data_ptr(frag_desc);
-}
-
-static inline void rmnet_frag_fill(struct rmnet_frag_descriptor *frag_desc,
-				   struct page *p, u32 page_offset, u32 len)
-{
-	get_page(p);
-	__skb_frag_set_page(&frag_desc->frag, p);
-	skb_frag_size_set(&frag_desc->frag, len);
-	frag_desc->frag.page_offset = page_offset;
-}
-
-static inline u8
-rmnet_frag_get_next_hdr_type(struct rmnet_frag_descriptor *frag_desc)
-{
-	unsigned char *data = rmnet_frag_data_ptr(frag_desc);
-
-	data += sizeof(struct rmnet_map_header);
-	return ((struct rmnet_map_v5_coal_header *)data)->header_type;
-}
-
-static inline bool
-rmnet_frag_get_csum_valid(struct rmnet_frag_descriptor *frag_desc)
-{
-	unsigned char *data = rmnet_frag_data_ptr(frag_desc);
-
-	data += sizeof(struct rmnet_map_header);
-	return ((struct rmnet_map_v5_csum_header *)data)->csum_valid_required;
-}
-
-#endif /* _RMNET_DESCRIPTOR_H_ */
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_handlers.c b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_handlers.c
deleted file mode 100644
index 6f1ce9de8..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_handlers.c
+++ /dev/null
@@ -1,374 +0,0 @@
-/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * RMNET Data ingress/egress handler
- *
- */
-
-#include <linux/netdevice.h>
-#include <linux/netdev_features.h>
-#include <linux/if_arp.h>
-#include <linux/ip.h>
-#include <linux/ipv6.h>
-#include <net/sock.h>
-#include <linux/tracepoint.h>
-#include "rmnet_private.h"
-#include "rmnet_config.h"
-#include "rmnet_vnd.h"
-#include "rmnet_map.h"
-#include "rmnet_handlers.h"
-#include "rmnet_descriptor.h"
-
-#define RMNET_IP_VERSION_4 0x40
-#define RMNET_IP_VERSION_6 0x60
-
-/* Helper Functions */
-
-static void rmnet_set_skb_proto(struct sk_buff *skb)
-{
-	switch (rmnet_map_data_ptr(skb)[0] & 0xF0) {
-	case RMNET_IP_VERSION_4:
-		skb->protocol = htons(ETH_P_IP);
-		break;
-	case RMNET_IP_VERSION_6:
-		skb->protocol = htons(ETH_P_IPV6);
-		break;
-	default:
-		skb->protocol = htons(ETH_P_MAP);
-		break;
-	}
-}
-
-/* Generic handler */
-
-static void
-rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_port *port)
-{
-	struct rmnet_nss_cb *nss_cb;
-
-	rmnet_vnd_rx_fixup(skb->dev, skb->len);
-
-	/* Pass off the packet to NSS driver if we can */
-	nss_cb = rcu_dereference(rmnet_nss_callbacks);
-	if (nss_cb) {
-		if (!port->chain_head)
-			port->chain_head = skb;
-		else
-			skb_shinfo(port->chain_tail)->frag_list = skb;
-
-		port->chain_tail = skb;
-		return;
-	}
-
-	skb_reset_transport_header(skb);
-	skb_reset_network_header(skb);
-
-	skb->pkt_type = PACKET_HOST;
-	skb_set_mac_header(skb, 0);
-
-	//if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) {
-	//} else {
-		//if (!rmnet_check_skb_can_gro(skb))
-		//	gro_cells_receive(&priv->gro_cells, skb);
-		//else
-			netif_receive_skb(skb);
-	//}
-}
-
-/* Deliver a list of skbs after undoing coalescing */
-static void rmnet_deliver_skb_list(struct sk_buff_head *head,
-				   struct rmnet_port *port)
-{
-	struct sk_buff *skb;
-
-	while ((skb = __skb_dequeue(head))) {
-		rmnet_set_skb_proto(skb);
-		rmnet_deliver_skb(skb, port);
-	}
-}
-
-/* MAP handler */
-
-static void
-_rmnet_map_ingress_handler(struct sk_buff *skb,
-			    struct rmnet_port *port)
-{
-	struct rmnet_map_header *qmap;
-	struct rmnet_endpoint *ep;
-	struct sk_buff_head list;
-	u16 len, pad;
-	u8 mux_id;
-
-	/* We don't need the spinlock since only we touch this */
-	__skb_queue_head_init(&list);
-
-	qmap = (struct rmnet_map_header *)rmnet_map_data_ptr(skb);
-	if (qmap->cd_bit) {
-		if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) {
-			//if (!rmnet_map_flow_command(skb, port, false))
-				return;
-		}
-
-		if (port->data_format & RMNET_FLAGS_INGRESS_MAP_COMMANDS)
-			return rmnet_map_command(skb, port);
-
-		goto free_skb;
-	}
-
-	mux_id = qmap->mux_id;
-	pad = qmap->pad_len;
-	len = ntohs(qmap->pkt_len) - pad;
-
-	if (mux_id >= RMNET_MAX_LOGICAL_EP)
-		goto free_skb;
-
-	ep = rmnet_get_endpoint(port, mux_id);
-	if (!ep)
-		goto free_skb;
-
-	skb->dev = ep->egress_dev;
-
-	/* Handle QMAPv5 packet */
-	if (qmap->next_hdr &&
-	    (port->data_format & (RMNET_FLAGS_INGRESS_COALESCE |
-				  RMNET_FLAGS_INGRESS_MAP_CKSUMV5))) {
-		if (rmnet_map_process_next_hdr_packet(skb, &list, len))
-			goto free_skb;
-	} else {
-		/* We only have the main QMAP header to worry about */
-		pskb_pull(skb, sizeof(*qmap));
-
-		rmnet_set_skb_proto(skb);
-
-		if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) {
-			//if (!rmnet_map_checksum_downlink_packet(skb, len + pad))
-			//	skb->ip_summed = CHECKSUM_UNNECESSARY;
-		}
-
-		pskb_trim(skb, len);
-
-		/* Push the single packet onto the list */
-		__skb_queue_tail(&list, skb);
-	}
-
-	rmnet_deliver_skb_list(&list, port);
-	return;
-
-free_skb:
-	kfree_skb(skb);
-}
-
-static void
-rmnet_map_ingress_handler(struct sk_buff *skb,
-			  struct rmnet_port *port)
-{
-	struct sk_buff *skbn;
-
-	if (port->data_format & (RMNET_FLAGS_INGRESS_COALESCE |
-				 RMNET_FLAGS_INGRESS_MAP_CKSUMV5)) {
-		if (skb_is_nonlinear(skb)) {
-			rmnet_frag_ingress_handler(skb, port);
-			return;
-		}
-	}
-
-	/* Deaggregation and freeing of HW originating
-	 * buffers is done within here
-	 */
-	while (skb) {
-		struct sk_buff *skb_frag = skb_shinfo(skb)->frag_list;
-
-		skb_shinfo(skb)->frag_list = NULL;
-		while ((skbn = rmnet_map_deaggregate(skb, port)) != NULL) {
-			_rmnet_map_ingress_handler(skbn, port);
-
-			if (skbn == skb)
-				goto next_skb;
-		}
-
-		consume_skb(skb);
-next_skb:
-		skb = skb_frag;
-	}
-}
-
-static int rmnet_map_egress_handler(struct sk_buff *skb,
-				    struct rmnet_port *port, u8 mux_id,
-				    struct net_device *orig_dev)
-{
-	int required_headroom, additional_header_len, csum_type;
-	struct rmnet_map_header *map_header;
-
-	additional_header_len = 0;
-	required_headroom = sizeof(struct rmnet_map_header);
-	csum_type = 0;
-
-	if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV4) {
-		additional_header_len = sizeof(struct rmnet_map_ul_csum_header);
-		csum_type = RMNET_FLAGS_EGRESS_MAP_CKSUMV4;
-	} else if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV5) {
-		additional_header_len = sizeof(struct rmnet_map_v5_csum_header);
-		csum_type = RMNET_FLAGS_EGRESS_MAP_CKSUMV5;
-	}
-
-	required_headroom += additional_header_len;
-
-	if (skb_headroom(skb) < required_headroom) {
-		if (pskb_expand_head(skb, required_headroom, 0, GFP_ATOMIC))
-			return -ENOMEM;
-	}
-
-	if (csum_type)
-		rmnet_map_checksum_uplink_packet(skb, orig_dev, csum_type);
-
-	map_header = rmnet_map_add_map_header(skb, additional_header_len, 0,
-					      port);
-	if (!map_header)
-		return -ENOMEM;
-
-	map_header->mux_id = mux_id;
-
-	if (port->data_format & RMNET_EGRESS_FORMAT_AGGREGATION) {
-		if (rmnet_map_tx_agg_skip(skb, required_headroom))
-			goto done;
-
-		rmnet_map_tx_aggregate(skb, port);
-		return -EINPROGRESS;
-	}
-
-done:
-	skb->protocol = htons(ETH_P_MAP);
-	return 0;
-}
-
-static void
-rmnet_bridge_handler(struct sk_buff *skb, struct net_device *bridge_dev)
-{
-	if (bridge_dev) {
-		skb->dev = bridge_dev;
-		dev_queue_xmit(skb);
-	}
-}
-
-/* Ingress / Egress Entry Points */
-
-/* Processes packet as per ingress data format for receiving device. Logical
- * endpoint is determined from packet inspection. Packet is then sent to the
- * egress device listed in the logical endpoint configuration.
- */
-static rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb)
-{
-	struct sk_buff *skb = *pskb;
-	struct rmnet_port *port;
-	struct net_device *dev;
-
-	if (!skb)
-		goto done;
-
-	if (skb->pkt_type == PACKET_LOOPBACK)
-		return RX_HANDLER_PASS;
-
-	dev = skb->dev;
-	port = rmnet_get_port(dev);
-
-	port->chain_head = NULL;
-	port->chain_tail = NULL;
-
-	switch (port->rmnet_mode) {
-	case RMNET_EPMODE_VND:
-		rmnet_map_ingress_handler(skb, port);
-		break;
-	case RMNET_EPMODE_BRIDGE:
-		rmnet_bridge_handler(skb, port->bridge_ep);
-		break;
-	}
-
-done:
-	return RX_HANDLER_CONSUMED;
-}
-
-static rx_handler_result_t rmnet_rx_priv_handler(struct sk_buff **pskb)
-{
-	struct sk_buff *skb = *pskb;
-	struct rmnet_nss_cb *nss_cb;
-
-	if (!skb)
-		return RX_HANDLER_CONSUMED;
-	if (nss_debug) printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len);
-
-	if (skb->pkt_type == PACKET_LOOPBACK)
-		return RX_HANDLER_PASS;
-
-	/* Check this so that we dont loop around netif_receive_skb */
-	if (skb->cb[0] == 1) {
-		skb->cb[0] = 0;
-
-		skb->dev->stats.rx_packets++;
-		return RX_HANDLER_PASS;
-	}
-
-	while (skb) {
-		struct sk_buff *skb_frag = skb_shinfo(skb)->frag_list;
-
-		skb_shinfo(skb)->frag_list = NULL;
-
-		nss_cb = rcu_dereference(rmnet_nss_callbacks);
-		if (nss_cb)
-			nss_cb->nss_tx(skb);
-
-		skb = skb_frag;
-	}
-
-	return RX_HANDLER_CONSUMED;
-}
-
-/* Modifies packet as per logical endpoint configuration and egress data format
- * for egress device configured in logical endpoint. Packet is then transmitted
- * on the egress device.
- */
-static void rmnet_egress_handler(struct sk_buff *skb)
-{
-	struct net_device *orig_dev;
-	struct rmnet_port *port;
-	struct rmnet_priv *priv;
-	u8 mux_id;
-	int err;
-	u32 skb_len;
-
-	skb_orphan(skb);
-
-	orig_dev = skb->dev;
-	priv = netdev_priv(orig_dev);
-	skb->dev = priv->real_dev;
-	mux_id = priv->mux_id;
-
-	port = rmnet_get_port(skb->dev);
-	if (!port)
-		goto drop;
-
-	skb_len = skb->len;
-	err = rmnet_map_egress_handler(skb, port, mux_id, orig_dev);
-	if (err == -ENOMEM)
-		goto drop;
-	else if (err == -EINPROGRESS) {
-		rmnet_vnd_tx_fixup(orig_dev, skb_len);
-		return;
-	}
-
-	rmnet_vnd_tx_fixup(orig_dev, skb_len);
-
-	dev_queue_xmit(skb);
-	return;
-
-drop:
-	this_cpu_inc(priv->pcpu_stats->stats.tx_drops);
-	kfree_skb(skb);
-}
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_handlers.h b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_handlers.h
deleted file mode 100644
index 29837baa7..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_handlers.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Copyright (c) 2013, 2016-2017, 2019
- * The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * RMNET Data ingress/egress handler
- *
- */
-
-#ifndef _RMNET_HANDLERS_H_
-#define _RMNET_HANDLERS_H_
-
-#include "rmnet_config.h"
-
-enum rmnet_packet_context {
-	RMNET_NET_RX_CTX,
-	RMNET_WQ_CTX,
-};
-
-static void rmnet_egress_handler(struct sk_buff *skb);
-static void rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_port *port);
-static void rmnet_set_skb_proto(struct sk_buff *skb);
-static rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb);
-static rx_handler_result_t rmnet_rx_priv_handler(struct sk_buff **pskb);
-#endif /* _RMNET_HANDLERS_H_ */
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map.h b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map.h
deleted file mode 100644
index ab4914933..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map.h
+++ /dev/null
@@ -1,272 +0,0 @@
-/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef _RMNET_MAP_H_
-#define _RMNET_MAP_H_
-
-#include <linux/skbuff.h>
-#include "rmnet_config.h"
-
-struct rmnet_map_control_command {
-	u8  command_name;
-	u8  cmd_type:2;
-	u8  reserved:6;
-	u16 reserved2;
-	u32 transaction_id;
-	union {
-		struct {
-			u16 ip_family:2;
-			u16 reserved:14;
-			__be16 flow_control_seq_num;
-			__be32 qos_id;
-		} flow_control;
-		u8 data[0];
-	};
-}  __aligned(1);
-
-enum rmnet_map_commands {
-	RMNET_MAP_COMMAND_NONE,
-	RMNET_MAP_COMMAND_FLOW_DISABLE,
-	RMNET_MAP_COMMAND_FLOW_ENABLE,
-	RMNET_MAP_COMMAND_FLOW_START = 7,
-	RMNET_MAP_COMMAND_FLOW_END = 8,
-	/* These should always be the last 2 elements */
-	RMNET_MAP_COMMAND_UNKNOWN,
-	RMNET_MAP_COMMAND_ENUM_LENGTH
-};
-
-enum rmnet_map_v5_header_type {
-	RMNET_MAP_HEADER_TYPE_UNKNOWN,
-	RMNET_MAP_HEADER_TYPE_COALESCING = 0x1,
-	RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD = 0x2,
-	RMNET_MAP_HEADER_TYPE_ENUM_LENGTH
-};
-
-enum rmnet_map_v5_close_type {
-	RMNET_MAP_COAL_CLOSE_NON_COAL,
-	RMNET_MAP_COAL_CLOSE_IP_MISS,
-	RMNET_MAP_COAL_CLOSE_TRANS_MISS,
-	RMNET_MAP_COAL_CLOSE_HW,
-	RMNET_MAP_COAL_CLOSE_COAL,
-};
-
-enum rmnet_map_v5_close_value {
-	RMNET_MAP_COAL_CLOSE_HW_NL,
-	RMNET_MAP_COAL_CLOSE_HW_PKT,
-	RMNET_MAP_COAL_CLOSE_HW_BYTE,
-	RMNET_MAP_COAL_CLOSE_HW_TIME,
-	RMNET_MAP_COAL_CLOSE_HW_EVICT,
-};
-
-/* Main QMAP header */
-struct rmnet_map_header {
-	u8  pad_len:6;
-	u8  next_hdr:1;
-	u8  cd_bit:1;
-	u8  mux_id;
-	__be16 pkt_len;
-}  __aligned(1);
-
-/* QMAP v5 headers */
-struct rmnet_map_v5_csum_header {
-	u8  next_hdr:1;
-	u8  header_type:7;
-	u8  hw_reserved:7;
-	u8  csum_valid_required:1;
-	__be16 reserved;
-} __aligned(1);
-
-struct rmnet_map_v5_nl_pair {
-	__be16 pkt_len;
-	u8  csum_error_bitmap;
-	u8  num_packets;
-} __aligned(1);
-
-/* NLO: Number-length object */
-#define RMNET_MAP_V5_MAX_NLOS         (6)
-#define RMNET_MAP_V5_MAX_PACKETS      (48)
-
-struct rmnet_map_v5_coal_header {
-	u8  next_hdr:1;
-	u8  header_type:7;
-	u8  reserved1:4;
-	u8  num_nlos:3;
-	u8  csum_valid:1;
-	u8  close_type:4;
-	u8  close_value:4;
-	u8  reserved2:4;
-	u8  virtual_channel_id:4;
-
-	struct rmnet_map_v5_nl_pair nl_pairs[RMNET_MAP_V5_MAX_NLOS];
-} __aligned(1);
-
-/* QMAP v4 headers */
-struct rmnet_map_dl_csum_trailer {
-	u8  reserved1;
-	u8  valid:1;
-	u8  reserved2:7;
-	u16 csum_start_offset;
-	u16 csum_length;
-	__be16 csum_value;
-} __aligned(1);
-
-struct rmnet_map_ul_csum_header {
-	__be16 csum_start_offset;
-	u16 csum_insert_offset:14;
-	u16 udp_ind:1;
-	u16 csum_enabled:1;
-} __aligned(1);
-
-struct rmnet_map_control_command_header {
-	u8 command_name;
-	u8 cmd_type:2;
-	u8 reserved:5;
-	u8 e:1;
-	u16 source_id:15;
-	u16 ext:1;
-	u32 transaction_id;
-}  __aligned(1);
-
-struct rmnet_map_flow_info_le {
-	__be32 mux_id;
-	__be32 flow_id;
-	__be32 bytes;
-	__be32 pkts;
-} __aligned(1);
-
-struct rmnet_map_flow_info_be {
-	u32 mux_id;
-	u32 flow_id;
-	u32 bytes;
-	u32 pkts;
-} __aligned(1);
-
-struct rmnet_map_dl_ind_hdr {
-	union {
-		struct {
-			u32 seq;
-			u32 bytes;
-			u32 pkts;
-			u32 flows;
-			struct rmnet_map_flow_info_le flow[0];
-		} le __aligned(1);
-		struct {
-			__be32 seq;
-			__be32 bytes;
-			__be32 pkts;
-			__be32 flows;
-			struct rmnet_map_flow_info_be flow[0];
-		} be __aligned(1);
-	} __aligned(1);
-} __aligned(1);
-
-struct rmnet_map_dl_ind_trl {
-	union {
-		__be32 seq_be;
-		u32 seq_le;
-	} __aligned(1);
-} __aligned(1);
-
-struct rmnet_map_dl_ind {
-	u8 priority;
-	union {
-		void (*dl_hdr_handler)(struct rmnet_map_dl_ind_hdr *);
-		void (*dl_hdr_handler_v2)(struct rmnet_map_dl_ind_hdr *,
-					  struct
-					  rmnet_map_control_command_header *);
-	} __aligned(1);
-	union {
-		void (*dl_trl_handler)(struct rmnet_map_dl_ind_trl *);
-		void (*dl_trl_handler_v2)(struct rmnet_map_dl_ind_trl *,
-					  struct
-					  rmnet_map_control_command_header *);
-	} __aligned(1);
-	struct list_head list;
-};
-
-#define RMNET_MAP_GET_MUX_ID(Y) (((struct rmnet_map_header *) \
-				 (Y)->data)->mux_id)
-#define RMNET_MAP_GET_CD_BIT(Y) (((struct rmnet_map_header *) \
-				(Y)->data)->cd_bit)
-#define RMNET_MAP_GET_PAD(Y) (((struct rmnet_map_header *) \
-				(Y)->data)->pad_len)
-#define RMNET_MAP_GET_CMD_START(Y) ((struct rmnet_map_control_command *) \
-				    ((Y)->data + \
-				      sizeof(struct rmnet_map_header)))
-#define RMNET_MAP_GET_LENGTH(Y) (ntohs(((struct rmnet_map_header *) \
-					(Y)->data)->pkt_len))
-
-#define RMNET_MAP_DEAGGR_SPACING  64
-#define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2)
-#define RMNET_MAP_DESC_HEADROOM   128
-
-#define RMNET_MAP_COMMAND_REQUEST     0
-#define RMNET_MAP_COMMAND_ACK         1
-#define RMNET_MAP_COMMAND_UNSUPPORTED 2
-#define RMNET_MAP_COMMAND_INVALID     3
-
-#define RMNET_MAP_NO_PAD_BYTES        0
-#define RMNET_MAP_ADD_PAD_BYTES       1
-
-static inline unsigned char *rmnet_map_data_ptr(struct sk_buff *skb)
-{
-	/* Nonlinear packets we receive are entirely within frag 0 */
-	if (skb_is_nonlinear(skb) && skb->len == skb->data_len)
-		return skb_frag_address(skb_shinfo(skb)->frags);
-
-	return skb->data;
-}
-
-static inline struct rmnet_map_control_command *
-rmnet_map_get_cmd_start(struct sk_buff *skb)
-{
-	unsigned char *data = rmnet_map_data_ptr(skb);
-
-	data += sizeof(struct rmnet_map_header);
-	return (struct rmnet_map_control_command *)data;
-}
-
-static inline u8 rmnet_map_get_next_hdr_type(struct sk_buff *skb)
-{
-	unsigned char *data = rmnet_map_data_ptr(skb);
-
-	data += sizeof(struct rmnet_map_header);
-	return ((struct rmnet_map_v5_coal_header *)data)->header_type;
-}
-
-static inline bool rmnet_map_get_csum_valid(struct sk_buff *skb)
-{
-	unsigned char *data = rmnet_map_data_ptr(skb);
-
-	data += sizeof(struct rmnet_map_header);
-	return ((struct rmnet_map_v5_csum_header *)data)->csum_valid_required;
-}
-
-static struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
-				      struct rmnet_port *port);
-static struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb,
-						  int hdrlen, int pad,
-						  struct rmnet_port *port);
-static void rmnet_map_command(struct sk_buff *skb, struct rmnet_port *port);
-static void rmnet_map_checksum_uplink_packet(struct sk_buff *skb,
-				      struct net_device *orig_dev,
-				      int csum_type);
-static int rmnet_map_process_next_hdr_packet(struct sk_buff *skb,
-				      struct sk_buff_head *list,
-				      u16 len);
-static int rmnet_map_tx_agg_skip(struct sk_buff *skb, int offset);
-static void rmnet_map_tx_aggregate(struct sk_buff *skb, struct rmnet_port *port);
-static void rmnet_map_tx_aggregate_init(struct rmnet_port *port);
-static void rmnet_map_tx_aggregate_exit(struct rmnet_port *port);
-static void rmnet_map_cmd_init(struct rmnet_port *port);
-static void rmnet_map_cmd_exit(struct rmnet_port *port);
-#endif /* _RMNET_MAP_H_ */
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map_command.c b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map_command.c
deleted file mode 100644
index 6c3318490..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map_command.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/netdevice.h>
-#include "rmnet_config.h"
-#include "rmnet_map.h"
-#include "rmnet_private.h"
-#include "rmnet_vnd.h"
-
-#define RMNET_DL_IND_HDR_SIZE (sizeof(struct rmnet_map_dl_ind_hdr) + \
-			       sizeof(struct rmnet_map_header) + \
-			       sizeof(struct rmnet_map_control_command_header))
-
-#define RMNET_MAP_CMD_SIZE (sizeof(struct rmnet_map_header) + \
-			    sizeof(struct rmnet_map_control_command_header))
-
-#define RMNET_DL_IND_TRL_SIZE (sizeof(struct rmnet_map_dl_ind_trl) + \
-			       sizeof(struct rmnet_map_header) + \
-			       sizeof(struct rmnet_map_control_command_header))
-
-static u8 rmnet_map_do_flow_control(struct sk_buff *skb,
-				    struct rmnet_port *port,
-				    int enable)
-{
-	struct rmnet_map_header *qmap;
-	struct rmnet_map_control_command *cmd;
-	struct rmnet_endpoint *ep;
-	struct net_device *vnd;
-	u16 ip_family;
-	u16 fc_seq;
-	u32 qos_id;
-	u8 mux_id;
-	int r;
-
-	qmap = (struct rmnet_map_header *)rmnet_map_data_ptr(skb);
-	mux_id = qmap->mux_id;
-	cmd = rmnet_map_get_cmd_start(skb);
-
-	if (mux_id >= RMNET_MAX_LOGICAL_EP) {
-		kfree_skb(skb);
-		return RX_HANDLER_CONSUMED;
-	}
-
-	ep = rmnet_get_endpoint(port, mux_id);
-	if (!ep) {
-		kfree_skb(skb);
-		return RX_HANDLER_CONSUMED;
-	}
-
-	vnd = ep->egress_dev;
-
-	ip_family = cmd->flow_control.ip_family;
-	fc_seq = ntohs(cmd->flow_control.flow_control_seq_num);
-	qos_id = ntohl(cmd->flow_control.qos_id);
-
-	/* Ignore the ip family and pass the sequence number for both v4 and v6
-	 * sequence. User space does not support creating dedicated flows for
-	 * the 2 protocols
-	 */
-	r = rmnet_vnd_do_flow_control(vnd, enable);
-	if (r) {
-		kfree_skb(skb);
-		return RMNET_MAP_COMMAND_UNSUPPORTED;
-	} else {
-		return RMNET_MAP_COMMAND_ACK;
-	}
-}
-
-static void rmnet_map_send_ack(struct sk_buff *skb,
-			       unsigned char type,
-			       struct rmnet_port *port)
-{
-	struct rmnet_map_control_command *cmd;
-	struct net_device *dev = skb->dev;
-
-	if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4)
-		pskb_trim(skb,
-			  skb->len - sizeof(struct rmnet_map_dl_csum_trailer));
-
-	skb->protocol = htons(ETH_P_MAP);
-
-	cmd = rmnet_map_get_cmd_start(skb);
-	cmd->cmd_type = type & 0x03;
-
-	netif_tx_lock(dev);
-	dev->netdev_ops->ndo_start_xmit(skb, dev);
-	netif_tx_unlock(dev);
-}
-
-/* Process MAP command frame and send N/ACK message as appropriate. Message cmd
- * name is decoded here and appropriate handler is called.
- */
-static void rmnet_map_command(struct sk_buff *skb, struct rmnet_port *port)
-{
-	struct rmnet_map_control_command *cmd;
-	unsigned char command_name;
-	unsigned char rc = 0;
-
-	cmd = rmnet_map_get_cmd_start(skb);
-	command_name = cmd->command_name;
-
-	switch (command_name) {
-	case RMNET_MAP_COMMAND_FLOW_ENABLE:
-		rc = rmnet_map_do_flow_control(skb, port, 1);
-		break;
-
-	case RMNET_MAP_COMMAND_FLOW_DISABLE:
-		rc = rmnet_map_do_flow_control(skb, port, 0);
-		break;
-
-	default:
-		rc = RMNET_MAP_COMMAND_UNSUPPORTED;
-		kfree_skb(skb);
-		break;
-	}
-	if (rc == RMNET_MAP_COMMAND_ACK)
-		rmnet_map_send_ack(skb, rc, port);
-}
-
-
-static void rmnet_map_cmd_exit(struct rmnet_port *port)
-{
-	struct rmnet_map_dl_ind *tmp, *idx;
-
-	list_for_each_entry_safe(tmp, idx, &port->dl_list, list)
-		list_del_rcu(&tmp->list);
-}
-
-static void rmnet_map_cmd_init(struct rmnet_port *port)
-{
-	INIT_LIST_HEAD(&port->dl_list);
-
-	port->dl_marker_flush = -1;
-}
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map_data.c b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map_data.c
deleted file mode 100644
index 783412c69..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_map_data.c
+++ /dev/null
@@ -1,682 +0,0 @@
-/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * RMNET Data MAP protocol
- *
- */
-
-#include <linux/netdevice.h>
-#include <linux/ip.h>
-#include <linux/ipv6.h>
-#include <net/ip6_checksum.h>
-#include "rmnet_config.h"
-#include "rmnet_map.h"
-#include "rmnet_private.h"
-#include "rmnet_handlers.h"
-
-#define RMNET_MAP_PKT_COPY_THRESHOLD 64
-#define RMNET_MAP_DEAGGR_SPACING  64
-#define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2)
-
-struct rmnet_map_coal_metadata {
-	void *ip_header;
-	void *trans_header;
-	u16 ip_len;
-	u16 trans_len;
-	u16 data_offset;
-	u16 data_len;
-	u8 ip_proto;
-	u8 trans_proto;
-	u8 pkt_id;
-	u8 pkt_count;
-};
-
-static __sum16 *rmnet_map_get_csum_field(unsigned char protocol,
-					 const void *txporthdr)
-{
-	__sum16 *check = NULL;
-
-	switch (protocol) {
-	case IPPROTO_TCP:
-		check = &(((struct tcphdr *)txporthdr)->check);
-		break;
-
-	case IPPROTO_UDP:
-		check = &(((struct udphdr *)txporthdr)->check);
-		break;
-
-	default:
-		check = NULL;
-		break;
-	}
-
-	return check;
-}
-
-static void rmnet_map_complement_ipv4_txporthdr_csum_field(void *iphdr)
-{
-	struct iphdr *ip4h = (struct iphdr *)iphdr;
-	void *txphdr;
-	u16 *csum;
-
-	txphdr = iphdr + ip4h->ihl * 4;
-
-	if (ip4h->protocol == IPPROTO_TCP || ip4h->protocol == IPPROTO_UDP) {
-		csum = (u16 *)rmnet_map_get_csum_field(ip4h->protocol, txphdr);
-		*csum = ~(*csum);
-	}
-}
-
-static void
-rmnet_map_ipv4_ul_csum_header(void *iphdr,
-			      struct rmnet_map_ul_csum_header *ul_header,
-			      struct sk_buff *skb)
-{
-	struct iphdr *ip4h = (struct iphdr *)iphdr;
-	__be16 *hdr = (__be16 *)ul_header, offset;
-
-	offset = htons((__force u16)(skb_transport_header(skb) -
-				     (unsigned char *)iphdr));
-	ul_header->csum_start_offset = offset;
-	ul_header->csum_insert_offset = skb->csum_offset;
-	ul_header->csum_enabled = 1;
-	if (ip4h->protocol == IPPROTO_UDP)
-		ul_header->udp_ind = 1;
-	else
-		ul_header->udp_ind = 0;
-
-	/* Changing remaining fields to network order */
-	hdr++;
-	*hdr = htons((__force u16)*hdr);
-
-	skb->ip_summed = CHECKSUM_NONE;
-
-	rmnet_map_complement_ipv4_txporthdr_csum_field(iphdr);
-}
-
-#if IS_ENABLED(CONFIG_IPV6)
-static void rmnet_map_complement_ipv6_txporthdr_csum_field(void *ip6hdr)
-{
-	struct ipv6hdr *ip6h = (struct ipv6hdr *)ip6hdr;
-	void *txphdr;
-	u16 *csum;
-
-	txphdr = ip6hdr + sizeof(struct ipv6hdr);
-
-	if (ip6h->nexthdr == IPPROTO_TCP || ip6h->nexthdr == IPPROTO_UDP) {
-		csum = (u16 *)rmnet_map_get_csum_field(ip6h->nexthdr, txphdr);
-		*csum = ~(*csum);
-	}
-}
-
-static void
-rmnet_map_ipv6_ul_csum_header(void *ip6hdr,
-			      struct rmnet_map_ul_csum_header *ul_header,
-			      struct sk_buff *skb)
-{
-	struct ipv6hdr *ip6h = (struct ipv6hdr *)ip6hdr;
-	__be16 *hdr = (__be16 *)ul_header, offset;
-
-	offset = htons((__force u16)(skb_transport_header(skb) -
-				     (unsigned char *)ip6hdr));
-	ul_header->csum_start_offset = offset;
-	ul_header->csum_insert_offset = skb->csum_offset;
-	ul_header->csum_enabled = 1;
-
-	if (ip6h->nexthdr == IPPROTO_UDP)
-		ul_header->udp_ind = 1;
-	else
-		ul_header->udp_ind = 0;
-
-	/* Changing remaining fields to network order */
-	hdr++;
-	*hdr = htons((__force u16)*hdr);
-
-	skb->ip_summed = CHECKSUM_NONE;
-
-	rmnet_map_complement_ipv6_txporthdr_csum_field(ip6hdr);
-}
-#endif
-
-/* Adds MAP header to front of skb->data
- * Padding is calculated and set appropriately in MAP header. Mux ID is
- * initialized to 0.
- */
-static struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb,
-						  int hdrlen, int pad,
-						  struct rmnet_port *port)
-{
-	struct rmnet_map_header *map_header;
-	u32 padding, map_datalen;
-	u8 *padbytes;
-
-	map_datalen = skb->len - hdrlen;
-	map_header = (struct rmnet_map_header *)
-			skb_push(skb, sizeof(struct rmnet_map_header));
-	memset(map_header, 0, sizeof(struct rmnet_map_header));
-
-	/* Set next_hdr bit for csum offload packets */
-	if (port->data_format & RMNET_FLAGS_EGRESS_MAP_CKSUMV5)
-		map_header->next_hdr = 1;
-
-	if (pad == RMNET_MAP_NO_PAD_BYTES) {
-		map_header->pkt_len = htons(map_datalen);
-		return map_header;
-	}
-
-	padding = ALIGN(map_datalen, 4) - map_datalen;
-
-	if (padding == 0)
-		goto done;
-
-	if (skb_tailroom(skb) < padding)
-		return NULL;
-
-	padbytes = (u8 *)skb_put(skb, padding);
-	memset(padbytes, 0, padding);
-
-done:
-	map_header->pkt_len = htons(map_datalen + padding);
-	map_header->pad_len = padding & 0x3F;
-
-	return map_header;
-}
-
-/* Deaggregates a single packet
- * A whole new buffer is allocated for each portion of an aggregated frame.
- * Caller should keep calling deaggregate() on the source skb until 0 is
- * returned, indicating that there are no more packets to deaggregate. Caller
- * is responsible for freeing the original skb.
- */
-static struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
-				      struct rmnet_port *port)
-{
-	struct rmnet_map_header *maph;
-	struct sk_buff *skbn;
-	unsigned char *data = rmnet_map_data_ptr(skb), *next_hdr = NULL;
-	u32 packet_len;
-
-	if (skb->len == 0)
-		return NULL;
-
-	maph = (struct rmnet_map_header *)data;
-	packet_len = ntohs(maph->pkt_len) + sizeof(struct rmnet_map_header);
-
-	if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4)
-		packet_len += sizeof(struct rmnet_map_dl_csum_trailer);
-	else if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) {
-		if (!maph->cd_bit) {
-			packet_len += sizeof(struct rmnet_map_v5_csum_header);
-
-			/* Coalescing headers require MAPv5 */
-			next_hdr = data + sizeof(*maph);
-		}
-	}
-
-	if (((int)skb->len - (int)packet_len) < 0)
-		return NULL;
-
-	/* Some hardware can send us empty frames. Catch them */
-	if (ntohs(maph->pkt_len) == 0)
-		return NULL;
-
-	if (next_hdr &&
-	    ((struct rmnet_map_v5_coal_header *)next_hdr)->header_type ==
-	     RMNET_MAP_HEADER_TYPE_COALESCING)
-		return skb;
-
-	if (skb_is_nonlinear(skb)) {
-		skb_frag_t *frag0 = skb_shinfo(skb)->frags;
-		struct page *page = skb_frag_page(frag0);
-
-		skbn = alloc_skb(RMNET_MAP_DEAGGR_HEADROOM, GFP_ATOMIC);
-		if (!skbn)
-			return NULL;
-
-		skb_append_pagefrags(skbn, page, frag0->page_offset,
-				     packet_len);
-		skbn->data_len += packet_len;
-		skbn->len += packet_len;
-	} else {
-		skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING,
-				 GFP_ATOMIC);
-		if (!skbn)
-			return NULL;
-
-		skb_reserve(skbn, RMNET_MAP_DEAGGR_HEADROOM);
-		skb_put(skbn, packet_len);
-		memcpy(skbn->data, data, packet_len);
-	}
-
-	pskb_pull(skb, packet_len);
-
-	return skbn;
-}
-
-static void rmnet_map_v4_checksum_uplink_packet(struct sk_buff *skb,
-					 struct net_device *orig_dev)
-{
-	struct rmnet_priv *priv = netdev_priv(orig_dev);
-	struct rmnet_map_ul_csum_header *ul_header;
-	void *iphdr;
-
-	ul_header = (struct rmnet_map_ul_csum_header *)
-		    skb_push(skb, sizeof(struct rmnet_map_ul_csum_header));
-
-	if (unlikely(!(orig_dev->features &
-		     (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))))
-		goto sw_csum;
-
-	if (skb->ip_summed == CHECKSUM_PARTIAL) {
-		iphdr = (char *)ul_header +
-			sizeof(struct rmnet_map_ul_csum_header);
-
-		if (skb->protocol == htons(ETH_P_IP)) {
-			rmnet_map_ipv4_ul_csum_header(iphdr, ul_header, skb);
-			priv->stats.csum_hw++;
-			return;
-		} else if (skb->protocol == htons(ETH_P_IPV6)) {
-#if IS_ENABLED(CONFIG_IPV6)
-			rmnet_map_ipv6_ul_csum_header(iphdr, ul_header, skb);
-			priv->stats.csum_hw++;
-			return;
-#else
-			priv->stats.csum_err_invalid_ip_version++;
-			goto sw_csum;
-#endif
-		} else {
-			priv->stats.csum_err_invalid_ip_version++;
-		}
-	}
-
-sw_csum:
-	ul_header->csum_start_offset = 0;
-	ul_header->csum_insert_offset = 0;
-	ul_header->csum_enabled = 0;
-	ul_header->udp_ind = 0;
-
-	priv->stats.csum_sw++;
-}
-
-static void rmnet_map_v5_checksum_uplink_packet(struct sk_buff *skb,
-					 struct net_device *orig_dev)
-{
-	struct rmnet_priv *priv = netdev_priv(orig_dev);
-	struct rmnet_map_v5_csum_header *ul_header;
-
-	ul_header = (struct rmnet_map_v5_csum_header *)
-		    skb_push(skb, sizeof(*ul_header));
-	memset(ul_header, 0, sizeof(*ul_header));
-	ul_header->header_type = RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD;
-
-	if (skb->ip_summed == CHECKSUM_PARTIAL) {
-		void *iph = (char *)ul_header + sizeof(*ul_header);
-		void *trans;
-		__sum16 *check;
-		u8 proto;
-
-		if (skb->protocol == htons(ETH_P_IP)) {
-			u16 ip_len = ((struct iphdr *)iph)->ihl * 4;
-
-			proto = ((struct iphdr *)iph)->protocol;
-			trans = iph + ip_len;
-		} else if (skb->protocol == htons(ETH_P_IPV6)) {
-			u16 ip_len = sizeof(struct ipv6hdr);
-
-			proto = ((struct ipv6hdr *)iph)->nexthdr;
-			trans = iph + ip_len;
-		} else {
-			priv->stats.csum_err_invalid_ip_version++;
-			goto sw_csum;
-		}
-
-		check = rmnet_map_get_csum_field(proto, trans);
-		if (check) {
-			*check = 0;
-			skb->ip_summed = CHECKSUM_NONE;
-			/* Ask for checksum offloading */
-			ul_header->csum_valid_required = 1;
-			priv->stats.csum_hw++;
-			return;
-		}
-	}
-
-sw_csum:
-	priv->stats.csum_sw++;
-}
-
-
-/* Generates UL checksum meta info header for IPv4 and IPv6 over TCP and UDP
- * packets that are supported for UL checksum offload.
- */
-void rmnet_map_checksum_uplink_packet(struct sk_buff *skb,
-				      struct net_device *orig_dev,
-				      int csum_type)
-{
-	switch (csum_type) {
-	case RMNET_FLAGS_EGRESS_MAP_CKSUMV4:
-		rmnet_map_v4_checksum_uplink_packet(skb, orig_dev);
-		break;
-	case RMNET_FLAGS_EGRESS_MAP_CKSUMV5:
-		rmnet_map_v5_checksum_uplink_packet(skb, orig_dev);
-		break;
-	default:
-		break;
-	}
-}
-
-static void rmnet_map_move_headers(struct sk_buff *skb)
-{
-	struct iphdr *iph;
-	u16 ip_len;
-	u16 trans_len = 0;
-	u8 proto;
-
-	/* This only applies to non-linear SKBs */
-	if (!skb_is_nonlinear(skb))
-		return;
-
-	iph = (struct iphdr *)rmnet_map_data_ptr(skb);
-	if (iph->version == 4) {
-		ip_len = iph->ihl * 4;
-		proto = iph->protocol;
-		if (iph->frag_off & htons(IP_OFFSET))
-			/* No transport header information */
-			goto pull;
-	} else if (iph->version == 6) {
-		struct ipv6hdr *ip6h = (struct ipv6hdr *)iph;
-		__be16 frag_off;
-		u8 nexthdr = ip6h->nexthdr;
-
-		ip_len = ipv6_skip_exthdr(skb, sizeof(*ip6h), &nexthdr,
-					  &frag_off);
-		if (ip_len < 0)
-			return;
-
-		proto = nexthdr;
-	} else {
-		return;
-	}
-
-	if (proto == IPPROTO_TCP) {
-		struct tcphdr *tp = (struct tcphdr *)((u8 *)iph + ip_len);
-
-		trans_len = tp->doff * 4;
-	} else if (proto == IPPROTO_UDP) {
-		trans_len = sizeof(struct udphdr);
-	} else if (proto == NEXTHDR_FRAGMENT) {
-		/* Non-first fragments don't have the fragment length added by
-		 * ipv6_skip_exthdr() and sho up as proto NEXTHDR_FRAGMENT, so
-		 * we account for the length here.
-		 */
-		ip_len += sizeof(struct frag_hdr);
-	}
-
-pull:
-	__pskb_pull_tail(skb, ip_len + trans_len);
-	skb_reset_network_header(skb);
-	if (trans_len)
-		skb_set_transport_header(skb, ip_len);
-}
-
-
-/* Process a QMAPv5 packet header */
-static int rmnet_map_process_next_hdr_packet(struct sk_buff *skb,
-				      struct sk_buff_head *list,
-				      u16 len)
-{
-	struct rmnet_priv *priv = netdev_priv(skb->dev);
-	int rc = 0;
-
-	switch (rmnet_map_get_next_hdr_type(skb)) {
-	case RMNET_MAP_HEADER_TYPE_COALESCING:
-		priv->stats.coal.coal_rx++;
-		break;
-	case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD:
-		if (rmnet_map_get_csum_valid(skb)) {
-			priv->stats.csum_ok++;
-			skb->ip_summed = CHECKSUM_UNNECESSARY;
-		} else {
-			priv->stats.csum_valid_unset++;
-		}
-
-		/* Pull unnecessary headers and move the rest to the linear
-		 * section of the skb.
-		 */
-		pskb_pull(skb,
-			  (sizeof(struct rmnet_map_header) +
-			   sizeof(struct rmnet_map_v5_csum_header)));
-		rmnet_map_move_headers(skb);
-
-		/* Remove padding only for csum offload packets.
-		 * Coalesced packets should never have padding.
-		 */
-		pskb_trim(skb, len);
-		__skb_queue_tail(list, skb);
-		break;
-	default:
-		rc = -EINVAL;
-		break;
-	}
-
-	return rc;
-}
-
-long rmnet_agg_time_limit __read_mostly = 1000000L;
-long rmnet_agg_bypass_time __read_mostly = 10000000L;
-
-static int rmnet_map_tx_agg_skip(struct sk_buff *skb, int offset)
-{
-	u8 *packet_start = skb->data + offset;
-	int is_icmp = 0;
-
-	if (skb->protocol == htons(ETH_P_IP)) {
-		struct iphdr *ip4h = (struct iphdr *)(packet_start);
-
-		if (ip4h->protocol == IPPROTO_ICMP)
-			is_icmp = 1;
-	} else if (skb->protocol == htons(ETH_P_IPV6)) {
-		struct ipv6hdr *ip6h = (struct ipv6hdr *)(packet_start);
-
-		if (ip6h->nexthdr == IPPROTO_ICMPV6) {
-			is_icmp = 1;
-		} else if (ip6h->nexthdr == NEXTHDR_FRAGMENT) {
-			struct frag_hdr *frag;
-
-			frag = (struct frag_hdr *)(packet_start
-						   + sizeof(struct ipv6hdr));
-			if (frag->nexthdr == IPPROTO_ICMPV6)
-				is_icmp = 1;
-		}
-	}
-
-	return is_icmp;
-}
-
-static void rmnet_map_flush_tx_packet_work(struct work_struct *work)
-{
-	struct sk_buff *skb = NULL;
-	struct rmnet_port *port;
-	unsigned long flags;
-
-	port = container_of(work, struct rmnet_port, agg_wq);
-
-	spin_lock_irqsave(&port->agg_lock, flags);
-	if (likely(port->agg_state == -EINPROGRESS)) {
-		/* Buffer may have already been shipped out */
-		if (likely(port->agg_skb)) {
-			skb = port->agg_skb;
-			port->agg_skb = NULL;
-			port->agg_count = 0;
-			memset(&port->agg_time, 0, sizeof(struct timespec));
-		}
-		port->agg_state = 0;
-	}
-
-	spin_unlock_irqrestore(&port->agg_lock, flags);
-	if (skb)
-		dev_queue_xmit(skb);
-}
-
-static enum hrtimer_restart rmnet_map_flush_tx_packet_queue(struct hrtimer *t)
-{
-	struct rmnet_port *port;
-
-	port = container_of(t, struct rmnet_port, hrtimer);
-
-	schedule_work(&port->agg_wq);
-	return HRTIMER_NORESTART;
-}
-
-static void rmnet_map_linearize_copy(struct sk_buff *dst, struct sk_buff *src)
-{
-	unsigned int linear = src->len - src->data_len, target = src->len;
-	unsigned char *src_buf;
-	struct sk_buff *skb;
-
-	src_buf = src->data;
-	skb_put_data(dst, src_buf, linear);
-	target -= linear;
-
-	skb = src;
-
-	while (target) {
-		unsigned int i = 0, non_linear = 0;
-
-		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
-			non_linear = skb_frag_size(&skb_shinfo(skb)->frags[i]);
-			src_buf = skb_frag_address(&skb_shinfo(skb)->frags[i]);
-
-			skb_put_data(dst, src_buf, non_linear);
-			target -= non_linear;
-		}
-
-		if (skb_shinfo(skb)->frag_list) {
-			skb = skb_shinfo(skb)->frag_list;
-			continue;
-		}
-
-		if (skb->next)
-			skb = skb->next;
-	}
-}
-
-static void rmnet_map_tx_aggregate(struct sk_buff *skb, struct rmnet_port *port)
-{
-	struct timespec diff, last;
-	int size, agg_count = 0;
-	struct sk_buff *agg_skb;
-	unsigned long flags;
-
-new_packet:
-	spin_lock_irqsave(&port->agg_lock, flags);
-	memcpy(&last, &port->agg_last, sizeof(struct timespec));
-	getnstimeofday(&port->agg_last);
-
-	if (!port->agg_skb) {
-		/* Check to see if we should agg first. If the traffic is very
-		 * sparse, don't aggregate. We will need to tune this later
-		 */
-		diff = timespec_sub(port->agg_last, last);
-		size = port->egress_agg_params.agg_size - skb->len;
-
-		if (diff.tv_sec > 0 || diff.tv_nsec > rmnet_agg_bypass_time ||
-		    size <= 0) {
-			spin_unlock_irqrestore(&port->agg_lock, flags);
-			skb->protocol = htons(ETH_P_MAP);
-			dev_queue_xmit(skb);
-			return;
-		}
-
-		port->agg_skb = alloc_skb(port->egress_agg_params.agg_size,
-					  GFP_ATOMIC);
-		if (!port->agg_skb) {
-			port->agg_skb = 0;
-			port->agg_count = 0;
-			memset(&port->agg_time, 0, sizeof(struct timespec));
-			spin_unlock_irqrestore(&port->agg_lock, flags);
-			skb->protocol = htons(ETH_P_MAP);
-			dev_queue_xmit(skb);
-			return;
-		}
-		rmnet_map_linearize_copy(port->agg_skb, skb);
-		port->agg_skb->dev = skb->dev;
-		port->agg_skb->protocol = htons(ETH_P_MAP);
-		port->agg_count = 1;
-		getnstimeofday(&port->agg_time);
-		dev_kfree_skb_any(skb);
-		goto schedule;
-	}
-	diff = timespec_sub(port->agg_last, port->agg_time);
-	size = port->egress_agg_params.agg_size - port->agg_skb->len;
-
-	if (skb->len > size ||
-	    port->agg_count >= port->egress_agg_params.agg_count ||
-	    diff.tv_sec > 0 || diff.tv_nsec > rmnet_agg_time_limit) {
-		agg_skb = port->agg_skb;
-		agg_count = port->agg_count;
-		port->agg_skb = 0;
-		port->agg_count = 0;
-		memset(&port->agg_time, 0, sizeof(struct timespec));
-		port->agg_state = 0;
-		spin_unlock_irqrestore(&port->agg_lock, flags);
-		hrtimer_cancel(&port->hrtimer);
-		dev_queue_xmit(agg_skb);
-		goto new_packet;
-	}
-
-	rmnet_map_linearize_copy(port->agg_skb, skb);
-	port->agg_count++;
-	dev_kfree_skb_any(skb);
-
-schedule:
-	if (port->agg_state != -EINPROGRESS) {
-		port->agg_state = -EINPROGRESS;
-		hrtimer_start(&port->hrtimer,
-			      ns_to_ktime(port->egress_agg_params.agg_time),
-			      HRTIMER_MODE_REL);
-	}
-	spin_unlock_irqrestore(&port->agg_lock, flags);
-}
-
-static void rmnet_map_tx_aggregate_init(struct rmnet_port *port)
-{
-	hrtimer_init(&port->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-	port->hrtimer.function = rmnet_map_flush_tx_packet_queue;
-	port->egress_agg_params.agg_size = 8192;
-	port->egress_agg_params.agg_count = 20;
-	port->egress_agg_params.agg_time = 3000000;
-	spin_lock_init(&port->agg_lock);
-
-	INIT_WORK(&port->agg_wq, rmnet_map_flush_tx_packet_work);
-}
-
-static void rmnet_map_tx_aggregate_exit(struct rmnet_port *port)
-{
-	unsigned long flags;
-
-	hrtimer_cancel(&port->hrtimer);
-	cancel_work_sync(&port->agg_wq);
-
-	spin_lock_irqsave(&port->agg_lock, flags);
-	if (port->agg_state == -EINPROGRESS) {
-		if (port->agg_skb) {
-			kfree_skb(port->agg_skb);
-			port->agg_skb = NULL;
-			port->agg_count = 0;
-			memset(&port->agg_time, 0, sizeof(struct timespec));
-		}
-
-		port->agg_state = 0;
-	}
-
-	spin_unlock_irqrestore(&port->agg_lock, flags);
-}
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_private.h b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_private.h
deleted file mode 100644
index d384b7b9a..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_private.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Copyright (c) 2013-2014, 2016-2019 The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef _RMNET_PRIVATE_H_
-#define _RMNET_PRIVATE_H_
-
-#define RMNET_MAX_PACKET_SIZE      16384
-#define RMNET_DFLT_PACKET_SIZE     1500
-#define RMNET_NEEDED_HEADROOM      16
-#define RMNET_TX_QUEUE_LEN         1000
-
-/* Constants */
-#define RMNET_EGRESS_FORMAT_AGGREGATION         BIT(31)
-#define RMNET_INGRESS_FORMAT_DL_MARKER_V1       BIT(30)
-#define RMNET_INGRESS_FORMAT_DL_MARKER_V2       BIT(29)
-
-#define RMNET_INGRESS_FORMAT_DL_MARKER  (RMNET_INGRESS_FORMAT_DL_MARKER_V1 |\
-RMNET_INGRESS_FORMAT_DL_MARKER_V2)
-
-/* Replace skb->dev to a virtual rmnet device and pass up the stack */
-#define RMNET_EPMODE_VND (1)
-/* Pass the frame directly to another device with dev_queue_xmit() */
-#define RMNET_EPMODE_BRIDGE (2)
-
-#endif /* _RMNET_PRIVATE_H_ */
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_trace.h b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_trace.h
deleted file mode 100644
index d453fc5d0..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_trace.h
+++ /dev/null
@@ -1,257 +0,0 @@
-/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM rmnet
-#define TRACE_INCLUDE_FILE rmnet_trace
-
-#if !defined(_RMNET_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
-#define _RMNET_TRACE_H_
-
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/tracepoint.h>
-
-/*****************************************************************************/
-/* Trace events for rmnet module */
-/*****************************************************************************/
-DECLARE_EVENT_CLASS
-	(rmnet_mod_template,
-
-	 TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
-		  u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
-
-	 TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2),
-
-	 TP_STRUCT__entry(
-		__field(u8, func)
-		__field(u8, evt)
-		__field(u32, uint1)
-		__field(u32, uint2)
-		__field(u64, ulong1)
-		__field(u64, ulong2)
-		__field(void *, ptr1)
-		__field(void *, ptr2)
-	 ),
-
-	 TP_fast_assign(
-		__entry->func = func;
-		__entry->evt = evt;
-		__entry->uint1 = uint1;
-		__entry->uint2 = uint2;
-		__entry->ulong1 = ulong1;
-		__entry->ulong2 = ulong2;
-		__entry->ptr1 = ptr1;
-		__entry->ptr2 = ptr2;
-	 ),
-
-TP_printk("fun:%u ev:%u u1:%u u2:%u ul1:%llu ul2:%llu p1:0x%pK p2:0x%pK",
-	  __entry->func, __entry->evt,
-	  __entry->uint1, __entry->uint2,
-	  __entry->ulong1, __entry->ulong2,
-	  __entry->ptr1, __entry->ptr2)
-)
-
-DEFINE_EVENT
-	(rmnet_mod_template, rmnet_low,
-
-	 TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
-		  u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
-
-	 TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
-
-);
-
-DEFINE_EVENT
-	(rmnet_mod_template, rmnet_high,
-
-	 TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
-		  u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
-
-	 TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
-
-);
-
-DEFINE_EVENT
-	(rmnet_mod_template, rmnet_err,
-
-	 TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
-		  u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
-
-	 TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
-
-);
-
-/*****************************************************************************/
-/* Trace events for rmnet_perf module */
-/*****************************************************************************/
-DEFINE_EVENT
-	(rmnet_mod_template, rmnet_perf_low,
-
-	 TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
-		  u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
-
-	 TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
-
-);
-
-DEFINE_EVENT
-	(rmnet_mod_template, rmnet_perf_high,
-
-	 TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
-		  u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
-
-	 TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
-
-);
-
-DEFINE_EVENT
-	(rmnet_mod_template, rmnet_perf_err,
-
-	 TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
-		  u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
-
-	 TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
-
-);
-
-/*****************************************************************************/
-/* Trace events for rmnet_shs module */
-/*****************************************************************************/
-DEFINE_EVENT
-	(rmnet_mod_template, rmnet_shs_low,
-
-	 TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
-		  u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
-
-	 TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
-
-);
-
-DEFINE_EVENT
-	(rmnet_mod_template, rmnet_shs_high,
-
-	 TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
-		  u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
-
-	 TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
-
-);
-
-DEFINE_EVENT
-	(rmnet_mod_template, rmnet_shs_err,
-
-	 TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
-		  u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
-
-	 TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
-
-);
-
-DEFINE_EVENT
-	(rmnet_mod_template, rmnet_shs_wq_low,
-
-	 TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
-		  u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
-
-	 TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
-
-);
-
-DEFINE_EVENT
-	(rmnet_mod_template, rmnet_shs_wq_high,
-
-	 TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
-		  u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
-
-	 TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
-
-);
-
-DEFINE_EVENT
-	(rmnet_mod_template, rmnet_shs_wq_err,
-
-	 TP_PROTO(u8 func, u8 evt, u32 uint1, u32 uint2,
-		  u64 ulong1, u64 ulong2, void *ptr1, void *ptr2),
-
-	 TP_ARGS(func, evt, uint1, uint2, ulong1, ulong2, ptr1, ptr2)
-
-);
-
-DECLARE_EVENT_CLASS
-	(rmnet_freq_template,
-
-	 TP_PROTO(u8 core, u32 newfreq),
-
-	 TP_ARGS(core, newfreq),
-
-	 TP_STRUCT__entry(
-		__field(u8, core)
-		__field(u32, newfreq)
-	 ),
-
-	 TP_fast_assign(
-		__entry->core = core;
-		__entry->newfreq = newfreq;
-
-	 ),
-
-TP_printk("freq policy core:%u freq floor :%u",
-	  __entry->core, __entry->newfreq)
-
-);
-
-DEFINE_EVENT
-	(rmnet_freq_template, rmnet_freq_boost,
-
-	 TP_PROTO(u8 core, u32 newfreq),
-
-	 TP_ARGS(core, newfreq)
-);
-
-DEFINE_EVENT
-	(rmnet_freq_template, rmnet_freq_reset,
-
-	 TP_PROTO(u8 core, u32 newfreq),
-
-	 TP_ARGS(core, newfreq)
-);
-
-TRACE_EVENT
-	(rmnet_freq_update,
-
-	 TP_PROTO(u8 core, u32 lowfreq, u32 highfreq),
-
-	 TP_ARGS(core, lowfreq, highfreq),
-
-	 TP_STRUCT__entry(
-		__field(u8, core)
-		__field(u32, lowfreq)
-		__field(u32, highfreq)
-	 ),
-
-	 TP_fast_assign(
-		__entry->core = core;
-		__entry->lowfreq = lowfreq;
-		__entry->highfreq = highfreq;
-
-	 ),
-
-TP_printk("freq policy update core:%u policy freq floor :%u freq ceil :%u",
-	  __entry->core, __entry->lowfreq, __entry->highfreq)
-);
-#endif /* _RMNET_TRACE_H_ */
-
-/* This part must be outside protection */
-#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH ../drivers/net/ethernet/qualcomm/rmnet
-#include <trace/define_trace.h>
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_vnd.c b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_vnd.c
deleted file mode 100644
index 4ef645daf..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_vnd.c
+++ /dev/null
@@ -1,382 +0,0 @@
-/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- *
- * RMNET Data virtual network driver
- *
- */
-
-#include <linux/etherdevice.h>
-#include <linux/if_arp.h>
-#include <linux/ip.h>
-#include <net/pkt_sched.h>
-#include "rmnet_config.h"
-#include "rmnet_handlers.h"
-#include "rmnet_private.h"
-#include "rmnet_map.h"
-#include "rmnet_vnd.h"
-
-/* RX/TX Fixup */
-
-static void rmnet_vnd_rx_fixup(struct net_device *dev, u32 skb_len)
-{
-	struct rmnet_priv *priv = netdev_priv(dev);
-	struct rmnet_pcpu_stats *pcpu_ptr;
-
-	pcpu_ptr = this_cpu_ptr(priv->pcpu_stats);
-
-	u64_stats_update_begin(&pcpu_ptr->syncp);
-	pcpu_ptr->stats.rx_pkts++;
-	pcpu_ptr->stats.rx_bytes += skb_len;
-	u64_stats_update_end(&pcpu_ptr->syncp);
-}
-
-static void rmnet_vnd_tx_fixup(struct net_device *dev, u32 skb_len)
-{
-	struct rmnet_priv *priv = netdev_priv(dev);
-	struct rmnet_pcpu_stats *pcpu_ptr;
-
-	pcpu_ptr = this_cpu_ptr(priv->pcpu_stats);
-
-	u64_stats_update_begin(&pcpu_ptr->syncp);
-	pcpu_ptr->stats.tx_pkts++;
-	pcpu_ptr->stats.tx_bytes += skb_len;
-	u64_stats_update_end(&pcpu_ptr->syncp);
-}
-
-/* Network Device Operations */
-
-static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb,
-					struct net_device *dev)
-{
-	struct rmnet_priv *priv;
-
-	priv = netdev_priv(dev);
-	if (priv->real_dev) {
-		rmnet_egress_handler(skb);
-	} else {
-		this_cpu_inc(priv->pcpu_stats->stats.tx_drops);
-		kfree_skb(skb);
-	}
-	return NETDEV_TX_OK;
-}
-
-static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu)
-{
-	if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE)
-		return -EINVAL;
-
-	rmnet_dev->mtu = new_mtu;
-	return 0;
-}
-
-static int rmnet_vnd_get_iflink(const struct net_device *dev)
-{
-	struct rmnet_priv *priv = netdev_priv(dev);
-
-	return priv->real_dev->ifindex;
-}
-
-static int rmnet_vnd_init(struct net_device *dev)
-{
-	struct rmnet_priv *priv = netdev_priv(dev);
-	int err;
-
-	priv->pcpu_stats = alloc_percpu(struct rmnet_pcpu_stats);
-	if (!priv->pcpu_stats)
-		return -ENOMEM;
-
-	err = gro_cells_init(&priv->gro_cells, dev);
-	if (err) {
-		free_percpu(priv->pcpu_stats);
-		return err;
-	}
-
-	return 0;
-}
-
-static void rmnet_vnd_uninit(struct net_device *dev)
-{
-	struct rmnet_priv *priv = netdev_priv(dev);
-
-	gro_cells_destroy(&priv->gro_cells);
-	free_percpu(priv->pcpu_stats);
-}
-
-static struct rtnl_link_stats64* rmnet_get_stats64(struct net_device *dev,
-			      struct rtnl_link_stats64 *s)
-{
-	struct rmnet_priv *priv = netdev_priv(dev);
-	struct rmnet_vnd_stats total_stats;
-	struct rmnet_pcpu_stats *pcpu_ptr;
-	unsigned int cpu, start;
-
-	memset(&total_stats, 0, sizeof(struct rmnet_vnd_stats));
-
-	for_each_possible_cpu(cpu) {
-		pcpu_ptr = per_cpu_ptr(priv->pcpu_stats, cpu);
-
-		do {
-			start = u64_stats_fetch_begin_irq(&pcpu_ptr->syncp);
-			total_stats.rx_pkts += pcpu_ptr->stats.rx_pkts;
-			total_stats.rx_bytes += pcpu_ptr->stats.rx_bytes;
-			total_stats.tx_pkts += pcpu_ptr->stats.tx_pkts;
-			total_stats.tx_bytes += pcpu_ptr->stats.tx_bytes;
-		} while (u64_stats_fetch_retry_irq(&pcpu_ptr->syncp, start));
-
-		total_stats.tx_drops += pcpu_ptr->stats.tx_drops;
-	}
-
-	s->rx_packets = total_stats.rx_pkts;
-	s->rx_bytes = total_stats.rx_bytes;
-	s->tx_packets = total_stats.tx_pkts;
-	s->tx_bytes = total_stats.tx_bytes;
-	s->tx_dropped = total_stats.tx_drops;
-
-	return s;
-}
-
-static const struct net_device_ops rmnet_vnd_ops = {
-	.ndo_start_xmit = rmnet_vnd_start_xmit,
-	.ndo_change_mtu = rmnet_vnd_change_mtu,
-	.ndo_get_iflink = rmnet_vnd_get_iflink,
-	//.ndo_add_slave  = rmnet_add_bridge,
-	//.ndo_del_slave  = rmnet_del_bridge,
-	.ndo_init       = rmnet_vnd_init,
-	.ndo_uninit     = rmnet_vnd_uninit,
-	.ndo_get_stats64 = rmnet_get_stats64,
-};
-
-static const char rmnet_gstrings_stats[][ETH_GSTRING_LEN] = {
-	"Checksum ok",
-	"Checksum valid bit not set",
-	"Checksum validation failed",
-	"Checksum error bad buffer",
-	"Checksum error bad ip version",
-	"Checksum error bad transport",
-	"Checksum skipped on ip fragment",
-	"Checksum skipped",
-	"Checksum computed in software",
-	"Checksum computed in hardware",
-	"Coalescing packets received",
-	"Coalesced packets",
-	"Coalescing header NLO errors",
-	"Coalescing header pcount errors",
-	"Coalescing checksum errors",
-	"Coalescing packet reconstructs",
-	"Coalescing IP version invalid",
-	"Coalescing L4 header invalid",
-	"Coalescing close Non-coalescable",
-	"Coalescing close L3 mismatch",
-	"Coalescing close L4 mismatch",
-	"Coalescing close HW NLO limit",
-	"Coalescing close HW packet limit",
-	"Coalescing close HW byte limit",
-	"Coalescing close HW time limit",
-	"Coalescing close HW eviction",
-	"Coalescing close Coalescable",
-	"Coalescing packets over VEID0",
-	"Coalescing packets over VEID1",
-	"Coalescing packets over VEID2",
-	"Coalescing packets over VEID3",
-};
-
-static const char rmnet_port_gstrings_stats[][ETH_GSTRING_LEN] = {
-	"MAP Cmd last version",
-	"MAP Cmd last ep id",
-	"MAP Cmd last transaction id",
-	"DL header last seen sequence",
-	"DL header last seen bytes",
-	"DL header last seen packets",
-	"DL header last seen flows",
-	"DL header pkts received",
-	"DL header total bytes received",
-	"DL header total pkts received",
-	"DL trailer last seen sequence",
-	"DL trailer pkts received",
-};
-
-static void rmnet_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
-{
-	switch (stringset) {
-	case ETH_SS_STATS:
-		memcpy(buf, &rmnet_gstrings_stats,
-		       sizeof(rmnet_gstrings_stats));
-		memcpy(buf + sizeof(rmnet_gstrings_stats),
-		       &rmnet_port_gstrings_stats,
-		       sizeof(rmnet_port_gstrings_stats));
-		break;
-	}
-}
-
-static int rmnet_get_sset_count(struct net_device *dev, int sset)
-{
-	switch (sset) {
-	case ETH_SS_STATS:
-		return ARRAY_SIZE(rmnet_gstrings_stats) +
-		       ARRAY_SIZE(rmnet_port_gstrings_stats);
-	default:
-		return -EOPNOTSUPP;
-	}
-}
-
-static void rmnet_get_ethtool_stats(struct net_device *dev,
-				    struct ethtool_stats *stats, u64 *data)
-{
-	struct rmnet_priv *priv = netdev_priv(dev);
-	struct rmnet_priv_stats *st = &priv->stats;
-	struct rmnet_port_priv_stats *stp;
-	struct rmnet_port *port;
-
-	port = rmnet_get_port(priv->real_dev);
-
-	if (!data || !port)
-		return;
-
-	stp = &port->stats;
-
-	memcpy(data, st, ARRAY_SIZE(rmnet_gstrings_stats) * sizeof(u64));
-	memcpy(data + ARRAY_SIZE(rmnet_gstrings_stats), stp,
-	       ARRAY_SIZE(rmnet_port_gstrings_stats) * sizeof(u64));
-}
-
-static int rmnet_stats_reset(struct net_device *dev)
-{
-	struct rmnet_priv *priv = netdev_priv(dev);
-	struct rmnet_port_priv_stats *stp;
-	struct rmnet_port *port;
-
-	port = rmnet_get_port(priv->real_dev);
-	if (!port)
-		return -EINVAL;
-
-	stp = &port->stats;
-
-	memset(stp, 0, sizeof(*stp));
-	return 0;
-}
-
-static const struct ethtool_ops rmnet_ethtool_ops = {
-	.get_ethtool_stats = rmnet_get_ethtool_stats,
-	.get_strings = rmnet_get_strings,
-	.get_sset_count = rmnet_get_sset_count,
-	.nway_reset = rmnet_stats_reset,
-};
-
-/* Called by kernel whenever a new rmnet<n> device is created. Sets MTU,
- * flags, ARP type, needed headroom, etc...
- */
-void rmnet_vnd_setup(struct net_device *rmnet_dev)
-{
-	rmnet_dev->netdev_ops = &rmnet_vnd_ops;
-	rmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE;
-	rmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM;
-	random_ether_addr(rmnet_dev->dev_addr);
-	rmnet_dev->tx_queue_len = RMNET_TX_QUEUE_LEN;
-
-	/* Raw IP mode */
-	rmnet_dev->header_ops = NULL;  /* No header */
-	rmnet_dev->type = ARPHRD_RAWIP;
-	rmnet_dev->hard_header_len = 0;
-	rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
-
-	//rmnet_dev->needs_free_netdev = true;
-
-	rmnet_dev->hw_features = NETIF_F_RXCSUM;
-	rmnet_dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
-	//rmnet_dev->hw_features |= NETIF_F_SG;
-	//rmnet_dev->hw_features |= NETIF_F_GRO_HW;	
-}
-
-/* Exposed API */
-
-static int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,
-		      struct rmnet_port *port,
-		      struct net_device *real_dev,
-		      struct rmnet_endpoint *ep)
-{
-	struct rmnet_priv *priv = netdev_priv(rmnet_dev);
-	struct rmnet_nss_cb *nss_cb;
-	int rc;
-
-	if (ep->egress_dev)
-		return -EINVAL;
-
-	if (rmnet_get_endpoint(port, id))
-		return -EBUSY;
-
-	rmnet_dev->hw_features = NETIF_F_RXCSUM;
-	rmnet_dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
-	rmnet_dev->hw_features |= NETIF_F_SG;
-
-	priv->real_dev = real_dev;
-
-	rc = register_netdevice(rmnet_dev);
-	if (!rc) {
-		ep->egress_dev = rmnet_dev;
-		ep->mux_id = id;
-		port->nr_rmnet_devs++;
-
-		//rmnet_dev->rtnl_link_ops = &rmnet_link_ops;
-
-		priv->mux_id = id;
-
-		netdev_dbg(rmnet_dev, "rmnet dev created\n");
-	}
-
-	nss_cb = rcu_dereference(rmnet_nss_callbacks);
-	if (nss_cb) {
-		rc = nss_cb->nss_create(rmnet_dev);
-		if (rc) {
-			/* Log, but don't fail the device creation */
-			netdev_err(rmnet_dev, "Device will not use NSS path: %d\n", rc);
-			rc = 0;
-		} else {
-			netdev_dbg(rmnet_dev, "NSS context created\n");
-		}
-	}
-
-	return rc;
-}
-
-static int rmnet_vnd_dellink(u8 id, struct rmnet_port *port,
-		      struct rmnet_endpoint *ep)
-{
-	struct rmnet_nss_cb *nss_cb;
-
-	if (id >= RMNET_MAX_LOGICAL_EP || !ep->egress_dev)
-		return -EINVAL;
-
-	if (ep->egress_dev) {
-		nss_cb = rcu_dereference(rmnet_nss_callbacks);
-		if (nss_cb)
-			nss_cb->nss_free(ep->egress_dev);
-	}
-	ep->egress_dev = NULL;
-	port->nr_rmnet_devs--;
-
-	return 0;
-}
-
-static int rmnet_vnd_do_flow_control(struct net_device *rmnet_dev, int enable)
-{
-	netdev_dbg(rmnet_dev, "Setting VND TX queue state to %d\n", enable);
-	/* Although we expect similar number of enable/disable
-	 * commands, optimize for the disable. That is more
-	 * latency sensitive than enable
-	 */
-	if (unlikely(enable))
-		netif_wake_queue(rmnet_dev);
-	else
-		netif_stop_queue(rmnet_dev);
-
-	return 0;
-}
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_vnd.h b/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_vnd.h
deleted file mode 100644
index f8a65a953..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet/rmnet_vnd.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * RMNET Data Virtual Network Device APIs
- *
- */
-
-#ifndef _RMNET_VND_H_
-#define _RMNET_VND_H_
-
-static int rmnet_vnd_do_flow_control(struct net_device *dev, int enable);
-static int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,
-		      struct rmnet_port *port,
-		      struct net_device *real_dev,
-		      struct rmnet_endpoint *ep);
-static int rmnet_vnd_dellink(u8 id, struct rmnet_port *port,
-		      struct rmnet_endpoint *ep);
-static void rmnet_vnd_rx_fixup(struct net_device *dev, u32 skb_len);
-static void rmnet_vnd_tx_fixup(struct net_device *dev, u32 skb_len);
-static void rmnet_vnd_setup(struct net_device *dev);
-#endif /* _RMNET_VND_H_ */
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet_handler.c b/package/wwan/quectel_MHI/src/devices/rmnet_handler.c
deleted file mode 100644
index b10026292..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet_handler.c
+++ /dev/null
@@ -1,1129 +0,0 @@
-#if 0
-
-#define RMNET_MAX_PACKET_SIZE      16384
-#define RMNET_DFLT_PACKET_SIZE     1500
-#define RMNET_NEEDED_HEADROOM      16
-#define RMNET_TX_QUEUE_LEN         1000
-
-#define RMNET_MAX_LOGICAL_EP 255
-#define RMNET_MAP_DESC_HEADROOM   128
-#define RMNET_FRAG_DESCRIPTOR_POOL_SIZE 64
-
-/* Pass the frame up the stack with no modifications to skb->dev */
-#define RMNET_EPMODE_NONE (0)
-/* Replace skb->dev to a virtual rmnet device and pass up the stack */
-#define RMNET_EPMODE_VND (1)
-/* Pass the frame directly to another device with dev_queue_xmit() */
-#define RMNET_EPMODE_BRIDGE (2)
-
-/* rmnet section */
-
-#define RMNET_FLAGS_INGRESS_DEAGGREGATION         (1U << 0)
-#define RMNET_FLAGS_INGRESS_MAP_COMMANDS          (1U << 1)
-#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4           (1U << 2)
-#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4            (1U << 3)
-#define RMNET_FLAGS_INGRESS_COALESCE              (1U << 4)
-#define RMNET_FLAGS_INGRESS_MAP_CKSUMV5           (1U << 5)
-#define RMNET_FLAGS_EGRESS_MAP_CKSUMV5            (1U << 6)
-
-enum rmnet_map_v5_header_type {
-	RMNET_MAP_HEADER_TYPE_UNKNOWN,
-	RMNET_MAP_HEADER_TYPE_COALESCING = 0x1,
-	RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD = 0x2,
-	RMNET_MAP_HEADER_TYPE_ENUM_LENGTH
-};
-
-/* Main QMAP header */
-struct rmnet_map_header {
-	u8  pad_len:6;
-	u8  next_hdr:1;
-	u8  cd_bit:1;
-	u8  mux_id;
-	__be16 pkt_len;
-}  __aligned(1);
-
-/* QMAP v5 headers */
-struct rmnet_map_v5_csum_header {
-	u8  next_hdr:1;
-	u8  header_type:7;
-	u8  hw_reserved:7;
-	u8  csum_valid_required:1;
-	__be16 reserved;
-} __aligned(1);
-
-struct rmnet_map_v5_nl_pair {
-	__be16 pkt_len;
-	u8  csum_error_bitmap;
-	u8  num_packets;
-} __aligned(1);
-
-/* NLO: Number-length object */
-#define RMNET_MAP_V5_MAX_NLOS         (6)
-#define RMNET_MAP_V5_MAX_PACKETS      (48)
-
-struct rmnet_map_v5_coal_header {
-	u8  next_hdr:1;
-	u8  header_type:7;
-	u8  reserved1:4;
-	u8  num_nlos:3;
-	u8  csum_valid:1;
-	u8  close_type:4;
-	u8  close_value:4;
-	u8  reserved2:4;
-	u8  virtual_channel_id:4;
-
-	struct rmnet_map_v5_nl_pair nl_pairs[RMNET_MAP_V5_MAX_NLOS];
-} __aligned(1);
-
-/* QMAP v4 headers */
-struct rmnet_map_dl_csum_trailer {
-	u8  reserved1;
-	u8  valid:1;
-	u8  reserved2:7;
-	u16 csum_start_offset;
-	u16 csum_length;
-	__be16 csum_value;
-} __aligned(1);
-
-struct rmnet_frag_descriptor_pool {
-	struct list_head free_list;
-	u32 pool_size;
-};
-
-struct rmnet_frag_descriptor {
-	struct list_head list;
-	struct list_head sub_frags;
-	skb_frag_t frag;
-	u8 *hdr_ptr;
-	struct net_device *dev;
-	u32 hash;
-	__be32 tcp_seq;
-	__be16 ip_id;
-	u16 data_offset;
-	u16 gso_size;
-	u16 gso_segs;
-	u16 ip_len;
-	u16 trans_len;
-	u8 ip_proto;
-	u8 trans_proto;
-	u8 pkt_id;
-	u8 csum_valid:1,
-	   hdrs_valid:1,
-	   ip_id_set:1,
-	   tcp_seq_set:1,
-	   flush_shs:1,
-	   reserved:3;
-};
-
-struct rmnet_endpoint {
-	u8 rmnet_mode;
-	u8 mux_id;
-	struct net_device *rmnet_dev;
-};
-
-/* One instance of this structure is instantiated for each real_dev associated
- * with rmnet.
- */
-struct rmnet_port {
-	struct net_device *dev;
-	u8 rmnet_mode;
-	u32 data_format;
-	u32 nr_rmnet_devs;
-	struct rmnet_endpoint muxed_ep[16];
-
-	/* Descriptor pool */
-	spinlock_t desc_pool_lock;
-	struct rmnet_frag_descriptor_pool *frag_desc_pool;
-	struct sk_buff *chain_head;
-	struct sk_buff *chain_tail;
-};
-
-static struct sk_buff * add_qhdr_v5(struct sk_buff *skb, u8 mux_id)
-{
-	struct rmnet_map_header *map_header;
-	struct rmnet_map_v5_csum_header *ul_header;
-	u32 padding, map_datalen;
-
-	map_datalen = skb->len;
-	padding = map_datalen%4;
-	if (padding) {
-		padding = 4 - padding;
-		if (skb_tailroom(skb) < padding) {
-			printk("skb_tailroom small!\n");
-			padding = 0;
-		}
-		if (padding)
-			__skb_put(skb, padding);
-	}
-					
-	map_header = (struct rmnet_map_header *)skb_push(skb, (sizeof(struct rmnet_map_header) + sizeof(struct rmnet_map_v5_csum_header)));
-
-	BUILD_BUG_ON((sizeof(struct rmnet_map_header) + sizeof(struct rmnet_map_v5_csum_header)) != 8);
-
-	map_header->cd_bit = 0;
-	map_header->next_hdr = 1;
-	map_header->pad_len = padding;
-	map_header->mux_id = mux_id;
-	map_header->pkt_len = htons(map_datalen + padding);
-
-	ul_header = (struct rmnet_map_v5_csum_header *)(map_header + 1);
-	memset(ul_header, 0, sizeof(*ul_header));
-	ul_header->header_type = RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD;
-
-	return skb;
-}
-
-struct rmnet_endpoint *rmnet_get_endpoint(struct rmnet_port *port, u8 mux_id)
-{
-	return &port->muxed_ep[0];
-}
-
-static void
-rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_port *port)
-{
-	struct rmnet_nss_cb *nss_cb;
-
-	//rmnet_vnd_rx_fixup(skb->dev, skb->len);
-
-	/* Pass off the packet to NSS driver if we can */
-	nss_cb = rcu_dereference(rmnet_nss_callbacks);
-	if (nss_cb) {
-		if (!port->chain_head)
-			port->chain_head = skb;
-		else
-			skb_shinfo(port->chain_tail)->frag_list = skb;
-
-		port->chain_tail = skb;
-		return;
-	}
-
-	skb_reset_transport_header(skb);
-	skb_reset_network_header(skb);
-
-	skb->pkt_type = PACKET_HOST;
-	skb_set_mac_header(skb, 0);
-
-	//if (port->data_format & RMNET_INGRESS_FORMAT_DL_MARKER) {
-	//} else {
-		//if (!rmnet_check_skb_can_gro(skb))
-		//	gro_cells_receive(&priv->gro_cells, skb);
-		//else
-			netif_receive_skb(skb);
-	//}
-}
-
-static inline unsigned char *rmnet_map_data_ptr(struct sk_buff *skb)
-{
-	/* Nonlinear packets we receive are entirely within frag 0 */
-	if (skb_is_nonlinear(skb) && skb->len == skb->data_len)
-		return skb_frag_address(skb_shinfo(skb)->frags);
-
-	return skb->data;
-}
-
-static inline void *rmnet_frag_data_ptr(struct rmnet_frag_descriptor *frag_desc)
-{
-	return skb_frag_address(&frag_desc->frag);
-}
-
-static struct rmnet_frag_descriptor *
-rmnet_get_frag_descriptor(struct rmnet_port *port)
-{
-	struct rmnet_frag_descriptor_pool *pool = port->frag_desc_pool;
-	struct rmnet_frag_descriptor *frag_desc;
-
-	spin_lock(&port->desc_pool_lock);
-	if (!list_empty(&pool->free_list)) {
-		frag_desc = list_first_entry(&pool->free_list,
-					     struct rmnet_frag_descriptor,
-					     list);
-		list_del_init(&frag_desc->list);
-	} else {
-		frag_desc = kzalloc(sizeof(*frag_desc), GFP_ATOMIC);
-		if (!frag_desc)
-			goto out;
-
-		INIT_LIST_HEAD(&frag_desc->list);
-		INIT_LIST_HEAD(&frag_desc->sub_frags);
-		pool->pool_size++;
-	}
-
-out:
-	spin_unlock(&port->desc_pool_lock);
-	return frag_desc;
-}
-
-static void rmnet_recycle_frag_descriptor(struct rmnet_frag_descriptor *frag_desc,
-				   struct rmnet_port *port)
-{
-	struct rmnet_frag_descriptor_pool *pool = port->frag_desc_pool;
-	struct page *page = skb_frag_page(&frag_desc->frag);
-
-	list_del(&frag_desc->list);
-	if (page)
-		put_page(page);
-
-	memset(frag_desc, 0, sizeof(*frag_desc));
-	INIT_LIST_HEAD(&frag_desc->list);
-	INIT_LIST_HEAD(&frag_desc->sub_frags);
-	spin_lock(&port->desc_pool_lock);
-	list_add_tail(&frag_desc->list, &pool->free_list);
-	spin_unlock(&port->desc_pool_lock);
-}
-
-static inline void rmnet_frag_fill(struct rmnet_frag_descriptor *frag_desc,
-				   struct page *p, u32 page_offset, u32 len)
-{
-	get_page(p);
-	__skb_frag_set_page(&frag_desc->frag, p);
-	skb_frag_size_set(&frag_desc->frag, len);
-	frag_desc->frag.page_offset = page_offset;
-}
-
-static inline void *rmnet_frag_pull(struct rmnet_frag_descriptor *frag_desc,
-				    struct rmnet_port *port,
-				    unsigned int size)
-{
-	if (size >= skb_frag_size(&frag_desc->frag)) {
-		pr_info("%s(): Pulling %u bytes from %u byte pkt. Dropping\n",
-			__func__, size, skb_frag_size(&frag_desc->frag));
-		rmnet_recycle_frag_descriptor(frag_desc, port);
-		return NULL;
-	}
-
-	frag_desc->frag.page_offset += size;
-	skb_frag_size_sub(&frag_desc->frag, size);
-
-	return rmnet_frag_data_ptr(frag_desc);
-}
-
-static inline void *rmnet_frag_trim(struct rmnet_frag_descriptor *frag_desc,
-				    struct rmnet_port *port,
-				    unsigned int size)
-{
-	if (!size) {
-		pr_info("%s(): Trimming %u byte pkt to 0. Dropping\n",
-			__func__, skb_frag_size(&frag_desc->frag));
-		rmnet_recycle_frag_descriptor(frag_desc, port);
-		return NULL;
-	}
-
-	if (size < skb_frag_size(&frag_desc->frag))
-		skb_frag_size_set(&frag_desc->frag, size);
-
-	return rmnet_frag_data_ptr(frag_desc);
-}
-
-static inline u8
-rmnet_frag_get_next_hdr_type(struct rmnet_frag_descriptor *frag_desc)
-{
-	unsigned char *data = rmnet_frag_data_ptr(frag_desc);
-
-	data += sizeof(struct rmnet_map_header);
-	return ((struct rmnet_map_v5_coal_header *)data)->header_type;
-}
-
-static inline bool
-rmnet_frag_get_csum_valid(struct rmnet_frag_descriptor *frag_desc)
-{
-	unsigned char *data = rmnet_frag_data_ptr(frag_desc);
-
-	data += sizeof(struct rmnet_map_header);
-	return ((struct rmnet_map_v5_csum_header *)data)->csum_valid_required;
-}
-
-static void rmnet_descriptor_add_frag(struct rmnet_port *port, struct list_head *list,
-			       struct page *p, u32 page_offset, u32 len)
-{
-	struct rmnet_frag_descriptor *frag_desc;
-
-	frag_desc = rmnet_get_frag_descriptor(port);
-	if (!frag_desc)
-		return;
-
-	rmnet_frag_fill(frag_desc, p, page_offset, len);
-	list_add_tail(&frag_desc->list, list);
-}
-
-static void rmnet_frag_deaggregate(skb_frag_t *frag, struct rmnet_port *port,
-			    struct list_head *list)
-{
-	struct rmnet_map_header *maph;
-	u8 *data = skb_frag_address(frag);
-	u32 offset = 0;
-	u32 packet_len;
-
-	while (offset < skb_frag_size(frag)) {
-		maph = (struct rmnet_map_header *)data;
-		packet_len = ntohs(maph->pkt_len);
-
-		/* Some hardware can send us empty frames. Catch them */
-		if (packet_len == 0)
-			return;
-
-		packet_len += sizeof(*maph);
-
-		if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) {
-			packet_len += sizeof(struct rmnet_map_dl_csum_trailer);
-			WARN_ON(1);
-		} else if (port->data_format &
-			   (RMNET_FLAGS_INGRESS_MAP_CKSUMV5 |
-			    RMNET_FLAGS_INGRESS_COALESCE) && !maph->cd_bit) {
-			u32 hsize = 0;
-			u8 type;
-
-			type = ((struct rmnet_map_v5_coal_header *)
-				(data + sizeof(*maph)))->header_type;
-			switch (type) {
-			case RMNET_MAP_HEADER_TYPE_COALESCING:
-				hsize = sizeof(struct rmnet_map_v5_coal_header);
-				break;
-			case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD:
-				hsize = sizeof(struct rmnet_map_v5_csum_header);
-				break;
-			}
-
-			packet_len += hsize;
-		}
-		else {
-			qmap_hex_dump(__func__, data, 64);
-			WARN_ON(1);
-		}
-
-		if ((int)skb_frag_size(frag) - (int)packet_len < 0)
-			return;
-
-		rmnet_descriptor_add_frag(port, list, skb_frag_page(frag),
-					  frag->page_offset + offset,
-					  packet_len);
-
-		offset += packet_len;
-		data += packet_len;
-	}
-}
-
-
-#define RMNET_IP_VERSION_4 0x40
-#define RMNET_IP_VERSION_6 0x60
-
-/* Helper Functions */
-
-static void rmnet_set_skb_proto(struct sk_buff *skb)
-{
-	switch (rmnet_map_data_ptr(skb)[0] & 0xF0) {
-	case RMNET_IP_VERSION_4:
-		skb->protocol = htons(ETH_P_IP);
-		break;
-	case RMNET_IP_VERSION_6:
-		skb->protocol = htons(ETH_P_IPV6);
-		break;
-	default:
-		skb->protocol = htons(ETH_P_MAP);
-		WARN_ON(1);
-		break;
-	}
-}
-
-/* Allocate and populate an skb to contain the packet represented by the
- * frag descriptor.
- */
-static struct sk_buff *rmnet_alloc_skb(struct rmnet_frag_descriptor *frag_desc,
-				       struct rmnet_port *port)
-{
-	struct sk_buff *head_skb, *current_skb, *skb;
-	struct skb_shared_info *shinfo;
-	struct rmnet_frag_descriptor *sub_frag, *tmp;
-
-	/* Use the exact sizes if we know them (i.e. RSB/RSC, rmnet_perf) */
-	if (frag_desc->hdrs_valid) {
-		u16 hdr_len = frag_desc->ip_len + frag_desc->trans_len;
-
-		head_skb = alloc_skb(hdr_len + RMNET_MAP_DESC_HEADROOM,
-				     GFP_ATOMIC);
-		if (!head_skb)
-			return NULL;
-
-		skb_reserve(head_skb, RMNET_MAP_DESC_HEADROOM);
-		skb_put_data(head_skb, frag_desc->hdr_ptr, hdr_len);
-		skb_reset_network_header(head_skb);
-
-		if (frag_desc->trans_len)
-			skb_set_transport_header(head_skb, frag_desc->ip_len);
-
-		/* Packets that have no data portion don't need any frags */
-		if (hdr_len == skb_frag_size(&frag_desc->frag))
-			goto skip_frags;
-
-		/* If the headers we added are the start of the page,
-		 * we don't want to add them twice
-		 */
-		if (frag_desc->hdr_ptr == rmnet_frag_data_ptr(frag_desc)) {
-			if (!rmnet_frag_pull(frag_desc, port, hdr_len)) {
-				kfree_skb(head_skb);
-				return NULL;
-			}
-		}
-	} else {
-		/* Allocate enough space to avoid penalties in the stack
-		 * from __pskb_pull_tail()
-		 */
-		head_skb = alloc_skb(256 + RMNET_MAP_DESC_HEADROOM,
-				     GFP_ATOMIC);
-		if (!head_skb)
-			return NULL;
-
-		skb_reserve(head_skb, RMNET_MAP_DESC_HEADROOM);
-	}
-
-	/* Add main fragment */
-	get_page(skb_frag_page(&frag_desc->frag));
-	skb_add_rx_frag(head_skb, 0, skb_frag_page(&frag_desc->frag),
-			frag_desc->frag.page_offset,
-			skb_frag_size(&frag_desc->frag),
-			skb_frag_size(&frag_desc->frag));
-
-	shinfo = skb_shinfo(head_skb);
-	current_skb = head_skb;
-
-	/* Add in any frags from rmnet_perf */
-	list_for_each_entry_safe(sub_frag, tmp, &frag_desc->sub_frags, list) {
-		skb_frag_t *frag;
-		u32 frag_size;
-
-		frag = &sub_frag->frag;
-		frag_size = skb_frag_size(frag);
-
-add_frag:
-		if (shinfo->nr_frags < MAX_SKB_FRAGS) {
-			get_page(skb_frag_page(frag));
-			skb_add_rx_frag(current_skb, shinfo->nr_frags,
-					skb_frag_page(frag), frag->page_offset,
-					frag_size, frag_size);
-			if (current_skb != head_skb) {
-				head_skb->len += frag_size;
-				head_skb->data_len += frag_size;
-			}
-		} else {
-			/* Alloc a new skb and try again */
-			skb = alloc_skb(0, GFP_ATOMIC);
-			if (!skb)
-				break;
-
-			if (current_skb == head_skb)
-				shinfo->frag_list = skb;
-			else
-				current_skb->next = skb;
-
-			current_skb = skb;
-			shinfo = skb_shinfo(current_skb);
-			goto add_frag;
-		}
-
-		rmnet_recycle_frag_descriptor(sub_frag, port);
-	}
-
-skip_frags:
-	head_skb->dev = frag_desc->dev;
-	rmnet_set_skb_proto(head_skb);
-
-	/* Handle any header metadata that needs to be updated after RSB/RSC
-	 * segmentation
-	 */
-	if (frag_desc->ip_id_set) {
-		struct iphdr *iph;
-
-		iph = (struct iphdr *)rmnet_map_data_ptr(head_skb);
-		csum_replace2(&iph->check, iph->id, frag_desc->ip_id);
-		iph->id = frag_desc->ip_id;
-	}
-
-	if (frag_desc->tcp_seq_set) {
-		struct tcphdr *th;
-
-		th = (struct tcphdr *)
-		     (rmnet_map_data_ptr(head_skb) + frag_desc->ip_len);
-		th->seq = frag_desc->tcp_seq;
-	}
-
-	/* Handle csum offloading */
-	if (frag_desc->csum_valid && frag_desc->hdrs_valid) {
-		/* Set the partial checksum information */
-		//rmnet_frag_partial_csum(head_skb, frag_desc);
-		WARN_ON(1);
-	} else if (frag_desc->csum_valid) {
-		/* Non-RSB/RSC/perf packet. The current checksum is fine */
-		head_skb->ip_summed = CHECKSUM_UNNECESSARY;
-	} else if (frag_desc->hdrs_valid &&
-		   (frag_desc->trans_proto == IPPROTO_TCP ||
-		    frag_desc->trans_proto == IPPROTO_UDP)) {
-		/* Unfortunately, we have to fake a bad checksum here, since
-		 * the original bad value is lost by the hardware. The only
-		 * reliable way to do it is to calculate the actual checksum
-		 * and corrupt it.
-		 */
-		__sum16 *check;
-		__wsum csum;
-		unsigned int offset = skb_transport_offset(head_skb);
-		__sum16 pseudo;
-
-		WARN_ON(1);
-		/* Calculate pseudo header and update header fields */
-		if (frag_desc->ip_proto == 4) {
-			struct iphdr *iph = ip_hdr(head_skb);
-			__be16 tot_len = htons(head_skb->len);
-
-			csum_replace2(&iph->check, iph->tot_len, tot_len);
-			iph->tot_len = tot_len;
-			pseudo = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
-						    head_skb->len -
-						    frag_desc->ip_len,
-						    frag_desc->trans_proto, 0);
-		} else {
-			struct ipv6hdr *ip6h = ipv6_hdr(head_skb);
-
-			ip6h->payload_len = htons(head_skb->len -
-						  sizeof(*ip6h));
-			pseudo = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
-						  head_skb->len -
-						  frag_desc->ip_len,
-						  frag_desc->trans_proto, 0);
-		}
-
-		if (frag_desc->trans_proto == IPPROTO_TCP) {
-			check = &tcp_hdr(head_skb)->check;
-		} else {
-			udp_hdr(head_skb)->len = htons(head_skb->len -
-						       frag_desc->ip_len);
-			check = &udp_hdr(head_skb)->check;
-		}
-
-		*check = pseudo;
-		csum = skb_checksum(head_skb, offset, head_skb->len - offset,
-				    0);
-		/* Add 1 to corrupt. This cannot produce a final value of 0
-		 * since csum_fold() can't return a value of 0xFFFF
-		 */
-		*check = csum16_add(csum_fold(csum), htons(1));
-		head_skb->ip_summed = CHECKSUM_NONE;
-	}
-
-	/* Handle any rmnet_perf metadata */
-	if (frag_desc->hash) {
-		head_skb->hash = frag_desc->hash;
-		head_skb->sw_hash = 1;
-	}
-
-	if (frag_desc->flush_shs)
-		head_skb->cb[0] = 1;
-
-	/* Handle coalesced packets */
-	//if (frag_desc->gso_segs > 1)
-	//	rmnet_frag_gso_stamp(head_skb, frag_desc);
-
-	return head_skb;
-}				       
-
-/* Deliver the packets contained within a frag descriptor */
-static void rmnet_frag_deliver(struct rmnet_frag_descriptor *frag_desc,
-			struct rmnet_port *port)
-{
-	struct sk_buff *skb;
-
-	skb = rmnet_alloc_skb(frag_desc, port);
-	if (skb)
-		rmnet_deliver_skb(skb, port);
-	rmnet_recycle_frag_descriptor(frag_desc, port);
-}
-
-/* Process a QMAPv5 packet header */
-static int rmnet_frag_process_next_hdr_packet(struct rmnet_frag_descriptor *frag_desc,
-				       struct rmnet_port *port,
-				       struct list_head *list,
-				       u16 len)
-{
-	int rc = 0;
-
-	switch (rmnet_frag_get_next_hdr_type(frag_desc)) {
-	case RMNET_MAP_HEADER_TYPE_COALESCING:
-		rc = -1;
-		WARN_ON(1);
-		break;
-	case RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD:
-		if (rmnet_frag_get_csum_valid(frag_desc)) {
-			frag_desc->csum_valid = true;
-		} else {
-		}
-
-		if (!rmnet_frag_pull(frag_desc, port,
-				     sizeof(struct rmnet_map_header) +
-				     sizeof(struct rmnet_map_v5_csum_header))) {
-			rc = -EINVAL;
-			break;
-		}
-
-		frag_desc->hdr_ptr = rmnet_frag_data_ptr(frag_desc);
-
-		/* Remove padding only for csum offload packets.
-		 * Coalesced packets should never have padding.
-		 */
-		if (!rmnet_frag_trim(frag_desc, port, len)) {
-			rc = -EINVAL;
-			break;
-		}
-
-		list_del_init(&frag_desc->list);
-		list_add_tail(&frag_desc->list, list);
-		break;
-	default:
-		qmap_hex_dump(__func__, rmnet_frag_data_ptr(frag_desc), 64);
-		rc = -EINVAL;
-		break;
-	}
-
-	return rc;
-}
-
-static void
-__rmnet_frag_ingress_handler(struct rmnet_frag_descriptor *frag_desc,
-			     struct rmnet_port *port)
-{
-	struct rmnet_map_header *qmap;
-	struct rmnet_endpoint *ep;
-	struct rmnet_frag_descriptor *frag, *tmp;
-	LIST_HEAD(segs);
-	u16 len, pad;
-	u8 mux_id;
-
-	qmap = (struct rmnet_map_header *)skb_frag_address(&frag_desc->frag);
-	mux_id = qmap->mux_id;
-	pad = qmap->pad_len;
-	len = ntohs(qmap->pkt_len) - pad;
-
-	if (qmap->cd_bit) {
-		goto recycle;
-	}
-
-	if (mux_id >= RMNET_MAX_LOGICAL_EP)
-		goto recycle;
-
-	ep = rmnet_get_endpoint(port, mux_id);
-	if (!ep)
-		goto recycle;
-
-	frag_desc->dev = ep->rmnet_dev;
-
-	/* Handle QMAPv5 packet */
-	if (qmap->next_hdr &&
-	    (port->data_format & (RMNET_FLAGS_INGRESS_COALESCE |
-				  RMNET_FLAGS_INGRESS_MAP_CKSUMV5))) {
-		if (rmnet_frag_process_next_hdr_packet(frag_desc, port, &segs,
-						       len))
-			goto recycle;
-	} else {
-		/* We only have the main QMAP header to worry about */
-		if (!rmnet_frag_pull(frag_desc, port, sizeof(*qmap)))
-			return;
-
-		frag_desc->hdr_ptr = rmnet_frag_data_ptr(frag_desc);
-
-		if (!rmnet_frag_trim(frag_desc, port, len))
-			return;
-
-		list_add_tail(&frag_desc->list, &segs);
-	}
-
-	list_for_each_entry_safe(frag, tmp, &segs, list) {
-		list_del_init(&frag->list);
-		rmnet_frag_deliver(frag, port);
-	}
-	return;
-
-recycle:
-	rmnet_recycle_frag_descriptor(frag_desc, port);
-}
-
-static void rmnet_frag_ingress_handler(struct sk_buff *skb,
-				struct rmnet_port *port)
-{
-	LIST_HEAD(desc_list);
-	int i = 0;
-	struct rmnet_nss_cb *nss_cb;
-
-	/* Deaggregation and freeing of HW originating
-	 * buffers is done within here
-	 */
-	while (skb) {
-		struct sk_buff *skb_frag;
-
-		port->chain_head = NULL;
-		port->chain_tail = NULL;
-
-		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
-			rmnet_frag_deaggregate(&skb_shinfo(skb)->frags[i], port,
-					       &desc_list);
-			if (!list_empty(&desc_list)) {
-				struct rmnet_frag_descriptor *frag_desc, *tmp;
-
-				list_for_each_entry_safe(frag_desc, tmp,
-							 &desc_list, list) {
-					list_del_init(&frag_desc->list);
-					__rmnet_frag_ingress_handler(frag_desc,
-								     port);
-				}
-			}
-		}
-
-		nss_cb = rcu_dereference(rmnet_nss_callbacks);
-		if (nss_cb && port->chain_head) {
-			port->chain_head->cb[0] = 0;
-			netif_receive_skb(port->chain_head);
-		}
-
-		skb_frag = skb_shinfo(skb)->frag_list;
-		skb_shinfo(skb)->frag_list = NULL;
-		consume_skb(skb);
-		skb = skb_frag;
-	}
-}
-
-static void
-rmnet_map_ingress_handler(struct sk_buff *skb,
-			  struct rmnet_port *port)
-{
-	if (port->data_format & (RMNET_FLAGS_INGRESS_COALESCE |
-				 RMNET_FLAGS_INGRESS_MAP_CKSUMV5)) {
-		if (skb_is_nonlinear(skb)) {
-			rmnet_frag_ingress_handler(skb, port);
-			return;
-		}
-	}
-
-	WARN_ON(1);
-}
-
-static rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb);
-static int rmnet_is_real_dev_registered(const struct net_device *real_dev)
-{
-	return rcu_access_pointer(real_dev->rx_handler) == rmnet_rx_handler;
-}
-
-
-/* Needs either rcu_read_lock() or rtnl lock */
-struct rmnet_port *rmnet_get_port(struct net_device *real_dev)
-{
-	if (rmnet_is_real_dev_registered(real_dev))
-		return rcu_dereference_rtnl(real_dev->rx_handler_data);
-	else
-		return NULL;
-}
-
-static rx_handler_result_t rmnet_rx_priv_handler(struct sk_buff **pskb)
-{
-	struct sk_buff *skb = *pskb;
-	struct rmnet_nss_cb *nss_cb;
-
-	if (!skb)
-		return RX_HANDLER_CONSUMED;
-	if (nss_debug) printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len);
-
-	if (skb->pkt_type == PACKET_LOOPBACK)
-		return RX_HANDLER_PASS;
-
-	/* Check this so that we dont loop around netif_receive_skb */
-	if (skb->cb[0] == 1) {
-		skb->cb[0] = 0;
-
-		skb->dev->stats.rx_packets++;
-		return RX_HANDLER_PASS;
-	}
-
-	while (skb) {
-		struct sk_buff *skb_frag = skb_shinfo(skb)->frag_list;
-
-		skb_shinfo(skb)->frag_list = NULL;
-
-		nss_cb = rcu_dereference(rmnet_nss_callbacks);
-		if (nss_cb)
-			nss_cb->nss_tx(skb);
-
-		skb = skb_frag;
-	}
-
-	return RX_HANDLER_CONSUMED;
-}
-
-/* Ingress / Egress Entry Points */
-
-/* Processes packet as per ingress data format for receiving device. Logical
- * endpoint is determined from packet inspection. Packet is then sent to the
- * egress device listed in the logical endpoint configuration.
- */
-static rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb)
-{
-	struct sk_buff *skb = *pskb;
-	struct rmnet_port *port;
-	struct net_device *dev;
-
-	if (!skb)
-		goto done;
-
-	if (nss_debug) printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len);
-
-	if (skb->pkt_type == PACKET_LOOPBACK)
-		return RX_HANDLER_PASS;
-
-	if (skb->protocol != htons(ETH_P_MAP)) {
-		WARN_ON(1);
-		return RX_HANDLER_PASS;
-	}
-
-	dev = skb->dev;
-	port = rmnet_get_port(dev);
-
-	if (port == NULL)
-		return RX_HANDLER_PASS;
-
-	port->chain_head = NULL;
-	port->chain_tail = NULL;
-
-	switch (port->rmnet_mode) {
-	case RMNET_EPMODE_VND:
-		rmnet_map_ingress_handler(skb, port);
-		break;
-	case RMNET_EPMODE_BRIDGE:
-		//rmnet_bridge_handler(skb, port->bridge_ep);
-		break;
-	}
-
-done:
-	return RX_HANDLER_CONSUMED;
-}
-
-static void rmnet_descriptor_deinit(struct rmnet_port *port)
-{
-	struct rmnet_frag_descriptor_pool *pool;
-	struct rmnet_frag_descriptor *frag_desc, *tmp;
-
-	pool = port->frag_desc_pool;
-
-	list_for_each_entry_safe(frag_desc, tmp, &pool->free_list, list) {
-		kfree(frag_desc);
-		pool->pool_size--;
-	}
-
-	kfree(pool);
-}
-
-static int rmnet_descriptor_init(struct rmnet_port *port)
-{
-	struct rmnet_frag_descriptor_pool *pool;
-	int i;
-
-	spin_lock_init(&port->desc_pool_lock);
-	pool = kzalloc(sizeof(*pool), GFP_ATOMIC);
-	if (!pool)
-		return -ENOMEM;
-
-	INIT_LIST_HEAD(&pool->free_list);
-	port->frag_desc_pool = pool;
-
-	for (i = 0; i < RMNET_FRAG_DESCRIPTOR_POOL_SIZE; i++) {
-		struct rmnet_frag_descriptor *frag_desc;
-
-		frag_desc = kzalloc(sizeof(*frag_desc), GFP_ATOMIC);
-		if (!frag_desc)
-			return -ENOMEM;
-
-		INIT_LIST_HEAD(&frag_desc->list);
-		INIT_LIST_HEAD(&frag_desc->sub_frags);
-		list_add_tail(&frag_desc->list, &pool->free_list);
-		pool->pool_size++;
-	}
-
-	return 0;
-}
-
-struct rmnet_priv {
-	//struct rmnet_endpoint local_ep;
-	struct net_device *real_dev;
-	u8 mux_id;
-};
-
-static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb,
-					struct net_device *dev)
-{
-	struct rmnet_priv *priv;
-
-	if (nss_debug) printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len);
-
-	priv = netdev_priv(dev);
-	if (priv->real_dev) {
-		add_qhdr_v5(skb, priv->mux_id);
-		skb->protocol = htons(ETH_P_MAP);
-		skb->dev = priv->real_dev;
-		dev_queue_xmit(skb);
-		dev->stats.tx_packets++;
-		//rmnet_egress_handler(skb);
-	} else {
-		//this_cpu_inc(priv->pcpu_stats->stats.tx_drops);
-		kfree_skb(skb);
-	}
-	return NETDEV_TX_OK;
-}
-
-static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu)
-{
-	if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE)
-		return -EINVAL;
-
-	rmnet_dev->mtu = new_mtu;
-	return 0;
-}
-
-static const struct net_device_ops rmnet_vnd_ops = {
-	.ndo_start_xmit = rmnet_vnd_start_xmit,
-	.ndo_change_mtu = rmnet_vnd_change_mtu,
-	//.ndo_get_iflink = rmnet_vnd_get_iflink,
-	//.ndo_add_slave  = rmnet_add_bridge,
-	//.ndo_del_slave  = rmnet_del_bridge,
-	//.ndo_init       = rmnet_vnd_init,
-	//.ndo_uninit     = rmnet_vnd_uninit,
-	//.ndo_get_stats64 = rmnet_get_stats64,
-};
-
-static void rmnet_vnd_setup(struct net_device *rmnet_dev)
-{
-	rmnet_dev->netdev_ops = &rmnet_vnd_ops;
-	rmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE;
-	rmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM;
-	random_ether_addr(rmnet_dev->dev_addr);
-	rmnet_dev->tx_queue_len = RMNET_TX_QUEUE_LEN;
-
-	/* Raw IP mode */
-	rmnet_dev->header_ops = NULL;  /* No header */
-	rmnet_dev->type = ARPHRD_RAWIP;
-	rmnet_dev->hard_header_len = 0;
-	rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
-
-	//rmnet_dev->needs_free_netdev = true;
-
-	rmnet_dev->hw_features = NETIF_F_RXCSUM;
-	rmnet_dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
-	//rmnet_dev->hw_features |= NETIF_F_SG;
-	//rmnet_dev->hw_features |= NETIF_F_GRO_HW;	
-}
-#else
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/etherdevice.h>
-
-static uint nss_debug = 0;
-module_param( nss_debug, uint, S_IRUGO | S_IWUSR);
-
-/* rmnet section */
-
-#define RMNET_FLAGS_INGRESS_DEAGGREGATION         (1U << 0)
-#define RMNET_FLAGS_INGRESS_MAP_COMMANDS          (1U << 1)
-#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4           (1U << 2)
-#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4            (1U << 3)
-#define RMNET_FLAGS_INGRESS_COALESCE              (1U << 4)
-#define RMNET_FLAGS_INGRESS_MAP_CKSUMV5           (1U << 5)
-#define RMNET_FLAGS_EGRESS_MAP_CKSUMV5            (1U << 6)
-
-#ifdef CONFIG_ARCH_IPQ807x
-#define CONFIG_QCA_NSS_DRV
-#endif
-#ifdef CONFIG_QCA_NSS_DRV
-#include "rmnet/rmnet_nss.c"
-#else
-#include "rmnet_nss.h"
-#endif
-
-#include "rmnet/rmnet_vnd.c"
-#include "rmnet/rmnet_map_command.c"
-#include "rmnet/rmnet_map_data.c"
-#include "rmnet/rmnet_descriptor.c"
-#include "rmnet/rmnet_config.c"
-#include "rmnet/rmnet_handlers.c"
-
-struct rmnet_nss_cb *rmnet_nss_callbacks __rcu __read_mostly;
-
-void rmnet_init(struct net_device *real_dev, u32 nr_rmnet_devs)
-{
-	struct rmnet_port *port;
-	struct rmnet_endpoint *ep;
-	struct net_device *rmnet_dev = NULL;
-	u32 nr = 0;
-	struct rmnet_nss_cb *nss_cb = rcu_dereference(rmnet_nss_callbacks);
-
-	if (!nss_cb) {
-#ifdef CONFIG_QCA_NSS_DRV
-		rmnet_nss_init();
-#endif
-	}
-	
-	rmnet_register_real_device(real_dev);
-
-	port = rmnet_get_port_rtnl(real_dev);
-
-	port->data_format = RMNET_FLAGS_INGRESS_DEAGGREGATION
-		| RMNET_FLAGS_INGRESS_MAP_CKSUMV5 | RMNET_FLAGS_EGRESS_MAP_CKSUMV5;
-	port->rmnet_mode = RMNET_EPMODE_VND;
-
-	for (nr = 0;  nr < nr_rmnet_devs; nr++) {
-		u8 mux_id = 0x81+nr;
-
-		rmnet_dev = alloc_netdev(sizeof(struct rmnet_priv),
-					"rmnet_data%d", NET_NAME_PREDICTABLE,
-					rmnet_vnd_setup);
-
-		ep = kzalloc(sizeof(*ep), GFP_ATOMIC);
-
-		rmnet_vnd_newlink(mux_id, rmnet_dev, port, real_dev, ep);
-		netdev_rx_handler_register(rmnet_dev, rmnet_rx_priv_handler, NULL);
-		hlist_add_head_rcu(&ep->hlnode, &port->muxed_ep[mux_id]);
-	}
-
-	port->nr_rmnet_devs = nr_rmnet_devs;
-}
-
-void rmnet_deinit(struct net_device *real_dev, u32 nr_rmnet_devs)
-{
-	struct rmnet_port *port;
-	u32 nr = 0;
-	struct rmnet_nss_cb *nss_cb = rcu_dereference(rmnet_nss_callbacks);
-	
-	port = rmnet_get_port_rtnl(real_dev);
-
-	if (!real_dev || !rmnet_is_real_dev_registered(real_dev))
-		return;
-
-	port = rmnet_get_port_rtnl(real_dev);
-
-	for (nr = 0;  nr < nr_rmnet_devs; nr++) {
-		struct rmnet_endpoint *ep;
-		u8 mux_id = 0x81+nr;
-
-		ep = rmnet_get_endpoint(port, mux_id);
-		if (ep) {
-			hlist_del_init_rcu(&ep->hlnode);
-			rmnet_vnd_dellink(mux_id, port, ep);
-			synchronize_rcu();
-			kfree(ep);
-		}
-	}
-
-	rmnet_unregister_real_device(real_dev, port);
-
-	if (nss_cb) {
-#ifdef CONFIG_QCA_NSS_DRV
-		rmnet_nss_exit();
-#endif
-	}
-}
-#endif
diff --git a/package/wwan/quectel_MHI/src/devices/rmnet_nss.c b/package/wwan/quectel_MHI/src/devices/rmnet_nss.c
deleted file mode 100644
index e6e841468..000000000
--- a/package/wwan/quectel_MHI/src/devices/rmnet_nss.c
+++ /dev/null
@@ -1,424 +0,0 @@
-/* Copyright (c) 2019, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/hashtable.h>
-#include <linux/if_ether.h>
-#include <linux/ip.h>
-#include <qca-nss-drv/nss_api_if.h>
-
-#include <linux/rmnet_nss.h>
-
-#define RMNET_NSS_HASH_BITS 8
-#define hash_add_ptr(table, node, key) \
-	hlist_add_head(node, &table[hash_ptr(key, HASH_BITS(table))])
-
-static DEFINE_HASHTABLE(rmnet_nss_ctx_hashtable, RMNET_NSS_HASH_BITS);
-
-struct rmnet_nss_ctx {
-	struct hlist_node hnode;
-	struct net_device *rmnet_dev;
-	struct nss_rmnet_rx_handle *nss_ctx;
-};
-
-enum __rmnet_nss_stat {
-	RMNET_NSS_RX_ETH,
-	RMNET_NSS_RX_FAIL,
-	RMNET_NSS_RX_NON_ETH,
-	RMNET_NSS_RX_BUSY,
-	RMNET_NSS_TX_NO_CTX,
-	RMNET_NSS_TX_SUCCESS,
-	RMNET_NSS_TX_FAIL,
-	RMNET_NSS_TX_NONLINEAR,
-	RMNET_NSS_TX_BAD_IP,
-	RMNET_NSS_EXCEPTIONS,
-	RMNET_NSS_EX_BAD_HDR,
-	RMNET_NSS_EX_BAD_IP,
-	RMNET_NSS_EX_SUCCESS,
-	RMNET_NSS_TX_BAD_FRAGS,
-	RMNET_NSS_TX_LINEARIZE_FAILS,
-	RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS,
-	RMNET_NSS_TX_BUSY_LOOP,
-	RMNET_NSS_NUM_STATS,
-};
-
-static unsigned long rmnet_nss_stats[RMNET_NSS_NUM_STATS];
-
-#define RMNET_NSS_STAT(name, counter, desc) \
-	module_param_named(name, rmnet_nss_stats[counter], ulong, 0444); \
-	MODULE_PARM_DESC(name, desc)
-
-RMNET_NSS_STAT(rmnet_nss_rx_ethernet, RMNET_NSS_RX_ETH,
-	       "Number of Ethernet headers successfully removed");
-RMNET_NSS_STAT(rmnet_nss_rx_fail, RMNET_NSS_RX_FAIL,
-	       "Number of Ethernet headers that could not be removed");
-RMNET_NSS_STAT(rmnet_nss_rx_non_ethernet, RMNET_NSS_RX_NON_ETH,
-	       "Number of non-Ethernet packets received");
-RMNET_NSS_STAT(rmnet_nss_rx_busy, RMNET_NSS_RX_BUSY,
-	       "Number of packets dropped decause rmnet_data device was busy");
-RMNET_NSS_STAT(rmnet_nss_tx_slow, RMNET_NSS_TX_NO_CTX,
-	       "Number of packets sent over non-NSS-accelerated rmnet device");
-RMNET_NSS_STAT(rmnet_nss_tx_fast, RMNET_NSS_TX_SUCCESS,
-	       "Number of packets sent over NSS-accelerated rmnet device");
-RMNET_NSS_STAT(rmnet_nss_tx_fail, RMNET_NSS_TX_FAIL,
-	       "Number of packets that NSS could not transmit");
-RMNET_NSS_STAT(rmnet_nss_tx_nonlinear, RMNET_NSS_TX_NONLINEAR,
-	       "Number of non linear sent over NSS-accelerated rmnet device");
-RMNET_NSS_STAT(rmnet_nss_tx_invalid_ip, RMNET_NSS_TX_BAD_IP,
-	       "Number of ingress packets with invalid IP headers");
-RMNET_NSS_STAT(rmnet_nss_tx_invalid_frags, RMNET_NSS_TX_BAD_FRAGS,
-	       "Number of ingress packets with invalid frag format");
-RMNET_NSS_STAT(rmnet_nss_tx_linearize_fail, RMNET_NSS_TX_LINEARIZE_FAILS,
-	       "Number of ingress packets where linearize in tx fails");
-RMNET_NSS_STAT(rmnet_nss_tx_exceptions, RMNET_NSS_EXCEPTIONS,
-	       "Number of times our DL exception handler was invoked");
-RMNET_NSS_STAT(rmnet_nss_exception_non_ethernet, RMNET_NSS_EX_BAD_HDR,
-	       "Number of non-Ethernet exception packets");
-RMNET_NSS_STAT(rmnet_nss_exception_invalid_ip, RMNET_NSS_EX_BAD_IP,
-	       "Number of exception packets with invalid IP headers");
-RMNET_NSS_STAT(rmnet_nss_exception_success, RMNET_NSS_EX_SUCCESS,
-	       "Number of exception packets handled successfully");
-RMNET_NSS_STAT(rmnet_nss_tx_non_zero_headlen_frags, RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS,
-	       "Number of packets with non zero headlen");
-RMNET_NSS_STAT(rmnet_nss_tx_busy_loop, RMNET_NSS_TX_BUSY_LOOP,
-	       "Number of times tx packets busy looped");
-
-static void rmnet_nss_inc_stat(enum __rmnet_nss_stat stat)
-{
-	if (stat >= 0 && stat < RMNET_NSS_NUM_STATS)
-		rmnet_nss_stats[stat]++;
-}
-
-static struct rmnet_nss_ctx *rmnet_nss_find_ctx(struct net_device *dev)
-{
-	struct rmnet_nss_ctx *ctx;
-	struct hlist_head *bucket;
-	u32 hash;
-
-	hash = hash_ptr(dev, HASH_BITS(rmnet_nss_ctx_hashtable));
-	bucket = &rmnet_nss_ctx_hashtable[hash];
-	hlist_for_each_entry(ctx, bucket, hnode) {
-		if (ctx->rmnet_dev == dev)
-			return ctx;
-	}
-
-	return NULL;
-}
-
-static void rmnet_nss_free_ctx(struct rmnet_nss_ctx *ctx)
-{
-	if (ctx) {
-		hash_del(&ctx->hnode);
-		nss_rmnet_rx_xmit_callback_unregister(ctx->nss_ctx);
-		nss_rmnet_rx_destroy_sync(ctx->nss_ctx);
-		kfree(ctx);
-	}
-}
-
-/* Pull off an ethernet header, if possible */
-static int rmnet_nss_ethhdr_pull(struct sk_buff *skb)
-{
-	if (!skb->protocol || skb->protocol == htons(ETH_P_802_3)) {
-		void *ret = skb_pull(skb, sizeof(struct ethhdr));
-
-		rmnet_nss_inc_stat((ret) ? RMNET_NSS_RX_ETH :
-					   RMNET_NSS_RX_FAIL);
-		return !ret;
-	}
-
-	rmnet_nss_inc_stat(RMNET_NSS_RX_NON_ETH);
-	return -1;
-}
-
-/* Copy headers to linear section for non linear packets */
-static int rmnet_nss_adjust_header(struct sk_buff *skb)
-{
-	struct iphdr *iph;
-	skb_frag_t *frag;
-	int bytes = 0;
-	u8 transport;
-
-	if (skb_shinfo(skb)->nr_frags != 1) {
-		rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_FRAGS);
-		return -EINVAL;
-	}
-
-	if (skb_headlen(skb)) {
-		rmnet_nss_inc_stat(RMNET_NSS_TX_NON_ZERO_HEADLEN_FRAGS);
-		return 0;
-	}
-
-	frag = &skb_shinfo(skb)->frags[0];
-
-	iph = (struct iphdr *)(skb_frag_address(frag));
-
-	if (iph->version == 4) {
-		bytes = iph->ihl*4;
-		transport = iph->protocol;
-	} else if (iph->version == 6) {
-		struct ipv6hdr *ip6h = (struct ipv6hdr *)iph;
-
-		bytes = sizeof(struct ipv6hdr);
-		/* Dont have to account for extension headers yet */
-		transport = ip6h->nexthdr;
-	} else {
-		rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_IP);
-		return -EINVAL;
-	}
-
-	if (transport == IPPROTO_TCP) {
-		struct tcphdr *th;
-
-		th = (struct tcphdr *)((u8 *)iph + bytes);
-		bytes += th->doff * 4;
-	} else if (transport == IPPROTO_UDP) {
-		bytes += sizeof(struct udphdr);
-	} else {
-		/* cant do anything else here unfortunately so linearize */
-		if (skb_linearize(skb)) {
-			rmnet_nss_inc_stat(RMNET_NSS_TX_LINEARIZE_FAILS);
-			return -EINVAL;
-		} else {
-			return 0;
-		}
-	}
-
-	if (bytes > skb_frag_size(frag)) {
-		rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_FRAGS);
-		return -EINVAL;
-	}
-
-	skb_push(skb, bytes);
-	memcpy(skb->data, iph, bytes);
-
-	/* subtract to account for skb_push */
-	skb->len -= bytes;
-
-	frag->page_offset += bytes;
-	skb_frag_size_sub(frag, bytes);
-
-	/* subtract to account for skb_frag_size_sub */
-	skb->data_len -= bytes;
-
-	return 0;
-}
-
-/* Main downlink handler
- * Looks up NSS contex associated with the device. If the context is found,
- * we add a dummy ethernet header with the approriate protocol field set,
- * the pass the packet off to NSS for hardware acceleration.
- */
-int rmnet_nss_tx(struct sk_buff *skb)
-{
-	struct ethhdr *eth;
-	struct rmnet_nss_ctx *ctx;
-	struct net_device *dev = skb->dev;
-	nss_tx_status_t rc;
-	unsigned int len;
-	u8 version;
-
-	if (skb_is_nonlinear(skb)) {
-		if (rmnet_nss_adjust_header(skb))
-			goto fail;
-		else
-			rmnet_nss_inc_stat(RMNET_NSS_TX_NONLINEAR);
-	}
-
-	version = ((struct iphdr *)skb->data)->version;
-
-	ctx = rmnet_nss_find_ctx(dev);
-	if (!ctx) {
-		rmnet_nss_inc_stat(RMNET_NSS_TX_NO_CTX);
-		return -EINVAL;
-	}
-
-	eth = (struct ethhdr *)skb_push(skb, sizeof(*eth));
-	memset(&eth->h_dest, 0, ETH_ALEN * 2);
-	if (version == 4) {
-		eth->h_proto = htons(ETH_P_IP);
-	} else if (version == 6) {
-		eth->h_proto = htons(ETH_P_IPV6);
-	} else {
-		rmnet_nss_inc_stat(RMNET_NSS_TX_BAD_IP);
-		goto fail;
-	}
-
-	skb->protocol = htons(ETH_P_802_3);
-	/* Get length including ethhdr */
-	len = skb->len;
-
-transmit:
-	rc = nss_rmnet_rx_tx_buf(ctx->nss_ctx, skb);
-	if (rc == NSS_TX_SUCCESS) {
-		/* Increment rmnet_data device stats.
-		 * Don't call rmnet_data_vnd_rx_fixup() to do this, as
-		 * there's no guarantee the skb pointer is still valid.
-		 */
-		dev->stats.rx_packets++;
-		dev->stats.rx_bytes += len;
-		rmnet_nss_inc_stat(RMNET_NSS_TX_SUCCESS);
-		return 0;
-	} else if (rc == NSS_TX_FAILURE_QUEUE) {
-		rmnet_nss_inc_stat(RMNET_NSS_TX_BUSY_LOOP);
-		goto transmit;
-	}
-
-fail:
-	rmnet_nss_inc_stat(RMNET_NSS_TX_FAIL);
-	kfree_skb(skb);
-	return 1;
-}
-
-/* Called by NSS in the DL exception case.
- * Since the packet cannot be sent over the accelerated path, we need to
- * handle it. Remove the ethernet header and pass it onward to the stack
- * if possible.
- */
-void rmnet_nss_receive(struct net_device *dev, struct sk_buff *skb,
-		       struct napi_struct *napi)
-{
-	rmnet_nss_inc_stat(RMNET_NSS_EXCEPTIONS);
-
-	if (!skb)
-		return;
-
-	if (rmnet_nss_ethhdr_pull(skb)) {
-		rmnet_nss_inc_stat(RMNET_NSS_EX_BAD_HDR);
-		goto drop;
-	}
-
-	/* reset header pointers */
-	skb_reset_transport_header(skb);
-	skb_reset_network_header(skb);
-	skb_reset_mac_header(skb);
-
-	/* reset packet type */
-	skb->pkt_type = PACKET_HOST;
-
-	skb->dev = dev;
-
-	/* reset protocol type */
-	switch (skb->data[0] & 0xF0) {
-	case 0x40:
-		skb->protocol = htons(ETH_P_IP);
-		break;
-	case 0x60:
-		skb->protocol = htons(ETH_P_IPV6);
-		break;
-	default:
-		rmnet_nss_inc_stat(RMNET_NSS_EX_BAD_IP);
-		goto drop;
-	}
-
-	rmnet_nss_inc_stat(RMNET_NSS_EX_SUCCESS);
-
-	/* Set this so that we dont loop around netif_receive_skb */
-
-	skb->cb[0] = 1;
-
-	netif_receive_skb(skb);
-	return;
-
-drop:
-	kfree_skb(skb);
-}
-
-/* Called by NSS in the UL acceleration case.
- * We are guaranteed to have an ethernet packet here from the NSS hardware,
- * We need to pull the header off and invoke our ndo_start_xmit function
- * to handle transmitting the packet to the network stack.
- */
-void rmnet_nss_xmit(struct net_device *dev, struct sk_buff *skb)
-{
-	netdev_tx_t ret;
-
-	skb_pull(skb, sizeof(struct ethhdr));
-	rmnet_nss_inc_stat(RMNET_NSS_RX_ETH);
-
-	/* NSS takes care of shaping, so bypassing Qdiscs like this is OK */
-	ret = dev->netdev_ops->ndo_start_xmit(skb, dev);
-	if (unlikely(ret == NETDEV_TX_BUSY)) {
-		dev_kfree_skb_any(skb);
-		rmnet_nss_inc_stat(RMNET_NSS_RX_BUSY);
-	}
-}
-
-/* Create and register an NSS context for an rmnet_data device */
-int rmnet_nss_create_vnd(struct net_device *dev)
-{
-	struct rmnet_nss_ctx *ctx;
-
-	ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
-	if (!ctx)
-		return -ENOMEM;
-
-	ctx->rmnet_dev = dev;
-	ctx->nss_ctx = nss_rmnet_rx_create_sync_nexthop(dev, NSS_N2H_INTERFACE,
-						       NSS_C2C_TX_INTERFACE);
-	if (!ctx->nss_ctx) {
-		kfree(ctx);
-		return -1;
-	}
-
-	nss_rmnet_rx_register(ctx->nss_ctx, rmnet_nss_receive, dev);
-	nss_rmnet_rx_xmit_callback_register(ctx->nss_ctx, rmnet_nss_xmit);
-	hash_add_ptr(rmnet_nss_ctx_hashtable, &ctx->hnode, dev);
-	return 0;
-}
-
-/* Unregister and destroy the NSS context for an rmnet_data device */
-int rmnet_nss_free_vnd(struct net_device *dev)
-{
-	struct rmnet_nss_ctx *ctx;
-
-	ctx = rmnet_nss_find_ctx(dev);
-	rmnet_nss_free_ctx(ctx);
-
-	return 0;
-}
-
-static const struct rmnet_nss_cb rmnet_nss = {
-	.nss_create = rmnet_nss_create_vnd,
-	.nss_free = rmnet_nss_free_vnd,
-	.nss_tx = rmnet_nss_tx,
-};
-
-int __init rmnet_nss_init(void)
-{
-	pr_err("%s(): initializing rmnet_nss\n", __func__);
-	RCU_INIT_POINTER(rmnet_nss_callbacks, &rmnet_nss);
-	return 0;
-}
-
-void __exit rmnet_nss_exit(void)
-{
-	struct hlist_node *tmp;
-	struct rmnet_nss_ctx *ctx;
-	int bkt;
-
-	pr_err("%s(): exiting rmnet_nss\n", __func__);
-	RCU_INIT_POINTER(rmnet_nss_callbacks, NULL);
-
-	/* Tear down all NSS contexts */
-	hash_for_each_safe(rmnet_nss_ctx_hashtable, bkt, tmp, ctx, hnode)
-		rmnet_nss_free_ctx(ctx);
-}
-
-#if 0
-MODULE_LICENSE("GPL v2");
-module_init(rmnet_nss_init);
-module_exit(rmnet_nss_exit);
-#endif