
安装编译依赖
sudo apt install -y build-essential bc bison flex libssl-dev libelf-dev dwarves \
kmod wget tar xz-utils zstd libncurses-dev
mkdir -p ~/jetson_qmi_driver
cd ~/jetson_qmi_driver
下载对应版本的内核源码
jetson@ubuntu:~/jetson_qmi_driver$cat /etc/nv_tegra_release
R36 (release), REVISION: 5.0, GCID: 43688277, BOARD: generic, EABI: aarch64, DATE: Fri Jan 16 03:50:45 UTC 2026
KERNEL_VARIANT: oot
TARGET_USERSPACE_LIB_DIR=nvidia
TARGET_USERSPACE_LIB_DIR_PATH=usr/lib/aarch64-linux-gnu/nvidia
jetson@ubuntu:~/jetson_qmi_driver$uname -a
Linux ubuntu 5.15.185-tegra #1 SMP PREEMPT Thu Jan 15 19:24:38 PST 2026 aarch64 aarch64 aarch64 GNU/Linux
wget -c -O public_sources.tbz2 https://developer.nvidia.com/downloads/embedded/l4t/r36_release_v5.0/sources/public_sources.tbz2
windows下载也可以
https://developer.download.nvidia.com/embedded/L4T/r36_Release_v5.0/sources/public_sources.tbz2
解压
tar -xvjf public_sources.tbz2
找到 kernel_src.tbz2
find ~/jetson_qmi_driver -name kernel_src.tbz2
cd ~/jetson_qmi_driver/Linux_for_Tegra/source
解压内核源码
tar -xvjf kernel_src.tbz2
进入内核源码目录
cd kernel/kernel-jammy-src
复制当前系统配置
zcat /proc/config.gz > .config
如果提示没有 /proc/config.gz 用这个
cp /boot/config-$(uname -r) .config
打开要编译的驱动为模块
./scripts/config --module USB_USBNET
./scripts/config --module USB_NET_QMI_WWAN
./scripts/config --module USB_WDM
./scripts/config --module USB_NET_CDCETHER
./scripts/config --module USB_NET_CDC_NCM
./scripts/config --module USB_NET_CDC_MBIM
./scripts/config --module USB_NET_RNDIS_HOST
./scripts/config --module USB_SERIAL
./scripts/config --module USB_SERIAL_WWAN
./scripts/config --module USB_SERIAL_OPTION
./scripts/config --module PPP
./scripts/config --module PPP_ASYNC
./scripts/config --module PPP_SYNC_TTY
./scripts/config --module SLHC
./scripts/config --set-str LOCALVERSION "-tegra"
生成配置和编译准备
make olddefconfig
make prepare
make modules_prepare
编译 USB 网络驱动
make -j$(nproc) M=drivers/net/usb modules
编译 cdc-wdm
make -j$(nproc) M=drivers/usb/class modules
编译 USB 串口驱动
make -j$(nproc) M=drivers/usb/serial modules
编译 PPP 驱动
make -j$(nproc) M=drivers/net/ppp modules
建立安装目录
sudo mkdir -p /lib/modules/$(uname -r)/extra/qmi
复制 ko 文件
sudo cp -f drivers/net/usb/usbnet.ko /lib/modules/$(uname -r)/extra/qmi/ 2>/dev/null || true
sudo cp -f drivers/net/usb/qmi_wwan.ko /lib/modules/$(uname -r)/extra/qmi/ 2>/dev/null || true
sudo cp -f drivers/net/usb/cdc_ether.ko /lib/modules/$(uname -r)/extra/qmi/ 2>/dev/null || true
sudo cp -f drivers/net/usb/cdc_ncm.ko /lib/modules/$(uname -r)/extra/qmi/ 2>/dev/null || true
sudo cp -f drivers/net/usb/cdc_mbim.ko /lib/modules/$(uname -r)/extra/qmi/ 2>/dev/null || true
sudo cp -f drivers/net/usb/rndis_host.ko /lib/modules/$(uname -r)/extra/qmi/ 2>/dev/null || true
sudo cp -f drivers/usb/class/cdc-wdm.ko /lib/modules/$(uname -r)/extra/qmi/ 2>/dev/null || true
sudo cp -f drivers/usb/serial/usbserial.ko /lib/modules/$(uname -r)/extra/qmi/ 2>/dev/null || true
sudo cp -f drivers/usb/serial/usb_wwan.ko /lib/modules/$(uname -r)/extra/qmi/ 2>/dev/null || true
sudo cp -f drivers/usb/serial/option.ko /lib/modules/$(uname -r)/extra/qmi/ 2>/dev/null || true
sudo cp -f drivers/net/ppp/ppp_generic.ko /lib/modules/$(uname -r)/extra/qmi/ 2>/dev/null || true
sudo cp -f drivers/net/ppp/ppp_async.ko /lib/modules/$(uname -r)/extra/qmi/ 2>/dev/null || true
sudo cp -f drivers/net/ppp/ppp_synctty.ko /lib/modules/$(uname -r)/extra/qmi/ 2>/dev/null || true
sudo cp -f drivers/net/ppp/slhc.ko /lib/modules/$(uname -r)/extra/qmi/ 2>/dev/null || true
刷新模块依赖
sudo depmod -a
加载驱动
sudo modprobe usbnet
sudo modprobe cdc_wdm
sudo modprobe qmi_wwan
sudo modprobe cdc_ether
sudo modprobe cdc_ncm
sudo modprobe cdc_mbim
sudo modprobe rndis_host
sudo modprobe usbserial
sudo modprobe usb_wwan
sudo modprobe option
sudo modprobe ppp_async
检查驱动是否加载
lsmod | egrep "cdc_ether|cdc_ncm|cdc_mbim|cdc_wdm|qmi_wwan|rndis_host|option|usb_wwan|ppp_async"
检查 ko 版本
modinfo qmi_wwan | grep vermagic
modinfo cdc_wdm | grep vermagic
modinfo option | grep vermagic
uname -r
必须都是
5.15.185-tegra
插入模块后检查设备
lsusb
dmesg | grep -iE "qmi|cdc-wdm|wwan|ttyUSB|option|2c7c" | tail -80
ls /dev/cdc-wdm*
ls /dev/ttyUSB*
ip -br link | grep -E "wwan|usb|enx"
安装 QMI 工具
sudo apt install -y libqmi-utils udhcpc isc-dhcp-client
写 QMI 配置
sudo tee /etc/qmi-network.conf >/dev/null <<'EOF'
APN=CMNET
APN_USER=
APN_PASS=
APN_AUTH=none
IP_TYPE=4
PROXY=yes
EOF
停止抢占服务
sudo systemctl stop ModemManager 2>/dev/null || true
sudo killall ModemManager 2>/dev/null || true
设置 raw-ip
sudo ip link set wwan0 down
echo Y | sudo tee /sys/class/net/wwan0/qmi/raw_ip
sudo ip link set wwan0 up
查询模块状态
sudo qmicli -d /dev/cdc-wdm0 --device-open-proxy --dms-get-operating-mode
sudo qmicli -d /dev/cdc-wdm0 --device-open-proxy --nas-get-signal-strength
sudo qmicli -d /dev/cdc-wdm0 --device-open-proxy --nas-get-serving-system
开始 QMI 拨号
sudo qmi-network /dev/cdc-wdm0 stop
sudo qmi-network /dev/cdc-wdm0 start
获取 IP
sudo udhcpc -i wwan0
如果 udhcpc 不成功 用这个
sudo dhclient -v wwan0
设置默认路由
sudo ip route replace default dev wwan0
设置 DNS
echo "nameserver 114.114.114.114" | sudo tee /etc/resolv.conf
echo "nameserver 8.8.8.8" | sudo tee -a /etc/resolv.conf
测试
ping -I wwan0 8.8.8.8 -c 4
ping -I wwan0 qq.com -c 4
如果有 /dev/cdc-wdm0 但没有 wwan0 执行这个绑定
先看 PID
lsusb
比如看到 2c7c 0801 就执行
echo "2c7c 0801" | sudo tee /sys/bus/usb/drivers/qmi_wwan/new_id
echo "2c7c 0801" | sudo tee /sys/bus/usb-serial/drivers/option1/new_id
再检查
dmesg | tail -80
ls /dev/cdc-wdm*
ip -br link | grep wwan
#!/usr/bin/env python3
import argparse
import os
import select
import subprocess
import sys
import termios
import time
DEFAULT_AT_PORTS = ["/dev/ttyUSB2", "/dev/ttyUSB3", "/dev/ttyUSB1", "/dev/ttyUSB0"]
def run(cmd, timeout=30, check=False):
print("+ " + " ".join(cmd))
p = subprocess.run(
cmd,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
timeout=timeout,
check=False,
)
if p.stdout:
print(p.stdout.rstrip())
if check and p.returncode != 0:
raise RuntimeError("command failed: %s" % " ".join(cmd))
return p
def setup_serial(fd):
attrs = termios.tcgetattr(fd)
attrs[0] = 0
attrs[1] = 0
attrs[2] = termios.B115200 | termios.CS8 | termios.CREAD | termios.CLOCAL
attrs[3] = 0
attrs[6][termios.VMIN] = 0
attrs[6][termios.VTIME] = 0
termios.tcsetattr(fd, termios.TCSANOW, attrs)
def at(fd, cmd, wait=1.5):
os.write(fd, (cmd + "\r").encode("ascii"))
os.set_blocking(fd, False)
end = time.time() + wait
data = b""
while time.time() < end:
ready, _, _ = select.select([fd], [], [], 0.1)
if not ready:
continue
try:
data += os.read(fd, 4096)
except BlockingIOError:
pass
os.set_blocking(fd, True)
text = data.decode(errors="replace")
print(">>> " + cmd)
print(text.rstrip())
return text
def open_at_port(candidates):
for port in candidates:
if not os.path.exists(port):
continue
try:
fd = os.open(port, os.O_RDWR | os.O_NOCTTY)
setup_serial(fd)
out = at(fd, "AT", wait=1.0)
if "OK" in out:
return port, fd
os.close(fd)
except OSError as exc:
print("skip %s: %s" % (port, exc))
raise RuntimeError("No AT port found. Try passing --at-port /dev/ttyUSBx")
def driver_for_iface(iface):
path = "/sys/class/net/%s/device/driver" % iface
try:
return os.path.basename(os.readlink(path))
except OSError:
return ""
def list_net_ifaces():
return [
name
for name in os.listdir("/sys/class/net")
if name not in ("lo",) and os.path.isdir("/sys/class/net/" + name)
]
def find_rndis_iface():
for iface in list_net_ifaces():
if driver_for_iface(iface) == "rndis_host":
return iface
for iface in list_net_ifaces():
if iface.startswith("usb") or iface.startswith("enx"):
return iface
return None
def wait_for_rndis(timeout):
print("Waiting for RNDIS/NDIS network interface...")
deadline = time.time() + timeout
while time.time() < deadline:
iface = find_rndis_iface()
if iface:
print("Found interface: %s (driver=%s)" % (iface, driver_for_iface(iface)))
return iface
time.sleep(1)
raise RuntimeError("RNDIS interface not found")
def ip_br_addr(iface=None):
cmd = ["ip", "-br", "addr"]
if iface:
cmd += ["show", iface]
run(cmd, timeout=10)
def get_ipv4(iface):
p = subprocess.run(
["ip", "-o", "-4", "addr", "show", iface],
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
)
fields = p.stdout.split()
for i, field in enumerate(fields):
if field == "inet" and i + 1 < len(fields):
return fields[i + 1].split("/")[0]
return ""
def dhcp(iface):
run(["ip", "link", "set", iface, "up"], timeout=10)
if subprocess.run(["command", "-v", "dhclient"], shell=True).returncode == 0:
run(["dhclient", "-v", iface], timeout=35)
elif subprocess.run(["command", "-v", "udhcpc"], shell=True).returncode == 0:
run(["udhcpc", "-i", iface, "-q", "-n"], timeout=35)
else:
raise RuntimeError("Neither dhclient nor udhcpc found")
def ensure_default_route(iface):
ipv4 = get_ipv4(iface)
if ipv4.startswith("192.168.225."):
run(["ip", "route", "replace", "default", "via", "192.168.225.1", "dev", iface], timeout=10)
def ping_tests(iface):
run(["ping", "-c", "4", "-I", iface, "8.8.8.8"], timeout=20)
run(["ping", "-c", "4", "-I", iface, "qq.com"], timeout=25)
def main():
parser = argparse.ArgumentParser(description="Switch Quectel RM520/RM500U to NDIS/RNDIS and test network.")
parser.add_argument("--at-port", help="AT port, for example /dev/ttyUSB2")
parser.add_argument("--skip-switch", action="store_true", help="Do not send AT+QCFG/CFUN; only DHCP and test")
parser.add_argument("--wait", type=int, default=60, help="Seconds to wait for RNDIS interface")
args = parser.parse_args()
if os.geteuid() != 0:
print("Please run as root: sudo python3 rm520_set_ndis.py", file=sys.stderr)
return 1
if not args.skip_switch:
candidates = [args.at_port] if args.at_port else DEFAULT_AT_PORTS
port, fd = open_at_port(candidates)
print("Using AT port: %s" % port)
try:
at(fd, 'AT+QCFG="usbnet"', wait=1.5)
at(fd, 'AT+QCFG="usbnet",3', wait=1.5)
at(fd, 'AT+QCFG="usbnet"', wait=1.5)
at(fd, "AT+CFUN=1,1", wait=1.5)
finally:
os.close(fd)
print("Module is rebooting/re-enumerating after AT+CFUN=1,1...")
time.sleep(12)
iface = wait_for_rndis(args.wait)
ip_br_addr()
dhcp(iface)
ensure_default_route(iface)
ip_br_addr(iface)
run(["ip", "route"], timeout=10)
ping_tests(iface)
print("\nNDIS/RNDIS test finished. Interface: %s" % iface)
return 0
if __name__ == "__main__":
raise SystemExit(main())