mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-08-01 07:07:52 +08:00
🗽 Sync 2026-06-04 15:57:16
This commit is contained in:
parent
f6e1fb7da4
commit
0f4cdc85d2
@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=qmi_wwan_f
|
||||
PKG_VERSION:=1.0
|
||||
PKG_RELEASE:=1
|
||||
PKG_RELEASE:=2
|
||||
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
@ -712,27 +712,19 @@ static struct rtnl_link_stats64 *_rmnet_vnd_get_stats64(struct net_device *net,
|
||||
stats64 = per_cpu_ptr(dev->stats64, cpu);
|
||||
|
||||
do {
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6,6,0)
|
||||
start = u64_stats_fetch_begin_irq(&stats64->syncp);
|
||||
#else
|
||||
start = u64_stats_fetch_begin(&stats64->syncp);
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0)
|
||||
rx_packets = stats64->rx_packets;
|
||||
rx_bytes = stats64->rx_bytes;
|
||||
tx_packets = stats64->tx_packets;
|
||||
tx_bytes = stats64->tx_bytes;
|
||||
#else
|
||||
rx_packets = u64_stats_read(&stats64->rx_packets);
|
||||
rx_bytes = u64_stats_read(&stats64->rx_bytes);
|
||||
tx_packets = u64_stats_read(&stats64->tx_packets);
|
||||
tx_bytes = u64_stats_read(&stats64->tx_bytes);
|
||||
rx_packets = u64_stats_read(&stats64->rx_packets);
|
||||
rx_bytes = u64_stats_read(&stats64->rx_bytes);
|
||||
tx_packets = u64_stats_read(&stats64->tx_packets);
|
||||
tx_bytes = u64_stats_read(&stats64->tx_bytes);
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6,6,0)
|
||||
} while (u64_stats_fetch_retry_irq(&stats64->syncp, start));
|
||||
#else
|
||||
} while (u64_stats_fetch_retry(&stats64->syncp, start));
|
||||
#endif
|
||||
|
||||
stats->rx_packets += rx_packets;
|
||||
stats->rx_bytes += rx_bytes;
|
||||
@ -1195,11 +1187,7 @@ static int qmap_register_device(sQmiWwanQmap * pDev, u8 offset_id)
|
||||
priv->mux_id = FIBOCOM_QMAP_MUX_ID + offset_id;
|
||||
sprintf(qmap_net->name, "%s.%d", real_dev->name, offset_id + 1);
|
||||
qmap_net->netdev_ops = &qmap_netdev_ops;
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,17,0)
|
||||
memcpy(qmap_net->dev_addr, real_dev->dev_addr, ETH_ALEN);
|
||||
#else
|
||||
__dev_addr_set(qmap_net, real_dev->dev_addr, ETH_ALEN);
|
||||
#endif
|
||||
memcpy (qmap_net->dev_addr, real_dev->dev_addr, ETH_ALEN);
|
||||
|
||||
#ifdef FIBOCOM_BRIDGE_MODE
|
||||
priv->bridge_mode = !!(pDev->bridge_mode & BIT(offset_id));
|
||||
@ -1772,14 +1760,8 @@ static void ql_net_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *i
|
||||
{
|
||||
/* Inherit standard device info */
|
||||
usbnet_get_drvinfo(net, info);
|
||||
/* strlcpy() is deprecated in kernel 6.8.0+, using strscpy instead */
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6,8,0))
|
||||
strlcpy(info->driver, driver_name, sizeof(info->driver));
|
||||
strlcpy(info->version, VERSION_NUMBER, sizeof(info->version));
|
||||
#else
|
||||
strscpy(info->driver, driver_name, sizeof(info->driver));
|
||||
strscpy(info->version, VERSION_NUMBER, sizeof(info->version));
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct ethtool_ops ql_net_ethtool_ops;
|
||||
|
||||
@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-passwall
|
||||
PKG_VERSION:=26.6.2
|
||||
PKG_RELEASE:=153
|
||||
PKG_RELEASE:=154
|
||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
|
||||
@ -1762,6 +1762,12 @@ function gen_config(var)
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(route.rules, {
|
||||
action = "route",
|
||||
ip_is_private = true,
|
||||
outbound = "direct"
|
||||
})
|
||||
|
||||
if COMMON.default_outbound_tag then
|
||||
route.final = COMMON.default_outbound_tag
|
||||
end
|
||||
|
||||
@ -1482,6 +1482,11 @@ function gen_config(var)
|
||||
end
|
||||
end)
|
||||
|
||||
table.insert(rules, {
|
||||
outboundTag = "direct",
|
||||
ip = { "geoip:private" }
|
||||
})
|
||||
|
||||
if default_outboundTag then
|
||||
local rule = {
|
||||
_flag = "default",
|
||||
|
||||
@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-passwall2
|
||||
PKG_VERSION:=26.6.3
|
||||
PKG_RELEASE:=51
|
||||
PKG_RELEASE:=52
|
||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
|
||||
@ -1797,6 +1797,12 @@ function gen_config(var)
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(route.rules, {
|
||||
action = "route",
|
||||
ip_is_private = true,
|
||||
outbound = "direct"
|
||||
})
|
||||
|
||||
if COMMON.default_outbound_tag then
|
||||
route.final = COMMON.default_outbound_tag
|
||||
end
|
||||
|
||||
@ -1518,6 +1518,11 @@ function gen_config(var)
|
||||
end
|
||||
end)
|
||||
|
||||
table.insert(rules, {
|
||||
outboundTag = "direct",
|
||||
ip = { "geoip:private" }
|
||||
})
|
||||
|
||||
if default_outboundTag then
|
||||
local rule = {
|
||||
ruleTag = "default",
|
||||
|
||||
@ -15,7 +15,54 @@ const callSystemValidateFirmwareImage = rpc.declare({
|
||||
expect: { '': { valid: false, forcable: true } }
|
||||
});
|
||||
|
||||
function findStorageSize(procmtd, procpart) {
|
||||
function getBlockDevice(name) {
|
||||
const diskPatterns = [
|
||||
/^(sd[a-z]+)$/,
|
||||
/^(sd[a-z]+)\d+$/,
|
||||
/^(vd[a-z]+)$/,
|
||||
/^(vd[a-z]+)\d+$/,
|
||||
/^(xvd[a-z]+)$/,
|
||||
/^(xvd[a-z]+)\d+$/,
|
||||
/^(hd[a-z]+)$/,
|
||||
/^(hd[a-z]+)\d+$/,
|
||||
/^(mmcblk\d+)$/,
|
||||
/^(mmcblk\d+)p\d+$/,
|
||||
/^(nvme\d+n\d+)$/,
|
||||
/^(nvme\d+n\d+)p\d+$/
|
||||
];
|
||||
|
||||
for (const pattern of diskPatterns) {
|
||||
const match = name.match(pattern);
|
||||
|
||||
if (match)
|
||||
return match[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function findMountedDisk(procmounts) {
|
||||
const preferredMounts = new Set([ '/', '/rom', '/boot', '/boot/efi' ]);
|
||||
const mountedDisks = new Set();
|
||||
|
||||
procmounts.split(/\n/).forEach(function(ln) {
|
||||
const fields = ln.trim().split(/\s+/);
|
||||
const source = fields[0];
|
||||
const target = fields[1];
|
||||
|
||||
if (!preferredMounts.has(target) || !source || !source.startsWith('/dev/'))
|
||||
return;
|
||||
|
||||
const disk = getBlockDevice(source.substring(5));
|
||||
|
||||
if (disk)
|
||||
mountedDisks.add(disk);
|
||||
});
|
||||
|
||||
return mountedDisks;
|
||||
}
|
||||
|
||||
function findStorageSize(procmtd, procpart, procmounts) {
|
||||
let kernsize = 0, rootsize = 0, wholesize = 0;
|
||||
|
||||
procmtd.split(/\n/).forEach(function(ln) {
|
||||
@ -48,16 +95,43 @@ function findStorageSize(procmtd, procpart) {
|
||||
else if (kernsize > 0 && rootsize > kernsize)
|
||||
return kernsize + rootsize;
|
||||
|
||||
const mountedDisks = findMountedDisk(procmounts);
|
||||
const disks = {};
|
||||
const partitionedDisks = new Set();
|
||||
|
||||
procpart.split(/\n/).forEach(function(ln) {
|
||||
const match = ln.match(/^\s*\d+\s+\d+\s+(\d+)\s+(\S+)$/);
|
||||
if (match) {
|
||||
const size = parseInt(match[1], 10);
|
||||
if (!match)
|
||||
return;
|
||||
|
||||
if (!match[2].match(/\d/) && size > 2048 && wholesize == 0)
|
||||
wholesize = size * 1024;
|
||||
const size = parseInt(match[1], 10) * 1024;
|
||||
const name = match[2];
|
||||
const disk = getBlockDevice(name);
|
||||
|
||||
if (!disk || size <= 2048 * 1024)
|
||||
return;
|
||||
|
||||
if (name == disk) {
|
||||
disks[disk] = size;
|
||||
return;
|
||||
}
|
||||
|
||||
partitionedDisks.add(disk);
|
||||
});
|
||||
|
||||
for (const disk of mountedDisks) {
|
||||
if (disks[disk] > wholesize)
|
||||
wholesize = disks[disk];
|
||||
}
|
||||
|
||||
if (wholesize > 0)
|
||||
return wholesize;
|
||||
|
||||
for (const disk of partitionedDisks) {
|
||||
if (disks[disk] > wholesize)
|
||||
wholesize = disks[disk];
|
||||
}
|
||||
|
||||
return wholesize;
|
||||
}
|
||||
|
||||
@ -381,7 +455,7 @@ return view.extend({
|
||||
render([p_fstat, hostname, procmtd, procpart, procmounts]) {
|
||||
const has_sysupgrade = (p_fstat.type == 'file');
|
||||
const has_rootfs_data = (procmtd.match(/"rootfs_data"/) != null) || (procmounts.match("overlayfs:/overlay / ") != null);
|
||||
const storage_size = findStorageSize(procmtd, procpart);
|
||||
const storage_size = findStorageSize(procmtd, procpart, procmounts);
|
||||
let m, s, o, ss;
|
||||
|
||||
m = new form.JSONMap(mapdata, _('Flash operations'));
|
||||
|
||||
@ -9,8 +9,8 @@ include $(TOPDIR)/rules.mk
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
|
||||
PKG_NAME:=natflow
|
||||
PKG_VERSION:=20260313
|
||||
PKG_RELEASE:=10
|
||||
PKG_VERSION:=20260531
|
||||
PKG_RELEASE:=11
|
||||
|
||||
PKG_SOURCE:=$(PKG_VERSION).tar.xz
|
||||
PKG_SOURCE_URL:=https://github.com/ptpt52/natflow.git
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=quectel-cm
|
||||
PKG_VERSION:=1.6.7
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://github.com/quectel-open-source/Quectel_QConnectManager_Linux
|
||||
PKG_SOURCE_VERSION:=170ed7177cc3beb831bc85571aad956492ff7e7e
|
||||
PKG_MIRROR_HASH:=skip
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/quectel-cm
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=Quectel dial program
|
||||
DEPENDS:=+libpthread
|
||||
endef
|
||||
|
||||
define Package/quectel-cm/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/quectel-CM $(1)/usr/bin
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,quectel-cm))
|
||||
4
quectel_MHI/Makefile
Executable file → Normal file
4
quectel_MHI/Makefile
Executable file → Normal file
@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=pcie_mhi
|
||||
PKG_VERSION:=1.3.8
|
||||
PKG_RELEASE:=3
|
||||
PKG_RELEASE:=4
|
||||
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
@ -17,7 +17,7 @@ include $(INCLUDE_DIR)/package.mk
|
||||
define KernelPackage/pcie_mhi
|
||||
SUBMENU:=WWAN Support
|
||||
TITLE:=Kernel pcie driver for MHI device
|
||||
DEPENDS:=@PCIE_SUPPORT
|
||||
DEPENDS:=+pciids +pciutils +quectel-cm
|
||||
FILES:=$(PKG_BUILD_DIR)/pcie_mhi.ko
|
||||
AUTOLOAD:=$(call AutoLoad,90,pcie_mhi)
|
||||
endef
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
ccflags-y += -Wno-error=declaration-after-statement
|
||||
#ccflags-y += -g
|
||||
obj-m += pcie_mhi.o
|
||||
pcie_mhi-objs := core/mhi_init.o core/mhi_main.o core/mhi_pm.o core/mhi_boot.o core/mhi_dtr.o controllers/mhi_qti.o
|
||||
pcie_mhi-objs += devices/mhi_uci.o
|
||||
|
||||
@ -1,36 +1,36 @@
|
||||
1. porting pcie_mhi driver as next
|
||||
|
||||
$ git diff drivers/Makefile
|
||||
diff --git a/drivers/Makefile b/drivers/Makefile
|
||||
index 77fbc52..e45837e 100644
|
||||
--- a/drivers/Makefile
|
||||
+++ b/drivers/Makefile
|
||||
@@ -184,3 +184,4 @@ obj-$(CONFIG_FPGA) += fpga/
|
||||
obj-$(CONFIG_FSI) += fsi/
|
||||
obj-$(CONFIG_TEE) += tee/
|
||||
obj-$(CONFIG_MULTIPLEXER) += mux/
|
||||
+obj-y += pcie_mhi/
|
||||
|
||||
$ tree drivers/pcie_mhi/ -L 1
|
||||
drivers/pcie_mhi/
|
||||
controllers
|
||||
core
|
||||
devices
|
||||
Makefile
|
||||
|
||||
2. check RG500 attach pcie_mhi driver successful
|
||||
|
||||
root@OpenWrt:/# lspci
|
||||
00:00.0 Class 0604: 17cb:0302
|
||||
01:00.0 Class ff00: 17cb:0306
|
||||
|
||||
root@OpenWrt:~# dmesg | grep mhi
|
||||
[ 138.483252] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.6
|
||||
[ 138.492350] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306
|
||||
|
||||
3. how to use, see next logs
|
||||
|
||||
log/QXDM_OVER_PCIE.txt
|
||||
log/AT_OVER_PCIE.txt
|
||||
log/MBIM_OVER_PCIE.txt
|
||||
log/QMI_OVER_PCIE.txt
|
||||
1. porting pcie_mhi driver as next
|
||||
|
||||
$ git diff drivers/Makefile
|
||||
diff --git a/drivers/Makefile b/drivers/Makefile
|
||||
index 77fbc52..e45837e 100644
|
||||
--- a/drivers/Makefile
|
||||
+++ b/drivers/Makefile
|
||||
@@ -184,3 +184,4 @@ obj-$(CONFIG_FPGA) += fpga/
|
||||
obj-$(CONFIG_FSI) += fsi/
|
||||
obj-$(CONFIG_TEE) += tee/
|
||||
obj-$(CONFIG_MULTIPLEXER) += mux/
|
||||
+obj-y += pcie_mhi/
|
||||
|
||||
$ tree drivers/pcie_mhi/ -L 1
|
||||
drivers/pcie_mhi/
|
||||
controllers
|
||||
core
|
||||
devices
|
||||
Makefile
|
||||
|
||||
2. check RG500 attach pcie_mhi driver successful
|
||||
|
||||
root@OpenWrt:/# lspci
|
||||
00:00.0 Class 0604: 17cb:0302
|
||||
01:00.0 Class ff00: 17cb:0306
|
||||
|
||||
root@OpenWrt:~# dmesg | grep mhi
|
||||
[ 138.483252] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.6
|
||||
[ 138.492350] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306
|
||||
|
||||
3. how to use, see next logs
|
||||
|
||||
log/QXDM_OVER_PCIE.txt
|
||||
log/AT_OVER_PCIE.txt
|
||||
log/MBIM_OVER_PCIE.txt
|
||||
log/QMI_OVER_PCIE.txt
|
||||
|
||||
@ -1,103 +1,132 @@
|
||||
Release Notes
|
||||
|
||||
[V1.3.4]
|
||||
Date: 12/8/2022
|
||||
enhancement:
|
||||
1. only allow to enable autosuspend when module is in MHI_EE_AMSS
|
||||
2. show pcie link speed and width when driver probe
|
||||
3. check pcie link status by read pcie vid and pid when driver probe,
|
||||
if pcie link is down, return -EIO
|
||||
4. support RM520 (1eac:1004)
|
||||
5. support qmap command packet
|
||||
fix:
|
||||
1. fix tx queue is wrong stop when do uplink TPUT
|
||||
2. fix after QFirehose, module fail to bootup at very small probability
|
||||
3. mhi uci add mutex lock for concurrent reads/writes
|
||||
|
||||
[V1.3.3]
|
||||
Date: 30/6/2022
|
||||
enhancement:
|
||||
1. remove one un-necessary kmalloc when do qfirehose
|
||||
2. support mhi monitor (like usbmon), usage: cat /sys/kernel/debug/mhi_q/0306_00\:01.00/mhimon
|
||||
3. set ring size of event 0 to 256 (from 1024), required by x6x
|
||||
4. support PCIE local network card mhi_swip0 (chan 46/47), default disabled
|
||||
5. porting IPQ5018 mhi rate controll code from spf11.5
|
||||
6. set pcie rmnet download max qmap packet size to 15KB (same to IPQ MHI Driver)
|
||||
7. support set different mac address for rmnet net card
|
||||
8. when mhi netdev fail to malloc, use delay_work instead work
|
||||
9. optimize code for 'when driver load, modem is still in MHI_EE_PTHRU'
|
||||
fix:
|
||||
1. Fix not synchronize access rp/wp when mhi_queue_xxx and mhi_process_xxx_ring run on different CPU
|
||||
2. set dma mask when driver probe, some SOC like rpi_4 need it
|
||||
|
||||
[V1.3.2]
|
||||
Date: 12/16/2021
|
||||
enhancement:
|
||||
1. support Linux Kernel V5.14
|
||||
2. mhi_netdev_quectel.c do not print log in softirq context
|
||||
|
||||
[V1.3.1]
|
||||
Date: 9/26/2021
|
||||
enhancement:
|
||||
fix:
|
||||
|
||||
[V1.3.0.19]
|
||||
Date: 9/18/2021
|
||||
enhancement:
|
||||
1. support sdx62 (17cb:0308)
|
||||
2. support IPQ5018's NSS
|
||||
3. use 'qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c' instead myself rmnet_nss.c
|
||||
and pcie_mhi.ko must load after then rmnet_nss.ko
|
||||
4. allow bhi irq is not 0 (for ipq5018)
|
||||
fix:
|
||||
|
||||
[V1.3.0.18]
|
||||
Date: 4/14/2021
|
||||
enhancement:
|
||||
1. support mbim multiple call, usage:
|
||||
# insmod pcie_mhi.ko mhi_mbim_enabeld=1 qmap_mode=4
|
||||
# quectel-mbim-proxy -d /dev/mhi_MBIM &
|
||||
# quectel-CM -n X
|
||||
fix:
|
||||
|
||||
[V1.3.0.17]
|
||||
Date: 3/11/2021
|
||||
enhancement:
|
||||
fix:
|
||||
1. fix CPU loading very high when TPUT test when only one MSI interrupt
|
||||
2. fix error on latest X24 modem
|
||||
|
||||
[V1.3.0.16]
|
||||
Date: 11/18/2020
|
||||
enhancement:
|
||||
fix:
|
||||
1. add ring size to 32, for in-bound chan, if one ring is full, modem will not generate MSI interrupt for all chan
|
||||
|
||||
[V1.3.0.15]
|
||||
Date: 10/30/2020
|
||||
enhancement:
|
||||
1. support multi-modems, named as /dev/mhi_<chan_name>X
|
||||
fix:
|
||||
1. fix compile error on kernel v5.8
|
||||
|
||||
[V1.3.0.14]
|
||||
Date: 10/9/2020
|
||||
enhancement:
|
||||
1. suppport EM120&EM160
|
||||
fix:
|
||||
1. fix compile error on kernel v5.6
|
||||
2. support runtime suspend
|
||||
|
||||
[V1.3.0.13]
|
||||
Date: 9/7/2020
|
||||
enhancement:
|
||||
1. suppport EM120&EM160
|
||||
fix:
|
||||
1. fix error on X55 + PCIE2.0(e.g IPQ4019)
|
||||
2. support runtime suspend
|
||||
|
||||
[V1.3.0.12]
|
||||
Date: 7/7/2020
|
||||
enhancement:
|
||||
1. suppport create only none netcard (enabled by marco MHI_NETDEV_ONE_CARD_MODE),
|
||||
Release Notes
|
||||
[V1.3.8]
|
||||
Date: 27/06/2024
|
||||
enhancement:
|
||||
1. support SDX7x's PID/VID
|
||||
|
||||
Release Notes
|
||||
[V1.3.7]
|
||||
Date: 27/03/2024
|
||||
enhancement:
|
||||
1. support SDX35's PID/VID
|
||||
2. support IPQ QSDK MHI used with rmnetdata driver
|
||||
|
||||
Release Notes
|
||||
[V1.3.6]
|
||||
Date: 01/08/2023
|
||||
enhancement:
|
||||
1. support Linux Kernel V6.4
|
||||
2. support change mtu
|
||||
fix:
|
||||
1. fix compile error on ipq's spf12.x
|
||||
|
||||
Release Notes
|
||||
[V1.3.5]
|
||||
Date: 25/02/2023
|
||||
enhancement:
|
||||
1. support efuse SDX sleep
|
||||
2. support IPQ9574 SFE
|
||||
fix:
|
||||
1. fix cannot find the node when dialing. Nodes in the /sys/bus/mhi_q/devices directory named hex
|
||||
|
||||
[V1.3.4]
|
||||
Date: 12/8/2022
|
||||
enhancement:
|
||||
1. only allow to enable autosuspend when module is in MHI_EE_AMSS
|
||||
2. show pcie link speed and width when driver probe
|
||||
3. check pcie link status by read pcie vid and pid when driver probe,
|
||||
if pcie link is down, return -EIO
|
||||
4. support RM520 (1eac:1004)
|
||||
5. support qmap command packet
|
||||
fix:
|
||||
1. fix tx queue is wrong stop when do uplink TPUT
|
||||
2. fix after QFirehose, module fail to bootup at very small probability
|
||||
3. mhi uci add mutex lock for concurrent reads/writes
|
||||
|
||||
[V1.3.3]
|
||||
Date: 30/6/2022
|
||||
enhancement:
|
||||
1. remove one un-necessary kmalloc when do qfirehose
|
||||
2. support mhi monitor (like usbmon), usage: cat /sys/kernel/debug/mhi_q/0306_00\:01.00/mhimon
|
||||
3. set ring size of event 0 to 256 (from 1024), required by x6x
|
||||
4. support PCIE local network card mhi_swip0 (chan 46/47), default disabled
|
||||
5. porting IPQ5018 mhi rate controll code from spf11.5
|
||||
6. set pcie rmnet download max qmap packet size to 15KB (same to IPQ MHI Driver)
|
||||
7. support set different mac address for rmnet net card
|
||||
8. when mhi netdev fail to malloc, use delay_work instead work
|
||||
9. optimize code for 'when driver load, modem is still in MHI_EE_PTHRU'
|
||||
fix:
|
||||
1. Fix not synchronize access rp/wp when mhi_queue_xxx and mhi_process_xxx_ring run on different CPU
|
||||
2. set dma mask when driver probe, some SOC like rpi_4 need it
|
||||
|
||||
[V1.3.2]
|
||||
Date: 12/16/2021
|
||||
enhancement:
|
||||
1. support Linux Kernel V5.14
|
||||
2. mhi_netdev_quectel.c do not print log in softirq context
|
||||
|
||||
[V1.3.1]
|
||||
Date: 9/26/2021
|
||||
enhancement:
|
||||
fix:
|
||||
|
||||
[V1.3.0.19]
|
||||
Date: 9/18/2021
|
||||
enhancement:
|
||||
1. support sdx62 (17cb:0308)
|
||||
2. support IPQ5018's NSS
|
||||
3. use 'qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c' instead myself rmnet_nss.c
|
||||
and pcie_mhi.ko must load after then rmnet_nss.ko
|
||||
4. allow bhi irq is not 0 (for ipq5018)
|
||||
fix:
|
||||
|
||||
[V1.3.0.18]
|
||||
Date: 4/14/2021
|
||||
enhancement:
|
||||
1. support mbim multiple call, usage:
|
||||
# insmod pcie_mhi.ko mhi_mbim_enabeld=1 qmap_mode=4
|
||||
# quectel-mbim-proxy -d /dev/mhi_MBIM &
|
||||
# quectel-CM -n X
|
||||
fix:
|
||||
|
||||
[V1.3.0.17]
|
||||
Date: 3/11/2021
|
||||
enhancement:
|
||||
fix:
|
||||
1. fix CPU loading very high when TPUT test when only one MSI interrupt
|
||||
2. fix error on latest X24 modem
|
||||
|
||||
[V1.3.0.16]
|
||||
Date: 11/18/2020
|
||||
enhancement:
|
||||
fix:
|
||||
1. add ring size to 32, for in-bound chan, if one ring is full, modem will not generate MSI interrupt for all chan
|
||||
|
||||
[V1.3.0.15]
|
||||
Date: 10/30/2020
|
||||
enhancement:
|
||||
1. support multi-modems, named as /dev/mhi_<chan_name>X
|
||||
fix:
|
||||
1. fix compile error on kernel v5.8
|
||||
|
||||
[V1.3.0.14]
|
||||
Date: 10/9/2020
|
||||
enhancement:
|
||||
1. suppport EM120&EM160
|
||||
fix:
|
||||
1. fix compile error on kernel v5.6
|
||||
2. support runtime suspend
|
||||
|
||||
[V1.3.0.13]
|
||||
Date: 9/7/2020
|
||||
enhancement:
|
||||
1. suppport EM120&EM160
|
||||
fix:
|
||||
1. fix error on X55 + PCIE2.0(e.g IPQ4019)
|
||||
2. support runtime suspend
|
||||
|
||||
[V1.3.0.12]
|
||||
Date: 7/7/2020
|
||||
enhancement:
|
||||
1. suppport create only none netcard (enabled by marco MHI_NETDEV_ONE_CARD_MODE),
|
||||
fix:
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,92 +1,92 @@
|
||||
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef _MHI_QCOM_
|
||||
#define _MHI_QCOM_
|
||||
|
||||
/* iova cfg bitmask */
|
||||
#define MHI_SMMU_ATTACH BIT(0)
|
||||
#define MHI_SMMU_S1_BYPASS BIT(1)
|
||||
#define MHI_SMMU_FAST BIT(2)
|
||||
#define MHI_SMMU_ATOMIC BIT(3)
|
||||
#define MHI_SMMU_FORCE_COHERENT BIT(4)
|
||||
|
||||
#define MHI_PCIE_VENDOR_ID (0x17cb)
|
||||
#define MHI_PCIE_DEBUG_ID (0xffff)
|
||||
#define MHI_RPM_SUSPEND_TMR_MS (3000)
|
||||
#define MHI_PCI_BAR_NUM (0)
|
||||
|
||||
struct mhi_dev {
|
||||
struct pci_dev *pci_dev;
|
||||
u32 smmu_cfg;
|
||||
int resn;
|
||||
void *arch_info;
|
||||
bool powered_on;
|
||||
bool debug_mode;
|
||||
};
|
||||
|
||||
void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl);
|
||||
int mhi_pci_probe(struct pci_dev *pci_dev,
|
||||
const struct pci_device_id *device_id);
|
||||
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,65 ))
|
||||
static inline int dma_set_mask_and_coherent(struct device *dev, u64 mask)
|
||||
{
|
||||
int rc = dma_set_mask(dev, mask);
|
||||
if (rc == 0)
|
||||
dma_set_coherent_mask(dev, mask);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
|
||||
|
||||
mhi_cntrl->dev = &mhi_dev->pci_dev->dev;
|
||||
|
||||
return dma_set_mask_and_coherent(mhi_cntrl->dev, DMA_BIT_MASK(64));
|
||||
}
|
||||
|
||||
static inline void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mhi_arch_platform_init(struct mhi_dev *mhi_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mhi_arch_platform_deinit(struct mhi_dev *mhi_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mhi_arch_link_off(struct mhi_controller *mhi_cntrl,
|
||||
bool graceful)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mhi_arch_link_on(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* _MHI_QCOM_ */
|
||||
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef _MHI_QCOM_
|
||||
#define _MHI_QCOM_
|
||||
|
||||
/* iova cfg bitmask */
|
||||
#define MHI_SMMU_ATTACH BIT(0)
|
||||
#define MHI_SMMU_S1_BYPASS BIT(1)
|
||||
#define MHI_SMMU_FAST BIT(2)
|
||||
#define MHI_SMMU_ATOMIC BIT(3)
|
||||
#define MHI_SMMU_FORCE_COHERENT BIT(4)
|
||||
|
||||
#define MHI_PCIE_VENDOR_ID (0x17cb)
|
||||
#define MHI_PCIE_DEBUG_ID (0xffff)
|
||||
#define MHI_RPM_SUSPEND_TMR_MS (3000)
|
||||
#define MHI_PCI_BAR_NUM (0)
|
||||
|
||||
struct mhi_dev {
|
||||
struct pci_dev *pci_dev;
|
||||
u32 smmu_cfg;
|
||||
int resn;
|
||||
void *arch_info;
|
||||
bool powered_on;
|
||||
bool debug_mode;
|
||||
};
|
||||
|
||||
void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl);
|
||||
int mhi_pci_probe(struct pci_dev *pci_dev,
|
||||
const struct pci_device_id *device_id);
|
||||
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,10,65 ))
|
||||
static inline int dma_set_mask_and_coherent(struct device *dev, u64 mask)
|
||||
{
|
||||
int rc = dma_set_mask(dev, mask);
|
||||
if (rc == 0)
|
||||
dma_set_coherent_mask(dev, mask);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
|
||||
|
||||
mhi_cntrl->dev = &mhi_dev->pci_dev->dev;
|
||||
|
||||
return dma_set_mask_and_coherent(mhi_cntrl->dev, DMA_BIT_MASK(64));
|
||||
}
|
||||
|
||||
static inline void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mhi_arch_platform_init(struct mhi_dev *mhi_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mhi_arch_platform_deinit(struct mhi_dev *mhi_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mhi_arch_link_off(struct mhi_controller *mhi_cntrl,
|
||||
bool graceful)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mhi_arch_link_on(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* _MHI_QCOM_ */
|
||||
|
||||
@ -430,8 +430,7 @@ rpm_resume_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mhi_system_resume(struct device *dev)
|
||||
static int __maybe_unused mhi_system_resume(struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev);
|
||||
@ -475,7 +474,6 @@ int mhi_system_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* checks if link is down */
|
||||
static int mhi_link_status(struct mhi_controller *mhi_cntrl, void *priv)
|
||||
|
||||
@ -644,9 +644,7 @@ static int mon_text_release(struct inode *inode, struct file *file)
|
||||
static const struct file_operations mon_fops_text_u = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = mon_text_open,
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 12, 0))
|
||||
.llseek = no_llseek,
|
||||
#endif
|
||||
.read = mon_text_read_u,
|
||||
.release = mon_text_release,
|
||||
};
|
||||
@ -2371,11 +2369,7 @@ void mhi_unprepare_after_power_down(struct mhi_controller *mhi_cntrl)
|
||||
}
|
||||
|
||||
/* match dev to drv */
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0))
|
||||
static int mhi_match(struct device *dev, const struct device_driver *drv)
|
||||
#else
|
||||
static int mhi_match(struct device *dev, struct device_driver *drv)
|
||||
#endif
|
||||
{
|
||||
struct mhi_device *mhi_dev = to_mhi_device(dev);
|
||||
struct mhi_driver *mhi_drv = to_mhi_driver(drv);
|
||||
|
||||
@ -1,362 +1,362 @@
|
||||
#ifndef __SDX20_MHI_H
|
||||
#define __SDX20_MHI_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* MHI control data structures alloted by the host, including
|
||||
* channel context array, event context array, command context and rings */
|
||||
|
||||
/* Channel context state */
|
||||
enum mhi_dev_ch_ctx_state {
|
||||
MHI_DEV_CH_STATE_DISABLED,
|
||||
MHI_DEV_CH_STATE_ENABLED,
|
||||
MHI_DEV_CH_STATE_RUNNING,
|
||||
MHI_DEV_CH_STATE_SUSPENDED,
|
||||
MHI_DEV_CH_STATE_STOP,
|
||||
MHI_DEV_CH_STATE_ERROR,
|
||||
MHI_DEV_CH_STATE_RESERVED,
|
||||
MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF
|
||||
};
|
||||
|
||||
/* Channel type */
|
||||
enum mhi_dev_ch_ctx_type {
|
||||
MHI_DEV_CH_TYPE_NONE,
|
||||
MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL,
|
||||
MHI_DEV_CH_TYPE_INBOUND_CHANNEL,
|
||||
MHI_DEV_CH_RESERVED
|
||||
};
|
||||
|
||||
/* Channel context type */
|
||||
struct mhi_dev_ch_ctx {
|
||||
enum mhi_dev_ch_ctx_state ch_state;
|
||||
enum mhi_dev_ch_ctx_type ch_type;
|
||||
uint32_t err_indx;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_ring_element_type_id {
|
||||
MHI_DEV_RING_EL_INVALID = 0,
|
||||
MHI_DEV_RING_EL_NOOP = 1,
|
||||
MHI_DEV_RING_EL_TRANSFER = 2,
|
||||
MHI_DEV_RING_EL_RESET = 16,
|
||||
MHI_DEV_RING_EL_STOP = 17,
|
||||
MHI_DEV_RING_EL_START = 18,
|
||||
MHI_DEV_RING_EL_MHI_STATE_CHG = 32,
|
||||
MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33,
|
||||
MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34,
|
||||
MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64,
|
||||
MHI_DEV_RING_EL_UNDEF
|
||||
};
|
||||
|
||||
enum mhi_dev_ring_state {
|
||||
RING_STATE_UINT = 0,
|
||||
RING_STATE_IDLE,
|
||||
RING_STATE_PENDING,
|
||||
};
|
||||
|
||||
enum mhi_dev_ring_type {
|
||||
RING_TYPE_CMD = 0,
|
||||
RING_TYPE_ER,
|
||||
RING_TYPE_CH,
|
||||
RING_TYPE_INVAL
|
||||
};
|
||||
|
||||
/* Event context interrupt moderation */
|
||||
enum mhi_dev_evt_ctx_int_mod_timer {
|
||||
MHI_DEV_EVT_INT_MODERATION_DISABLED
|
||||
};
|
||||
|
||||
/* Event ring type */
|
||||
enum mhi_dev_evt_ctx_event_ring_type {
|
||||
MHI_DEV_EVT_TYPE_DEFAULT,
|
||||
MHI_DEV_EVT_TYPE_VALID,
|
||||
MHI_DEV_EVT_RESERVED
|
||||
};
|
||||
|
||||
/* Event ring context type */
|
||||
struct mhi_dev_ev_ctx {
|
||||
uint32_t res1:16;
|
||||
enum mhi_dev_evt_ctx_int_mod_timer intmodt:16;
|
||||
enum mhi_dev_evt_ctx_event_ring_type ertype;
|
||||
uint32_t msivec;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* Command context */
|
||||
struct mhi_dev_cmd_ctx {
|
||||
uint32_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* generic context */
|
||||
struct mhi_dev_gen_ctx {
|
||||
uint32_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* Transfer ring element */
|
||||
struct mhi_dev_transfer_ring_element {
|
||||
uint64_t data_buf_ptr;
|
||||
uint32_t len:16;
|
||||
uint32_t res1:16;
|
||||
uint32_t chain:1;
|
||||
uint32_t res2:7;
|
||||
uint32_t ieob:1;
|
||||
uint32_t ieot:1;
|
||||
uint32_t bei:1;
|
||||
uint32_t res3:5;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res4:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring element */
|
||||
/* Command ring No op command */
|
||||
struct mhi_dev_cmd_ring_op {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring reset channel command */
|
||||
struct mhi_dev_cmd_ring_reset_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring stop channel command */
|
||||
struct mhi_dev_cmd_ring_stop_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring start channel command */
|
||||
struct mhi_dev_cmd_ring_start_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t seqnum;
|
||||
uint32_t reliable:1;
|
||||
uint32_t res2:15;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_cmd_completion_code {
|
||||
MHI_CMD_COMPL_CODE_INVALID = 0,
|
||||
MHI_CMD_COMPL_CODE_SUCCESS = 1,
|
||||
MHI_CMD_COMPL_CODE_EOT = 2,
|
||||
MHI_CMD_COMPL_CODE_OVERFLOW = 3,
|
||||
MHI_CMD_COMPL_CODE_EOB = 4,
|
||||
MHI_CMD_COMPL_CODE_UNDEFINED = 16,
|
||||
MHI_CMD_COMPL_CODE_RING_EL = 17,
|
||||
MHI_CMD_COMPL_CODE_RES
|
||||
};
|
||||
|
||||
/* Event ring elements */
|
||||
/* Transfer completion event */
|
||||
struct mhi_dev_event_ring_transfer_completion {
|
||||
uint64_t ptr;
|
||||
uint32_t len:16;
|
||||
uint32_t res1:8;
|
||||
enum mhi_dev_cmd_completion_code code:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command completion event */
|
||||
struct mhi_dev_event_ring_cmd_completion {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_cmd_completion_code code:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_state {
|
||||
MHI_DEV_RESET_STATE = 0,
|
||||
MHI_DEV_READY_STATE,
|
||||
MHI_DEV_M0_STATE,
|
||||
MHI_DEV_M1_STATE,
|
||||
MHI_DEV_M2_STATE,
|
||||
MHI_DEV_M3_STATE,
|
||||
MHI_DEV_MAX_STATE,
|
||||
MHI_DEV_SYSERR_STATE = 0xff
|
||||
};
|
||||
|
||||
/* MHI state change event */
|
||||
struct mhi_dev_event_ring_state_change {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_state mhistate:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_execenv {
|
||||
MHI_DEV_SBL_EE = 1,
|
||||
MHI_DEV_AMSS_EE = 2,
|
||||
MHI_DEV_UNRESERVED
|
||||
};
|
||||
|
||||
/* EE state change event */
|
||||
struct mhi_dev_event_ring_ee_state_change {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_execenv execenv:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
/* Generic cmd to parse common details like type and channel id */
|
||||
struct mhi_dev_ring_generic {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_state mhistate:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
struct mhi_config {
|
||||
uint32_t mhi_reg_len;
|
||||
uint32_t version;
|
||||
uint32_t event_rings;
|
||||
uint32_t channels;
|
||||
uint32_t chdb_offset;
|
||||
uint32_t erdb_offset;
|
||||
};
|
||||
|
||||
#define NUM_CHANNELS 128
|
||||
#define HW_CHANNEL_BASE 100
|
||||
#define HW_CHANNEL_END 107
|
||||
#define MHI_ENV_VALUE 2
|
||||
#define MHI_MASK_ROWS_CH_EV_DB 4
|
||||
#define TRB_MAX_DATA_SIZE 8192
|
||||
#define MHI_CTRL_STATE 25
|
||||
#define IPA_DMA_SYNC 1
|
||||
#define IPA_DMA_ASYNC 0
|
||||
|
||||
/*maximum trasnfer completion events buffer*/
|
||||
#define MAX_TR_EVENTS 50
|
||||
/*maximum event requests */
|
||||
#define MHI_MAX_EVT_REQ 50
|
||||
|
||||
/* Possible ring element types */
|
||||
union mhi_dev_ring_element_type {
|
||||
struct mhi_dev_cmd_ring_op cmd_no_op;
|
||||
struct mhi_dev_cmd_ring_reset_channel_cmd cmd_reset;
|
||||
struct mhi_dev_cmd_ring_stop_channel_cmd cmd_stop;
|
||||
struct mhi_dev_cmd_ring_start_channel_cmd cmd_start;
|
||||
struct mhi_dev_transfer_ring_element cmd_transfer;
|
||||
struct mhi_dev_event_ring_transfer_completion evt_tr_comp;
|
||||
struct mhi_dev_event_ring_cmd_completion evt_cmd_comp;
|
||||
struct mhi_dev_event_ring_state_change evt_state_change;
|
||||
struct mhi_dev_event_ring_ee_state_change evt_ee_state;
|
||||
struct mhi_dev_ring_generic generic;
|
||||
};
|
||||
|
||||
/* Transfer ring element type */
|
||||
union mhi_dev_ring_ctx {
|
||||
struct mhi_dev_cmd_ctx cmd;
|
||||
struct mhi_dev_ev_ctx ev;
|
||||
struct mhi_dev_ch_ctx ch;
|
||||
struct mhi_dev_gen_ctx generic;
|
||||
};
|
||||
|
||||
/* MHI host Control and data address region */
|
||||
struct mhi_host_addr {
|
||||
uint32_t ctrl_base_lsb;
|
||||
uint32_t ctrl_base_msb;
|
||||
uint32_t ctrl_limit_lsb;
|
||||
uint32_t ctrl_limit_msb;
|
||||
uint32_t data_base_lsb;
|
||||
uint32_t data_base_msb;
|
||||
uint32_t data_limit_lsb;
|
||||
uint32_t data_limit_msb;
|
||||
};
|
||||
|
||||
/* MHI physical and virtual address region */
|
||||
struct mhi_meminfo {
|
||||
struct device *dev;
|
||||
uintptr_t pa_aligned;
|
||||
uintptr_t pa_unaligned;
|
||||
uintptr_t va_aligned;
|
||||
uintptr_t va_unaligned;
|
||||
uintptr_t size;
|
||||
};
|
||||
|
||||
struct mhi_addr {
|
||||
uint64_t host_pa;
|
||||
uintptr_t device_pa;
|
||||
uintptr_t device_va;
|
||||
size_t size;
|
||||
dma_addr_t phy_addr;
|
||||
void *virt_addr;
|
||||
bool use_ipa_dma;
|
||||
};
|
||||
|
||||
struct mhi_interrupt_state {
|
||||
uint32_t mask;
|
||||
uint32_t status;
|
||||
};
|
||||
|
||||
enum mhi_dev_channel_state {
|
||||
MHI_DEV_CH_UNINT,
|
||||
MHI_DEV_CH_STARTED,
|
||||
MHI_DEV_CH_PENDING_START,
|
||||
MHI_DEV_CH_PENDING_STOP,
|
||||
MHI_DEV_CH_STOPPED,
|
||||
MHI_DEV_CH_CLOSED,
|
||||
};
|
||||
|
||||
enum mhi_dev_ch_operation {
|
||||
MHI_DEV_OPEN_CH,
|
||||
MHI_DEV_CLOSE_CH,
|
||||
MHI_DEV_READ_CH,
|
||||
MHI_DEV_READ_WR,
|
||||
MHI_DEV_POLL,
|
||||
};
|
||||
|
||||
enum mhi_ctrl_info {
|
||||
MHI_STATE_CONFIGURED = 0,
|
||||
MHI_STATE_CONNECTED = 1,
|
||||
MHI_STATE_DISCONNECTED = 2,
|
||||
MHI_STATE_INVAL,
|
||||
};
|
||||
|
||||
enum mhi_dev_tr_compl_evt_type {
|
||||
SEND_EVENT_BUFFER,
|
||||
SEND_EVENT_RD_OFFSET,
|
||||
};
|
||||
|
||||
enum mhi_dev_transfer_type {
|
||||
MHI_DEV_DMA_SYNC,
|
||||
MHI_DEV_DMA_ASYNC,
|
||||
};
|
||||
#endif /* _SDX20_MHI_H_ */
|
||||
#ifndef __SDX20_MHI_H
|
||||
#define __SDX20_MHI_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* MHI control data structures alloted by the host, including
|
||||
* channel context array, event context array, command context and rings */
|
||||
|
||||
/* Channel context state */
|
||||
enum mhi_dev_ch_ctx_state {
|
||||
MHI_DEV_CH_STATE_DISABLED,
|
||||
MHI_DEV_CH_STATE_ENABLED,
|
||||
MHI_DEV_CH_STATE_RUNNING,
|
||||
MHI_DEV_CH_STATE_SUSPENDED,
|
||||
MHI_DEV_CH_STATE_STOP,
|
||||
MHI_DEV_CH_STATE_ERROR,
|
||||
MHI_DEV_CH_STATE_RESERVED,
|
||||
MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF
|
||||
};
|
||||
|
||||
/* Channel type */
|
||||
enum mhi_dev_ch_ctx_type {
|
||||
MHI_DEV_CH_TYPE_NONE,
|
||||
MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL,
|
||||
MHI_DEV_CH_TYPE_INBOUND_CHANNEL,
|
||||
MHI_DEV_CH_RESERVED
|
||||
};
|
||||
|
||||
/* Channel context type */
|
||||
struct mhi_dev_ch_ctx {
|
||||
enum mhi_dev_ch_ctx_state ch_state;
|
||||
enum mhi_dev_ch_ctx_type ch_type;
|
||||
uint32_t err_indx;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_ring_element_type_id {
|
||||
MHI_DEV_RING_EL_INVALID = 0,
|
||||
MHI_DEV_RING_EL_NOOP = 1,
|
||||
MHI_DEV_RING_EL_TRANSFER = 2,
|
||||
MHI_DEV_RING_EL_RESET = 16,
|
||||
MHI_DEV_RING_EL_STOP = 17,
|
||||
MHI_DEV_RING_EL_START = 18,
|
||||
MHI_DEV_RING_EL_MHI_STATE_CHG = 32,
|
||||
MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33,
|
||||
MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34,
|
||||
MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64,
|
||||
MHI_DEV_RING_EL_UNDEF
|
||||
};
|
||||
|
||||
enum mhi_dev_ring_state {
|
||||
RING_STATE_UINT = 0,
|
||||
RING_STATE_IDLE,
|
||||
RING_STATE_PENDING,
|
||||
};
|
||||
|
||||
enum mhi_dev_ring_type {
|
||||
RING_TYPE_CMD = 0,
|
||||
RING_TYPE_ER,
|
||||
RING_TYPE_CH,
|
||||
RING_TYPE_INVAL
|
||||
};
|
||||
|
||||
/* Event context interrupt moderation */
|
||||
enum mhi_dev_evt_ctx_int_mod_timer {
|
||||
MHI_DEV_EVT_INT_MODERATION_DISABLED
|
||||
};
|
||||
|
||||
/* Event ring type */
|
||||
enum mhi_dev_evt_ctx_event_ring_type {
|
||||
MHI_DEV_EVT_TYPE_DEFAULT,
|
||||
MHI_DEV_EVT_TYPE_VALID,
|
||||
MHI_DEV_EVT_RESERVED
|
||||
};
|
||||
|
||||
/* Event ring context type */
|
||||
struct mhi_dev_ev_ctx {
|
||||
uint32_t res1:16;
|
||||
enum mhi_dev_evt_ctx_int_mod_timer intmodt:16;
|
||||
enum mhi_dev_evt_ctx_event_ring_type ertype;
|
||||
uint32_t msivec;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* Command context */
|
||||
struct mhi_dev_cmd_ctx {
|
||||
uint32_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* generic context */
|
||||
struct mhi_dev_gen_ctx {
|
||||
uint32_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* Transfer ring element */
|
||||
struct mhi_dev_transfer_ring_element {
|
||||
uint64_t data_buf_ptr;
|
||||
uint32_t len:16;
|
||||
uint32_t res1:16;
|
||||
uint32_t chain:1;
|
||||
uint32_t res2:7;
|
||||
uint32_t ieob:1;
|
||||
uint32_t ieot:1;
|
||||
uint32_t bei:1;
|
||||
uint32_t res3:5;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res4:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring element */
|
||||
/* Command ring No op command */
|
||||
struct mhi_dev_cmd_ring_op {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring reset channel command */
|
||||
struct mhi_dev_cmd_ring_reset_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring stop channel command */
|
||||
struct mhi_dev_cmd_ring_stop_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring start channel command */
|
||||
struct mhi_dev_cmd_ring_start_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t seqnum;
|
||||
uint32_t reliable:1;
|
||||
uint32_t res2:15;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_cmd_completion_code {
|
||||
MHI_CMD_COMPL_CODE_INVALID = 0,
|
||||
MHI_CMD_COMPL_CODE_SUCCESS = 1,
|
||||
MHI_CMD_COMPL_CODE_EOT = 2,
|
||||
MHI_CMD_COMPL_CODE_OVERFLOW = 3,
|
||||
MHI_CMD_COMPL_CODE_EOB = 4,
|
||||
MHI_CMD_COMPL_CODE_UNDEFINED = 16,
|
||||
MHI_CMD_COMPL_CODE_RING_EL = 17,
|
||||
MHI_CMD_COMPL_CODE_RES
|
||||
};
|
||||
|
||||
/* Event ring elements */
|
||||
/* Transfer completion event */
|
||||
struct mhi_dev_event_ring_transfer_completion {
|
||||
uint64_t ptr;
|
||||
uint32_t len:16;
|
||||
uint32_t res1:8;
|
||||
enum mhi_dev_cmd_completion_code code:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command completion event */
|
||||
struct mhi_dev_event_ring_cmd_completion {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_cmd_completion_code code:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_state {
|
||||
MHI_DEV_RESET_STATE = 0,
|
||||
MHI_DEV_READY_STATE,
|
||||
MHI_DEV_M0_STATE,
|
||||
MHI_DEV_M1_STATE,
|
||||
MHI_DEV_M2_STATE,
|
||||
MHI_DEV_M3_STATE,
|
||||
MHI_DEV_MAX_STATE,
|
||||
MHI_DEV_SYSERR_STATE = 0xff
|
||||
};
|
||||
|
||||
/* MHI state change event */
|
||||
struct mhi_dev_event_ring_state_change {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_state mhistate:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_execenv {
|
||||
MHI_DEV_SBL_EE = 1,
|
||||
MHI_DEV_AMSS_EE = 2,
|
||||
MHI_DEV_UNRESERVED
|
||||
};
|
||||
|
||||
/* EE state change event */
|
||||
struct mhi_dev_event_ring_ee_state_change {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_execenv execenv:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
/* Generic cmd to parse common details like type and channel id */
|
||||
struct mhi_dev_ring_generic {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_state mhistate:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
struct mhi_config {
|
||||
uint32_t mhi_reg_len;
|
||||
uint32_t version;
|
||||
uint32_t event_rings;
|
||||
uint32_t channels;
|
||||
uint32_t chdb_offset;
|
||||
uint32_t erdb_offset;
|
||||
};
|
||||
|
||||
#define NUM_CHANNELS 128
|
||||
#define HW_CHANNEL_BASE 100
|
||||
#define HW_CHANNEL_END 107
|
||||
#define MHI_ENV_VALUE 2
|
||||
#define MHI_MASK_ROWS_CH_EV_DB 4
|
||||
#define TRB_MAX_DATA_SIZE 8192
|
||||
#define MHI_CTRL_STATE 25
|
||||
#define IPA_DMA_SYNC 1
|
||||
#define IPA_DMA_ASYNC 0
|
||||
|
||||
/*maximum trasnfer completion events buffer*/
|
||||
#define MAX_TR_EVENTS 50
|
||||
/*maximum event requests */
|
||||
#define MHI_MAX_EVT_REQ 50
|
||||
|
||||
/* Possible ring element types */
|
||||
union mhi_dev_ring_element_type {
|
||||
struct mhi_dev_cmd_ring_op cmd_no_op;
|
||||
struct mhi_dev_cmd_ring_reset_channel_cmd cmd_reset;
|
||||
struct mhi_dev_cmd_ring_stop_channel_cmd cmd_stop;
|
||||
struct mhi_dev_cmd_ring_start_channel_cmd cmd_start;
|
||||
struct mhi_dev_transfer_ring_element cmd_transfer;
|
||||
struct mhi_dev_event_ring_transfer_completion evt_tr_comp;
|
||||
struct mhi_dev_event_ring_cmd_completion evt_cmd_comp;
|
||||
struct mhi_dev_event_ring_state_change evt_state_change;
|
||||
struct mhi_dev_event_ring_ee_state_change evt_ee_state;
|
||||
struct mhi_dev_ring_generic generic;
|
||||
};
|
||||
|
||||
/* Transfer ring element type */
|
||||
union mhi_dev_ring_ctx {
|
||||
struct mhi_dev_cmd_ctx cmd;
|
||||
struct mhi_dev_ev_ctx ev;
|
||||
struct mhi_dev_ch_ctx ch;
|
||||
struct mhi_dev_gen_ctx generic;
|
||||
};
|
||||
|
||||
/* MHI host Control and data address region */
|
||||
struct mhi_host_addr {
|
||||
uint32_t ctrl_base_lsb;
|
||||
uint32_t ctrl_base_msb;
|
||||
uint32_t ctrl_limit_lsb;
|
||||
uint32_t ctrl_limit_msb;
|
||||
uint32_t data_base_lsb;
|
||||
uint32_t data_base_msb;
|
||||
uint32_t data_limit_lsb;
|
||||
uint32_t data_limit_msb;
|
||||
};
|
||||
|
||||
/* MHI physical and virtual address region */
|
||||
struct mhi_meminfo {
|
||||
struct device *dev;
|
||||
uintptr_t pa_aligned;
|
||||
uintptr_t pa_unaligned;
|
||||
uintptr_t va_aligned;
|
||||
uintptr_t va_unaligned;
|
||||
uintptr_t size;
|
||||
};
|
||||
|
||||
struct mhi_addr {
|
||||
uint64_t host_pa;
|
||||
uintptr_t device_pa;
|
||||
uintptr_t device_va;
|
||||
size_t size;
|
||||
dma_addr_t phy_addr;
|
||||
void *virt_addr;
|
||||
bool use_ipa_dma;
|
||||
};
|
||||
|
||||
struct mhi_interrupt_state {
|
||||
uint32_t mask;
|
||||
uint32_t status;
|
||||
};
|
||||
|
||||
enum mhi_dev_channel_state {
|
||||
MHI_DEV_CH_UNINT,
|
||||
MHI_DEV_CH_STARTED,
|
||||
MHI_DEV_CH_PENDING_START,
|
||||
MHI_DEV_CH_PENDING_STOP,
|
||||
MHI_DEV_CH_STOPPED,
|
||||
MHI_DEV_CH_CLOSED,
|
||||
};
|
||||
|
||||
enum mhi_dev_ch_operation {
|
||||
MHI_DEV_OPEN_CH,
|
||||
MHI_DEV_CLOSE_CH,
|
||||
MHI_DEV_READ_CH,
|
||||
MHI_DEV_READ_WR,
|
||||
MHI_DEV_POLL,
|
||||
};
|
||||
|
||||
enum mhi_ctrl_info {
|
||||
MHI_STATE_CONFIGURED = 0,
|
||||
MHI_STATE_CONNECTED = 1,
|
||||
MHI_STATE_DISCONNECTED = 2,
|
||||
MHI_STATE_INVAL,
|
||||
};
|
||||
|
||||
enum mhi_dev_tr_compl_evt_type {
|
||||
SEND_EVENT_BUFFER,
|
||||
SEND_EVENT_RD_OFFSET,
|
||||
};
|
||||
|
||||
enum mhi_dev_transfer_type {
|
||||
MHI_DEV_DMA_SYNC,
|
||||
MHI_DEV_DMA_ASYNC,
|
||||
};
|
||||
#endif /* _SDX20_MHI_H_ */
|
||||
|
||||
@ -1,426 +1,426 @@
|
||||
#ifndef __SDX20_MHI_H
|
||||
#define __SDX20_MHI_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* MHI control data structures alloted by the host, including
|
||||
* channel context array, event context array, command context and rings */
|
||||
|
||||
/* Channel context state */
|
||||
enum mhi_dev_ch_ctx_state {
|
||||
MHI_DEV_CH_STATE_DISABLED,
|
||||
MHI_DEV_CH_STATE_ENABLED,
|
||||
MHI_DEV_CH_STATE_RUNNING,
|
||||
MHI_DEV_CH_STATE_SUSPENDED,
|
||||
MHI_DEV_CH_STATE_STOP,
|
||||
MHI_DEV_CH_STATE_ERROR,
|
||||
MHI_DEV_CH_STATE_RESERVED,
|
||||
MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF
|
||||
};
|
||||
|
||||
/* Channel type */
|
||||
enum mhi_dev_ch_ctx_type {
|
||||
MHI_DEV_CH_TYPE_NONE,
|
||||
MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL,
|
||||
MHI_DEV_CH_TYPE_INBOUND_CHANNEL,
|
||||
MHI_DEV_CH_RESERVED
|
||||
};
|
||||
|
||||
/* Channel context type */
|
||||
struct mhi_dev_ch_ctx {
|
||||
enum mhi_dev_ch_ctx_state ch_state;
|
||||
enum mhi_dev_ch_ctx_type ch_type;
|
||||
uint32_t err_indx;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_ring_element_type_id {
|
||||
MHI_DEV_RING_EL_INVALID = 0,
|
||||
MHI_DEV_RING_EL_NOOP = 1,
|
||||
MHI_DEV_RING_EL_TRANSFER = 2,
|
||||
MHI_DEV_RING_EL_RESET = 16,
|
||||
MHI_DEV_RING_EL_STOP = 17,
|
||||
MHI_DEV_RING_EL_START = 18,
|
||||
MHI_DEV_RING_EL_MHI_STATE_CHG = 32,
|
||||
MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33,
|
||||
MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34,
|
||||
MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64,
|
||||
MHI_DEV_RING_EL_UNDEF
|
||||
};
|
||||
|
||||
enum mhi_dev_ring_state {
|
||||
RING_STATE_UINT = 0,
|
||||
RING_STATE_IDLE,
|
||||
RING_STATE_PENDING,
|
||||
};
|
||||
|
||||
enum mhi_dev_ring_type {
|
||||
RING_TYPE_CMD = 0,
|
||||
RING_TYPE_ER,
|
||||
RING_TYPE_CH,
|
||||
RING_TYPE_INVAL
|
||||
};
|
||||
|
||||
/* Event context interrupt moderation */
|
||||
enum mhi_dev_evt_ctx_int_mod_timer {
|
||||
MHI_DEV_EVT_INT_MODERATION_DISABLED
|
||||
};
|
||||
|
||||
/* Event ring type */
|
||||
enum mhi_dev_evt_ctx_event_ring_type {
|
||||
MHI_DEV_EVT_TYPE_DEFAULT,
|
||||
MHI_DEV_EVT_TYPE_VALID,
|
||||
MHI_DEV_EVT_RESERVED
|
||||
};
|
||||
|
||||
/* Event ring context type */
|
||||
struct mhi_dev_ev_ctx {
|
||||
uint32_t res1:16;
|
||||
enum mhi_dev_evt_ctx_int_mod_timer intmodt:16;
|
||||
enum mhi_dev_evt_ctx_event_ring_type ertype;
|
||||
uint32_t msivec;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* Command context */
|
||||
struct mhi_dev_cmd_ctx {
|
||||
uint32_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* generic context */
|
||||
struct mhi_dev_gen_ctx {
|
||||
uint32_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* Transfer ring element */
|
||||
struct mhi_dev_transfer_ring_element {
|
||||
uint64_t data_buf_ptr;
|
||||
uint32_t len:16;
|
||||
uint32_t res1:16;
|
||||
uint32_t chain:1;
|
||||
uint32_t res2:7;
|
||||
uint32_t ieob:1;
|
||||
uint32_t ieot:1;
|
||||
uint32_t bei:1;
|
||||
uint32_t res3:5;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res4:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring element */
|
||||
/* Command ring No op command */
|
||||
struct mhi_dev_cmd_ring_op {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring reset channel command */
|
||||
struct mhi_dev_cmd_ring_reset_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring stop channel command */
|
||||
struct mhi_dev_cmd_ring_stop_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring start channel command */
|
||||
struct mhi_dev_cmd_ring_start_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t seqnum;
|
||||
uint32_t reliable:1;
|
||||
uint32_t res2:15;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_cmd_completion_code {
|
||||
MHI_CMD_COMPL_CODE_INVALID = 0,
|
||||
MHI_CMD_COMPL_CODE_SUCCESS = 1,
|
||||
MHI_CMD_COMPL_CODE_EOT = 2,
|
||||
MHI_CMD_COMPL_CODE_OVERFLOW = 3,
|
||||
MHI_CMD_COMPL_CODE_EOB = 4,
|
||||
MHI_CMD_COMPL_CODE_UNDEFINED = 16,
|
||||
MHI_CMD_COMPL_CODE_RING_EL = 17,
|
||||
MHI_CMD_COMPL_CODE_RES
|
||||
};
|
||||
|
||||
/* Event ring elements */
|
||||
/* Transfer completion event */
|
||||
struct mhi_dev_event_ring_transfer_completion {
|
||||
uint64_t ptr;
|
||||
uint32_t len:16;
|
||||
uint32_t res1:8;
|
||||
enum mhi_dev_cmd_completion_code code:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command completion event */
|
||||
struct mhi_dev_event_ring_cmd_completion {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_cmd_completion_code code:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_state {
|
||||
MHI_DEV_RESET_STATE = 0,
|
||||
MHI_DEV_READY_STATE,
|
||||
MHI_DEV_M0_STATE,
|
||||
MHI_DEV_M1_STATE,
|
||||
MHI_DEV_M2_STATE,
|
||||
MHI_DEV_M3_STATE,
|
||||
MHI_DEV_MAX_STATE,
|
||||
MHI_DEV_SYSERR_STATE = 0xff
|
||||
};
|
||||
|
||||
/* MHI state change event */
|
||||
struct mhi_dev_event_ring_state_change {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_state mhistate:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_execenv {
|
||||
MHI_DEV_SBL_EE = 1,
|
||||
MHI_DEV_AMSS_EE = 2,
|
||||
MHI_DEV_UNRESERVED
|
||||
};
|
||||
|
||||
/* EE state change event */
|
||||
struct mhi_dev_event_ring_ee_state_change {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_execenv execenv:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
/* Generic cmd to parse common details like type and channel id */
|
||||
struct mhi_dev_ring_generic {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_state mhistate:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
struct mhi_config {
|
||||
uint32_t mhi_reg_len;
|
||||
uint32_t version;
|
||||
uint32_t event_rings;
|
||||
uint32_t channels;
|
||||
uint32_t chdb_offset;
|
||||
uint32_t erdb_offset;
|
||||
};
|
||||
|
||||
#define NUM_CHANNELS 128
|
||||
#define HW_CHANNEL_BASE 100
|
||||
#define HW_CHANNEL_END 107
|
||||
#define MHI_ENV_VALUE 2
|
||||
#define MHI_MASK_ROWS_CH_EV_DB 4
|
||||
#define TRB_MAX_DATA_SIZE 8192
|
||||
#define MHI_CTRL_STATE 25
|
||||
#define IPA_DMA_SYNC 1
|
||||
#define IPA_DMA_ASYNC 0
|
||||
|
||||
/*maximum trasnfer completion events buffer*/
|
||||
#define MAX_TR_EVENTS 50
|
||||
/*maximum event requests */
|
||||
#define MHI_MAX_EVT_REQ 50
|
||||
|
||||
/* Possible ring element types */
|
||||
union mhi_dev_ring_element_type {
|
||||
struct mhi_dev_cmd_ring_op cmd_no_op;
|
||||
struct mhi_dev_cmd_ring_reset_channel_cmd cmd_reset;
|
||||
struct mhi_dev_cmd_ring_stop_channel_cmd cmd_stop;
|
||||
struct mhi_dev_cmd_ring_start_channel_cmd cmd_start;
|
||||
struct mhi_dev_transfer_ring_element tre;
|
||||
struct mhi_dev_event_ring_transfer_completion evt_tr_comp;
|
||||
struct mhi_dev_event_ring_cmd_completion evt_cmd_comp;
|
||||
struct mhi_dev_event_ring_state_change evt_state_change;
|
||||
struct mhi_dev_event_ring_ee_state_change evt_ee_state;
|
||||
struct mhi_dev_ring_generic generic;
|
||||
};
|
||||
|
||||
/* Transfer ring element type */
|
||||
union mhi_dev_ring_ctx {
|
||||
struct mhi_dev_cmd_ctx cmd;
|
||||
struct mhi_dev_ev_ctx ev;
|
||||
struct mhi_dev_ch_ctx ch;
|
||||
struct mhi_dev_gen_ctx generic;
|
||||
};
|
||||
|
||||
/* MHI host Control and data address region */
|
||||
struct mhi_host_addr {
|
||||
uint32_t ctrl_base_lsb;
|
||||
uint32_t ctrl_base_msb;
|
||||
uint32_t ctrl_limit_lsb;
|
||||
uint32_t ctrl_limit_msb;
|
||||
uint32_t data_base_lsb;
|
||||
uint32_t data_base_msb;
|
||||
uint32_t data_limit_lsb;
|
||||
uint32_t data_limit_msb;
|
||||
};
|
||||
|
||||
/* MHI physical and virtual address region */
|
||||
struct mhi_meminfo {
|
||||
struct device *dev;
|
||||
uintptr_t pa_aligned;
|
||||
uintptr_t pa_unaligned;
|
||||
uintptr_t va_aligned;
|
||||
uintptr_t va_unaligned;
|
||||
uintptr_t size;
|
||||
};
|
||||
|
||||
struct mhi_addr {
|
||||
uint64_t host_pa;
|
||||
uintptr_t device_pa;
|
||||
uintptr_t device_va;
|
||||
size_t size;
|
||||
dma_addr_t phy_addr;
|
||||
void *virt_addr;
|
||||
bool use_ipa_dma;
|
||||
};
|
||||
|
||||
struct mhi_interrupt_state {
|
||||
uint32_t mask;
|
||||
uint32_t status;
|
||||
};
|
||||
|
||||
enum mhi_dev_channel_state {
|
||||
MHI_DEV_CH_UNINT,
|
||||
MHI_DEV_CH_STARTED,
|
||||
MHI_DEV_CH_PENDING_START,
|
||||
MHI_DEV_CH_PENDING_STOP,
|
||||
MHI_DEV_CH_STOPPED,
|
||||
MHI_DEV_CH_CLOSED,
|
||||
};
|
||||
|
||||
enum mhi_dev_ch_operation {
|
||||
MHI_DEV_OPEN_CH,
|
||||
MHI_DEV_CLOSE_CH,
|
||||
MHI_DEV_READ_CH,
|
||||
MHI_DEV_READ_WR,
|
||||
MHI_DEV_POLL,
|
||||
};
|
||||
|
||||
enum mhi_ctrl_info {
|
||||
MHI_STATE_CONFIGURED = 0,
|
||||
MHI_STATE_CONNECTED = 1,
|
||||
MHI_STATE_DISCONNECTED = 2,
|
||||
MHI_STATE_INVAL,
|
||||
};
|
||||
|
||||
enum mhi_dev_tr_compl_evt_type {
|
||||
SEND_EVENT_BUFFER,
|
||||
SEND_EVENT_RD_OFFSET,
|
||||
};
|
||||
|
||||
enum mhi_dev_transfer_type {
|
||||
MHI_DEV_DMA_SYNC,
|
||||
MHI_DEV_DMA_ASYNC,
|
||||
};
|
||||
|
||||
#if 0
|
||||
/* SW channel client list */
|
||||
enum mhi_client_channel {
|
||||
MHI_CLIENT_LOOPBACK_OUT = 0,
|
||||
MHI_CLIENT_LOOPBACK_IN = 1,
|
||||
MHI_CLIENT_SAHARA_OUT = 2,
|
||||
MHI_CLIENT_SAHARA_IN = 3,
|
||||
MHI_CLIENT_DIAG_OUT = 4,
|
||||
MHI_CLIENT_DIAG_IN = 5,
|
||||
MHI_CLIENT_SSR_OUT = 6,
|
||||
MHI_CLIENT_SSR_IN = 7,
|
||||
MHI_CLIENT_QDSS_OUT = 8,
|
||||
MHI_CLIENT_QDSS_IN = 9,
|
||||
MHI_CLIENT_EFS_OUT = 10,
|
||||
MHI_CLIENT_EFS_IN = 11,
|
||||
MHI_CLIENT_MBIM_OUT = 12,
|
||||
MHI_CLIENT_MBIM_IN = 13,
|
||||
MHI_CLIENT_QMI_OUT = 14,
|
||||
MHI_CLIENT_QMI_IN = 15,
|
||||
MHI_CLIENT_IP_CTRL_0_OUT = 16,
|
||||
MHI_CLIENT_IP_CTRL_0_IN = 17,
|
||||
MHI_CLIENT_IP_CTRL_1_OUT = 18,
|
||||
MHI_CLIENT_IP_CTRL_1_IN = 19,
|
||||
MHI_CLIENT_DCI_OUT = 20,
|
||||
MHI_CLIENT_DCI_IN = 21,
|
||||
MHI_CLIENT_IP_CTRL_3_OUT = 22,
|
||||
MHI_CLIENT_IP_CTRL_3_IN = 23,
|
||||
MHI_CLIENT_IP_CTRL_4_OUT = 24,
|
||||
MHI_CLIENT_IP_CTRL_4_IN = 25,
|
||||
MHI_CLIENT_IP_CTRL_5_OUT = 26,
|
||||
MHI_CLIENT_IP_CTRL_5_IN = 27,
|
||||
MHI_CLIENT_IP_CTRL_6_OUT = 28,
|
||||
MHI_CLIENT_IP_CTRL_6_IN = 29,
|
||||
MHI_CLIENT_IP_CTRL_7_OUT = 30,
|
||||
MHI_CLIENT_IP_CTRL_7_IN = 31,
|
||||
MHI_CLIENT_DUN_OUT = 32,
|
||||
MHI_CLIENT_DUN_IN = 33,
|
||||
MHI_CLIENT_IP_SW_0_OUT = 34,
|
||||
MHI_CLIENT_IP_SW_0_IN = 35,
|
||||
MHI_CLIENT_IP_SW_1_OUT = 36,
|
||||
MHI_CLIENT_IP_SW_1_IN = 37,
|
||||
MHI_CLIENT_IP_SW_2_OUT = 38,
|
||||
MHI_CLIENT_IP_SW_2_IN = 39,
|
||||
MHI_CLIENT_IP_SW_3_OUT = 40,
|
||||
MHI_CLIENT_IP_SW_3_IN = 41,
|
||||
MHI_CLIENT_CSVT_OUT = 42,
|
||||
MHI_CLIENT_CSVT_IN = 43,
|
||||
MHI_CLIENT_SMCT_OUT = 44,
|
||||
MHI_CLIENT_SMCT_IN = 45,
|
||||
MHI_CLIENT_IP_SW_4_OUT = 46,
|
||||
MHI_CLIENT_IP_SW_4_IN = 47,
|
||||
MHI_MAX_SOFTWARE_CHANNELS = 48,
|
||||
MHI_CLIENT_TEST_OUT = 60,
|
||||
MHI_CLIENT_TEST_IN = 61,
|
||||
MHI_CLIENT_RESERVED_1_LOWER = 62,
|
||||
MHI_CLIENT_RESERVED_1_UPPER = 99,
|
||||
MHI_CLIENT_IP_HW_0_OUT = 100,
|
||||
MHI_CLIENT_IP_HW_0_IN = 101,
|
||||
MHI_CLIENT_RESERVED_2_LOWER = 102,
|
||||
MHI_CLIENT_RESERVED_2_UPPER = 127,
|
||||
MHI_MAX_CHANNELS = 102,
|
||||
};
|
||||
#endif
|
||||
#endif /* _SDX20_MHI_H_ */
|
||||
#ifndef __SDX20_MHI_H
|
||||
#define __SDX20_MHI_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* MHI control data structures alloted by the host, including
|
||||
* channel context array, event context array, command context and rings */
|
||||
|
||||
/* Channel context state */
|
||||
enum mhi_dev_ch_ctx_state {
|
||||
MHI_DEV_CH_STATE_DISABLED,
|
||||
MHI_DEV_CH_STATE_ENABLED,
|
||||
MHI_DEV_CH_STATE_RUNNING,
|
||||
MHI_DEV_CH_STATE_SUSPENDED,
|
||||
MHI_DEV_CH_STATE_STOP,
|
||||
MHI_DEV_CH_STATE_ERROR,
|
||||
MHI_DEV_CH_STATE_RESERVED,
|
||||
MHI_DEV_CH_STATE_32BIT = 0x7FFFFFFF
|
||||
};
|
||||
|
||||
/* Channel type */
|
||||
enum mhi_dev_ch_ctx_type {
|
||||
MHI_DEV_CH_TYPE_NONE,
|
||||
MHI_DEV_CH_TYPE_OUTBOUND_CHANNEL,
|
||||
MHI_DEV_CH_TYPE_INBOUND_CHANNEL,
|
||||
MHI_DEV_CH_RESERVED
|
||||
};
|
||||
|
||||
/* Channel context type */
|
||||
struct mhi_dev_ch_ctx {
|
||||
enum mhi_dev_ch_ctx_state ch_state;
|
||||
enum mhi_dev_ch_ctx_type ch_type;
|
||||
uint32_t err_indx;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_ring_element_type_id {
|
||||
MHI_DEV_RING_EL_INVALID = 0,
|
||||
MHI_DEV_RING_EL_NOOP = 1,
|
||||
MHI_DEV_RING_EL_TRANSFER = 2,
|
||||
MHI_DEV_RING_EL_RESET = 16,
|
||||
MHI_DEV_RING_EL_STOP = 17,
|
||||
MHI_DEV_RING_EL_START = 18,
|
||||
MHI_DEV_RING_EL_MHI_STATE_CHG = 32,
|
||||
MHI_DEV_RING_EL_CMD_COMPLETION_EVT = 33,
|
||||
MHI_DEV_RING_EL_TRANSFER_COMPLETION_EVENT = 34,
|
||||
MHI_DEV_RING_EL_EE_STATE_CHANGE_NOTIFY = 64,
|
||||
MHI_DEV_RING_EL_UNDEF
|
||||
};
|
||||
|
||||
enum mhi_dev_ring_state {
|
||||
RING_STATE_UINT = 0,
|
||||
RING_STATE_IDLE,
|
||||
RING_STATE_PENDING,
|
||||
};
|
||||
|
||||
enum mhi_dev_ring_type {
|
||||
RING_TYPE_CMD = 0,
|
||||
RING_TYPE_ER,
|
||||
RING_TYPE_CH,
|
||||
RING_TYPE_INVAL
|
||||
};
|
||||
|
||||
/* Event context interrupt moderation */
|
||||
enum mhi_dev_evt_ctx_int_mod_timer {
|
||||
MHI_DEV_EVT_INT_MODERATION_DISABLED
|
||||
};
|
||||
|
||||
/* Event ring type */
|
||||
enum mhi_dev_evt_ctx_event_ring_type {
|
||||
MHI_DEV_EVT_TYPE_DEFAULT,
|
||||
MHI_DEV_EVT_TYPE_VALID,
|
||||
MHI_DEV_EVT_RESERVED
|
||||
};
|
||||
|
||||
/* Event ring context type */
|
||||
struct mhi_dev_ev_ctx {
|
||||
uint32_t res1:16;
|
||||
enum mhi_dev_evt_ctx_int_mod_timer intmodt:16;
|
||||
enum mhi_dev_evt_ctx_event_ring_type ertype;
|
||||
uint32_t msivec;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* Command context */
|
||||
struct mhi_dev_cmd_ctx {
|
||||
uint32_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* generic context */
|
||||
struct mhi_dev_gen_ctx {
|
||||
uint32_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3;
|
||||
uint64_t rbase;
|
||||
uint64_t rlen;
|
||||
uint64_t rp;
|
||||
uint64_t wp;
|
||||
} __packed;
|
||||
|
||||
/* Transfer ring element */
|
||||
struct mhi_dev_transfer_ring_element {
|
||||
uint64_t data_buf_ptr;
|
||||
uint32_t len:16;
|
||||
uint32_t res1:16;
|
||||
uint32_t chain:1;
|
||||
uint32_t res2:7;
|
||||
uint32_t ieob:1;
|
||||
uint32_t ieot:1;
|
||||
uint32_t bei:1;
|
||||
uint32_t res3:5;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res4:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring element */
|
||||
/* Command ring No op command */
|
||||
struct mhi_dev_cmd_ring_op {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring reset channel command */
|
||||
struct mhi_dev_cmd_ring_reset_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring stop channel command */
|
||||
struct mhi_dev_cmd_ring_stop_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t res2;
|
||||
uint32_t res3:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command ring start channel command */
|
||||
struct mhi_dev_cmd_ring_start_channel_cmd {
|
||||
uint64_t res1;
|
||||
uint32_t seqnum;
|
||||
uint32_t reliable:1;
|
||||
uint32_t res2:15;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_cmd_completion_code {
|
||||
MHI_CMD_COMPL_CODE_INVALID = 0,
|
||||
MHI_CMD_COMPL_CODE_SUCCESS = 1,
|
||||
MHI_CMD_COMPL_CODE_EOT = 2,
|
||||
MHI_CMD_COMPL_CODE_OVERFLOW = 3,
|
||||
MHI_CMD_COMPL_CODE_EOB = 4,
|
||||
MHI_CMD_COMPL_CODE_UNDEFINED = 16,
|
||||
MHI_CMD_COMPL_CODE_RING_EL = 17,
|
||||
MHI_CMD_COMPL_CODE_RES
|
||||
};
|
||||
|
||||
/* Event ring elements */
|
||||
/* Transfer completion event */
|
||||
struct mhi_dev_event_ring_transfer_completion {
|
||||
uint64_t ptr;
|
||||
uint32_t len:16;
|
||||
uint32_t res1:8;
|
||||
enum mhi_dev_cmd_completion_code code:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
/* Command completion event */
|
||||
struct mhi_dev_event_ring_cmd_completion {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_cmd_completion_code code:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_state {
|
||||
MHI_DEV_RESET_STATE = 0,
|
||||
MHI_DEV_READY_STATE,
|
||||
MHI_DEV_M0_STATE,
|
||||
MHI_DEV_M1_STATE,
|
||||
MHI_DEV_M2_STATE,
|
||||
MHI_DEV_M3_STATE,
|
||||
MHI_DEV_MAX_STATE,
|
||||
MHI_DEV_SYSERR_STATE = 0xff
|
||||
};
|
||||
|
||||
/* MHI state change event */
|
||||
struct mhi_dev_event_ring_state_change {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_state mhistate:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
enum mhi_dev_execenv {
|
||||
MHI_DEV_SBL_EE = 1,
|
||||
MHI_DEV_AMSS_EE = 2,
|
||||
MHI_DEV_UNRESERVED
|
||||
};
|
||||
|
||||
/* EE state change event */
|
||||
struct mhi_dev_event_ring_ee_state_change {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_execenv execenv:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t res3:8;
|
||||
} __packed;
|
||||
|
||||
/* Generic cmd to parse common details like type and channel id */
|
||||
struct mhi_dev_ring_generic {
|
||||
uint64_t ptr;
|
||||
uint32_t res1:24;
|
||||
enum mhi_dev_state mhistate:8;
|
||||
uint32_t res2:16;
|
||||
enum mhi_dev_ring_element_type_id type:8;
|
||||
uint32_t chid:8;
|
||||
} __packed;
|
||||
|
||||
struct mhi_config {
|
||||
uint32_t mhi_reg_len;
|
||||
uint32_t version;
|
||||
uint32_t event_rings;
|
||||
uint32_t channels;
|
||||
uint32_t chdb_offset;
|
||||
uint32_t erdb_offset;
|
||||
};
|
||||
|
||||
#define NUM_CHANNELS 128
|
||||
#define HW_CHANNEL_BASE 100
|
||||
#define HW_CHANNEL_END 107
|
||||
#define MHI_ENV_VALUE 2
|
||||
#define MHI_MASK_ROWS_CH_EV_DB 4
|
||||
#define TRB_MAX_DATA_SIZE 8192
|
||||
#define MHI_CTRL_STATE 25
|
||||
#define IPA_DMA_SYNC 1
|
||||
#define IPA_DMA_ASYNC 0
|
||||
|
||||
/*maximum trasnfer completion events buffer*/
|
||||
#define MAX_TR_EVENTS 50
|
||||
/*maximum event requests */
|
||||
#define MHI_MAX_EVT_REQ 50
|
||||
|
||||
/* Possible ring element types */
|
||||
union mhi_dev_ring_element_type {
|
||||
struct mhi_dev_cmd_ring_op cmd_no_op;
|
||||
struct mhi_dev_cmd_ring_reset_channel_cmd cmd_reset;
|
||||
struct mhi_dev_cmd_ring_stop_channel_cmd cmd_stop;
|
||||
struct mhi_dev_cmd_ring_start_channel_cmd cmd_start;
|
||||
struct mhi_dev_transfer_ring_element tre;
|
||||
struct mhi_dev_event_ring_transfer_completion evt_tr_comp;
|
||||
struct mhi_dev_event_ring_cmd_completion evt_cmd_comp;
|
||||
struct mhi_dev_event_ring_state_change evt_state_change;
|
||||
struct mhi_dev_event_ring_ee_state_change evt_ee_state;
|
||||
struct mhi_dev_ring_generic generic;
|
||||
};
|
||||
|
||||
/* Transfer ring element type */
|
||||
union mhi_dev_ring_ctx {
|
||||
struct mhi_dev_cmd_ctx cmd;
|
||||
struct mhi_dev_ev_ctx ev;
|
||||
struct mhi_dev_ch_ctx ch;
|
||||
struct mhi_dev_gen_ctx generic;
|
||||
};
|
||||
|
||||
/* MHI host Control and data address region */
|
||||
struct mhi_host_addr {
|
||||
uint32_t ctrl_base_lsb;
|
||||
uint32_t ctrl_base_msb;
|
||||
uint32_t ctrl_limit_lsb;
|
||||
uint32_t ctrl_limit_msb;
|
||||
uint32_t data_base_lsb;
|
||||
uint32_t data_base_msb;
|
||||
uint32_t data_limit_lsb;
|
||||
uint32_t data_limit_msb;
|
||||
};
|
||||
|
||||
/* MHI physical and virtual address region */
|
||||
struct mhi_meminfo {
|
||||
struct device *dev;
|
||||
uintptr_t pa_aligned;
|
||||
uintptr_t pa_unaligned;
|
||||
uintptr_t va_aligned;
|
||||
uintptr_t va_unaligned;
|
||||
uintptr_t size;
|
||||
};
|
||||
|
||||
struct mhi_addr {
|
||||
uint64_t host_pa;
|
||||
uintptr_t device_pa;
|
||||
uintptr_t device_va;
|
||||
size_t size;
|
||||
dma_addr_t phy_addr;
|
||||
void *virt_addr;
|
||||
bool use_ipa_dma;
|
||||
};
|
||||
|
||||
struct mhi_interrupt_state {
|
||||
uint32_t mask;
|
||||
uint32_t status;
|
||||
};
|
||||
|
||||
enum mhi_dev_channel_state {
|
||||
MHI_DEV_CH_UNINT,
|
||||
MHI_DEV_CH_STARTED,
|
||||
MHI_DEV_CH_PENDING_START,
|
||||
MHI_DEV_CH_PENDING_STOP,
|
||||
MHI_DEV_CH_STOPPED,
|
||||
MHI_DEV_CH_CLOSED,
|
||||
};
|
||||
|
||||
enum mhi_dev_ch_operation {
|
||||
MHI_DEV_OPEN_CH,
|
||||
MHI_DEV_CLOSE_CH,
|
||||
MHI_DEV_READ_CH,
|
||||
MHI_DEV_READ_WR,
|
||||
MHI_DEV_POLL,
|
||||
};
|
||||
|
||||
enum mhi_ctrl_info {
|
||||
MHI_STATE_CONFIGURED = 0,
|
||||
MHI_STATE_CONNECTED = 1,
|
||||
MHI_STATE_DISCONNECTED = 2,
|
||||
MHI_STATE_INVAL,
|
||||
};
|
||||
|
||||
enum mhi_dev_tr_compl_evt_type {
|
||||
SEND_EVENT_BUFFER,
|
||||
SEND_EVENT_RD_OFFSET,
|
||||
};
|
||||
|
||||
enum mhi_dev_transfer_type {
|
||||
MHI_DEV_DMA_SYNC,
|
||||
MHI_DEV_DMA_ASYNC,
|
||||
};
|
||||
|
||||
#if 0
|
||||
/* SW channel client list */
|
||||
enum mhi_client_channel {
|
||||
MHI_CLIENT_LOOPBACK_OUT = 0,
|
||||
MHI_CLIENT_LOOPBACK_IN = 1,
|
||||
MHI_CLIENT_SAHARA_OUT = 2,
|
||||
MHI_CLIENT_SAHARA_IN = 3,
|
||||
MHI_CLIENT_DIAG_OUT = 4,
|
||||
MHI_CLIENT_DIAG_IN = 5,
|
||||
MHI_CLIENT_SSR_OUT = 6,
|
||||
MHI_CLIENT_SSR_IN = 7,
|
||||
MHI_CLIENT_QDSS_OUT = 8,
|
||||
MHI_CLIENT_QDSS_IN = 9,
|
||||
MHI_CLIENT_EFS_OUT = 10,
|
||||
MHI_CLIENT_EFS_IN = 11,
|
||||
MHI_CLIENT_MBIM_OUT = 12,
|
||||
MHI_CLIENT_MBIM_IN = 13,
|
||||
MHI_CLIENT_QMI_OUT = 14,
|
||||
MHI_CLIENT_QMI_IN = 15,
|
||||
MHI_CLIENT_IP_CTRL_0_OUT = 16,
|
||||
MHI_CLIENT_IP_CTRL_0_IN = 17,
|
||||
MHI_CLIENT_IP_CTRL_1_OUT = 18,
|
||||
MHI_CLIENT_IP_CTRL_1_IN = 19,
|
||||
MHI_CLIENT_DCI_OUT = 20,
|
||||
MHI_CLIENT_DCI_IN = 21,
|
||||
MHI_CLIENT_IP_CTRL_3_OUT = 22,
|
||||
MHI_CLIENT_IP_CTRL_3_IN = 23,
|
||||
MHI_CLIENT_IP_CTRL_4_OUT = 24,
|
||||
MHI_CLIENT_IP_CTRL_4_IN = 25,
|
||||
MHI_CLIENT_IP_CTRL_5_OUT = 26,
|
||||
MHI_CLIENT_IP_CTRL_5_IN = 27,
|
||||
MHI_CLIENT_IP_CTRL_6_OUT = 28,
|
||||
MHI_CLIENT_IP_CTRL_6_IN = 29,
|
||||
MHI_CLIENT_IP_CTRL_7_OUT = 30,
|
||||
MHI_CLIENT_IP_CTRL_7_IN = 31,
|
||||
MHI_CLIENT_DUN_OUT = 32,
|
||||
MHI_CLIENT_DUN_IN = 33,
|
||||
MHI_CLIENT_IP_SW_0_OUT = 34,
|
||||
MHI_CLIENT_IP_SW_0_IN = 35,
|
||||
MHI_CLIENT_IP_SW_1_OUT = 36,
|
||||
MHI_CLIENT_IP_SW_1_IN = 37,
|
||||
MHI_CLIENT_IP_SW_2_OUT = 38,
|
||||
MHI_CLIENT_IP_SW_2_IN = 39,
|
||||
MHI_CLIENT_IP_SW_3_OUT = 40,
|
||||
MHI_CLIENT_IP_SW_3_IN = 41,
|
||||
MHI_CLIENT_CSVT_OUT = 42,
|
||||
MHI_CLIENT_CSVT_IN = 43,
|
||||
MHI_CLIENT_SMCT_OUT = 44,
|
||||
MHI_CLIENT_SMCT_IN = 45,
|
||||
MHI_CLIENT_IP_SW_4_OUT = 46,
|
||||
MHI_CLIENT_IP_SW_4_IN = 47,
|
||||
MHI_MAX_SOFTWARE_CHANNELS = 48,
|
||||
MHI_CLIENT_TEST_OUT = 60,
|
||||
MHI_CLIENT_TEST_IN = 61,
|
||||
MHI_CLIENT_RESERVED_1_LOWER = 62,
|
||||
MHI_CLIENT_RESERVED_1_UPPER = 99,
|
||||
MHI_CLIENT_IP_HW_0_OUT = 100,
|
||||
MHI_CLIENT_IP_HW_0_IN = 101,
|
||||
MHI_CLIENT_RESERVED_2_LOWER = 102,
|
||||
MHI_CLIENT_RESERVED_2_UPPER = 127,
|
||||
MHI_MAX_CHANNELS = 102,
|
||||
};
|
||||
#endif
|
||||
#endif /* _SDX20_MHI_H_ */
|
||||
|
||||
@ -559,14 +559,9 @@ static int mhi_netdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
static void mhi_netdev_get_drvinfo (struct net_device *ndev, struct ethtool_drvinfo *info)
|
||||
{
|
||||
//struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev);
|
||||
/* strlcpy() is deprecated in kernel 6.8.0+, using strscpy instead */
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6,8,0))
|
||||
strlcpy(info->driver, "pcie_mhi", sizeof(info->driver));
|
||||
strlcpy(info->version, PCIE_MHI_DRIVER_VERSION, sizeof(info->version));
|
||||
#else
|
||||
strscpy(info->driver, "pcie_mhi", sizeof(info->driver));
|
||||
strscpy(info->version, PCIE_MHI_DRIVER_VERSION, sizeof(info->version));
|
||||
#endif
|
||||
|
||||
strlcpy (info->driver, "pcie_mhi", sizeof info->driver);
|
||||
strlcpy (info->version, PCIE_MHI_DRIVER_VERSION, sizeof info->version);
|
||||
}
|
||||
|
||||
static const struct ethtool_ops mhi_netdev_ethtool_ops = {
|
||||
|
||||
@ -63,8 +63,8 @@ struct rmnet_nss_cb {
|
||||
int (*nss_tx)(struct sk_buff *skb);
|
||||
};
|
||||
static struct rmnet_nss_cb __read_mostly *nss_cb = NULL;
|
||||
#if defined(CONFIG_PINCTRL_IPQ807x) || defined(CONFIG_PINCTRL_IPQ5018)
|
||||
#ifdef CONFIG_RMNET_DATA
|
||||
#if defined(CONFIG_PINCTRL_IPQ807x) || defined(CONFIG_PINCTRL_IPQ5018) || defined(CONFIG_PINCTRL_IPQ8074)
|
||||
//#ifdef CONFIG_RMNET_DATA //spf12.x have no macro defined, just for spf11.x
|
||||
#define CONFIG_QCA_NSS_DRV
|
||||
#define CONFIG_USE_RMNET_DATA_FOR_SKIP_MEMCPY
|
||||
/* define at qca/src/linux-4.4/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c */ //for spf11.x
|
||||
@ -72,9 +72,9 @@ static struct rmnet_nss_cb __read_mostly *nss_cb = NULL;
|
||||
/* set at qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c */
|
||||
/* need add DEPENDS:= kmod-rmnet-core in feeds/makefile */
|
||||
extern struct rmnet_nss_cb *rmnet_nss_callbacks __rcu __read_mostly;
|
||||
//#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
int mhi_netdev_use_xfer_type_dma(unsigned chan)
|
||||
{
|
||||
@ -2589,14 +2589,9 @@ static const struct net_device_ops mhi_netdev_ops_ip = {
|
||||
static void mhi_netdev_get_drvinfo (struct net_device *ndev, struct ethtool_drvinfo *info)
|
||||
{
|
||||
//struct mhi_netdev *mhi_netdev = ndev_to_mhi(ndev);
|
||||
/* strlcpy() is deprecated in kernel 6.8.0+, using strscpy instead */
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6,8,0))
|
||||
strlcpy(info->driver, "pcie_mhi", sizeof(info->driver));
|
||||
strlcpy(info->version, PCIE_MHI_DRIVER_VERSION, sizeof(info->version));
|
||||
#else
|
||||
strscpy(info->driver, "pcie_mhi", sizeof(info->driver));
|
||||
strscpy(info->version, PCIE_MHI_DRIVER_VERSION, sizeof(info->version));
|
||||
#endif
|
||||
|
||||
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 = {
|
||||
|
||||
@ -1,31 +1,31 @@
|
||||
root@imx6qsabresd:~# busybox microcom /dev/mhi_DUN
|
||||
[ 384.652992] [I][mhi_uci_open] Node open, ref counts 1
|
||||
[ 384.658144] [I][mhi_uci_open] Starting channel
|
||||
[ 384.662612] [I][__mhi_prepare_channel] Entered: preparing channel:32
|
||||
[ 384.680397] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 384.685890] [I][__mhi_prepare_channel] Chan:32 successfully moved to start state
|
||||
[ 384.693312] [I][__mhi_prepare_channel] Entered: preparing channel:33
|
||||
[ 384.708692] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 384.714324] [I][__mhi_prepare_channel] Chan:33 successfully moved to start state
|
||||
|
||||
RDY
|
||||
|
||||
+CFUN: 1
|
||||
|
||||
+CPIN: READY
|
||||
|
||||
+QUSIM: 1
|
||||
|
||||
+QIND: SMS DONE
|
||||
|
||||
+QIND: PB DONE
|
||||
ati
|
||||
Quectel
|
||||
EM20
|
||||
Revision: EM20GR01A01M4G
|
||||
|
||||
OK
|
||||
at+cpin?
|
||||
+CPIN: READY
|
||||
|
||||
OK
|
||||
root@imx6qsabresd:~# busybox microcom /dev/mhi_DUN
|
||||
[ 384.652992] [I][mhi_uci_open] Node open, ref counts 1
|
||||
[ 384.658144] [I][mhi_uci_open] Starting channel
|
||||
[ 384.662612] [I][__mhi_prepare_channel] Entered: preparing channel:32
|
||||
[ 384.680397] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 384.685890] [I][__mhi_prepare_channel] Chan:32 successfully moved to start state
|
||||
[ 384.693312] [I][__mhi_prepare_channel] Entered: preparing channel:33
|
||||
[ 384.708692] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 384.714324] [I][__mhi_prepare_channel] Chan:33 successfully moved to start state
|
||||
|
||||
RDY
|
||||
|
||||
+CFUN: 1
|
||||
|
||||
+CPIN: READY
|
||||
|
||||
+QUSIM: 1
|
||||
|
||||
+QIND: SMS DONE
|
||||
|
||||
+QIND: PB DONE
|
||||
ati
|
||||
Quectel
|
||||
EM20
|
||||
Revision: EM20GR01A01M4G
|
||||
|
||||
OK
|
||||
at+cpin?
|
||||
+CPIN: READY
|
||||
|
||||
OK
|
||||
|
||||
@ -1,145 +1,145 @@
|
||||
root@OpenWrt:~# insmod pcie_mhi.ko mhi_mbim_enabled=1
|
||||
root@OpenWrt:~# dmesg | grep mhi
|
||||
[ 65.587160] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.6
|
||||
[ 65.597089] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306
|
||||
[ 65.602250] mhi_q 0000:01:00.0: BAR 0: assigned [mem 0x20300000-0x20300fff 64bit]
|
||||
[ 65.611690] mhi_q 0000:01:00.0: enabling device (0140 -> 0142)
|
||||
[ 65.619307] [I][mhi_init_pci_dev] msi_required = 5, msi_allocated = 5, msi_irq = 63
|
||||
[ 65.619327] [I][mhi_power_up] dev_state:RESET
|
||||
[ 65.619331] [I][mhi_async_power_up] Requested to power on
|
||||
[ 65.619449] [I][mhi_alloc_coherent] size = 114688, dma_handle = 6fca0000
|
||||
[ 65.619462] [I][mhi_init_dev_ctxt] mhi_ctxt->ctrl_seg = c221e000
|
||||
[ 65.619731] [I][mhi_async_power_up] dev_state:RESET ee:AMSS
|
||||
[ 65.619747] [I][mhi_pm_st_worker] Transition to state:READY
|
||||
[ 65.619760] [I][mhi_pm_st_worker] INVALID_EE -> AMSS
|
||||
[ 65.619764] [I][mhi_ready_state_transition] Waiting to enter READY state
|
||||
[ 65.619885] [I][mhi_async_power_up] Power on setup success
|
||||
[ 65.619897] [I][mhi_pci_probe] Return successful
|
||||
[ 65.665114] [I][mhi_ready_state_transition] Device in READY State
|
||||
[ 65.665125] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, pm_state:POR
|
||||
[ 65.665131] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, INVALID_EE
|
||||
[ 65.665133] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:POR
|
||||
[ 65.665137] [I][mhi_init_mmio] Initializing MMIO
|
||||
[ 65.665142] [I][mhi_init_mmio] CHDBOFF:0x300
|
||||
[ 65.665151] [I][mhi_init_mmio] ERDBOFF:0x700
|
||||
[ 65.665156] [I][mhi_init_mmio] Programming all MMIO values.
|
||||
[ 65.786283] [I][mhi_dump_tre] carl_ev evt_state_change mhistate=2
|
||||
[ 65.786289] [I][mhi_process_ctrl_ev_ring] MHI state change event to state:M0
|
||||
[ 65.786295] [I][mhi_pm_m0_transition] Entered With State:READY PM_STATE:POR
|
||||
[ 65.786300] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:M0
|
||||
[ 65.789734] [I][mhi_dump_tre] carl_ev evt_ee_state execenv=2
|
||||
[ 65.789739] [I][mhi_process_ctrl_ev_ring] MHI EE received event:AMSS
|
||||
[ 65.789756] [I][mhi_pm_st_worker] Transition to state:MISSION MODE
|
||||
[ 65.789767] [I][mhi_pm_st_worker] INVALID_EE -> AMSS
|
||||
[ 65.789771] [I][mhi_pm_mission_mode_transition] Processing Mission Mode Transition
|
||||
[ 65.789787] [I][mhi_init_timesync] No timesync capability found
|
||||
[ 65.789791] [I][mhi_pm_mission_mode_transition] Adding new devices
|
||||
[ 65.790570] [I][mhi_dtr_probe] Enter for DTR control channel
|
||||
[ 65.790577] [I][__mhi_prepare_channel] Entered: preparing channel:18
|
||||
[ 65.797036] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 65.797051] [I][__mhi_prepare_channel] Chan:18 successfully moved to start state
|
||||
[ 65.797055] [I][__mhi_prepare_channel] Entered: preparing channel:19
|
||||
[ 65.802457] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 65.802469] [I][__mhi_prepare_channel] Chan:19 successfully moved to start state
|
||||
[ 65.802485] [I][mhi_dtr_probe] Exit with ret:0
|
||||
[ 65.802748] [I][mhi_netdev_enable_iface] Prepare the channels for transfer
|
||||
[ 65.802772] [I][__mhi_prepare_channel] Entered: preparing channel:100
|
||||
[ 65.825279] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 65.825293] [I][__mhi_prepare_channel] Chan:100 successfully moved to start state
|
||||
[ 65.825297] [I][__mhi_prepare_channel] Entered: preparing channel:101
|
||||
[ 65.835565] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 65.835578] [I][__mhi_prepare_channel] Chan:101 successfully moved to start state
|
||||
[ 65.839141] [I][mhi_netdev_enable_iface] Exited.
|
||||
[ 65.839875] rmnet_vnd_register_device(rmnet_mhi0.1)=0
|
||||
[ 65.843278] net rmnet_mhi0 rmnet_mhi0.1: NSS context created
|
||||
[ 65.861808] [I][mhi_pm_mission_mode_transition] Exit with ret:0
|
||||
[ 68.625595] [I][__mhi_prepare_channel] Entered: preparing channel:12
|
||||
[ 68.634610] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 68.634622] [I][__mhi_prepare_channel] Chan:12 successfully moved to start state
|
||||
[ 68.634625] [I][__mhi_prepare_channel] Entered: preparing channel:13
|
||||
[ 68.644978] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 68.644987] [I][__mhi_prepare_channel] Chan:13 successfully moved to start state
|
||||
[ 69.170666] net rmnet_mhi0: link_state 0x0 -> 0x1
|
||||
[ 69.177035] [I][mhi_netdev_open] Opened net dev interface
|
||||
[ 71.655431] [I][mhi_netdev_open] Opened net dev interface
|
||||
|
||||
root@OpenWrt:~# ./quectel-CM &
|
||||
[04-02_04:14:12:134] Quectel_QConnectManager_Linux_V1.6.0.5
|
||||
[04-02_04:14:12:134] Find /sys/bus/usb/devices/4-1 idVendor=0x2c7c idProduct=0x800, bus=0x004, dev=0x002
|
||||
[04-02_04:14:12:135] network interface '' or qmidev '' is not exist
|
||||
[04-02_04:14:12:135] netcard driver = pcie_mhi, driver version = V1.3.0.6
|
||||
[04-02_04:14:12:135] Modem works in MBIM mode
|
||||
[04-02_04:14:12:135] apn (null), user (null), passwd (null), auth 0
|
||||
[04-02_04:14:12:135] IP Proto MBIMContextIPTypeIPv4
|
||||
[04-02_04:14:12:154] mbim_read_thread is created
|
||||
sh: can't create /sys/class/net/rmnet_mhi0/mbim/link_state: nonexistent directory
|
||||
[04-02_04:14:12:156] system(echo 0 > /sys/class/net/rmnet_mhi0/mbim/link_state)=256
|
||||
[04-02_04:14:12:185] system(ip address flush dev rmnet_mhi0)=0
|
||||
[04-02_04:14:12:187] system(ip link set dev rmnet_mhi0 down)=0
|
||||
[04-02_04:14:12:188] mbim_open_device()
|
||||
[04-02_04:14:12:605] mbim_device_caps_query()
|
||||
[04-02_04:14:12:610] DeviceId: 869710030002905
|
||||
[04-02_04:14:12:610] HardwareInfo: 0
|
||||
[04-02_04:14:12:610] mbim_set_radio_state( 1 )
|
||||
[04-02_04:14:12:613] HwRadioState: 1, SwRadioState: 1
|
||||
[04-02_04:14:12:613] mbim_subscriber_status_query()
|
||||
[04-02_04:14:12:620] SubscriberReadyState NotInitialized -> Initialized
|
||||
[04-02_04:14:12:620] mbim_register_state_query()
|
||||
[04-02_04:14:12:625] RegisterState Unknown -> Home
|
||||
[04-02_04:14:12:625] mbim_packet_service_query()
|
||||
[04-02_04:14:12:629] PacketServiceState Unknown -> Attached
|
||||
[04-02_04:14:12:629] mbim_query_connect(sessionID=0)
|
||||
[04-02_04:14:12:633] ActivationState Unknown -> Deactivated
|
||||
[04-02_04:14:12:633] mbim_set_connect(onoff=1, sessionID=0)
|
||||
[ 69.170666] net rmnet_mhi0: link_state 0x0 -> 0x1
|
||||
[04-02_04:14:12:680] ActivationState Deactivated -> Activated
|
||||
[ 69.177035] [I][mhi_netdev_open] Opened net dev interface
|
||||
[04-02_04:14:12:680] mbim_ip_config(sessionID=0)
|
||||
[04-02_04:14:12:683] < SessionId = 0
|
||||
[04-02_04:14:12:683] < IPv4ConfigurationAvailable = 0xf
|
||||
[04-02_04:14:12:683] < IPv6ConfigurationAvailable = 0x0
|
||||
[04-02_04:14:12:683] < IPv4AddressCount = 0x1
|
||||
[04-02_04:14:12:683] < IPv4AddressOffset = 0x3c
|
||||
[04-02_04:14:12:683] < IPv6AddressCount = 0x0
|
||||
[04-02_04:14:12:683] < IPv6AddressOffset = 0x0
|
||||
[04-02_04:14:12:683] < IPv4 = 10.129.59.93/30
|
||||
[04-02_04:14:12:683] < gw = 10.129.59.94
|
||||
[04-02_04:14:12:683] < dns1 = 211.138.180.2
|
||||
[04-02_04:14:12:683] < dns2 = 211.138.180.3
|
||||
[04-02_04:14:12:683] < ipv4 mtu = 1500
|
||||
sh: can't create /sys/class/net/rmnet_mhi0/mbim/link_state: nonexistent directory
|
||||
[04-02_04:14:12:684] system(echo 1 > /sys/class/net/rmnet_mhi0/mbim/link_state)=256
|
||||
[04-02_04:14:12:689] system(ip link set dev rmnet_mhi0 up)=0
|
||||
[04-02_04:14:12:692] system(ip -4 address flush dev rmnet_mhi0)=0
|
||||
[04-02_04:14:12:694] system(ip -4 address add 10.129.59.93/30 dev rmnet_mhi0)=0
|
||||
[04-02_04:14:12:697] system(ip -4 route add default via 10.129.59.94 dev rmnet_mhi0)=0
|
||||
[04-02_04:14:12:699] system(ip -4 link set dev rmnet_mhi0 mtu 1500)=0
|
||||
|
||||
root@OpenWrt:~# ifconfig rmnet_mhi0
|
||||
rmnet_mhi0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
|
||||
UP RUNNING NOARP MTU:1500 Metric:1
|
||||
RX packets:99379 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:176569 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:1528181052 (1.4 GiB) TX bytes:62467192 (59.5 MiB)
|
||||
|
||||
root@OpenWrt:~# ifconfig rmnet_mhi0.1
|
||||
rmnet_mhi0.1 Link encap:UNSPEC HWaddr 02-50-F4-00-00-00-00-00-00-00-00-00-00-00-00-00
|
||||
inet addr:10.129.59.93 Mask:255.255.255.252
|
||||
inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link
|
||||
UP RUNNING NOARP MTU:1500 Metric:1
|
||||
RX packets:1089360 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:176581 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:1521449058 (1.4 GiB) TX bytes:57525792 (54.8 MiB)
|
||||
|
||||
# adjust CPU load balancing
|
||||
root@OpenWrt:~# echo 2 > /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus
|
||||
root@OpenWrt:~# echo 4 > /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus
|
||||
root@OpenWrt:~# echo 2000 > /proc/sys/net/core/netdev_max_backlog
|
||||
root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus
|
||||
2
|
||||
root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus
|
||||
4
|
||||
root@OpenWrt:~# cat /proc/sys/net/core/netdev_max_backlog
|
||||
2000
|
||||
root@OpenWrt:~# insmod pcie_mhi.ko mhi_mbim_enabled=1
|
||||
root@OpenWrt:~# dmesg | grep mhi
|
||||
[ 65.587160] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.6
|
||||
[ 65.597089] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306
|
||||
[ 65.602250] mhi_q 0000:01:00.0: BAR 0: assigned [mem 0x20300000-0x20300fff 64bit]
|
||||
[ 65.611690] mhi_q 0000:01:00.0: enabling device (0140 -> 0142)
|
||||
[ 65.619307] [I][mhi_init_pci_dev] msi_required = 5, msi_allocated = 5, msi_irq = 63
|
||||
[ 65.619327] [I][mhi_power_up] dev_state:RESET
|
||||
[ 65.619331] [I][mhi_async_power_up] Requested to power on
|
||||
[ 65.619449] [I][mhi_alloc_coherent] size = 114688, dma_handle = 6fca0000
|
||||
[ 65.619462] [I][mhi_init_dev_ctxt] mhi_ctxt->ctrl_seg = c221e000
|
||||
[ 65.619731] [I][mhi_async_power_up] dev_state:RESET ee:AMSS
|
||||
[ 65.619747] [I][mhi_pm_st_worker] Transition to state:READY
|
||||
[ 65.619760] [I][mhi_pm_st_worker] INVALID_EE -> AMSS
|
||||
[ 65.619764] [I][mhi_ready_state_transition] Waiting to enter READY state
|
||||
[ 65.619885] [I][mhi_async_power_up] Power on setup success
|
||||
[ 65.619897] [I][mhi_pci_probe] Return successful
|
||||
[ 65.665114] [I][mhi_ready_state_transition] Device in READY State
|
||||
[ 65.665125] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, pm_state:POR
|
||||
[ 65.665131] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, INVALID_EE
|
||||
[ 65.665133] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:POR
|
||||
[ 65.665137] [I][mhi_init_mmio] Initializing MMIO
|
||||
[ 65.665142] [I][mhi_init_mmio] CHDBOFF:0x300
|
||||
[ 65.665151] [I][mhi_init_mmio] ERDBOFF:0x700
|
||||
[ 65.665156] [I][mhi_init_mmio] Programming all MMIO values.
|
||||
[ 65.786283] [I][mhi_dump_tre] carl_ev evt_state_change mhistate=2
|
||||
[ 65.786289] [I][mhi_process_ctrl_ev_ring] MHI state change event to state:M0
|
||||
[ 65.786295] [I][mhi_pm_m0_transition] Entered With State:READY PM_STATE:POR
|
||||
[ 65.786300] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:M0
|
||||
[ 65.789734] [I][mhi_dump_tre] carl_ev evt_ee_state execenv=2
|
||||
[ 65.789739] [I][mhi_process_ctrl_ev_ring] MHI EE received event:AMSS
|
||||
[ 65.789756] [I][mhi_pm_st_worker] Transition to state:MISSION MODE
|
||||
[ 65.789767] [I][mhi_pm_st_worker] INVALID_EE -> AMSS
|
||||
[ 65.789771] [I][mhi_pm_mission_mode_transition] Processing Mission Mode Transition
|
||||
[ 65.789787] [I][mhi_init_timesync] No timesync capability found
|
||||
[ 65.789791] [I][mhi_pm_mission_mode_transition] Adding new devices
|
||||
[ 65.790570] [I][mhi_dtr_probe] Enter for DTR control channel
|
||||
[ 65.790577] [I][__mhi_prepare_channel] Entered: preparing channel:18
|
||||
[ 65.797036] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 65.797051] [I][__mhi_prepare_channel] Chan:18 successfully moved to start state
|
||||
[ 65.797055] [I][__mhi_prepare_channel] Entered: preparing channel:19
|
||||
[ 65.802457] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 65.802469] [I][__mhi_prepare_channel] Chan:19 successfully moved to start state
|
||||
[ 65.802485] [I][mhi_dtr_probe] Exit with ret:0
|
||||
[ 65.802748] [I][mhi_netdev_enable_iface] Prepare the channels for transfer
|
||||
[ 65.802772] [I][__mhi_prepare_channel] Entered: preparing channel:100
|
||||
[ 65.825279] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 65.825293] [I][__mhi_prepare_channel] Chan:100 successfully moved to start state
|
||||
[ 65.825297] [I][__mhi_prepare_channel] Entered: preparing channel:101
|
||||
[ 65.835565] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 65.835578] [I][__mhi_prepare_channel] Chan:101 successfully moved to start state
|
||||
[ 65.839141] [I][mhi_netdev_enable_iface] Exited.
|
||||
[ 65.839875] rmnet_vnd_register_device(rmnet_mhi0.1)=0
|
||||
[ 65.843278] net rmnet_mhi0 rmnet_mhi0.1: NSS context created
|
||||
[ 65.861808] [I][mhi_pm_mission_mode_transition] Exit with ret:0
|
||||
[ 68.625595] [I][__mhi_prepare_channel] Entered: preparing channel:12
|
||||
[ 68.634610] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 68.634622] [I][__mhi_prepare_channel] Chan:12 successfully moved to start state
|
||||
[ 68.634625] [I][__mhi_prepare_channel] Entered: preparing channel:13
|
||||
[ 68.644978] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 68.644987] [I][__mhi_prepare_channel] Chan:13 successfully moved to start state
|
||||
[ 69.170666] net rmnet_mhi0: link_state 0x0 -> 0x1
|
||||
[ 69.177035] [I][mhi_netdev_open] Opened net dev interface
|
||||
[ 71.655431] [I][mhi_netdev_open] Opened net dev interface
|
||||
|
||||
root@OpenWrt:~# ./quectel-CM &
|
||||
[04-02_04:14:12:134] Quectel_QConnectManager_Linux_V1.6.0.5
|
||||
[04-02_04:14:12:134] Find /sys/bus/usb/devices/4-1 idVendor=0x2c7c idProduct=0x800, bus=0x004, dev=0x002
|
||||
[04-02_04:14:12:135] network interface '' or qmidev '' is not exist
|
||||
[04-02_04:14:12:135] netcard driver = pcie_mhi, driver version = V1.3.0.6
|
||||
[04-02_04:14:12:135] Modem works in MBIM mode
|
||||
[04-02_04:14:12:135] apn (null), user (null), passwd (null), auth 0
|
||||
[04-02_04:14:12:135] IP Proto MBIMContextIPTypeIPv4
|
||||
[04-02_04:14:12:154] mbim_read_thread is created
|
||||
sh: can't create /sys/class/net/rmnet_mhi0/mbim/link_state: nonexistent directory
|
||||
[04-02_04:14:12:156] system(echo 0 > /sys/class/net/rmnet_mhi0/mbim/link_state)=256
|
||||
[04-02_04:14:12:185] system(ip address flush dev rmnet_mhi0)=0
|
||||
[04-02_04:14:12:187] system(ip link set dev rmnet_mhi0 down)=0
|
||||
[04-02_04:14:12:188] mbim_open_device()
|
||||
[04-02_04:14:12:605] mbim_device_caps_query()
|
||||
[04-02_04:14:12:610] DeviceId: 869710030002905
|
||||
[04-02_04:14:12:610] HardwareInfo: 0
|
||||
[04-02_04:14:12:610] mbim_set_radio_state( 1 )
|
||||
[04-02_04:14:12:613] HwRadioState: 1, SwRadioState: 1
|
||||
[04-02_04:14:12:613] mbim_subscriber_status_query()
|
||||
[04-02_04:14:12:620] SubscriberReadyState NotInitialized -> Initialized
|
||||
[04-02_04:14:12:620] mbim_register_state_query()
|
||||
[04-02_04:14:12:625] RegisterState Unknown -> Home
|
||||
[04-02_04:14:12:625] mbim_packet_service_query()
|
||||
[04-02_04:14:12:629] PacketServiceState Unknown -> Attached
|
||||
[04-02_04:14:12:629] mbim_query_connect(sessionID=0)
|
||||
[04-02_04:14:12:633] ActivationState Unknown -> Deactivated
|
||||
[04-02_04:14:12:633] mbim_set_connect(onoff=1, sessionID=0)
|
||||
[ 69.170666] net rmnet_mhi0: link_state 0x0 -> 0x1
|
||||
[04-02_04:14:12:680] ActivationState Deactivated -> Activated
|
||||
[ 69.177035] [I][mhi_netdev_open] Opened net dev interface
|
||||
[04-02_04:14:12:680] mbim_ip_config(sessionID=0)
|
||||
[04-02_04:14:12:683] < SessionId = 0
|
||||
[04-02_04:14:12:683] < IPv4ConfigurationAvailable = 0xf
|
||||
[04-02_04:14:12:683] < IPv6ConfigurationAvailable = 0x0
|
||||
[04-02_04:14:12:683] < IPv4AddressCount = 0x1
|
||||
[04-02_04:14:12:683] < IPv4AddressOffset = 0x3c
|
||||
[04-02_04:14:12:683] < IPv6AddressCount = 0x0
|
||||
[04-02_04:14:12:683] < IPv6AddressOffset = 0x0
|
||||
[04-02_04:14:12:683] < IPv4 = 10.129.59.93/30
|
||||
[04-02_04:14:12:683] < gw = 10.129.59.94
|
||||
[04-02_04:14:12:683] < dns1 = 211.138.180.2
|
||||
[04-02_04:14:12:683] < dns2 = 211.138.180.3
|
||||
[04-02_04:14:12:683] < ipv4 mtu = 1500
|
||||
sh: can't create /sys/class/net/rmnet_mhi0/mbim/link_state: nonexistent directory
|
||||
[04-02_04:14:12:684] system(echo 1 > /sys/class/net/rmnet_mhi0/mbim/link_state)=256
|
||||
[04-02_04:14:12:689] system(ip link set dev rmnet_mhi0 up)=0
|
||||
[04-02_04:14:12:692] system(ip -4 address flush dev rmnet_mhi0)=0
|
||||
[04-02_04:14:12:694] system(ip -4 address add 10.129.59.93/30 dev rmnet_mhi0)=0
|
||||
[04-02_04:14:12:697] system(ip -4 route add default via 10.129.59.94 dev rmnet_mhi0)=0
|
||||
[04-02_04:14:12:699] system(ip -4 link set dev rmnet_mhi0 mtu 1500)=0
|
||||
|
||||
root@OpenWrt:~# ifconfig rmnet_mhi0
|
||||
rmnet_mhi0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
|
||||
UP RUNNING NOARP MTU:1500 Metric:1
|
||||
RX packets:99379 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:176569 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:1528181052 (1.4 GiB) TX bytes:62467192 (59.5 MiB)
|
||||
|
||||
root@OpenWrt:~# ifconfig rmnet_mhi0.1
|
||||
rmnet_mhi0.1 Link encap:UNSPEC HWaddr 02-50-F4-00-00-00-00-00-00-00-00-00-00-00-00-00
|
||||
inet addr:10.129.59.93 Mask:255.255.255.252
|
||||
inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link
|
||||
UP RUNNING NOARP MTU:1500 Metric:1
|
||||
RX packets:1089360 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:176581 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:1521449058 (1.4 GiB) TX bytes:57525792 (54.8 MiB)
|
||||
|
||||
# adjust CPU load balancing
|
||||
root@OpenWrt:~# echo 2 > /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus
|
||||
root@OpenWrt:~# echo 4 > /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus
|
||||
root@OpenWrt:~# echo 2000 > /proc/sys/net/core/netdev_max_backlog
|
||||
root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus
|
||||
2
|
||||
root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus
|
||||
4
|
||||
root@OpenWrt:~# cat /proc/sys/net/core/netdev_max_backlog
|
||||
2000
|
||||
|
||||
@ -1,134 +1,134 @@
|
||||
disable ccflags-y += -DCONFIG_MHI_NETDEV_MBIM in pcie_mhi/Makefile
|
||||
|
||||
root@OpenWrt:~# insmod pcie_mhi.ko
|
||||
|
||||
root@OpenWrt:~# dmesg | grep mhi
|
||||
[ 138.483252] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.6
|
||||
[ 138.492350] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306
|
||||
[ 138.497564] mhi_q 0000:01:00.0: BAR 0: assigned [mem 0x20300000-0x20300fff 64bit]
|
||||
[ 138.506952] mhi_q 0000:01:00.0: enabling device (0140 -> 0142)
|
||||
[ 138.514562] [I][mhi_init_pci_dev] msi_required = 5, msi_allocated = 5, msi_irq = 63
|
||||
[ 138.514581] [I][mhi_power_up] dev_state:RESET
|
||||
[ 138.514587] [I][mhi_async_power_up] Requested to power on
|
||||
[ 138.514728] [I][mhi_alloc_coherent] size = 114688, dma_handle = 72160000
|
||||
[ 138.514734] [I][mhi_init_dev_ctxt] mhi_ctxt->ctrl_seg = c221f000
|
||||
[ 138.515030] [I][mhi_async_power_up] dev_state:RESET ee:AMSS
|
||||
[ 138.515056] [I][mhi_pm_st_worker] Transition to state:READY
|
||||
[ 138.515067] [I][mhi_pm_st_worker] INVALID_EE -> AMSS
|
||||
[ 138.515073] [I][mhi_ready_state_transition] Waiting to enter READY state
|
||||
[ 138.515210] [I][mhi_async_power_up] Power on setup success
|
||||
[ 138.515227] [I][mhi_pci_probe] Return successful
|
||||
[ 138.589013] [I][mhi_ready_state_transition] Device in READY State
|
||||
[ 138.589029] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, pm_state:POR
|
||||
[ 138.589038] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, INVALID_EE
|
||||
[ 138.589041] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:POR
|
||||
[ 138.589046] [I][mhi_init_mmio] Initializing MMIO
|
||||
[ 138.589050] [I][mhi_init_mmio] CHDBOFF:0x300
|
||||
[ 138.589060] [I][mhi_init_mmio] ERDBOFF:0x700
|
||||
[ 138.589065] [I][mhi_init_mmio] Programming all MMIO values.
|
||||
[ 138.706124] [I][mhi_dump_tre] carl_ev evt_state_change mhistate=2
|
||||
[ 138.706132] [I][mhi_process_ctrl_ev_ring] MHI state change event to state:M0
|
||||
[ 138.706140] [I][mhi_pm_m0_transition] Entered With State:READY PM_STATE:POR
|
||||
[ 138.706146] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:M0
|
||||
[ 138.708699] [I][mhi_dump_tre] carl_ev evt_ee_state execenv=2
|
||||
[ 138.708706] [I][mhi_process_ctrl_ev_ring] MHI EE received event:AMSS
|
||||
[ 138.708726] [I][mhi_pm_st_worker] Transition to state:MISSION MODE
|
||||
[ 138.708736] [I][mhi_pm_st_worker] INVALID_EE -> AMSS
|
||||
[ 138.708742] [I][mhi_pm_mission_mode_transition] Processing Mission Mode Transition
|
||||
[ 138.708758] [I][mhi_init_timesync] No timesync capability found
|
||||
[ 138.708764] [I][mhi_pm_mission_mode_transition] Adding new devices
|
||||
[ 138.709785] [I][mhi_dtr_probe] Enter for DTR control channel
|
||||
[ 138.709794] [I][__mhi_prepare_channel] Entered: preparing channel:18
|
||||
[ 138.715378] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 138.715397] [I][__mhi_prepare_channel] Chan:18 successfully moved to start state
|
||||
[ 138.715403] [I][__mhi_prepare_channel] Entered: preparing channel:19
|
||||
[ 138.720201] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 138.720218] [I][__mhi_prepare_channel] Chan:19 successfully moved to start state
|
||||
[ 138.720236] [I][mhi_dtr_probe] Exit with ret:0
|
||||
[ 138.720590] [I][mhi_netdev_enable_iface] Prepare the channels for transfer
|
||||
[ 138.720630] [I][__mhi_prepare_channel] Entered: preparing channel:100
|
||||
[ 138.757230] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 138.757253] [I][__mhi_prepare_channel] Chan:100 successfully moved to start state
|
||||
[ 138.757259] [I][__mhi_prepare_channel] Entered: preparing channel:101
|
||||
[ 138.774352] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 138.774370] [I][__mhi_prepare_channel] Chan:101 successfully moved to start state
|
||||
[ 138.778137] [I][mhi_netdev_enable_iface] Exited.
|
||||
[ 138.779018] rmnet_vnd_register_device(rmnet_mhi0.1)=0
|
||||
[ 138.782283] net rmnet_mhi0 rmnet_mhi0.1: NSS context created
|
||||
[ 138.800865] [I][mhi_pm_mission_mode_transition] Exit with ret:0
|
||||
|
||||
root@OpenWrt:~# ./quectel-CM &
|
||||
root@OpenWrt:~# [04-02_04:12:16:477] Quectel_QConnectManager_Linux_V1.6.0.5
|
||||
[04-02_04:12:16:477] Find /sys/bus/usb/devices/4-1 idVendor=0x2c7c idProduct=0x800, bus=0x004, dev=0x002
|
||||
[04-02_04:12:16:478] network interface '' or qmidev '' is not exist
|
||||
[04-02_04:12:16:478] netcard driver = pcie_mhi, driver version = V1.3.0.6
|
||||
[04-02_04:12:16:479] qmap_mode = 1, qmap_version = 9, qmap_size = 16384, muxid = 0x81, qmap_netcard = rmnet_mhi0.1
|
||||
[04-02_04:12:16:479] Modem works in QMI mode
|
||||
[04-02_04:12:16:505] cdc_wdm_fd = 7
|
||||
[04-02_04:12:17:506] QmiThreadSendQMITimeout pthread_cond_timeout_np timeout
|
||||
[04-02_04:12:18:516] Get clientWDS = 19
|
||||
[04-02_04:12:18:520] Get clientDMS = 1
|
||||
[04-02_04:12:18:524] Get clientNAS = 3
|
||||
[04-02_04:12:18:527] Get clientUIM = 1
|
||||
[04-02_04:12:18:531] Get clientWDA = 1
|
||||
[04-02_04:12:18:535] requestBaseBandVersion RM500QGLAAR03A01M4G_BETA_20200107F 1 [Dec 30 2019 17:00:00]
|
||||
[04-02_04:12:18:539] qmap_settings.rx_urb_size = 16384
|
||||
[04-02_04:12:18:539] qmap_settings.ul_data_aggregation_max_datagrams = 16
|
||||
[04-02_04:12:18:539] qmap_settings.ul_data_aggregation_max_size = 8192
|
||||
[04-02_04:12:18:539] qmap_settings.dl_minimum_padding = 0
|
||||
[04-02_04:12:18:550] requestSetLoopBackState(loopback_state=1, replication_factor=14)
|
||||
[04-02_04:12:18:557] requestGetSIMStatus SIMStatus: SIM_ABSENT
|
||||
[04-02_04:12:18:560] requestGetProfile[1] ///0
|
||||
[04-02_04:12:18:563] requestRegistrationState2 MCC: 0, MNC: 0, PS: Detached, DataCap: UNKNOW
|
||||
[04-02_04:12:18:565] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED
|
||||
[04-02_04:12:18:566] ifconfig rmnet_mhi0.1 down
|
||||
[04-02_04:12:18:571] ifconfig rmnet_mhi0.1 0.0.0.0
|
||||
ifconfig: SIOCSIFFLAGS: Network is down
|
||||
[04-02_04:12:18:575] SetLoopBackInd: loopback_state=1, replication_factor=14
|
||||
[04-02_04:12:18:591] requestSetupDataCall WdsConnectionIPv4Handle: 0xe40182a0
|
||||
[04-02_04:12:18:601] ifconfig rmnet_mhi0 up
|
||||
[04-02_04:12:18:607] ifconfig rmnet_mhi0.1 up
|
||||
[04-02_04:12:18:613] you are use OpenWrt?
|
||||
[04-02_04:12:18:614] should not calling udhcpc manually?
|
||||
[04-02_04:12:18:614] should modify /etc/config/network as below?
|
||||
[04-02_04:12:18:614] config interface wan
|
||||
[04-02_04:12:18:614] option ifname rmnet_mhi0.1
|
||||
[04-02_04:12:18:614] option proto dhcp
|
||||
[04-02_04:12:18:614] should use "/sbin/ifstaus wan" to check rmnet_mhi0.1 's status?
|
||||
[04-02_04:12:18:614] busybox udhcpc -f -n -q -t 5 -i rmnet_mhi0.1
|
||||
udhcpc: started, v1.28.3
|
||||
udhcpc: sending discover
|
||||
udhcpc: sending select for 192.168.48.171
|
||||
udhcpc: lease of 192.168.48.171 obtained, lease time 7200
|
||||
[04-02_04:12:18:809] udhcpc: ifconfig rmnet_mhi0.1 192.168.48.171 netmask 255.255.255.248 broadcast +
|
||||
[04-02_04:12:18:819] udhcpc: setting default routers: 192.168.48.172
|
||||
|
||||
root@OpenWrt:~# ifconfig rmnet_mhi0
|
||||
rmnet_mhi0 Link encap:Ethernet HWaddr 02:50:F4:00:00:00
|
||||
inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link
|
||||
UP RUNNING NOARP MTU:1500 Metric:1
|
||||
RX packets:2 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:608 (608.0 B) TX bytes:672 (672.0 B)
|
||||
|
||||
root@OpenWrt:~# ifconfig rmnet_mhi0.1
|
||||
rmnet_mhi0.1 Link encap:UNSPEC HWaddr 02-50-F4-00-00-00-00-00-00-00-00-00-00-00-00-00
|
||||
inet addr:192.168.48.171 Mask:255.255.255.248
|
||||
inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link
|
||||
UP RUNNING NOARP MTU:1500 Metric:1
|
||||
RX packets:2 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:592 (592.0 B) TX bytes:656 (656.0 B)
|
||||
|
||||
# adjust CPU load balancing
|
||||
root@OpenWrt:~# echo 2 > /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus
|
||||
root@OpenWrt:~# echo 4 > /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus
|
||||
root@OpenWrt:~# echo 2000 > /proc/sys/net/core/netdev_max_backlog
|
||||
root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus
|
||||
2
|
||||
root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus
|
||||
4
|
||||
root@OpenWrt:~# cat /proc/sys/net/core/netdev_max_backlog
|
||||
disable ccflags-y += -DCONFIG_MHI_NETDEV_MBIM in pcie_mhi/Makefile
|
||||
|
||||
root@OpenWrt:~# insmod pcie_mhi.ko
|
||||
|
||||
root@OpenWrt:~# dmesg | grep mhi
|
||||
[ 138.483252] mhi_init Quectel_Linux_PCIE_MHI_Driver_V1.3.0.6
|
||||
[ 138.492350] mhi_pci_probe pci_dev->name = 0000:01:00.0, domain=0, bus=1, slot=0, vendor=17CB, device=0306
|
||||
[ 138.497564] mhi_q 0000:01:00.0: BAR 0: assigned [mem 0x20300000-0x20300fff 64bit]
|
||||
[ 138.506952] mhi_q 0000:01:00.0: enabling device (0140 -> 0142)
|
||||
[ 138.514562] [I][mhi_init_pci_dev] msi_required = 5, msi_allocated = 5, msi_irq = 63
|
||||
[ 138.514581] [I][mhi_power_up] dev_state:RESET
|
||||
[ 138.514587] [I][mhi_async_power_up] Requested to power on
|
||||
[ 138.514728] [I][mhi_alloc_coherent] size = 114688, dma_handle = 72160000
|
||||
[ 138.514734] [I][mhi_init_dev_ctxt] mhi_ctxt->ctrl_seg = c221f000
|
||||
[ 138.515030] [I][mhi_async_power_up] dev_state:RESET ee:AMSS
|
||||
[ 138.515056] [I][mhi_pm_st_worker] Transition to state:READY
|
||||
[ 138.515067] [I][mhi_pm_st_worker] INVALID_EE -> AMSS
|
||||
[ 138.515073] [I][mhi_ready_state_transition] Waiting to enter READY state
|
||||
[ 138.515210] [I][mhi_async_power_up] Power on setup success
|
||||
[ 138.515227] [I][mhi_pci_probe] Return successful
|
||||
[ 138.589013] [I][mhi_ready_state_transition] Device in READY State
|
||||
[ 138.589029] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, pm_state:POR
|
||||
[ 138.589038] [I][mhi_intvec_threaded_handlr] device ee:AMSS dev_state:READY, INVALID_EE
|
||||
[ 138.589041] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:POR
|
||||
[ 138.589046] [I][mhi_init_mmio] Initializing MMIO
|
||||
[ 138.589050] [I][mhi_init_mmio] CHDBOFF:0x300
|
||||
[ 138.589060] [I][mhi_init_mmio] ERDBOFF:0x700
|
||||
[ 138.589065] [I][mhi_init_mmio] Programming all MMIO values.
|
||||
[ 138.706124] [I][mhi_dump_tre] carl_ev evt_state_change mhistate=2
|
||||
[ 138.706132] [I][mhi_process_ctrl_ev_ring] MHI state change event to state:M0
|
||||
[ 138.706140] [I][mhi_pm_m0_transition] Entered With State:READY PM_STATE:POR
|
||||
[ 138.706146] [I][mhi_tryset_pm_state] Transition to pm state from:POR to:M0
|
||||
[ 138.708699] [I][mhi_dump_tre] carl_ev evt_ee_state execenv=2
|
||||
[ 138.708706] [I][mhi_process_ctrl_ev_ring] MHI EE received event:AMSS
|
||||
[ 138.708726] [I][mhi_pm_st_worker] Transition to state:MISSION MODE
|
||||
[ 138.708736] [I][mhi_pm_st_worker] INVALID_EE -> AMSS
|
||||
[ 138.708742] [I][mhi_pm_mission_mode_transition] Processing Mission Mode Transition
|
||||
[ 138.708758] [I][mhi_init_timesync] No timesync capability found
|
||||
[ 138.708764] [I][mhi_pm_mission_mode_transition] Adding new devices
|
||||
[ 138.709785] [I][mhi_dtr_probe] Enter for DTR control channel
|
||||
[ 138.709794] [I][__mhi_prepare_channel] Entered: preparing channel:18
|
||||
[ 138.715378] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 138.715397] [I][__mhi_prepare_channel] Chan:18 successfully moved to start state
|
||||
[ 138.715403] [I][__mhi_prepare_channel] Entered: preparing channel:19
|
||||
[ 138.720201] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 138.720218] [I][__mhi_prepare_channel] Chan:19 successfully moved to start state
|
||||
[ 138.720236] [I][mhi_dtr_probe] Exit with ret:0
|
||||
[ 138.720590] [I][mhi_netdev_enable_iface] Prepare the channels for transfer
|
||||
[ 138.720630] [I][__mhi_prepare_channel] Entered: preparing channel:100
|
||||
[ 138.757230] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 138.757253] [I][__mhi_prepare_channel] Chan:100 successfully moved to start state
|
||||
[ 138.757259] [I][__mhi_prepare_channel] Entered: preparing channel:101
|
||||
[ 138.774352] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 138.774370] [I][__mhi_prepare_channel] Chan:101 successfully moved to start state
|
||||
[ 138.778137] [I][mhi_netdev_enable_iface] Exited.
|
||||
[ 138.779018] rmnet_vnd_register_device(rmnet_mhi0.1)=0
|
||||
[ 138.782283] net rmnet_mhi0 rmnet_mhi0.1: NSS context created
|
||||
[ 138.800865] [I][mhi_pm_mission_mode_transition] Exit with ret:0
|
||||
|
||||
root@OpenWrt:~# ./quectel-CM &
|
||||
root@OpenWrt:~# [04-02_04:12:16:477] Quectel_QConnectManager_Linux_V1.6.0.5
|
||||
[04-02_04:12:16:477] Find /sys/bus/usb/devices/4-1 idVendor=0x2c7c idProduct=0x800, bus=0x004, dev=0x002
|
||||
[04-02_04:12:16:478] network interface '' or qmidev '' is not exist
|
||||
[04-02_04:12:16:478] netcard driver = pcie_mhi, driver version = V1.3.0.6
|
||||
[04-02_04:12:16:479] qmap_mode = 1, qmap_version = 9, qmap_size = 16384, muxid = 0x81, qmap_netcard = rmnet_mhi0.1
|
||||
[04-02_04:12:16:479] Modem works in QMI mode
|
||||
[04-02_04:12:16:505] cdc_wdm_fd = 7
|
||||
[04-02_04:12:17:506] QmiThreadSendQMITimeout pthread_cond_timeout_np timeout
|
||||
[04-02_04:12:18:516] Get clientWDS = 19
|
||||
[04-02_04:12:18:520] Get clientDMS = 1
|
||||
[04-02_04:12:18:524] Get clientNAS = 3
|
||||
[04-02_04:12:18:527] Get clientUIM = 1
|
||||
[04-02_04:12:18:531] Get clientWDA = 1
|
||||
[04-02_04:12:18:535] requestBaseBandVersion RM500QGLAAR03A01M4G_BETA_20200107F 1 [Dec 30 2019 17:00:00]
|
||||
[04-02_04:12:18:539] qmap_settings.rx_urb_size = 16384
|
||||
[04-02_04:12:18:539] qmap_settings.ul_data_aggregation_max_datagrams = 16
|
||||
[04-02_04:12:18:539] qmap_settings.ul_data_aggregation_max_size = 8192
|
||||
[04-02_04:12:18:539] qmap_settings.dl_minimum_padding = 0
|
||||
[04-02_04:12:18:550] requestSetLoopBackState(loopback_state=1, replication_factor=14)
|
||||
[04-02_04:12:18:557] requestGetSIMStatus SIMStatus: SIM_ABSENT
|
||||
[04-02_04:12:18:560] requestGetProfile[1] ///0
|
||||
[04-02_04:12:18:563] requestRegistrationState2 MCC: 0, MNC: 0, PS: Detached, DataCap: UNKNOW
|
||||
[04-02_04:12:18:565] requestQueryDataCall IPv4ConnectionStatus: DISCONNECTED
|
||||
[04-02_04:12:18:566] ifconfig rmnet_mhi0.1 down
|
||||
[04-02_04:12:18:571] ifconfig rmnet_mhi0.1 0.0.0.0
|
||||
ifconfig: SIOCSIFFLAGS: Network is down
|
||||
[04-02_04:12:18:575] SetLoopBackInd: loopback_state=1, replication_factor=14
|
||||
[04-02_04:12:18:591] requestSetupDataCall WdsConnectionIPv4Handle: 0xe40182a0
|
||||
[04-02_04:12:18:601] ifconfig rmnet_mhi0 up
|
||||
[04-02_04:12:18:607] ifconfig rmnet_mhi0.1 up
|
||||
[04-02_04:12:18:613] you are use OpenWrt?
|
||||
[04-02_04:12:18:614] should not calling udhcpc manually?
|
||||
[04-02_04:12:18:614] should modify /etc/config/network as below?
|
||||
[04-02_04:12:18:614] config interface wan
|
||||
[04-02_04:12:18:614] option ifname rmnet_mhi0.1
|
||||
[04-02_04:12:18:614] option proto dhcp
|
||||
[04-02_04:12:18:614] should use "/sbin/ifstaus wan" to check rmnet_mhi0.1 's status?
|
||||
[04-02_04:12:18:614] busybox udhcpc -f -n -q -t 5 -i rmnet_mhi0.1
|
||||
udhcpc: started, v1.28.3
|
||||
udhcpc: sending discover
|
||||
udhcpc: sending select for 192.168.48.171
|
||||
udhcpc: lease of 192.168.48.171 obtained, lease time 7200
|
||||
[04-02_04:12:18:809] udhcpc: ifconfig rmnet_mhi0.1 192.168.48.171 netmask 255.255.255.248 broadcast +
|
||||
[04-02_04:12:18:819] udhcpc: setting default routers: 192.168.48.172
|
||||
|
||||
root@OpenWrt:~# ifconfig rmnet_mhi0
|
||||
rmnet_mhi0 Link encap:Ethernet HWaddr 02:50:F4:00:00:00
|
||||
inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link
|
||||
UP RUNNING NOARP MTU:1500 Metric:1
|
||||
RX packets:2 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:608 (608.0 B) TX bytes:672 (672.0 B)
|
||||
|
||||
root@OpenWrt:~# ifconfig rmnet_mhi0.1
|
||||
rmnet_mhi0.1 Link encap:UNSPEC HWaddr 02-50-F4-00-00-00-00-00-00-00-00-00-00-00-00-00
|
||||
inet addr:192.168.48.171 Mask:255.255.255.248
|
||||
inet6 addr: fe80::50:f4ff:fe00:0/64 Scope:Link
|
||||
UP RUNNING NOARP MTU:1500 Metric:1
|
||||
RX packets:2 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:592 (592.0 B) TX bytes:656 (656.0 B)
|
||||
|
||||
# adjust CPU load balancing
|
||||
root@OpenWrt:~# echo 2 > /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus
|
||||
root@OpenWrt:~# echo 4 > /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus
|
||||
root@OpenWrt:~# echo 2000 > /proc/sys/net/core/netdev_max_backlog
|
||||
root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0/queues/rx-0/rps_cpus
|
||||
2
|
||||
root@OpenWrt:~# cat /sys/class/net/rmnet_mhi0.1/queues/rx-0/rps_cpus
|
||||
4
|
||||
root@OpenWrt:~# cat /proc/sys/net/core/netdev_max_backlog
|
||||
2000
|
||||
@ -1,14 +1,14 @@
|
||||
root@imx6qsabresd:~# ./QLog -p /dev/mhi_DIAG -s log &
|
||||
root@imx6qsabresd:~# [000.000]QLog Version: Quectel_QLog_Linux&Android_V1.2.4
|
||||
[ 298.597963] [I][mhi_uci_open] Node open, ref counts 1
|
||||
[ 298.605601] [I][mhi_uci_open] Starting channel
|
||||
[ 298.612159] [I][__mhi_prepare_channel] Entered: preparing channel:4
|
||||
[ 298.629906] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 298.635415] [I][__mhi_prepare_channel] Chan:4 successfully moved to start state
|
||||
[ 298.642749] [I][__mhi_prepare_channel] Entered: preparing channel:5
|
||||
[ 298.658043] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 298.663543] [I][__mhi_prepare_channel] Chan:5 successfully moved to start state
|
||||
[000.075]open /dev/mhi_DIAG ttyfd = 3
|
||||
[000.075]Press CTRL+C to stop catch log.
|
||||
[000.096]qlog_logfile_create log/20160920_145758_0000.qmdl logfd=4
|
||||
[005.268]recv: 0M 70K 490B in 5181 msec
|
||||
root@imx6qsabresd:~# ./QLog -p /dev/mhi_DIAG -s log &
|
||||
root@imx6qsabresd:~# [000.000]QLog Version: Quectel_QLog_Linux&Android_V1.2.4
|
||||
[ 298.597963] [I][mhi_uci_open] Node open, ref counts 1
|
||||
[ 298.605601] [I][mhi_uci_open] Starting channel
|
||||
[ 298.612159] [I][__mhi_prepare_channel] Entered: preparing channel:4
|
||||
[ 298.629906] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 298.635415] [I][__mhi_prepare_channel] Chan:4 successfully moved to start state
|
||||
[ 298.642749] [I][__mhi_prepare_channel] Entered: preparing channel:5
|
||||
[ 298.658043] [I][mhi_dump_tre] carl_ev evt_cmd_comp code=1
|
||||
[ 298.663543] [I][__mhi_prepare_channel] Chan:5 successfully moved to start state
|
||||
[000.075]open /dev/mhi_DIAG ttyfd = 3
|
||||
[000.075]Press CTRL+C to stop catch log.
|
||||
[000.096]qlog_logfile_create log/20160920_145758_0000.qmdl logfd=4
|
||||
[005.268]recv: 0M 70K 490B in 5181 msec
|
||||
|
||||
@ -8,8 +8,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=qmi_wwan_q
|
||||
PKG_VERSION:=1.2.9
|
||||
PKG_RELEASE:=1
|
||||
PKG_VERSION:=3.0
|
||||
PKG_RELEASE:=2
|
||||
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
@ -1,16 +1,13 @@
|
||||
/*
|
||||
Copyright (c) Bjørn Mork of author <bjorn@mork.no>
|
||||
|
||||
This program is free software; you can redistribute it and/ormodify it under the terms of the GNU General
|
||||
Public licenseas published byFree Software Foundation; either version 2theof the License,(at your option)
|
||||
any later version.O1
|
||||
This program isdistributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the
|
||||
implied warranty ofOr FITNESS FOR A PARTICULAR PURPOSE.MERCHANTABILITYSee theGNU General Public License
|
||||
for more details.
|
||||
You should have received a copy of the GNU General Public licensealong withthis program; if not, write to
|
||||
the Free SoftwareFoundation, Inc.r51 Franklin Street, Fifth Floor,Boston,MA 02110-1301,USA.
|
||||
|
||||
Based on version modification, the author is Quectel <fae-support@quectel.com>
|
||||
* Copyright (c) 2012 Bjørn Mork <bjorn@mork.no>
|
||||
*
|
||||
* The probing code is heavily inspired by cdc_ether, which is:
|
||||
* Copyright (C) 2003-2005 by David Brownell
|
||||
* Copyright (C) 2006 by Ole Andre Vadla Ravnas (ActiveSync)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@ -65,10 +62,8 @@ static struct rmnet_nss_cb __read_mostly *nss_cb = NULL;
|
||||
#if defined(CONFIG_PINCTRL_IPQ807x) || defined(CONFIG_PINCTRL_IPQ5018)
|
||||
#ifdef CONFIG_RMNET_DATA
|
||||
#define CONFIG_QCA_NSS_DRV
|
||||
/* define at qsdk/qca/src/linux-4.4/net/rmnet_data/rmnet_data_main.c */ //for spf11.x
|
||||
/* define at qsdk/qca/src/datarmnet/core/rmnet_config.c */ //for spf12.x
|
||||
/* define at qsdk/qca/src/linux-4.4/net/rmnet_data/rmnet_data_main.c */
|
||||
/* set at qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c */
|
||||
/* need add DEPENDS:= kmod-rmnet-core in feeds/makefile */
|
||||
extern struct rmnet_nss_cb *rmnet_nss_callbacks __rcu __read_mostly;
|
||||
#endif
|
||||
#endif
|
||||
@ -96,7 +91,7 @@ extern struct rmnet_nss_cb *rmnet_nss_callbacks __rcu __read_mostly;
|
||||
* These devices may alternatively/additionally be configured using AT
|
||||
* commands on a serial interface
|
||||
*/
|
||||
#define VERSION_NUMBER "V1.2.9"
|
||||
#define VERSION_NUMBER "V1.2.2"
|
||||
#define QUECTEL_WWAN_VERSION "Quectel_Linux&Android_QMI_WWAN_Driver_"VERSION_NUMBER
|
||||
static const char driver_name[] = "qmi_wwan_q";
|
||||
|
||||
@ -120,7 +115,6 @@ static const u8 default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3};
|
||||
1 - QMAP (Aggregation protocol)
|
||||
X - QMAP (Multiplexing and Aggregation protocol)
|
||||
*/
|
||||
//#define CONFIG_CLEAR_HALT
|
||||
#define QUECTEL_WWAN_QMAP 4 //MAX is 7
|
||||
|
||||
#if defined(QUECTEL_WWAN_QMAP)
|
||||
@ -222,7 +216,7 @@ struct qmap_priv {
|
||||
struct timespec64 agg_time;
|
||||
struct hrtimer agg_hrtimer;
|
||||
struct work_struct agg_wq;
|
||||
|
||||
|
||||
#ifdef QUECTEL_BRIDGE_MODE
|
||||
uint bridge_mode;
|
||||
uint bridge_ipv4;
|
||||
@ -231,7 +225,7 @@ struct qmap_priv {
|
||||
unsigned char bridge_self_mac[ETH_ALEN];
|
||||
#endif
|
||||
#endif
|
||||
uint use_qca_nss;
|
||||
uint use_qca_nss;
|
||||
};
|
||||
|
||||
struct qmap_hdr {
|
||||
@ -325,10 +319,10 @@ static int bridge_arp_reply(struct net_device *net, struct sk_buff *skb, uint br
|
||||
reply->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
reply->pkt_type = PACKET_HOST;
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION( 5,18,0 ))
|
||||
netif_rx(reply);
|
||||
#else
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0)
|
||||
netif_rx_ni(reply);
|
||||
#else
|
||||
netif_rx(reply);
|
||||
#endif
|
||||
}
|
||||
return 1;
|
||||
@ -363,14 +357,14 @@ static struct sk_buff *bridge_mode_tx_fixup(struct net_device *net, struct sk_bu
|
||||
bridge_mac[0], bridge_mac[1], bridge_mac[2], bridge_mac[3], bridge_mac[4], bridge_mac[5]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_BRIDGE_LAN
|
||||
//bridge Lan IP 192.168.0.0
|
||||
if (ehdr->h_proto == htons(ETH_P_IP) && (iph->daddr & 0xFFFF) == 0xA8C0)
|
||||
{
|
||||
struct sk_buff *reply = skb_copy(skb, GFP_ATOMIC);
|
||||
ehdr = eth_hdr(reply);
|
||||
|
||||
|
||||
memcpy(ehdr->h_source, default_modem_addr, ETH_ALEN);
|
||||
if(is_qmap_netdev(net))
|
||||
{
|
||||
@ -384,16 +378,20 @@ static struct sk_buff *bridge_mode_tx_fixup(struct net_device *net, struct sk_bu
|
||||
sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused;
|
||||
memcpy(ehdr->h_dest, pQmapDev->bridge_self_mac, ETH_ALEN);
|
||||
}
|
||||
|
||||
|
||||
//pr_info("%s br rx pkt addr: %02x:%02x:%02x:%02x:%02x:%02x -> %02x:%02x:%02x:%02x:%02x:%02x\n", netdev_name(net),
|
||||
// ehdr->h_source[0], ehdr->h_source[1], ehdr->h_source[2], ehdr->h_source[3], ehdr->h_source[4], ehdr->h_source[5],
|
||||
// ehdr->h_dest[0], ehdr->h_dest[1], ehdr->h_dest[2], ehdr->h_dest[3], ehdr->h_dest[4], ehdr->h_dest[5]);
|
||||
|
||||
|
||||
skb_reset_mac_header(reply);
|
||||
__skb_pull(reply, skb_network_offset(reply));
|
||||
reply->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
reply->pkt_type = PACKET_HOST;
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0)
|
||||
netif_rx_ni(reply);
|
||||
#else
|
||||
netif_rx(reply);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
@ -409,7 +407,7 @@ static void bridge_mode_rx_fixup(sQmiWwanQmap *pQmapDev, struct net_device *net,
|
||||
uint bridge_mode = 0;
|
||||
unsigned char *bridge_mac;
|
||||
|
||||
if (pQmapDev->qmap_mode > 1 || ((pQmapDev->use_rmnet_usb == 1) && !one_card_mode)) {
|
||||
if (pQmapDev->qmap_mode > 1 || pQmapDev->use_rmnet_usb == 1) {
|
||||
struct qmap_priv *priv = netdev_priv(net);
|
||||
bridge_mode = priv->bridge_mode;
|
||||
bridge_mac = priv->bridge_mac;
|
||||
@ -512,7 +510,7 @@ static ssize_t link_state_store(struct device *dev, struct device_attribute *att
|
||||
}
|
||||
}
|
||||
|
||||
if (usbnetdev->net->flags & IFF_UP) {
|
||||
if (usbnetdev->net->flags & IFF_UP) {
|
||||
if (!pQmapDev->link_state) {
|
||||
netif_carrier_off(usbnetdev->net);
|
||||
}
|
||||
@ -685,7 +683,7 @@ static void qmap_wake_queue(sQmiWwanQmap *pQmapDev)
|
||||
|
||||
if (!pQmapDev || !pQmapDev->use_rmnet_usb)
|
||||
return;
|
||||
|
||||
|
||||
for (i = 0; i < pQmapDev->qmap_mode; i++) {
|
||||
struct net_device *qmap_net = pQmapDev->mpQmapNetDev[i];
|
||||
|
||||
@ -695,7 +693,7 @@ static void qmap_wake_queue(sQmiWwanQmap *pQmapDev)
|
||||
}
|
||||
}
|
||||
|
||||
static struct sk_buff * add_qhdr(struct sk_buff *skb, u8 mux_id, int *hdr_data, int ip_offset) {
|
||||
static struct sk_buff * add_qhdr(struct sk_buff *skb, u8 mux_id) {
|
||||
struct qmap_hdr *qhdr;
|
||||
int pad = 0;
|
||||
|
||||
@ -703,55 +701,44 @@ static struct sk_buff * add_qhdr(struct sk_buff *skb, u8 mux_id, int *hdr_data,
|
||||
if (pad) {
|
||||
pad = 4 - pad;
|
||||
if (skb_tailroom(skb) < pad) {
|
||||
//printk("skb_tailroom small!\n");
|
||||
printk("skb_tailroom small!\n");
|
||||
pad = 0;
|
||||
}
|
||||
if (pad)
|
||||
__skb_put(skb, pad);
|
||||
}
|
||||
|
||||
if (hdr_data) {
|
||||
qhdr = (struct qmap_hdr *)hdr_data;
|
||||
qhdr->pkt_len = cpu_to_be16(skb->len - ip_offset);
|
||||
}
|
||||
else {
|
||||
qhdr = (struct qmap_hdr *)skb_push(skb, sizeof(struct qmap_hdr));
|
||||
qhdr->pkt_len = cpu_to_be16(skb->len - sizeof(struct qmap_hdr));
|
||||
}
|
||||
|
||||
qhdr = (struct qmap_hdr *)skb_push(skb, sizeof(struct qmap_hdr));
|
||||
qhdr->cd_rsvd_pad = pad;
|
||||
qhdr->mux_id = mux_id;
|
||||
qhdr->pkt_len = cpu_to_be16(skb->len - sizeof(struct qmap_hdr));
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static struct sk_buff * add_qhdr_v5(struct sk_buff *skb, u8 mux_id, int *hdr_data, int ip_offset) {
|
||||
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;
|
||||
int pad = 0;
|
||||
u32 padding, map_datalen;
|
||||
|
||||
pad = (skb->len - ip_offset) %4;
|
||||
if (pad) {
|
||||
pad = 4 - pad;
|
||||
if (skb_tailroom(skb) < pad) {
|
||||
//printk("skb_tailroom small!\n");
|
||||
pad = 0;
|
||||
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 (pad)
|
||||
__skb_put(skb, pad);
|
||||
}
|
||||
|
||||
if (hdr_data) {
|
||||
map_header = (struct rmnet_map_header *)hdr_data;
|
||||
map_header->pkt_len = htons(skb->len - ip_offset);
|
||||
}
|
||||
else {
|
||||
map_header = (struct rmnet_map_header *)skb_push(skb, (sizeof(struct rmnet_map_header) + sizeof(struct rmnet_map_v5_csum_header)));
|
||||
map_header->pkt_len = htons(skb->len - (sizeof(struct rmnet_map_header) + sizeof(struct rmnet_map_v5_csum_header)));
|
||||
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)));
|
||||
map_header->cd_bit = 0;
|
||||
map_header->next_hdr = 1;
|
||||
map_header->pad_len = pad;
|
||||
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));
|
||||
@ -774,38 +761,48 @@ static void rmnet_vnd_update_rx_stats(struct net_device *net,
|
||||
struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->stats64);
|
||||
|
||||
u64_stats_update_begin(&stats64->syncp);
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 6,0,0 ))
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0)
|
||||
stats64->rx_packets += rx_packets;
|
||||
stats64->rx_bytes += rx_bytes;
|
||||
#else
|
||||
u64_stats_add(&stats64->rx_packets, rx_packets);
|
||||
u64_stats_add(&stats64->rx_bytes, rx_bytes);
|
||||
u64_stats_add(&stats64->rx_packets, rx_packets);
|
||||
u64_stats_add(&stats64->rx_bytes, rx_bytes);
|
||||
#endif
|
||||
u64_stats_update_end(&stats64->syncp);
|
||||
#else
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0)
|
||||
net->stats.rx_packets += rx_packets;
|
||||
net->stats.rx_bytes += rx_bytes;
|
||||
#else
|
||||
u64_stats_add(&net->stats.rx_packets, rx_packets);
|
||||
u64_stats_add(&net->stats.rx_bytes, rx_bytes);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static void rmnet_vnd_update_tx_stats(struct net_device *net,
|
||||
unsigned tx_packets, unsigned tx_bytes) {
|
||||
unsigned tx_packets, unsigned tx_bytes) {
|
||||
#if defined(MHI_NETDEV_STATUS64)
|
||||
struct qmap_priv *dev = netdev_priv(net);
|
||||
struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->stats64);
|
||||
|
||||
u64_stats_update_begin(&stats64->syncp);
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 6,0,0 ))
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0)
|
||||
stats64->tx_packets += tx_packets;
|
||||
stats64->tx_bytes += tx_bytes;
|
||||
#else
|
||||
u64_stats_add(&stats64->tx_packets, tx_packets);
|
||||
u64_stats_add(&stats64->tx_bytes, tx_bytes);
|
||||
u64_stats_add(&stats64->tx_packets, tx_packets);
|
||||
u64_stats_add(&stats64->tx_bytes, tx_bytes);
|
||||
#endif
|
||||
u64_stats_update_end(&stats64->syncp);
|
||||
#else
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0)
|
||||
net->stats.tx_packets += tx_packets;
|
||||
net->stats.tx_bytes += tx_bytes;
|
||||
#else
|
||||
u64_stats_add(&net->stats.tx_packets, tx_packets);
|
||||
u64_stats_add(&net->tx_bytes, tx_bytes);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -825,43 +822,32 @@ static struct rtnl_link_stats64 *_rmnet_vnd_get_stats64(struct net_device *net,
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct pcpu_sw_netstats *stats64;
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 6,1,0 ))
|
||||
u64 rx_packets, rx_bytes;
|
||||
u64 tx_packets, tx_bytes;
|
||||
#else
|
||||
u64_stats_t rx_packets, rx_bytes;
|
||||
u64_stats_t tx_packets, tx_bytes;
|
||||
#endif
|
||||
|
||||
stats64 = per_cpu_ptr(dev->stats64, cpu);
|
||||
|
||||
do {
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION( 6,6,0 ))
|
||||
start = u64_stats_fetch_begin(&stats64->syncp);
|
||||
#else
|
||||
start = u64_stats_fetch_begin_irq(&stats64->syncp);
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0)
|
||||
rx_packets = stats64->rx_packets;
|
||||
rx_bytes = stats64->rx_bytes;
|
||||
tx_packets = stats64->tx_packets;
|
||||
tx_bytes = stats64->tx_bytes;
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION( 6,6,0 ))
|
||||
} while (u64_stats_fetch_retry(&stats64->syncp, start));
|
||||
#else
|
||||
} while (u64_stats_fetch_retry_irq(&stats64->syncp, start));
|
||||
rx_packets = u64_stats_read(&stats64->rx_packets);
|
||||
rx_bytes = u64_stats_read(&stats64->rx_bytes);
|
||||
tx_packets = u64_stats_read(&stats64->tx_packets);
|
||||
tx_bytes = u64_stats_read(&stats64->tx_bytes);
|
||||
#endif
|
||||
} while (u64_stats_fetch_retry(&stats64->syncp, start));
|
||||
|
||||
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 6,1,0 ))
|
||||
stats->rx_packets += rx_packets;
|
||||
stats->rx_bytes += rx_bytes;
|
||||
stats->tx_packets += tx_packets;
|
||||
stats->tx_bytes += tx_bytes;
|
||||
#else
|
||||
stats->rx_packets += u64_stats_read(&rx_packets);
|
||||
stats->rx_bytes += u64_stats_read(&rx_bytes);
|
||||
stats->tx_packets += u64_stats_read(&tx_packets);
|
||||
stats->tx_bytes += u64_stats_read(&tx_bytes);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
return stats;
|
||||
@ -911,7 +897,7 @@ static void rmnet_usb_tx_skb_destructor(struct sk_buff *skb) {
|
||||
|
||||
if (pQmapDev && pQmapDev->use_rmnet_usb) {
|
||||
int i;
|
||||
|
||||
|
||||
for (i = 0; i < pQmapDev->qmap_mode; i++) {
|
||||
struct net_device *qmap_net = pQmapDev->mpQmapNetDev[i];
|
||||
|
||||
@ -981,7 +967,7 @@ static void rmnet_usb_tx_agg_work(struct work_struct *work)
|
||||
ktime_get_ts64(&priv->agg_time);
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->agg_lock, flags);
|
||||
|
||||
|
||||
if (skb) {
|
||||
int err;
|
||||
#if 0
|
||||
@ -1012,7 +998,7 @@ static long agg_bypass_time __read_mostly = 10000000L;
|
||||
module_param(agg_bypass_time, long, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(agg_bypass_time, "Skip agg when apart spaced more than this");
|
||||
|
||||
static int rmnet_usb_tx_agg(struct sk_buff *skb, struct qmap_priv *priv, int *hdr_data, int hdr_len, int ip_offset) {
|
||||
static int rmnet_usb_tx_agg(struct sk_buff *skb, struct qmap_priv *priv) {
|
||||
struct qmi_wwan_state *info = (void *)&priv->dev->data;
|
||||
sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused;
|
||||
struct tx_agg_ctx *ctx = &pQmapDev->tx_ctx;
|
||||
@ -1035,22 +1021,15 @@ static int rmnet_usb_tx_agg(struct sk_buff *skb, struct qmap_priv *priv, int *hd
|
||||
rmnet_vnd_update_tx_stats(pNet, 1, skb->len);
|
||||
|
||||
if (ctx->ul_data_aggregation_max_datagrams == 1) {
|
||||
agg_skb = alloc_skb(skb->len + hdr_len, GFP_ATOMIC);
|
||||
if (agg_skb) {
|
||||
memcpy(skb_put(agg_skb, hdr_len), hdr_data, hdr_len);
|
||||
memcpy(skb_put(agg_skb, skb->len - ip_offset), skb->data + ip_offset, skb->len - ip_offset);
|
||||
agg_skb->protocol = htons(ETH_P_MAP);
|
||||
agg_skb->dev = priv->real_dev;
|
||||
skb->protocol = htons(ETH_P_MAP);
|
||||
skb->dev = priv->real_dev;
|
||||
#if 0
|
||||
if (!agg_skb->destructor)
|
||||
agg_skb->destructor = rmnet_usb_tx_skb_destructor;
|
||||
if (!skb->destructor)
|
||||
skb->destructor = rmnet_usb_tx_skb_destructor;
|
||||
#endif
|
||||
err = dev_queue_xmit(agg_skb);
|
||||
if (err != NET_XMIT_SUCCESS)
|
||||
err = dev_queue_xmit(skb);
|
||||
if (err != NET_XMIT_SUCCESS)
|
||||
pNet->stats.tx_errors++;
|
||||
}
|
||||
dev_kfree_skb_any(skb);
|
||||
skb = NULL;
|
||||
return NET_XMIT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -1062,9 +1041,8 @@ new_packet:
|
||||
diff = timespec64_sub(now, priv->agg_time);
|
||||
|
||||
if (priv->agg_skb) {
|
||||
if ((priv->agg_skb->len + skb->len + hdr_len) < ctx->ul_data_aggregation_max_size) {
|
||||
memcpy(skb_put(priv->agg_skb, hdr_len), hdr_data, hdr_len);
|
||||
memcpy(skb_put(priv->agg_skb, skb->len - ip_offset), skb->data + ip_offset, skb->len - ip_offset);
|
||||
if ((priv->agg_skb->len + skb->len) < ctx->ul_data_aggregation_max_size) {
|
||||
memcpy(skb_put(priv->agg_skb, skb->len), skb->data, skb->len);
|
||||
priv->agg_count++;
|
||||
|
||||
if (diff.tv_sec > 0 || diff.tv_nsec > agg_time_limit) {
|
||||
@ -1074,14 +1052,14 @@ new_packet:
|
||||
ready2send = 1;
|
||||
}
|
||||
else if (xmit_more == 0) {
|
||||
struct rmnet_map_header *map_header = (struct rmnet_map_header *)hdr_data;
|
||||
struct rmnet_map_header *map_header = (struct rmnet_map_header *)skb->data;
|
||||
size_t offset = sizeof(struct rmnet_map_header);
|
||||
if (map_header->next_hdr)
|
||||
offset += sizeof(struct rmnet_map_v5_csum_header);
|
||||
|
||||
ready2send = rmnet_usb_tx_agg_skip(skb, offset);
|
||||
}
|
||||
|
||||
|
||||
dev_kfree_skb_any(skb);
|
||||
skb = NULL;
|
||||
}
|
||||
@ -1100,7 +1078,7 @@ new_packet:
|
||||
ready2send = 1;
|
||||
}
|
||||
else if (xmit_more == 0) {
|
||||
struct rmnet_map_header *map_header = (struct rmnet_map_header *)hdr_data;
|
||||
struct rmnet_map_header *map_header = (struct rmnet_map_header *)skb->data;
|
||||
size_t offset = sizeof(struct rmnet_map_header);
|
||||
if (map_header->next_hdr)
|
||||
offset += sizeof(struct rmnet_map_v5_csum_header);
|
||||
@ -1112,8 +1090,7 @@ new_packet:
|
||||
priv->agg_skb = alloc_skb(ctx->ul_data_aggregation_max_size, GFP_ATOMIC);
|
||||
if (priv->agg_skb) {
|
||||
skb_reset_network_header(priv->agg_skb); //protocol da1a is buggy, dev wwan0
|
||||
memcpy(skb_put(priv->agg_skb, hdr_len), hdr_data, hdr_len);
|
||||
memcpy(skb_put(priv->agg_skb, skb->len - ip_offset), skb->data + ip_offset, skb->len - ip_offset);
|
||||
memcpy(skb_put(priv->agg_skb, skb->len), skb->data, skb->len);
|
||||
priv->agg_count++;
|
||||
dev_kfree_skb_any(skb);
|
||||
skb = NULL;
|
||||
@ -1124,12 +1101,7 @@ new_packet:
|
||||
}
|
||||
|
||||
if (ready2send) {
|
||||
agg_skb = alloc_skb(skb->len + hdr_len, GFP_ATOMIC);
|
||||
if (agg_skb) {
|
||||
memcpy(skb_put(agg_skb, hdr_len), hdr_data, hdr_len);
|
||||
memcpy(skb_put(agg_skb, skb->len - ip_offset), skb->data + ip_offset, skb->len - ip_offset);
|
||||
}
|
||||
dev_kfree_skb_any(skb);
|
||||
agg_skb = skb;
|
||||
skb = NULL;
|
||||
}
|
||||
}
|
||||
@ -1170,9 +1142,6 @@ static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb,
|
||||
{
|
||||
int err;
|
||||
struct qmap_priv *priv = netdev_priv(pNet);
|
||||
int qmap_hdr[2];
|
||||
int hdr_len = 0;
|
||||
int ip_offset = 0;
|
||||
|
||||
if (netif_queue_stopped(priv->real_dev)) {
|
||||
netif_stop_queue(pNet);
|
||||
@ -1190,24 +1159,18 @@ static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
ip_offset = ETH_HLEN;
|
||||
#else
|
||||
if (skb_pull(skb, ETH_HLEN) == NULL) {
|
||||
dev_kfree_skb_any (skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
//printk("%s 2 skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len);
|
||||
|
||||
if (priv->qmap_version == 5) {
|
||||
add_qhdr(skb, priv->mux_id, qmap_hdr, ip_offset);
|
||||
hdr_len = 4;
|
||||
add_qhdr(skb, priv->mux_id);
|
||||
}
|
||||
else if (priv->qmap_version == 9) {
|
||||
add_qhdr_v5(skb, priv->mux_id, qmap_hdr, ip_offset);
|
||||
hdr_len = 8;
|
||||
add_qhdr_v5(skb, priv->mux_id);
|
||||
}
|
||||
else {
|
||||
dev_kfree_skb_any (skb);
|
||||
@ -1215,7 +1178,7 @@ static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb,
|
||||
}
|
||||
//printk("%s skb=%p, len=%d, protocol=%x, hdr_len=%d\n", __func__, skb, skb->len, skb->protocol, skb->hdr_len);
|
||||
|
||||
err = rmnet_usb_tx_agg(skb, priv, qmap_hdr, hdr_len, ip_offset);
|
||||
err = rmnet_usb_tx_agg(skb, priv);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -1283,7 +1246,7 @@ static rx_handler_result_t qca_nss_rx_handler(struct sk_buff **pskb)
|
||||
|
||||
if (!skb)
|
||||
return RX_HANDLER_CONSUMED;
|
||||
|
||||
|
||||
//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)
|
||||
@ -1332,10 +1295,10 @@ static int qmap_register_device(sQmiWwanQmap * pDev, u8 offset_id)
|
||||
priv->dev = pDev->mpNetDev;
|
||||
priv->qmap_version = pDev->qmap_version;
|
||||
priv->mux_id = QUECTEL_QMAP_MUX_ID + offset_id;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,17,0)
|
||||
__dev_addr_set(qmap_net, real_dev->dev_addr, ETH_ALEN);
|
||||
#else
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 17, 0)
|
||||
memcpy (qmap_net->dev_addr, real_dev->dev_addr, ETH_ALEN);
|
||||
#else
|
||||
eth_hw_addr_set (real_dev, qmap_net->dev_addr);
|
||||
#endif
|
||||
|
||||
#ifdef QUECTEL_BRIDGE_MODE
|
||||
@ -1351,9 +1314,7 @@ static int qmap_register_device(sQmiWwanQmap * pDev, u8 offset_id)
|
||||
if (nss_cb && use_qca_nss) {
|
||||
rmnet_usb_rawip_setup(qmap_net);
|
||||
}
|
||||
#ifdef CONFIG_PINCTRL_IPQ9574
|
||||
rmnet_usb_rawip_setup(qmap_net);
|
||||
#endif
|
||||
|
||||
priv->agg_skb = NULL;
|
||||
priv->agg_count = 0;
|
||||
hrtimer_init(&priv->agg_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
@ -1389,7 +1350,7 @@ static int qmap_register_device(sQmiWwanQmap * pDev, u8 offset_id)
|
||||
netdev_info(qmap_net, "NSS context created\n");
|
||||
rtnl_lock();
|
||||
netdev_rx_handler_register(qmap_net, qca_nss_rx_handler, NULL);
|
||||
rtnl_unlock();
|
||||
rtnl_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1418,7 +1379,7 @@ static void qmap_unregister_device(sQmiWwanQmap * pDev, u8 offset_id) {
|
||||
pDev->mpQmapNetDev[offset_id] = NULL;
|
||||
netif_carrier_off( qmap_net );
|
||||
netif_stop_queue( qmap_net );
|
||||
|
||||
|
||||
hrtimer_cancel(&priv->agg_hrtimer);
|
||||
cancel_work_sync(&priv->agg_wq);
|
||||
spin_lock_irqsave(&priv->agg_lock, flags);
|
||||
@ -1460,8 +1421,6 @@ typedef struct {
|
||||
} BRMAC_SETTING;
|
||||
#endif
|
||||
|
||||
static int qma_setting_store(struct device *dev, QMAP_SETTING *qmap_settings, size_t size);
|
||||
|
||||
int qma_setting_store(struct device *dev, QMAP_SETTING *qmap_settings, size_t size) {
|
||||
struct net_device *netdev = to_net_dev(dev);
|
||||
struct usbnet * usbnetdev = netdev_priv( netdev );
|
||||
@ -1501,11 +1460,7 @@ static int qmap_ndo_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
#ifdef CONFIG_BRIDGE_LAN
|
||||
BRMAC_SETTING brmac_settings = {0};
|
||||
#endif
|
||||
#ifdef CONFIG_CLEAR_HALT
|
||||
uint clear_halt = 0;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
switch (cmd) {
|
||||
case 0x89F1: //SIOCDEVPRIVATE
|
||||
rc = copy_from_user(&link_state, ifr->ifr_ifru.ifru_data, sizeof(link_state));
|
||||
@ -1538,7 +1493,7 @@ static int qmap_ndo_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
rc = copy_to_user(ifr->ifr_ifru.ifru_data, &pQmapDev->rmnet_info, sizeof(pQmapDev->rmnet_info));
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
#ifdef CONFIG_BRIDGE_LAN
|
||||
case 0x89F4: //SIOCDEVPRIVATE
|
||||
rc = copy_from_user(&brmac_settings, ifr->ifr_ifru.ifru_data, sizeof(brmac_settings));
|
||||
@ -1560,16 +1515,6 @@ static int qmap_ndo_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_CLEAR_HALT
|
||||
case 0x89F5: //SIOCDEVPRIVATE
|
||||
rc = copy_from_user(&clear_halt, ifr->ifr_ifru.ifru_data, sizeof(clear_halt));
|
||||
if (rc == 0 && clear_halt == 1) {
|
||||
usb_clear_halt(usbnetdev->udev,usbnetdev->in);
|
||||
usb_clear_halt(usbnetdev->udev,usbnetdev->out);
|
||||
pr_info("usb_clear_halt EPIN EPOUT\n");
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
@ -1712,9 +1657,9 @@ static struct sk_buff *qmap_qmi_wwan_tx_fixup(struct usbnet *dev, struct sk_buff
|
||||
|
||||
if (skb) {
|
||||
if(pQmapDev->qmap_version == 5)
|
||||
add_qhdr(skb, QUECTEL_QMAP_MUX_ID, NULL, 0);
|
||||
add_qhdr(skb, QUECTEL_QMAP_MUX_ID);
|
||||
else
|
||||
add_qhdr_v5(skb, QUECTEL_QMAP_MUX_ID, NULL, 0);
|
||||
add_qhdr_v5(skb, QUECTEL_QMAP_MUX_ID);
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
@ -1745,7 +1690,7 @@ static void qmap_packet_decode(sQmiWwanQmap *pQmapDev,
|
||||
while (skb_in->len > sizeof(struct qmap_hdr)) {
|
||||
struct rmnet_map_header *map_header = (struct rmnet_map_header *)skb_in->data;
|
||||
struct rmnet_map_v5_csum_header *ul_header = NULL;
|
||||
size_t hdr_size = sizeof(struct rmnet_map_header);
|
||||
size_t hdr_size = sizeof(struct rmnet_map_header);
|
||||
struct net_device *qmap_net;
|
||||
int pkt_len = ntohs(map_header->pkt_len);
|
||||
int skb_len;
|
||||
@ -1757,7 +1702,7 @@ static void qmap_packet_decode(sQmiWwanQmap *pQmapDev,
|
||||
ul_header = (struct rmnet_map_v5_csum_header *)(map_header + 1);
|
||||
hdr_size += sizeof(struct rmnet_map_v5_csum_header);
|
||||
}
|
||||
|
||||
|
||||
skb_len = pkt_len - (map_header->pad_len&0x3F);
|
||||
skb_len -= dl_minimum_padding;
|
||||
|
||||
@ -1772,7 +1717,7 @@ static void qmap_packet_decode(sQmiWwanQmap *pQmapDev,
|
||||
dev_info(dev, "drop qmap unknow mux_id %x\n", map_header->mux_id);
|
||||
goto skip_pkt;
|
||||
}
|
||||
|
||||
|
||||
if (skb_len > qmap_net->mtu) {
|
||||
dev_info(dev, "drop skb_len=%x larger than mtu=%d\n", skb_len, qmap_net->mtu);
|
||||
goto error_pkt;
|
||||
@ -1815,7 +1760,7 @@ static void qmap_packet_decode(sQmiWwanQmap *pQmapDev,
|
||||
dev_info(dev, "unknow skb->protocol %02x\n", skb_in->data[hdr_size]);
|
||||
goto error_pkt;
|
||||
}
|
||||
|
||||
|
||||
qmap_skb = netdev_alloc_skb(qmap_net, skb_len);
|
||||
if (qmap_skb) {
|
||||
skb_put(qmap_skb, skb_len);
|
||||
@ -1870,7 +1815,7 @@ static int qmap_qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
|
||||
sQmiWwanQmap *pQmapDev = (sQmiWwanQmap *)info->unused;
|
||||
struct sk_buff *qmap_skb;
|
||||
struct sk_buff_head skb_chain;
|
||||
|
||||
|
||||
if (pQmapDev->qmap_mode == 0)
|
||||
return qmi_wwan_rx_fixup(dev, skb_in);
|
||||
|
||||
@ -1917,7 +1862,7 @@ static void (*_usbnet_get_stats64)(struct net_device *net, struct rtnl_link_stat
|
||||
static void qmi_wwan_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats) {
|
||||
if (_usbnet_get_stats64) ////c8b5d129ee293bcf972e7279ac996bb8a138505c
|
||||
return _usbnet_get_stats64(net, stats);
|
||||
|
||||
|
||||
netdev_stats_to_stats64(stats, &net->stats);
|
||||
}
|
||||
#else
|
||||
@ -1926,7 +1871,7 @@ static struct rtnl_link_stats64 * (*_usbnet_get_stats64)(struct net_device *net,
|
||||
static struct rtnl_link_stats64 * qmi_wwan_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats) {
|
||||
if (_usbnet_get_stats64)
|
||||
return _usbnet_get_stats64(net, stats);
|
||||
|
||||
|
||||
netdev_stats_to_stats64(stats, &net->stats);
|
||||
return stats;
|
||||
}
|
||||
@ -1951,7 +1896,7 @@ static int qmi_wwan_open (struct net_device *net) {
|
||||
}
|
||||
|
||||
static netdev_tx_t qmi_wwan_start_xmit (struct sk_buff *skb,
|
||||
struct net_device *net)
|
||||
struct net_device *net)
|
||||
{
|
||||
struct usbnet * usbnetdev = netdev_priv( net );
|
||||
struct qmi_wwan_state *info = (void *)&usbnetdev->data;
|
||||
@ -1962,7 +1907,7 @@ static netdev_tx_t qmi_wwan_start_xmit (struct sk_buff *skb,
|
||||
|
||||
if (netif_queue_stopped(net) && pQmapDev && pQmapDev->use_rmnet_usb) {
|
||||
int i;
|
||||
|
||||
|
||||
for (i = 0; i < pQmapDev->qmap_mode; i++) {
|
||||
struct net_device *qmap_net = pQmapDev->mpQmapNetDev[i];
|
||||
if (qmap_net) {
|
||||
@ -2004,14 +1949,8 @@ static void ql_net_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *i
|
||||
{
|
||||
/* Inherit standard device info */
|
||||
usbnet_get_drvinfo(net, info);
|
||||
/* strlcpy() is deprecated in kernel 6.8.0+, using strscpy instead */
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6,8,0))
|
||||
strlcpy(info->driver, driver_name, sizeof(info->driver));
|
||||
strlcpy(info->version, VERSION_NUMBER, sizeof(info->version));
|
||||
#else
|
||||
strscpy(info->driver, driver_name, sizeof(info->driver));
|
||||
strscpy(info->version, VERSION_NUMBER, sizeof(info->version));
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct ethtool_ops ql_net_ethtool_ops;
|
||||
@ -2076,7 +2015,7 @@ static int qmi_wwan_register_subdriver(struct usbnet *dev)
|
||||
atomic_set(&info->pmcount, 0);
|
||||
|
||||
/* register subdriver */
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION( 5,14,0 )) //cac6fb015f719104e60b1c68c15ca5b734f57b9c
|
||||
#if (LINUX_VERSION_CODE > KERNEL_VERSION( 5,12,0 )) //cac6fb015f719104e60b1c68c15ca5b734f57b9c
|
||||
subdriver = usb_cdc_wdm_register(info->control, &dev->status->desc,
|
||||
4096, WWAN_PORT_QMI, &qmi_wwan_cdc_wdm_manage_power);
|
||||
#else
|
||||
@ -2127,17 +2066,17 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
|
||||
/* make MAC addr easily distinguishable from an IP header */
|
||||
if (possibly_iphdr(dev->net->dev_addr)) {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,17,0)
|
||||
u8 temp_addr[ETH_ALEN];
|
||||
memcpy(temp_addr, dev->net->dev_addr, ETH_ALEN);
|
||||
temp_addr[0] |= 0x02; /* set local assignment bit */
|
||||
temp_addr[0] &= 0xbf; /* clear "IP" bit */
|
||||
__dev_addr_set(dev->net, temp_addr, ETH_ALEN);
|
||||
#else
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0)
|
||||
dev->net->dev_addr[0] |= 0x02; /* set local assignment bit */
|
||||
dev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */
|
||||
#else
|
||||
u8 addr = dev->net->dev_addr[0];
|
||||
|
||||
addr |= 0x02; /* set local assignment bit */
|
||||
addr &= 0xbf; /* clear "IP" bit */
|
||||
dev_addr_mod(dev->net, 0, &addr, 1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (!_usbnet_get_stats64)
|
||||
_usbnet_get_stats64 = dev->net->netdev_ops->ndo_get_stats64;
|
||||
dev->net->netdev_ops = &qmi_wwan_netdev_ops;
|
||||
@ -2170,9 +2109,6 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
NULL, 0, 100);
|
||||
}
|
||||
|
||||
usb_clear_halt(dev->udev,dev->in);
|
||||
usb_clear_halt(dev->udev,dev->out);
|
||||
|
||||
//to advoid module report mtu 1460, but rx 1500 bytes IP packets, and cause the customer's system crash
|
||||
//next setting can make usbnet.c:usbnet_change_mtu() do not modify rx_urb_size according to hard mtu
|
||||
dev->rx_urb_size = ETH_DATA_LEN + ETH_HLEN + 6;
|
||||
@ -2204,13 +2140,12 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
int qmap_version = (dev->driver_info->data>>8)&0xFF;
|
||||
int qmap_size = (dev->driver_info->data)&0xFF;
|
||||
int idProduct = le16_to_cpu(dev->udev->descriptor.idProduct);
|
||||
int lte_a = (idProduct == 0x0306 || idProduct == 0x030B || idProduct == 0x0512 || idProduct == 0x0620 ||
|
||||
idProduct == 0x0800 || idProduct == 0x0801 || idProduct == 0x0122 || idProduct == 0x0316);
|
||||
int lte_a = (idProduct == 0x0306 || idProduct == 0x030B || idProduct == 0x0512 || idProduct == 0x0620 || idProduct == 0x0800 || idProduct == 0x0801);
|
||||
|
||||
if (qmap_size > 4096 || dev->udev->speed >= USB_SPEED_SUPER) { //if meet this requirements, must be LTE-A or 5G
|
||||
lte_a = 1;
|
||||
}
|
||||
|
||||
|
||||
pQmapDev->qmap_mode = qmap_mode;
|
||||
if (lte_a && pQmapDev->qmap_mode == 0) {
|
||||
pQmapDev->qmap_mode = 1; //force use QMAP
|
||||
@ -2227,19 +2162,17 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
|
||||
if (pQmapDev->qmap_mode > 1)
|
||||
pQmapDev->use_rmnet_usb = 1;
|
||||
else if (idProduct == 0x0800 || idProduct == 0x0801 || idProduct == 0x0122)
|
||||
else if (idProduct == 0x0800 || idProduct == 0x0801)
|
||||
pQmapDev->use_rmnet_usb = 1; //benefit for ul data agg
|
||||
#ifdef QMI_NETDEV_ONE_CARD_MODE
|
||||
if(pQmapDev->use_rmnet_usb == 1 && pQmapDev->qmap_mode == 1)
|
||||
one_card_mode = 1;
|
||||
pQmapDev->rmnet_info.mux_id[0] = QUECTEL_QMAP_MUX_ID;
|
||||
#endif
|
||||
#endif
|
||||
pQmapDev->rmnet_info.size = sizeof(RMNET_INFO);
|
||||
pQmapDev->rmnet_info.rx_urb_size = pQmapDev->qmap_size;
|
||||
pQmapDev->rmnet_info.ep_type = 2; //DATA_EP_TYPE_HSUSB
|
||||
pQmapDev->rmnet_info.iface_id = 4;//Interface ID
|
||||
if(idProduct == 0x0316)
|
||||
pQmapDev->rmnet_info.iface_id = 3;// SDX35 Interface ID
|
||||
pQmapDev->rmnet_info.iface_id = 4;
|
||||
pQmapDev->rmnet_info.qmap_mode = pQmapDev->qmap_mode;
|
||||
pQmapDev->rmnet_info.qmap_version = pQmapDev->qmap_version;
|
||||
pQmapDev->rmnet_info.dl_minimum_padding = 0;
|
||||
@ -2249,7 +2182,7 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
pQmapDev->tx_ctx.ul_data_aggregation_max_size = 1500;
|
||||
#endif
|
||||
|
||||
if (pQmapDev->use_rmnet_usb && !one_card_mode) {
|
||||
if (pQmapDev->use_rmnet_usb && !one_card_mode) {
|
||||
pQmapDev->driver_info = rmnet_usb_info;
|
||||
pQmapDev->driver_info.data = dev->driver_info->data;
|
||||
dev->driver_info = &pQmapDev->driver_info;
|
||||
@ -2417,7 +2350,7 @@ static int rmnet_usb_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2478,7 +2411,7 @@ static const struct driver_info qmi_wwan_info = {
|
||||
};
|
||||
|
||||
#define qmi_wwan_raw_ip_info \
|
||||
.description = "WWAN/QMI Raw IP device", \
|
||||
.description = "WWAN/QMI device", \
|
||||
.flags = FLAG_WWAN | FLAG_RX_ASSEMBLE | FLAG_NOARP | FLAG_SEND_ZLP, \
|
||||
.bind = qmi_wwan_bind, \
|
||||
.unbind = qmi_wwan_unbind, \
|
||||
@ -2526,7 +2459,6 @@ static const struct usb_device_id products[] = {
|
||||
{ QMI_FIXED_INTF(0x05C6, 0x9215, 4) }, /* Quectel EC20 (MDM9215) */
|
||||
{ QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0125, 4, mdm9x07) }, /* Quectel EC20 (MDM9X07)/EC25/EG25 */
|
||||
{ QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0121, 4, mdm9x07) }, /* Quectel EC21 */
|
||||
{ QMI_FIXED_RAWIP_INTF(0x2C7C, 0x030E, 4, mdm9x07) }, /* Quectel EM05G */
|
||||
{ QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0191, 4, mdm9x07) }, /* Quectel EG91 */
|
||||
{ QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0195, 4, mdm9x07) }, /* Quectel EG95 */
|
||||
{ QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0700, 3, mdm9x07) }, /* Quectel BG95 (at+qcfgext="usbnet","rmnet") */
|
||||
@ -2536,10 +2468,8 @@ static const struct usb_device_id products[] = {
|
||||
{ QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0296, 4, mdm9x07) }, /* Quectel BG96 */
|
||||
{ QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0435, 4, mdm9x07) }, /* Quectel AG35 */
|
||||
{ QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0620, 4, mdm9x40) }, /* Quectel EG20 */
|
||||
{ QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0316, 3, mdm9x40) }, /* Quectel RG255 */
|
||||
{ QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0800, 4, sdx55) }, /* Quectel RG500 */
|
||||
{ QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0801, 4, sdx55) }, /* Quectel RG520 */
|
||||
{ QMI_FIXED_RAWIP_INTF(0x2C7C, 0x0122, 4, sdx55) }, /* Quectel RG650 */
|
||||
{ } /* END */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, products);
|
||||
@ -2646,11 +2576,11 @@ static void qmap_qmi_wwan_disconnect(struct usb_interface *intf)
|
||||
}
|
||||
|
||||
tasklet_kill(&pQmapDev->txq);
|
||||
|
||||
|
||||
usbnet_disconnect(intf);
|
||||
/* struct usbnet *dev had free by usbnet_disconnect()->free_netdev().
|
||||
so we should access info. */
|
||||
//info->unused = 0;
|
||||
//info->unused = 0;
|
||||
kfree(pQmapDev);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1,28 +1,34 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=sendat
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://github.com/ouyangzq/sendat.git
|
||||
PKG_SOURCE_DATE:=2023-10-26
|
||||
PKG_SOURCE_VERSION:=624c58f6331e5be3eac6d144ed985b4a4af94b10
|
||||
PKG_MIRROR_HASH:=skip
|
||||
|
||||
MAKE_PATH:=src
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
include $(TOPDIR)/rules.mk#将openwrt顶层目录下的rules.mk文件中的内容导入进来
|
||||
|
||||
PKG_NAME:=sendat#软件包名
|
||||
PKG_VERSION:=6.8.2#软件包版本
|
||||
PKG_RELEASE:=2
|
||||
PKG_BUILD_DIR:= $(BUILD_DIR)/$(PKG_NAME)#真正编译当前软件包的目录
|
||||
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk#将$(TOPDIR)/include目录下的package.mk文件中的内容导入进来
|
||||
|
||||
define Package/sendat
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
TITLE:=Send AT Commnad
|
||||
DEPENDS:=+libpthread
|
||||
SECTION:=wrtnode#软件包类型
|
||||
CATEGORY:=Daocaoren#menuconfig中软件包所属的一级目录
|
||||
SUBMENU :=CPE#menuconfig中软件包所属的二级目录
|
||||
TITLE:=Sendat AT#软件包标题
|
||||
DEPENDS:=+libpthread#运行本软件依赖的其他包
|
||||
endef
|
||||
|
||||
define Package/sendat/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/src/sendat $(1)/usr/bin/
|
||||
|
||||
define Package/sendat/description #软件包描述
|
||||
A sample for sendat test
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,sendat))
|
||||
|
||||
define Build/Prepare #编译之前的准备动作
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
$(CP) ./src/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
|
||||
|
||||
define Package/sendat/install #软件包的安装方法,主要就是将一系列编译好的文件、启动脚本、UCI配置文件等拷贝到指定位置
|
||||
$(INSTALL_DIR) $(1)/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/sendat $(1)/bin/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,sendat))
|
||||
5
sendat/README.md
Normal file
5
sendat/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# sendat
|
||||
AT 命令工具
|
||||
1简化使用AT命令请求方式
|
||||
使用命令说明
|
||||
sendat 2 'ATI' 2表示 /dev/ttyUSB2 'ATI' 为at命令
|
||||
9
sendat/src/Makefile
Normal file
9
sendat/src/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
all:sendat
|
||||
|
||||
OBJS = sendat.o
|
||||
|
||||
sendat:$(OBJS)
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJS) -lpthread
|
||||
|
||||
clean:
|
||||
rm -f sendat *.o
|
||||
420
sendat/src/pdu.c
Normal file
420
sendat/src/pdu.c
Normal file
@ -0,0 +1,420 @@
|
||||
/*
|
||||
* 2017 - 2021 Cezary Jackiewicz <cezary@eko.one.pl>
|
||||
* 2014 lovewilliam <ztong@vt.edu>
|
||||
*/
|
||||
// Copyright 2011 The Avalon Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by the Apache License 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
//
|
||||
// SMS encoding/decoding functions, which are based on examples from:
|
||||
// http://www.dreamfabric.com/sms/
|
||||
|
||||
#include "pdu.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
enum {
|
||||
BITMASK_7BITS = 0x7F,
|
||||
BITMASK_8BITS = 0xFF,
|
||||
BITMASK_HIGH_4BITS = 0xF0,
|
||||
BITMASK_LOW_4BITS = 0x0F,
|
||||
|
||||
TYPE_OF_ADDRESS_UNKNOWN = 0x81,
|
||||
TYPE_OF_ADDRESS_INTERNATIONAL_PHONE = 0x91,
|
||||
TYPE_OF_ADDRESS_NATIONAL_SUBSCRIBER = 0xC8,
|
||||
TYPE_OF_ADDRESS_ALPHANUMERIC = 0xD0,
|
||||
|
||||
SMS_DELIVER_ONE_MESSAGE = 0x04,
|
||||
SMS_SUBMIT = 0x11,
|
||||
|
||||
SMS_MAX_7BIT_TEXT_LENGTH = 160,
|
||||
};
|
||||
|
||||
// Swap decimal digits of a number (e.g. 12 -> 21).
|
||||
static unsigned char
|
||||
SwapDecimalNibble(const unsigned char x)
|
||||
{
|
||||
return (x / 16) + ((x % 16) * 10);
|
||||
}
|
||||
|
||||
// Encode/Decode PDU: Translate ASCII 7bit characters to 8bit buffer.
|
||||
// SMS encoding example from: http://www.dreamfabric.com/sms/.
|
||||
//
|
||||
// 7-bit ASCII: "hellohello"
|
||||
// [0]:h [1]:e [2]:l [3]:l [4]:o [5]:h [6]:e [7]:l [8]:l [9]:o
|
||||
// 1101000 1100101 1101100 1101100 1101111 1101000 1100101 1101100 1101100 1101111
|
||||
// | ||| ||||| | ||||||| ||||||
|
||||
// /-------------/ ///-------/// /////-///// \------------\ ||||||| \\\\\\ .
|
||||
// | ||| ||||| | ||||||| ||||||
|
||||
// input buffer position
|
||||
// 10000000 22111111 33322222 44443333 55555333 66666655 77777776 98888888 --999999
|
||||
// | ||| ||||| | ||||||| ||||||
|
||||
// 8bit encoded buffer
|
||||
// 11101000 00110010 10011011 11111101 01000110 10010111 11011001 11101100 00110111
|
||||
// E8 32 9B FD 46 97 D9 EC 37
|
||||
|
||||
|
||||
// Encode PDU message by merging 7 bit ASCII characters into 8 bit octets.
|
||||
int
|
||||
EncodePDUMessage(const char* sms_text, int sms_text_length, unsigned char* output_buffer, int buffer_size)
|
||||
{
|
||||
// Check if output buffer is big enough.
|
||||
if ((sms_text_length * 7 + 7) / 8 > buffer_size)
|
||||
return -1;
|
||||
|
||||
int output_buffer_length = 0;
|
||||
int carry_on_bits = 1;
|
||||
int i = 0;
|
||||
|
||||
for (; i < sms_text_length - 1; ++i) {
|
||||
output_buffer[output_buffer_length++] =
|
||||
((sms_text[i] & BITMASK_7BITS) >> (carry_on_bits - 1)) |
|
||||
((sms_text[i + 1] & BITMASK_7BITS) << (8 - carry_on_bits));
|
||||
carry_on_bits++;
|
||||
if (carry_on_bits == 8) {
|
||||
carry_on_bits = 1;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
if (i <= sms_text_length)
|
||||
output_buffer[output_buffer_length++] = (sms_text[i] & BITMASK_7BITS) >> (carry_on_bits - 1);
|
||||
|
||||
return output_buffer_length;
|
||||
}
|
||||
|
||||
// Decode PDU message by splitting 8 bit encoded buffer into 7 bit ASCII
|
||||
// characters.
|
||||
int
|
||||
DecodePDUMessage_GSM_7bit(const unsigned char* buffer, int buffer_length, char* output_sms_text, int sms_text_length)
|
||||
{
|
||||
int output_text_length = 0;
|
||||
if (buffer_length > 0)
|
||||
output_sms_text[output_text_length++] = BITMASK_7BITS & buffer[0];
|
||||
|
||||
if (sms_text_length > 1) {
|
||||
int carry_on_bits = 1;
|
||||
int i = 1;
|
||||
for (; i < buffer_length; ++i) {
|
||||
|
||||
output_sms_text[output_text_length++] = BITMASK_7BITS & ((buffer[i] << carry_on_bits) | (buffer[i - 1] >> (8 - carry_on_bits)));
|
||||
|
||||
if (output_text_length == sms_text_length) break;
|
||||
|
||||
carry_on_bits++;
|
||||
|
||||
if (carry_on_bits == 8) {
|
||||
carry_on_bits = 1;
|
||||
output_sms_text[output_text_length++] = buffer[i] & BITMASK_7BITS;
|
||||
if (output_text_length == sms_text_length) break;
|
||||
}
|
||||
|
||||
}
|
||||
if (output_text_length < sms_text_length) // Add last remainder.
|
||||
output_sms_text[output_text_length++] = buffer[i - 1] >> (8 - carry_on_bits);
|
||||
}
|
||||
|
||||
return output_text_length;
|
||||
}
|
||||
|
||||
#define GSM_7BITS_ESCAPE 0x1b
|
||||
|
||||
static const unsigned char gsm7bits_to_latin1[128] = {
|
||||
'@', 0xa3, '$', 0xa5, 0xe8, 0xe9, 0xf9, 0xec, 0xf2, 0xc7, '\n', 0xd8, 0xf8, '\r', 0xc5, 0xe5,
|
||||
0, '_', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc6, 0xe6, 0xdf, 0xc9,
|
||||
' ', '!', '"', '#', 0xa4, '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
|
||||
0xa1, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
||||
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0xc4, 0xd6, 0xd1, 0xdc, 0xa7,
|
||||
0xbf, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0xe4, 0xf6, 0xf1, 0xfc, 0xe0,
|
||||
};
|
||||
|
||||
static const unsigned char gsm7bits_extend_to_latin1[128] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\f', 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, '^', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, '{', '}', 0, 0, 0, 0, 0,'\\',
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '[', '~', ']', 0,
|
||||
'|', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
static int
|
||||
G7bitToAscii(char* buffer, int buffer_length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i<buffer_length; i++) {
|
||||
if (buffer[i] < 128) {
|
||||
if (buffer[i] == GSM_7BITS_ESCAPE) {
|
||||
buffer[i] = gsm7bits_extend_to_latin1[buffer[i + 1]];
|
||||
memmove(&buffer[i + 1], &buffer[i + 2], buffer_length - i - 1);
|
||||
buffer_length--;
|
||||
} else {
|
||||
buffer[i] = gsm7bits_to_latin1[buffer[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer_length;
|
||||
}
|
||||
|
||||
#define NPC '?'
|
||||
|
||||
static const int latin1_to_gsm7bits[256] = {
|
||||
NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, 0x0a, NPC,-0x0a, 0x0d, NPC, NPC,
|
||||
NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC,
|
||||
0x20, 0x21, 0x22, 0x23, 0x02, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
||||
0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,-0x3c,-0x2f,-0x3e,-0x14, 0x11,
|
||||
NPC, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
||||
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,-0x28,-0x40,-0x29,-0x3d, NPC,
|
||||
NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC,
|
||||
NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC,
|
||||
NPC, 0x40, NPC, 0x01, 0x24, 0x03, NPC, 0x5f, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC,
|
||||
NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, 0x60,
|
||||
NPC, NPC, NPC, NPC, 0x5b, 0x0e, 0x1c, 0x09, NPC, 0x1f, NPC, NPC, NPC, NPC, NPC, NPC,
|
||||
NPC, 0x5d, NPC, NPC, NPC, NPC, 0x5c, NPC, 0x0b, NPC, NPC, NPC, 0x5e, NPC, NPC, 0x1e,
|
||||
0x7f, NPC, NPC, NPC, 0x7b, 0x0f, 0x1d, NPC, 0x04, 0x05, NPC, NPC, 0x07, NPC, NPC, NPC,
|
||||
NPC, 0x7d, 0x08, NPC, NPC, NPC, 0x7c, NPC, 0x0c, 0x06, NPC, NPC, 0x7e, NPC, NPC, NPC,
|
||||
};
|
||||
|
||||
static int
|
||||
AsciiToG7bit(const char* buffer, int buffer_length, unsigned char* output_buffer)
|
||||
{
|
||||
int i, j, val;
|
||||
|
||||
j=0;
|
||||
for (i = 0; i < buffer_length; i++) {
|
||||
val = latin1_to_gsm7bits[buffer[i] & 0xFF];
|
||||
if (val < 0) {
|
||||
output_buffer[j++] = GSM_7BITS_ESCAPE;
|
||||
output_buffer[j++] = -1*val;
|
||||
} else {
|
||||
if (((buffer[i] & 0xFF) & 0xE0) == 0xC0) { /* test for two byte utf8 char */
|
||||
val = NPC;
|
||||
i++;
|
||||
} else if (((buffer[i] & 0xFF) & 0xF0) == 0xE0) { /* test for three byte utf8 char */
|
||||
val = NPC;
|
||||
i++;
|
||||
i++;
|
||||
}
|
||||
output_buffer[j++] = val;
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
// Encode a digit based phone number for SMS based format.
|
||||
static int
|
||||
EncodePhoneNumber(const char* phone_number, unsigned char* output_buffer, int buffer_size)
|
||||
{
|
||||
int output_buffer_length = 0;
|
||||
const int phone_number_length = strlen(phone_number);
|
||||
|
||||
// Check if the output buffer is big enough.
|
||||
if ((phone_number_length + 1) / 2 > buffer_size)
|
||||
return -1;
|
||||
|
||||
int i = 0;
|
||||
for (; i < phone_number_length; ++i) {
|
||||
|
||||
if (phone_number[i] < '0' && phone_number[i] > '9')
|
||||
return -1;
|
||||
|
||||
if (i % 2 == 0) {
|
||||
output_buffer[output_buffer_length++] = BITMASK_HIGH_4BITS | (phone_number[i] - '0');
|
||||
} else {
|
||||
output_buffer[output_buffer_length - 1] =
|
||||
(output_buffer[output_buffer_length - 1] & BITMASK_LOW_4BITS) |
|
||||
((phone_number[i] - '0') << 4);
|
||||
}
|
||||
}
|
||||
|
||||
return output_buffer_length;
|
||||
}
|
||||
|
||||
// Decode a digit based phone number for SMS based format.
|
||||
static int
|
||||
DecodePhoneNumber(const unsigned char* buffer, int phone_number_length, char* output_phone_number)
|
||||
{
|
||||
int i = 0;
|
||||
for (; i < phone_number_length; ++i) {
|
||||
if (i % 2 == 0)
|
||||
output_phone_number[i] = (buffer[i / 2] & BITMASK_LOW_4BITS) + '0';
|
||||
else
|
||||
output_phone_number[i] = ((buffer[i / 2] & BITMASK_HIGH_4BITS) >> 4) + '0';
|
||||
}
|
||||
output_phone_number[phone_number_length] = '\0'; // Terminate C string.
|
||||
return phone_number_length;
|
||||
}
|
||||
|
||||
// Encode a SMS message to PDU
|
||||
int
|
||||
pdu_encode(const char* service_center_number, const char* phone_number, const char* sms_text,
|
||||
unsigned char* output_buffer, int buffer_size)
|
||||
{
|
||||
if (buffer_size < 2)
|
||||
return -1;
|
||||
|
||||
int output_buffer_length = 0;
|
||||
|
||||
// 1. Set SMS center number.
|
||||
int length = 0;
|
||||
if (service_center_number && strlen(service_center_number) > 0) {
|
||||
output_buffer[1] = TYPE_OF_ADDRESS_INTERNATIONAL_PHONE;
|
||||
length = EncodePhoneNumber(service_center_number,
|
||||
output_buffer + 2, buffer_size - 2);
|
||||
if (length < 0 && length >= 254)
|
||||
return -1;
|
||||
length++; // Add type of address.
|
||||
}
|
||||
output_buffer[0] = length;
|
||||
output_buffer_length = length + 1;
|
||||
if (output_buffer_length + 4 > buffer_size)
|
||||
return -1; // Check if it has space for four more bytes.
|
||||
|
||||
// 2. Set type of message.
|
||||
output_buffer[output_buffer_length++] = SMS_SUBMIT;
|
||||
output_buffer[output_buffer_length++] = 0x00; // Message reference.
|
||||
|
||||
// 3. Set phone number.
|
||||
output_buffer[output_buffer_length] = strlen(phone_number);
|
||||
|
||||
if (strlen(phone_number) < 6) {
|
||||
output_buffer[output_buffer_length + 1] = TYPE_OF_ADDRESS_UNKNOWN;
|
||||
} else {
|
||||
output_buffer[output_buffer_length + 1] = TYPE_OF_ADDRESS_INTERNATIONAL_PHONE;
|
||||
}
|
||||
|
||||
length = EncodePhoneNumber(phone_number,
|
||||
output_buffer + output_buffer_length + 2,
|
||||
buffer_size - output_buffer_length - 2);
|
||||
output_buffer_length += length + 2;
|
||||
if (output_buffer_length + 4 > buffer_size)
|
||||
return -1; // Check if it has space for four more bytes.
|
||||
|
||||
|
||||
// 4. Protocol identifiers.
|
||||
output_buffer[output_buffer_length++] = 0x00; // TP-PID: Protocol identifier.
|
||||
output_buffer[output_buffer_length++] = 0x00; // TP-DCS: Data coding scheme.
|
||||
output_buffer[output_buffer_length++] = 0xB0; // TP-VP: Validity: 10 days
|
||||
|
||||
// 5. SMS message.
|
||||
int sms_text_length = strlen(sms_text);
|
||||
char sms_text_7bit[2*SMS_MAX_7BIT_TEXT_LENGTH];
|
||||
sms_text_length = AsciiToG7bit(sms_text, sms_text_length, sms_text_7bit);
|
||||
if (sms_text_length > SMS_MAX_7BIT_TEXT_LENGTH)
|
||||
return -1;
|
||||
output_buffer[output_buffer_length++] = sms_text_length;
|
||||
length = EncodePDUMessage(sms_text_7bit, sms_text_length,
|
||||
output_buffer + output_buffer_length,
|
||||
buffer_size - output_buffer_length);
|
||||
if (length < 0)
|
||||
return -1;
|
||||
output_buffer_length += length;
|
||||
|
||||
return output_buffer_length;
|
||||
}
|
||||
|
||||
int pdu_decode(const unsigned char* buffer, int buffer_length,
|
||||
time_t* output_sms_time,
|
||||
char* output_sender_phone_number, int sender_phone_number_size,
|
||||
char* output_sms_text, int sms_text_size,
|
||||
int* tp_dcs,
|
||||
int* ref_number,
|
||||
int* total_parts,
|
||||
int* part_number,
|
||||
int* skip_bytes)
|
||||
{
|
||||
|
||||
if (buffer_length <= 0)
|
||||
return -1;
|
||||
|
||||
const int sms_deliver_start = 1 + buffer[0];
|
||||
if (sms_deliver_start + 1 > buffer_length)
|
||||
return -2;
|
||||
|
||||
const int user_data_header_length = (buffer[sms_deliver_start]>>4);
|
||||
|
||||
const int sender_number_length = buffer[sms_deliver_start + 1];
|
||||
if (sender_number_length + 1 > sender_phone_number_size)
|
||||
return -3; // Buffer too small to hold decoded phone number.
|
||||
|
||||
const int sender_type_of_address = buffer[sms_deliver_start + 2];
|
||||
if (sender_type_of_address == TYPE_OF_ADDRESS_ALPHANUMERIC) {
|
||||
DecodePDUMessage_GSM_7bit(buffer + sms_deliver_start + 3, (sender_number_length + 1) / 2, output_sender_phone_number, sender_number_length);
|
||||
} else {
|
||||
DecodePhoneNumber(buffer + sms_deliver_start + 3, sender_number_length, output_sender_phone_number);
|
||||
}
|
||||
|
||||
const int sms_pid_start = sms_deliver_start + 3 + (buffer[sms_deliver_start + 1] + 1) / 2;
|
||||
|
||||
// Decode timestamp.
|
||||
struct tm sms_broken_time;
|
||||
sms_broken_time.tm_year = 100 + SwapDecimalNibble(buffer[sms_pid_start + 2]);
|
||||
sms_broken_time.tm_mon = SwapDecimalNibble(buffer[sms_pid_start + 3]) - 1;
|
||||
sms_broken_time.tm_mday = SwapDecimalNibble(buffer[sms_pid_start + 4]);
|
||||
sms_broken_time.tm_hour = SwapDecimalNibble(buffer[sms_pid_start + 5]);
|
||||
sms_broken_time.tm_min = SwapDecimalNibble(buffer[sms_pid_start + 6]);
|
||||
sms_broken_time.tm_sec = SwapDecimalNibble(buffer[sms_pid_start + 7]);
|
||||
(*output_sms_time) = timegm(&sms_broken_time);
|
||||
|
||||
const int sms_start = sms_pid_start + 2 + 7;
|
||||
if (sms_start + 1 > buffer_length) return -1; // Invalid input buffer.
|
||||
|
||||
int tmp;
|
||||
if((user_data_header_length&0x04)==0x04) {
|
||||
tmp = buffer[sms_start + 1] + 1;
|
||||
*skip_bytes = tmp;
|
||||
*ref_number = 0x000000FF&buffer[sms_start + tmp - 2];
|
||||
*total_parts = 0x000000FF&buffer[sms_start + tmp - 1];
|
||||
*part_number = 0x000000FF&buffer[sms_start + tmp];
|
||||
} else {
|
||||
tmp = 0;
|
||||
*skip_bytes = tmp;
|
||||
*ref_number = tmp;
|
||||
*total_parts = tmp;
|
||||
*part_number = tmp;
|
||||
}
|
||||
|
||||
int output_sms_text_length = buffer[sms_start];
|
||||
if (sms_text_size < output_sms_text_length) return -1; // Cannot hold decoded buffer.
|
||||
|
||||
const int sms_tp_dcs_start = sms_pid_start + 1;
|
||||
*tp_dcs = buffer[sms_tp_dcs_start];
|
||||
|
||||
switch((*tp_dcs / 4) % 4)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// GSM 7 bit
|
||||
int decoded_sms_text_size = DecodePDUMessage_GSM_7bit(buffer + sms_start + 1, buffer_length - (sms_start + 1),
|
||||
output_sms_text, output_sms_text_length);
|
||||
if (decoded_sms_text_size != output_sms_text_length) return -1; // Decoder length is not as expected.
|
||||
output_sms_text_length = G7bitToAscii(output_sms_text, output_sms_text_length);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// UCS2
|
||||
memcpy(output_sms_text, buffer + sms_start + 1, output_sms_text_length);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Add a C string end.
|
||||
if (output_sms_text_length < sms_text_size)
|
||||
output_sms_text[output_sms_text_length] = 0;
|
||||
else
|
||||
output_sms_text[sms_text_size-1] = 0;
|
||||
|
||||
return output_sms_text_length;
|
||||
}
|
||||
|
||||
52
sendat/src/pdu.h
Normal file
52
sendat/src/pdu.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 2017 - 2021 Cezary Jackiewicz <cezary@eko.one.pl>
|
||||
* 2014 lovewilliam <ztong@vt.edu>
|
||||
*/
|
||||
// Copyright 2011 The Avalon Project Authors. All rights reserved.
|
||||
// Use of this source code is governed by the Apache License 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
#ifndef SMS_PDU_H_
|
||||
#define SMS_PDU_H_
|
||||
|
||||
#include <time.h>
|
||||
|
||||
enum { SMS_MAX_PDU_LENGTH = 256 };
|
||||
|
||||
/*
|
||||
* Encode an SMS message. Output the encoded message into output pdu buffer.
|
||||
* Returns the length of the SMS encoded message in the output buffer or
|
||||
* a negative number in case encoding failed (for example provided output buffer
|
||||
* does not have enough space).
|
||||
*/
|
||||
int pdu_encode(const char* service_center_number, const char* phone_number, const char* text,
|
||||
unsigned char* pdu, int pdu_size);
|
||||
|
||||
/*
|
||||
* Decode an SMS message. Output the decoded message into the sms text buffer.
|
||||
* Returns the length of the SMS dencoded message or a negative number in
|
||||
* case encoding failed (for example provided output buffer has not enough
|
||||
* space).
|
||||
*/
|
||||
int pdu_decode(const unsigned char* pdu, int pdu_len,
|
||||
time_t* sms_time,
|
||||
char* phone_number, int phone_number_size,
|
||||
char* text, int text_size,
|
||||
int* tp_dcs,
|
||||
int* ref_number,
|
||||
int* total_parts,
|
||||
int* part_number,
|
||||
int* skip_bytes);
|
||||
|
||||
int ucs2_to_utf8 (int ucs2, unsigned char * utf8);
|
||||
|
||||
int DecodePDUMessage_GSM_7bit(const unsigned char* buffer,
|
||||
int buffer_length,
|
||||
char* output_sms_text,
|
||||
int sms_text_length);
|
||||
|
||||
int EncodePDUMessage(const char* sms_text,
|
||||
int sms_text_length,
|
||||
unsigned char* output_buffer,
|
||||
int buffer_size);
|
||||
|
||||
#endif // SMS_SMS_H_
|
||||
107
sendat/src/pdu_decoder.c
Normal file
107
sendat/src/pdu_decoder.c
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 2014 lovewilliam <ztong@vt.edu>
|
||||
* SMS PDU Decoder
|
||||
*/
|
||||
#include "pdu.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <wchar.h>
|
||||
|
||||
int ucs2_to_utf8 (int ucs2, unsigned char * utf8);
|
||||
|
||||
int sms_decode()
|
||||
{
|
||||
char buffer[2*SMS_MAX_PDU_LENGTH+4];
|
||||
char *p = buffer;
|
||||
char t[2];
|
||||
int d;
|
||||
do
|
||||
{
|
||||
t[0] = getchar();
|
||||
if(t[0]=='\n')
|
||||
{
|
||||
break;
|
||||
}
|
||||
t[1] = getchar();
|
||||
if(t[1]=='\n')
|
||||
{
|
||||
break;
|
||||
}
|
||||
*p = strtol(t,NULL,16);
|
||||
p++;
|
||||
}while(1);
|
||||
|
||||
time_t sms_time;
|
||||
char sms_phone[40];
|
||||
char sms_text[161];
|
||||
int tp_dcs_type;
|
||||
int ref_number;
|
||||
int total_parts;
|
||||
int part_number;
|
||||
int skip_bytes;
|
||||
|
||||
int sms_text_length = pdu_decode((const unsigned char*)buffer,
|
||||
sizeof(buffer),
|
||||
&sms_time,
|
||||
sms_phone, sizeof(sms_phone),
|
||||
sms_text, sizeof(sms_text),
|
||||
&tp_dcs_type,
|
||||
&ref_number,
|
||||
&total_parts,
|
||||
&part_number,
|
||||
&skip_bytes);
|
||||
|
||||
printf("From:%s\n",sms_phone);
|
||||
printf("Textlen=%d\n",sms_text_length);
|
||||
char time_data_str[64];
|
||||
strftime(time_data_str,64,"%D %T", localtime(&sms_time));
|
||||
printf("Date/Time:%s\n",time_data_str);
|
||||
|
||||
if (total_parts > 0) {
|
||||
printf("Reference number: %d\n", ref_number);
|
||||
printf("SMS segment %d of %d\n", part_number, total_parts);
|
||||
}
|
||||
|
||||
switch((tp_dcs_type / 4) % 4)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// GSM7 bit
|
||||
int i = skip_bytes;
|
||||
if(skip_bytes > 0) i = (skip_bytes*8+6)/7;
|
||||
for(;i<sms_text_length;i++)
|
||||
{
|
||||
printf("%c", sms_text[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// UCS2
|
||||
for(int i = skip_bytes;i<sms_text_length;i+=2)
|
||||
{
|
||||
int ucs2_char = 0x000000FF&sms_text[i+1];
|
||||
ucs2_char|=(0x0000FF00&(sms_text[i]<<8));
|
||||
unsigned char utf8_char[5];
|
||||
int len = ucs2_to_utf8(ucs2_char,utf8_char);
|
||||
int j;
|
||||
for(j=0;j<len;j++)
|
||||
{
|
||||
printf("%c",utf8_char[j]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
450
sendat/src/sendat.c
Normal file
450
sendat/src/sendat.c
Normal file
@ -0,0 +1,450 @@
|
||||
/*
|
||||
** Soft:Daocaoren Name:马上有房 qq:168620188
|
||||
*/
|
||||
#ifndef _UART_H_
|
||||
#define _UART_H_
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/select.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
// #include "pdu.h"
|
||||
|
||||
|
||||
#define MAX_PORTS 4
|
||||
/*this array hold information about each port we have opened */
|
||||
struct PortInfo {
|
||||
char name[16];
|
||||
int port_fd;
|
||||
};
|
||||
int serial_init(int port, int spd, int databits, int parity, \
|
||||
int stopbits, int RTSCTS, int need_line_input);
|
||||
int serial_write(int fd, void *src, int len);
|
||||
int serial_read(int fd, char *buf, int len);
|
||||
int serial_recv(int fd,char *rbuf,int rbuf_len, int timeout);
|
||||
//串口常用初始化接口
|
||||
#define serial_com_init(port, spd, databits, parity, stopbits)\
|
||||
serial_init(port, spd, databits, parity, stopbits, 0, 0)
|
||||
//串口默认初始化接口
|
||||
#define serial_def_init(port, spd) serial_init(port, spd, 8, 'n', 1, 0, 1)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** File: uart.c
|
||||
**
|
||||
** Description:
|
||||
** Provides an RS-232 interface that is very similar to the CVI provided
|
||||
** interface library
|
||||
*/
|
||||
/*this array hold information about each port we have opened */
|
||||
struct PortInfo ports[13] =
|
||||
{
|
||||
{"/dev/ttyUSB0", 0},
|
||||
{"/dev/ttyUSB1", 0},
|
||||
{"/dev/ttyUSB2", 0},
|
||||
{"/dev/ttyUSB3", 0},
|
||||
{"/dev/ttyUSB4", 0},
|
||||
{"/dev/ttyUSB5", 0},
|
||||
{"/dev/ttyUSB6", 0},
|
||||
{"/dev/ttyUSB7", 0},
|
||||
{"/dev/ttyUSB8", 0},
|
||||
{"/dev/ttyUSB9", 0},
|
||||
{"/dev/ttyUSB10", 0},
|
||||
{"/dev/ttyUSB11", 0},
|
||||
{"/dev/ttyUSB12", 0},
|
||||
};
|
||||
FILE* pf;
|
||||
FILE* pfi;
|
||||
int spd_arr[] = {B2000000, B1500000, B576000, B500000, B460800, B230400, B115200, B57600, B38400, B19200, B9600, B4800, B2400};
|
||||
int name_arr[] = { 2000000, 1500000, 576000, 500000, 460800, 230400, 115200, 57600, 38400, 19200, 9600, 4800, 2400 };
|
||||
/**
|
||||
*@brief 设置串口通信速率
|
||||
*@param fd 类型 int 打开串口的文件句柄
|
||||
*@param speed 类型 int 串口速度
|
||||
*@return void
|
||||
*/
|
||||
int set_speed(int fd, int speed)
|
||||
{
|
||||
int i;
|
||||
int status;
|
||||
struct termios Opt;
|
||||
tcgetattr(fd, &Opt);
|
||||
for ( i= 0; i < sizeof(spd_arr) / sizeof(int); i++) {
|
||||
if(speed == name_arr[i]) {
|
||||
tcflush(fd, TCIOFLUSH);
|
||||
cfsetispeed(&Opt, spd_arr[i]);
|
||||
cfsetospeed(&Opt, spd_arr[i]);
|
||||
status = tcsetattr(fd, TCSANOW, &Opt);
|
||||
if (status != 0) {
|
||||
printf("tcsetattr failed");
|
||||
return 1;
|
||||
}
|
||||
tcflush(fd,TCIOFLUSH);
|
||||
}
|
||||
}
|
||||
// printf("set_speed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*@brief 设置串口数据位,停止位和效验位
|
||||
*@param fd 类型 int 打开的串口文件句柄
|
||||
*@param databits 类型 int 数据位 取值为 7 或者8
|
||||
*@param stopbits 类型 int 停止位 取值为 1 或者2
|
||||
*@param parity 类型 int 效验类型 取值为N,E,O,,S
|
||||
*/
|
||||
int set_Parity(int fd, int databits, int parity, int stopbits, int RTSCTS)
|
||||
{
|
||||
struct termios options;
|
||||
if ( tcgetattr( fd,&options) != 0) {
|
||||
perror("SetupSerial 1");
|
||||
return -1;
|
||||
}
|
||||
options.c_cflag &= ~CSIZE;
|
||||
switch (databits) /*设置数据位数*/
|
||||
{
|
||||
case 7:
|
||||
options.c_cflag |= CS7;
|
||||
break;
|
||||
case 8:
|
||||
options.c_cflag |= CS8;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,"Unsupported data size\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
options.c_iflag |= INPCK;
|
||||
cfmakeraw(&options);
|
||||
//options.c_lflag |= (ICANON | ECHO | ECHOE);
|
||||
//options.c_lflag &= ~(ICANON | ECHO | ECHOE);
|
||||
//options.c_iflag &= ~(IXON | IXOFF);
|
||||
switch (parity)
|
||||
{
|
||||
case 'n':
|
||||
case 'N':
|
||||
options.c_cflag &= ~PARENB; /* Clear parity enable */
|
||||
options.c_iflag &= ~INPCK; /* Enable parity checking */
|
||||
break;
|
||||
case 'o':
|
||||
case 'O':
|
||||
options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
|
||||
break;
|
||||
case 'e':
|
||||
case 'E':
|
||||
options.c_cflag |= PARENB; /* Enable parity */
|
||||
options.c_cflag &= ~PARODD; /* 转换为偶效验*/
|
||||
break;
|
||||
case 'S':
|
||||
case 's': /*as no parity*/
|
||||
options.c_cflag &= ~PARENB;
|
||||
options.c_cflag &= ~CSTOPB;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,"Unsupported parity\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 设置停止位*/
|
||||
switch (stopbits)
|
||||
{
|
||||
case 1:
|
||||
options.c_cflag &= ~CSTOPB;
|
||||
break;
|
||||
case 2:
|
||||
options.c_cflag |= CSTOPB;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,"Unsupported stop bits\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set rts/cts */
|
||||
if (RTSCTS)
|
||||
{
|
||||
printf("Set rts/cts");
|
||||
options.c_cflag |= CRTSCTS;
|
||||
}
|
||||
|
||||
tcflush(fd,TCIFLUSH);
|
||||
options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/
|
||||
options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
|
||||
if (tcsetattr(fd,TCSANOW,&options) != 0)
|
||||
{
|
||||
printf("SetupSerial failed");
|
||||
return -1;
|
||||
}
|
||||
// printf("set_Parity\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
//接收消息结尾加换行符
|
||||
int serial_set_line_input(int fd)
|
||||
{
|
||||
struct termios options;
|
||||
if ( tcgetattr( fd,&options) != 0) {
|
||||
perror("SetupSerial 1");
|
||||
return -1;
|
||||
}
|
||||
|
||||
options.c_lflag |= ICANON;
|
||||
|
||||
tcflush(fd,TCIFLUSH);
|
||||
options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/
|
||||
options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
|
||||
if (tcsetattr(fd,TCSANOW,&options) != 0)
|
||||
{
|
||||
perror("SetupSerial 3");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*@brief
|
||||
*@param port 串口端口号
|
||||
*@param spd 串口速度
|
||||
*@param databits,parity,stopbits,RTSCTS,分别为数据位,校验位,停止位,rtscts位
|
||||
*@param need_line_input接收数据结尾是否加换行符?
|
||||
*/
|
||||
int serial_init(int port, int spd, int databits, int parity, int stopbits, int RTSCTS, int need_line_input)
|
||||
{
|
||||
int fd;
|
||||
if(port < 13)
|
||||
{
|
||||
// printf("open port:%d\n", port);
|
||||
}
|
||||
else {
|
||||
printf("error: port:%d > MAX_PORTS\n", port);
|
||||
return -1;
|
||||
}
|
||||
//
|
||||
fd = open(ports[port].name, O_RDWR|O_NOCTTY);//O_NONBLOCK 非阻塞, O_WRONLY 只读写, O_RDONLY 只读, O_RDWR 读写,O_NOCTTY 阻塞
|
||||
|
||||
if (-1 == fd) {
|
||||
printf("init %s failed\n", ports[port].name);
|
||||
return -1;
|
||||
}
|
||||
pf = fdopen(port, "w");
|
||||
pfi = fdopen(port, "r");
|
||||
if (!pf || ! pfi)
|
||||
fprintf(stderr,"open port failed\n");
|
||||
|
||||
|
||||
|
||||
set_speed(fd, spd);
|
||||
|
||||
set_Parity(fd, databits, parity, stopbits, RTSCTS);
|
||||
|
||||
if (need_line_input) {
|
||||
serial_set_line_input(fd);
|
||||
}
|
||||
|
||||
ports[port].port_fd = fd;
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
*@brief
|
||||
*@param fd 串口端口号文件描述符
|
||||
*@param src 需要通过串口发送的数据
|
||||
*@param len 需要发送的数据长度
|
||||
*@param 成功返回0, 否则返回-1
|
||||
*/
|
||||
int serial_write(int fd, void *src, int len)
|
||||
{
|
||||
int ret = write(fd, src, len);
|
||||
if (len != ret) {
|
||||
perror("oh, write serial failed!");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*@brief
|
||||
*@param fd 串口端口号文件描述符
|
||||
*@param src 串口接收数据的指针
|
||||
*@param len 需要接收的数据长度
|
||||
*@param 成功返回0, 否则返回-1
|
||||
*/
|
||||
int serial_read(int fd, char *buf, int len)
|
||||
{
|
||||
int ret = read(fd, buf, len-1);
|
||||
if (-1 == ret) {
|
||||
perror("oh, read serial failed!");
|
||||
return -1;
|
||||
}
|
||||
buf[ret] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
int serial_recv(int fd,char *rbuf,int rbuf_len, int timeout)
|
||||
{
|
||||
int retval;
|
||||
fd_set rset;
|
||||
struct timeval time_out;
|
||||
if(!rbuf || rbuf_len <= 0)
|
||||
{
|
||||
// printf("serial_recv Invalid parameter.\n");
|
||||
return -1;
|
||||
}
|
||||
if(timeout) //指定延时等待
|
||||
{
|
||||
time_out.tv_sec = (time_t)(timeout / 1000);
|
||||
time_out.tv_usec = 0;
|
||||
FD_ZERO(&rset);
|
||||
FD_SET(fd,&rset);
|
||||
|
||||
retval = select(fd,&rset,NULL,NULL,&time_out);
|
||||
if(retval < 0)
|
||||
{
|
||||
// printf("%s,Select failed:%s\n",strerror(errno));
|
||||
return -2;
|
||||
}
|
||||
else if(0 == retval)
|
||||
{
|
||||
// printf("Time Out.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
// usleep(1000);
|
||||
retval = read(fd, rbuf, rbuf_len);
|
||||
if( retval <= 0)
|
||||
{
|
||||
// printf("Read failed:%s\n",strerror(errno));
|
||||
return -3;
|
||||
}
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void timeout()
|
||||
{
|
||||
fprintf(stderr,"No response from modem.\n");
|
||||
exit(2);
|
||||
}
|
||||
/*字符包含判断*/
|
||||
static int starts_with(const char* prefix, const char* str)
|
||||
{
|
||||
while(*prefix)
|
||||
{
|
||||
if (*prefix++ != *str++)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/*判断是否存在*/
|
||||
int FileExist(const char* filename)
|
||||
{
|
||||
if (filename && access(filename, F_OK) == 0) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*字符转小写*/
|
||||
char* str_tolower(const char* str)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
char *lower = calloc(len+1, sizeof(char));
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
lower[i] = tolower((unsigned char)str[i]);
|
||||
}
|
||||
/*free(upper);*/
|
||||
return lower;
|
||||
}
|
||||
/*字符转大写*/
|
||||
char* str_toupper(const char* str)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
char *upper = calloc(len+1, sizeof(char));
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
upper[i] = toupper((unsigned char)str[i]);
|
||||
}
|
||||
/*free(upper);*/
|
||||
return upper;
|
||||
}
|
||||
/*char*转char[]*/
|
||||
char strx_tostrarr(const char* str)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if(argc<3)
|
||||
{
|
||||
printf("ERROR demo: sendat 2 'ATI'\n");
|
||||
exit(1);
|
||||
return 0;
|
||||
}
|
||||
int debug = 1;
|
||||
int port= 0;
|
||||
sscanf(argv[1], "%d", &port);
|
||||
if(FileExist(ports[port].name)==0)
|
||||
{
|
||||
printf("AT ERROR absent.\n");
|
||||
return 0;
|
||||
}
|
||||
char *message= argv[2];
|
||||
char *nty= "\r\n";
|
||||
char buff[1024];
|
||||
signal(SIGALRM,timeout);
|
||||
alarm(2);
|
||||
/*信号超时3秒自动退出*/
|
||||
int fd = serial_def_init(port, 1500000);
|
||||
if(fd < 0) return 0;
|
||||
char *send= strcat(message,nty);
|
||||
serial_write(fd,send, strlen(send));
|
||||
while(1) {
|
||||
int read = serial_read(fd, buff, sizeof(buff));
|
||||
if(starts_with("OK", buff)) {
|
||||
if (debug == 1)
|
||||
printf("%s", buff);
|
||||
close(fd);//关闭串口
|
||||
exit(0);
|
||||
}
|
||||
if(starts_with("ERROR", buff)) {
|
||||
if (debug == 1)
|
||||
printf("%s", buff);
|
||||
close(fd);//关闭串口
|
||||
exit(1);
|
||||
}
|
||||
if(starts_with("COMMAND NOT SUPPORT", buff)) {
|
||||
if (debug == 1)
|
||||
printf("%s", buff);
|
||||
close(fd);//关闭串口
|
||||
exit(1);
|
||||
}
|
||||
if(starts_with("+CME ERROR", buff)) {
|
||||
if (debug == 1)
|
||||
printf("%s", buff);
|
||||
close(fd);//关闭串口
|
||||
exit(1);
|
||||
}
|
||||
printf("%s", buff);
|
||||
}
|
||||
|
||||
exit(1);
|
||||
return 0;
|
||||
}
|
||||
53
sendat/src/ucs2_to_utf8.c
Normal file
53
sendat/src/ucs2_to_utf8.c
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 2014 lovewilliam <ztong@vt.edu>
|
||||
* from http://www.lemoda.net/c/ucs2-to-utf8/ucs2-to-utf8.c
|
||||
*/
|
||||
/* Input: a Unicode code point, "ucs2".
|
||||
|
||||
Output: UTF-8 characters in buffer "utf8".
|
||||
|
||||
Return value: the number of bytes written into "utf8", or -1 if
|
||||
there was an error.
|
||||
|
||||
This adds a zero byte to the end of the string. It assumes that the
|
||||
buffer "utf8" has at least four bytes of space to write to. */
|
||||
|
||||
#define UNICODE_SURROGATE_PAIR -2
|
||||
#define UNICODE_BAD_INPUT -1
|
||||
|
||||
int ucs2_to_utf8 (int ucs2, unsigned char * utf8)
|
||||
{
|
||||
if (ucs2 < 0x80) {
|
||||
utf8[0] = ucs2;
|
||||
utf8[1] = '\0';
|
||||
return 1;
|
||||
}
|
||||
if (ucs2 >= 0x80 && ucs2 < 0x800) {
|
||||
utf8[0] = (ucs2 >> 6) | 0xC0;
|
||||
utf8[1] = (ucs2 & 0x3F) | 0x80;
|
||||
utf8[2] = '\0';
|
||||
return 2;
|
||||
}
|
||||
if (ucs2 >= 0x800 && ucs2 < 0xFFFF) {
|
||||
if (ucs2 >= 0xD800 && ucs2 <= 0xDFFF) {
|
||||
/* Ill-formed. */
|
||||
return UNICODE_SURROGATE_PAIR;
|
||||
}
|
||||
utf8[0] = ((ucs2 >> 12) ) | 0xE0;
|
||||
utf8[1] = ((ucs2 >> 6 ) & 0x3F) | 0x80;
|
||||
utf8[2] = ((ucs2 ) & 0x3F) | 0x80;
|
||||
utf8[3] = '\0';
|
||||
return 3;
|
||||
}
|
||||
if (ucs2 >= 0x10000 && ucs2 < 0x10FFFF) {
|
||||
/* http://tidy.sourceforge.net/cgi-bin/lxr/source/src/utf8.c#L380 */
|
||||
utf8[0] = 0xF0 | (ucs2 >> 18);
|
||||
utf8[1] = 0x80 | ((ucs2 >> 12) & 0x3F);
|
||||
utf8[2] = 0x80 | ((ucs2 >> 6) & 0x3F);
|
||||
utf8[3] = 0x80 | ((ucs2 & 0x3F));
|
||||
utf8[4] = '\0';
|
||||
return 4;
|
||||
}
|
||||
return UNICODE_BAD_INPUT;
|
||||
}
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2015 OpenWrt.org
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=qmi_wwan_s
|
||||
PKG_VERSION:=1.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define KernelPackage/qmi_wwan_s
|
||||
SUBMENU:=WWAN Support
|
||||
TITLE:=Simcom Linux USB QMI WWAN Driver
|
||||
DEPENDS:=+kmod-usb-net +kmod-usb-wdm
|
||||
FILES:=$(PKG_BUILD_DIR)/qmi_wwan_s.ko
|
||||
AUTOLOAD:=$(call AutoLoad,82,qmi_wwan_s)
|
||||
endef
|
||||
|
||||
define KernelPackage/qmi_wwan_s/description
|
||||
Simcom Linux USB QMI WWAN Driver
|
||||
endef
|
||||
|
||||
MAKE_OPTS:= \
|
||||
ARCH="$(LINUX_KARCH)" \
|
||||
CROSS_COMPILE="$(TARGET_CROSS)" \
|
||||
CXXFLAGS="$(TARGET_CXXFLAGS)" \
|
||||
M="$(PKG_BUILD_DIR)" \
|
||||
$(EXTRA_KCONFIG)
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
$(CP) ./src/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
$(MAKE) -C "$(LINUX_DIR)" \
|
||||
$(MAKE_OPTS) \
|
||||
modules
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,qmi_wwan_s))
|
||||
@ -1,38 +0,0 @@
|
||||
obj-m += qmi_wwan_s.o
|
||||
|
||||
PWD := $(shell pwd)
|
||||
OUTPUTDIR=/lib/modules/`uname -r`/kernel/drivers/net/usb/
|
||||
|
||||
ifeq ($(ARCH),)
|
||||
ARCH := $(shell uname -m)
|
||||
endif
|
||||
ifeq ($(CROSS_COMPILE),)
|
||||
CROSS_COMPILE :=
|
||||
endif
|
||||
ifeq ($(KDIR),)
|
||||
KDIR := /lib/modules/$(shell uname -r)/build
|
||||
ifeq ($(ARCH),i686)
|
||||
ifeq ($(wildcard $KDIR/arch/$ARCH),)
|
||||
ARCH=i386
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(findstring &,${PWD}),)
|
||||
$(warning "${PWD}")
|
||||
$(warning "current directory contain special char '&' !")
|
||||
$(error "please remove it!")
|
||||
endif
|
||||
|
||||
default:
|
||||
$(MAKE) ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} -C $(KDIR) M=$(PWD) modules
|
||||
|
||||
install: default
|
||||
cp $(PWD)/qmi_wwan_s.ko /lib/modules/$(shell uname -r)/kernel/drivers/net/usb/
|
||||
depmod
|
||||
modprobe -r qmi_wwan_s
|
||||
modprobe -r qmi_wwan
|
||||
modprobe qmi_wwan_s
|
||||
clean:
|
||||
rm -rf *~ .tmp_versions modules.order Module.symvers
|
||||
find . -type f -name "*~" -o -name "*.o" -o -name "*.ko" -o -name "*.cmd" -o -name "*.mod.c" | xargs rm -rf
|
||||
@ -1,589 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Bjørn Mork <bjorn@mork.no>
|
||||
*
|
||||
* The probing code is heavily inspired by cdc_ether, which is:
|
||||
* Copyright (C) 2003-2005 by David Brownell
|
||||
* Copyright (C) 2006 by Ole Andre Vadla Ravnas (ActiveSync)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/cdc.h>
|
||||
#include <linux/usb/usbnet.h>
|
||||
#include <linux/usb/cdc-wdm.h>
|
||||
|
||||
/* This driver supports wwan (3G/LTE/?) devices using a vendor
|
||||
* specific management protocol called Qualcomm MSM Interface (QMI) -
|
||||
* in addition to the more common AT commands over serial interface
|
||||
* management
|
||||
*
|
||||
* QMI is wrapped in CDC, using CDC encapsulated commands on the
|
||||
* control ("master") interface of a two-interface CDC Union
|
||||
* resembling standard CDC ECM. The devices do not use the control
|
||||
* interface for any other CDC messages. Most likely because the
|
||||
* management protocol is used in place of the standard CDC
|
||||
* notifications NOTIFY_NETWORK_CONNECTION and NOTIFY_SPEED_CHANGE
|
||||
*
|
||||
* Alternatively, control and data functions can be combined in a
|
||||
* single USB interface.
|
||||
*
|
||||
* Handling a protocol like QMI is out of the scope for any driver.
|
||||
* It is exported as a character device using the cdc-wdm driver as
|
||||
* a subdriver, enabling userspace applications ("modem managers") to
|
||||
* handle it.
|
||||
*
|
||||
* These devices may alternatively/additionally be configured using AT
|
||||
* commands on a serial interface
|
||||
*/
|
||||
|
||||
/* driver specific data */
|
||||
struct qmi_wwan_state {
|
||||
struct usb_driver *subdriver;
|
||||
atomic_t pmcount;
|
||||
unsigned long unused;
|
||||
struct usb_interface *control;
|
||||
struct usb_interface *data;
|
||||
};
|
||||
|
||||
/* default ethernet address used by the modem */
|
||||
static const u8 default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3};
|
||||
|
||||
/* Make up an ethernet header if the packet doesn't have one.
|
||||
*
|
||||
* A firmware bug common among several devices cause them to send raw
|
||||
* IP packets under some circumstances. There is no way for the
|
||||
* driver/host to know when this will happen. And even when the bug
|
||||
* hits, some packets will still arrive with an intact header.
|
||||
*
|
||||
* The supported devices are only capably of sending IPv4, IPv6 and
|
||||
* ARP packets on a point-to-point link. Any packet with an ethernet
|
||||
* header will have either our address or a broadcast/multicast
|
||||
* address as destination. ARP packets will always have a header.
|
||||
*
|
||||
* This means that this function will reliably add the appropriate
|
||||
* header iff necessary, provided our hardware address does not start
|
||||
* with 4 or 6.
|
||||
*
|
||||
* Another common firmware bug results in all packets being addressed
|
||||
* to 00:a0:c6:00:00:00 despite the host address being different.
|
||||
* This function will also fixup such packets.
|
||||
*/
|
||||
static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
|
||||
{
|
||||
__be16 proto;
|
||||
|
||||
/* This check is no longer done by usbnet */
|
||||
if (skb->len < dev->net->hard_header_len)
|
||||
return 0;
|
||||
|
||||
switch (skb->data[0] & 0xf0) {
|
||||
case 0x40:
|
||||
proto = htons(ETH_P_IP);
|
||||
break;
|
||||
case 0x60:
|
||||
proto = htons(ETH_P_IPV6);
|
||||
break;
|
||||
case 0x00:
|
||||
if (is_multicast_ether_addr(skb->data))
|
||||
return 1;
|
||||
/* possibly bogus destination - rewrite just in case */
|
||||
skb_reset_mac_header(skb);
|
||||
goto fix_dest;
|
||||
default:
|
||||
/* pass along other packets without modifications */
|
||||
return 1;
|
||||
}
|
||||
if (skb_headroom(skb) < ETH_HLEN)
|
||||
return 0;
|
||||
skb_push(skb, ETH_HLEN);
|
||||
skb_reset_mac_header(skb);
|
||||
eth_hdr(skb)->h_proto = proto;
|
||||
memset(eth_hdr(skb)->h_source, 0, ETH_ALEN);
|
||||
fix_dest:
|
||||
memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct sk_buff *qmi_wwan_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
|
||||
{
|
||||
if (dev->udev->descriptor.idVendor != cpu_to_le16(0x1e0e))
|
||||
return skb;
|
||||
//skip ethernet header
|
||||
if (skb_pull(skb, ETH_HLEN))
|
||||
{
|
||||
return skb;
|
||||
}
|
||||
else
|
||||
{
|
||||
dev_err(&dev->intf->dev, "Packet Dropped\n");
|
||||
}
|
||||
|
||||
if (skb != NULL)
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* very simplistic detection of IPv4 or IPv6 headers */
|
||||
static bool possibly_iphdr(const char *data)
|
||||
{
|
||||
return (data[0] & 0xd0) == 0x40;
|
||||
}
|
||||
|
||||
/* disallow addresses which may be confused with IP headers */
|
||||
static int qmi_wwan_mac_addr(struct net_device *dev, void *p)
|
||||
{
|
||||
int ret;
|
||||
struct sockaddr *addr = p;
|
||||
|
||||
ret = eth_prepare_mac_addr_change(dev, p);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (possibly_iphdr(addr->sa_data))
|
||||
return -EADDRNOTAVAIL;
|
||||
eth_commit_mac_addr_change(dev, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct net_device_ops qmi_wwan_netdev_ops = {
|
||||
.ndo_open = usbnet_open,
|
||||
.ndo_stop = usbnet_stop,
|
||||
.ndo_start_xmit = usbnet_start_xmit,
|
||||
.ndo_tx_timeout = usbnet_tx_timeout,
|
||||
.ndo_change_mtu = usbnet_change_mtu,
|
||||
.ndo_set_mac_address = qmi_wwan_mac_addr,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
};
|
||||
|
||||
/* using a counter to merge subdriver requests with our own into a
|
||||
* combined state
|
||||
*/
|
||||
static int qmi_wwan_manage_power(struct usbnet *dev, int on)
|
||||
{
|
||||
struct qmi_wwan_state *info = (void *)&dev->data;
|
||||
int rv;
|
||||
|
||||
dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__,
|
||||
atomic_read(&info->pmcount), on);
|
||||
|
||||
if ((on && atomic_add_return(1, &info->pmcount) == 1) ||
|
||||
(!on && atomic_dec_and_test(&info->pmcount))) {
|
||||
/* need autopm_get/put here to ensure the usbcore sees
|
||||
* the new value
|
||||
*/
|
||||
rv = usb_autopm_get_interface(dev->intf);
|
||||
dev->intf->needs_remote_wakeup = on;
|
||||
if (!rv)
|
||||
usb_autopm_put_interface(dev->intf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on)
|
||||
{
|
||||
struct usbnet *dev = usb_get_intfdata(intf);
|
||||
|
||||
/* can be called while disconnecting */
|
||||
if (!dev)
|
||||
return 0;
|
||||
return qmi_wwan_manage_power(dev, on);
|
||||
}
|
||||
|
||||
/* collect all three endpoints and register subdriver */
|
||||
static int qmi_wwan_register_subdriver(struct usbnet *dev)
|
||||
{
|
||||
int rv;
|
||||
struct usb_driver *subdriver = NULL;
|
||||
struct qmi_wwan_state *info = (void *)&dev->data;
|
||||
|
||||
/* collect bulk endpoints */
|
||||
rv = usbnet_get_endpoints(dev, info->data);
|
||||
if (rv < 0)
|
||||
goto err;
|
||||
|
||||
/* update status endpoint if separate control interface */
|
||||
if (info->control != info->data)
|
||||
dev->status = &info->control->cur_altsetting->endpoint[0];
|
||||
|
||||
/* require interrupt endpoint for subdriver */
|
||||
if (!dev->status) {
|
||||
rv = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* for subdriver power management */
|
||||
atomic_set(&info->pmcount, 0);
|
||||
|
||||
/* register subdriver */
|
||||
#if (LINUX_VERSION_CODE > KERNEL_VERSION( 5,12,0 ))
|
||||
subdriver = usb_cdc_wdm_register(info->control, &dev->status->desc,
|
||||
4096, WWAN_PORT_QMI, &qmi_wwan_cdc_wdm_manage_power);
|
||||
#else
|
||||
subdriver = usb_cdc_wdm_register(info->control, &dev->status->desc,
|
||||
4096, &qmi_wwan_cdc_wdm_manage_power);
|
||||
#endif
|
||||
if (IS_ERR(subdriver)) {
|
||||
dev_err(&info->control->dev, "subdriver registration failed\n");
|
||||
rv = PTR_ERR(subdriver);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* prevent usbnet from using status endpoint */
|
||||
dev->status = NULL;
|
||||
|
||||
/* save subdriver struct for suspend/resume wrappers */
|
||||
info->subdriver = subdriver;
|
||||
|
||||
err:
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
int status = -1;
|
||||
u8 *buf = intf->cur_altsetting->extra;
|
||||
int len = intf->cur_altsetting->extralen;
|
||||
struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc;
|
||||
struct usb_cdc_union_desc *cdc_union = NULL;
|
||||
struct usb_cdc_ether_desc *cdc_ether = NULL;
|
||||
u32 found = 0;
|
||||
struct usb_driver *driver = driver_of(intf);
|
||||
struct qmi_wwan_state *info = (void *)&dev->data;
|
||||
|
||||
BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) <
|
||||
sizeof(struct qmi_wwan_state)));
|
||||
|
||||
/* set up initial state */
|
||||
info->control = intf;
|
||||
info->data = intf;
|
||||
|
||||
/* and a number of CDC descriptors */
|
||||
while (len > 3) {
|
||||
struct usb_descriptor_header *h = (void *)buf;
|
||||
|
||||
/* ignore any misplaced descriptors */
|
||||
if (h->bDescriptorType != USB_DT_CS_INTERFACE)
|
||||
goto next_desc;
|
||||
|
||||
/* buf[2] is CDC descriptor subtype */
|
||||
switch (buf[2]) {
|
||||
case USB_CDC_HEADER_TYPE:
|
||||
if (found & 1 << USB_CDC_HEADER_TYPE) {
|
||||
dev_dbg(&intf->dev, "extra CDC header\n");
|
||||
goto err;
|
||||
}
|
||||
if (h->bLength != sizeof(struct usb_cdc_header_desc)) {
|
||||
dev_dbg(&intf->dev, "CDC header len %u\n",
|
||||
h->bLength);
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case USB_CDC_UNION_TYPE:
|
||||
if (found & 1 << USB_CDC_UNION_TYPE) {
|
||||
dev_dbg(&intf->dev, "extra CDC union\n");
|
||||
goto err;
|
||||
}
|
||||
if (h->bLength != sizeof(struct usb_cdc_union_desc)) {
|
||||
dev_dbg(&intf->dev, "CDC union len %u\n",
|
||||
h->bLength);
|
||||
goto err;
|
||||
}
|
||||
cdc_union = (struct usb_cdc_union_desc *)buf;
|
||||
break;
|
||||
case USB_CDC_ETHERNET_TYPE:
|
||||
if (found & 1 << USB_CDC_ETHERNET_TYPE) {
|
||||
dev_dbg(&intf->dev, "extra CDC ether\n");
|
||||
goto err;
|
||||
}
|
||||
if (h->bLength != sizeof(struct usb_cdc_ether_desc)) {
|
||||
dev_dbg(&intf->dev, "CDC ether len %u\n",
|
||||
h->bLength);
|
||||
goto err;
|
||||
}
|
||||
cdc_ether = (struct usb_cdc_ether_desc *)buf;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Remember which CDC functional descriptors we've seen. Works
|
||||
* for all types we care about, of which USB_CDC_ETHERNET_TYPE
|
||||
* (0x0f) is the highest numbered
|
||||
*/
|
||||
if (buf[2] < 32)
|
||||
found |= 1 << buf[2];
|
||||
|
||||
next_desc:
|
||||
len -= h->bLength;
|
||||
buf += h->bLength;
|
||||
}
|
||||
|
||||
/* Use separate control and data interfaces if we found a CDC Union */
|
||||
if (cdc_union) {
|
||||
info->data = usb_ifnum_to_if(dev->udev,
|
||||
cdc_union->bSlaveInterface0);
|
||||
if (desc->bInterfaceNumber != cdc_union->bMasterInterface0 ||
|
||||
!info->data) {
|
||||
dev_err(&intf->dev,
|
||||
"bogus CDC Union: master=%u, slave=%u\n",
|
||||
cdc_union->bMasterInterface0,
|
||||
cdc_union->bSlaveInterface0);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
/* errors aren't fatal - we can live with the dynamic address */
|
||||
if (cdc_ether && cdc_ether->wMaxSegmentSize) {
|
||||
dev->hard_mtu = le16_to_cpu(cdc_ether->wMaxSegmentSize);
|
||||
usbnet_get_ethernet_addr(dev, cdc_ether->iMACAddress);
|
||||
}
|
||||
|
||||
/* claim data interface and set it up */
|
||||
if (info->control != info->data) {
|
||||
status = usb_driver_claim_interface(driver, info->data, dev);
|
||||
if (status < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
status = qmi_wwan_register_subdriver(dev);
|
||||
if (status < 0 && info->control != info->data) {
|
||||
usb_set_intfdata(info->data, NULL);
|
||||
usb_driver_release_interface(driver, info->data);
|
||||
}
|
||||
|
||||
/* Never use the same address on both ends of the link, even
|
||||
* if the buggy firmware told us to.
|
||||
*/
|
||||
if (ether_addr_equal(dev->net->dev_addr, default_modem_addr))
|
||||
eth_hw_addr_random(dev->net);
|
||||
|
||||
/* make MAC addr easily distinguishable from an IP header */
|
||||
if (possibly_iphdr(dev->net->dev_addr)) {
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,18,0)
|
||||
dev->net->dev_addr[0] |= 0x02; /* set local assignment bit */
|
||||
dev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */
|
||||
#else
|
||||
u8 addr = dev->net->dev_addr[0];
|
||||
|
||||
addr |= 0x02; /* set local assignment bit */
|
||||
addr &= 0xbf; /* clear "IP" bit */
|
||||
dev_addr_mod(dev->net, 0, &addr, 1);
|
||||
#endif
|
||||
}
|
||||
dev->net->netdev_ops = &qmi_wwan_netdev_ops;
|
||||
|
||||
#if 1 //Added by Simcom
|
||||
if (dev->udev->descriptor.idVendor == cpu_to_le16(0x1e0e)) {
|
||||
dev_info(&intf->dev, "SIMCom 8200 work on RawIP mode\n");
|
||||
dev->net->flags |= IFF_NOARP;
|
||||
usb_control_msg(
|
||||
interface_to_usbdev(intf),
|
||||
usb_sndctrlpipe(interface_to_usbdev(intf), 0),
|
||||
0x22, //USB_CDC_REQ_SET_CONTROL_LINE_STATE
|
||||
0x21, //USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE
|
||||
1, //active CDC DTR
|
||||
intf->cur_altsetting->desc.bInterfaceNumber,
|
||||
NULL,
|
||||
0,
|
||||
100
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
err:
|
||||
return status;
|
||||
}
|
||||
|
||||
static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
struct qmi_wwan_state *info = (void *)&dev->data;
|
||||
struct usb_driver *driver = driver_of(intf);
|
||||
struct usb_interface *other;
|
||||
|
||||
if (info->subdriver && info->subdriver->disconnect)
|
||||
info->subdriver->disconnect(info->control);
|
||||
|
||||
/* allow user to unbind using either control or data */
|
||||
if (intf == info->control)
|
||||
other = info->data;
|
||||
else
|
||||
other = info->control;
|
||||
|
||||
/* only if not shared */
|
||||
if (other && intf != other) {
|
||||
usb_set_intfdata(other, NULL);
|
||||
usb_driver_release_interface(driver, other);
|
||||
}
|
||||
|
||||
info->subdriver = NULL;
|
||||
info->data = NULL;
|
||||
info->control = NULL;
|
||||
}
|
||||
|
||||
/* suspend/resume wrappers calling both usbnet and the cdc-wdm
|
||||
* subdriver if present.
|
||||
*
|
||||
* NOTE: cdc-wdm also supports pre/post_reset, but we cannot provide
|
||||
* wrappers for those without adding usbnet reset support first.
|
||||
*/
|
||||
static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct usbnet *dev = usb_get_intfdata(intf);
|
||||
struct qmi_wwan_state *info = (void *)&dev->data;
|
||||
int ret;
|
||||
|
||||
/* Both usbnet_suspend() and subdriver->suspend() MUST return 0
|
||||
* in system sleep context, otherwise, the resume callback has
|
||||
* to recover device from previous suspend failure.
|
||||
*/
|
||||
ret = usbnet_suspend(intf, message);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
if (intf == info->control && info->subdriver &&
|
||||
info->subdriver->suspend)
|
||||
ret = info->subdriver->suspend(intf, message);
|
||||
if (ret < 0)
|
||||
usbnet_resume(intf);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qmi_wwan_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct usbnet *dev = usb_get_intfdata(intf);
|
||||
struct qmi_wwan_state *info = (void *)&dev->data;
|
||||
int ret = 0;
|
||||
bool callsub = (intf == info->control && info->subdriver &&
|
||||
info->subdriver->resume);
|
||||
|
||||
if (callsub)
|
||||
ret = info->subdriver->resume(intf);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
ret = usbnet_resume(intf);
|
||||
if (ret < 0 && callsub)
|
||||
info->subdriver->suspend(intf, PMSG_SUSPEND);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct driver_info qmi_wwan_info = {
|
||||
.description = "WWAN/QMI device",
|
||||
.flags = FLAG_WWAN,
|
||||
.bind = qmi_wwan_bind,
|
||||
.unbind = qmi_wwan_unbind,
|
||||
.manage_power = qmi_wwan_manage_power,
|
||||
.rx_fixup = qmi_wwan_rx_fixup,
|
||||
};
|
||||
|
||||
static const struct driver_info qmi_wwan_raw_ip_info = {
|
||||
.description = "WWAN/QMI device",
|
||||
.flags = FLAG_WWAN,
|
||||
.bind = qmi_wwan_bind,
|
||||
.unbind = qmi_wwan_unbind,
|
||||
.manage_power = qmi_wwan_manage_power,
|
||||
.rx_fixup = qmi_wwan_rx_fixup,
|
||||
.tx_fixup = qmi_wwan_tx_fixup,
|
||||
};
|
||||
|
||||
#define HUAWEI_VENDOR_ID 0x12D1
|
||||
|
||||
/* map QMI/wwan function by a fixed interface number */
|
||||
#define QMI_FIXED_INTF(vend, prod, num) \
|
||||
USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \
|
||||
.driver_info = (unsigned long)&qmi_wwan_info
|
||||
|
||||
#define QMI_FIXED_RAWIP_INTF(vend, prod, num) \
|
||||
USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \
|
||||
.driver_info = (unsigned long)&qmi_wwan_raw_ip_info
|
||||
|
||||
/* Gobi 1000 QMI/wwan interface number is 3 according to qcserial */
|
||||
#define QMI_GOBI1K_DEVICE(vend, prod) \
|
||||
QMI_FIXED_INTF(vend, prod, 3)
|
||||
|
||||
/* Gobi 2000/3000 QMI/wwan interface number is 0 according to qcserial */
|
||||
#define QMI_GOBI_DEVICE(vend, prod) \
|
||||
QMI_FIXED_INTF(vend, prod, 0)
|
||||
|
||||
static const struct usb_device_id products[] = {
|
||||
{QMI_FIXED_RAWIP_INTF(0x1e0e, 0x9001, 5)}, /* SIMCOM 8200 */
|
||||
{ } /* END */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, products);
|
||||
|
||||
static bool quectel_ec20_detected(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
|
||||
if (dev->actconfig &&
|
||||
le16_to_cpu(dev->descriptor.idVendor) == 0x05c6 &&
|
||||
le16_to_cpu(dev->descriptor.idProduct) == 0x9215 &&
|
||||
dev->actconfig->desc.bNumInterfaces == 5)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int qmi_wwan_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *prod)
|
||||
{
|
||||
struct usb_device_id *id = (struct usb_device_id *)prod;
|
||||
struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc;
|
||||
|
||||
/* Workaround to enable dynamic IDs. This disables usbnet
|
||||
* blacklisting functionality. Which, if required, can be
|
||||
* reimplemented here by using a magic "blacklist" value
|
||||
* instead of 0 in the static device id table
|
||||
*/
|
||||
if (!id->driver_info) {
|
||||
dev_dbg(&intf->dev, "setting defaults for dynamic device id\n");
|
||||
id->driver_info = (unsigned long)&qmi_wwan_info;
|
||||
}
|
||||
|
||||
/* There are devices where the same interface number can be
|
||||
* configured as different functions. We should only bind to
|
||||
* vendor specific functions when matching on interface number
|
||||
*/
|
||||
if (id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER &&
|
||||
desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) {
|
||||
dev_dbg(&intf->dev,
|
||||
"Rejecting interface number match for class %02x\n",
|
||||
desc->bInterfaceClass);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Quectel EC20 quirk where we've QMI on interface 4 instead of 0 */
|
||||
if (quectel_ec20_detected(intf) && desc->bInterfaceNumber == 0) {
|
||||
dev_dbg(&intf->dev, "Quectel EC20 quirk, skipping interface 0\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return usbnet_probe(intf, id);
|
||||
}
|
||||
|
||||
static struct usb_driver qmi_wwan_driver = {
|
||||
.name = "qmi_wwan_s",
|
||||
.id_table = products,
|
||||
.probe = qmi_wwan_probe,
|
||||
.disconnect = usbnet_disconnect,
|
||||
.suspend = qmi_wwan_suspend,
|
||||
.resume = qmi_wwan_resume,
|
||||
.reset_resume = qmi_wwan_resume,
|
||||
.supports_autosuspend = 1,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(qmi_wwan_driver);
|
||||
|
||||
MODULE_AUTHOR("Bjørn Mork <bjorn@mork.no>");
|
||||
MODULE_DESCRIPTION("Qualcomm MSM Interface (QMI) WWAN driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("Simcom_Linux_QMI_WWAN_Driver_V1.0");
|
||||
Loading…
Reference in New Issue
Block a user