Jetson Orin Quectel QMI 拨号上网

安装编译依赖

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())
相关推荐
是有头发的程序猿1 小时前
竞品分析 + 用户洞察自动化|基于 item_review 评论接口 + 多 AI Agent 实现淘宝评论全量采集与智能分析(附python源码)
java·python·自动化
keykey6.1 小时前
LSTM 文本情感分析:从词嵌入到分类实战
开发语言·人工智能·深度学习·机器学习
数据知道1 小时前
网站到底是如何通过JS读取你的浏览器指纹的?
开发语言·javascript·ecmascript·指纹浏览器
骑士雄师1 小时前
课程导航LangGraph核心概念
python
c238561 小时前
C++的IO流深入理解(上)
开发语言·c++
SilentSamsara1 小时前
DuckDB + Python:嵌入式 OLAP 数据库的轻量分析实战
开发语言·数据库·python·微服务
无限进步_2 小时前
【Linux】进程状态、僵尸与孤儿、进程调度
linux·运维·服务器·开发语言·数据结构·算法
爱上纯净的蓝天2 小时前
30 分钟上手 AtomCode:用它写一个 Python 批量整理文件/改名/生成报告小工具(新手教程)
python·开源·自动化脚本·atomcode·ai 编码助手
郝学胜-神的一滴2 小时前
力扣 662 :二叉树最大宽度
java·数据结构·c++·python·算法·leetcode·职场和发展