T113 4G模块udev拨号

前言

在此文档中已经介绍了如何适配4G模块,使其能够使用虚拟usb网卡拨号实现上网:

而udev自动拨号是基于虚拟usb网卡实现的,ppp拨号并不适用,

这里再简略复盘一下使用虚拟网卡的前置条件。在内核中需要打开以下宏,则开启自动识别usb虚拟网卡:

CONFIG_USB_SERIAL_GENERIC=y
CONFIG_USB_SERIAL_OPTION=y
CONFIG_USB_SERIAL_QT2=y

我们常用两种拨号模式,一种是ECM,需要在内核开启如下配置:

CONFIG_USB_USBNET=y
CONFIG_USB_NET_CDCETHER=y

另一种是RNDIS模式,相对少用,但拨号方法类似ECM,可以作为备选拨号模式,也要一并开启:

CONFIG_USB_USBNET=y
CONFIG_USB_NET_RNDIS_HOST=y

还需要在drivers/usb/serial/option.c源码中添加网卡可能出现的所有vid、pid,以保证驱动能生成/dev/ttyUSB*设备,使得能往模块发送AT指令拨号及网络通信:

...
static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(0x2DEE, 0x4D41) }, // MEIG SLM320-C 模式2
{ USB_DEVICE(0x2DEE, 0x4D42) }, // MEIG SLM320-C 模式3/模式5
{ USB_DEVICE(0x2DEE, 0x4D43) },
{ USB_DEVICE(0x0525, 0xA4A7) }, // MEIG SLM320-C 模式1
{ USB_DEVICE(0x2949, 0x8241) }, // MEIG SLM720
{ USB_DEVICE(0x2949, 0x8242) },
{ USB_DEVICE(0x2949, 0x8243) },
{ USB_DEVICE(0x05C6, 0xF601) }, // MEIG SLM750
{ USB_DEVICE(0x05C6, 0xF602) },
{ USB_DEVICE(0x05C6, 0xF603) },
{ USB_DEVICE(0x05C6, 0xF604) },
{ USB_DEVICE(0x05C6, 0xF610) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_LIGHT) },
...

一般情况下,4G模块在不同的工作模式或者拨号模式下,其vid、pid基本上都不相同,

我们无法确定客户手上的4G模块是工作在何种模式下,为了保证都能识别并生成/dev/ttyUSB*设备,应该将所有可能的vid、pid值都添加到option驱动中,

而这也是udev自动拨号能自动修改模块工作模式的前提条件,而且不同模式下的4G模块AT通信设备号也可能不一样,

udev工作时能根据匹配到的vid、pid号,通过脚本自动识别该模块处于什么工作模式下,从而对相应的AT指令通信设备做出相应动作,具体在下文中会详细说明。

udev规则

udev 其实本质是系统中的一个守护进程,通过侦听内核发出来的 uevent 来管理/dev 目录下的设备文件。

udev 规则文件是 udev 里最重要的部分,默认是存放在 /etc/udev/rule.d/ 下。所有的规则文件必须以 ".rules" 为后缀名。以sudo vi xxx.rules 进入文件(可自己创建) 保存,给配置文件设置下权限。

其文件开头的数字则代表规则生效的优先级,因为规则是有可能存在冲突的,冲突的规则则是以高优先级的为准,数字越小优先级越高,如文件:50 -android.rules,优先级为50。

在之前就已经实现使用udev将u盘和SD卡自动挂载到/run/media/下的目录,并以块设备文件本身的文件名命名:

root@bookworm-dev64mp:~# ll /run/media/
总计 20
drwxr-xr-x  5 root root    100 11月10日 08:25 ./
drwxr-xr-x 24 root root    720  4月 1日 16:57 ../
drwxr-xr-x  2 root root     40 11月10日 08:25 mmcblk1/
drwxrwxr-x  2 root users 16384 1970年 1月 1日 mmcblk1p1/
drwxr-xr-x 23 root root   4096  1月19日 17:48 mmcblk1p2/

所以这里就不详细展开介绍udev规则文件的语法了,具体可以百度。

回归正题,在4G模块自动拨号借助udev规则的目的很简单,一个是识别当前插入了一个什么模块,另一个是识别模块工作在什么模式下,

所以,udev规则写法很简单,当匹配到指定vid和pid后,将其传入相应脚本,并在脚本中做出相应动作,如:

# SLM320
ACTION=="add", ATTRS{idVendor}=="0525", ATTRS{idProduct}=="a4a7", RUN+="/usr/local/bin/usb-SLM320-inserted.sh 0525 a4a7 %k"
ACTION=="add", ATTRS{idVendor}=="2dee", ATTRS{idProduct}=="4d41", RUN+="/usr/local/bin/usb-SLM320-inserted.sh 2dee 4d41 %k"
ACTION=="add", ATTRS{idVendor}=="2dee", ATTRS{idProduct}=="4d42", RUN+="/usr/local/bin/usb-SLM320-inserted.sh 2dee 4d42 %k"

这是SLM320的udev规则,规则有三条,这里介绍第一条,其他如此类推:

ACTION=="add":表示udev要监听的行为,"add"就是设备插入时触发。

ATTRS{idVendor}=="0525", ATTRS{idProduct}=="a4a7":表示触发的是什么vid、pid号。

RUN+="/usr/local/bin/usb-SLM320-inserted.sh 0525 a4a7 %k":表示触发后执行的动作,这里我写的是调用了一个脚本,并将vid、pid传入脚本,以及传入的%k,%k是触发以上条件的设备名称

在调用脚本时为什么传入这三个参数,稍后会介绍。

拨号脚本

这里以SLM320为例,先贴代码:

#!/bin/bash

# 使用flock限制脚本在短时间内重复执行
lock_file="/var/lock/usb-SLM320-inserted.lock"
(
# 尝试获取锁
flock -n 320 || exit 1

# 检查参数个数
if [ "$#" -ne 3 ]; then
    echo "Error: Three arguments required."
    exit 2
fi

vid="$1"
pid="$2"
device_name="$3"
# 默认为ttyUSB0
ttyusb="/dev/ttyUSB0"

# 检查$3是否为网卡名称
if [ ! -d "/sys/class/net/$device_name" ]; then
    if [ ! -c "/dev/$device_name" ]; then
        echo "$(date) -- $device_name is not device name." >> /tmp/usb-log.txt #debug
        exit 1
    fi
fi

# 等待字符设备文件的出现
wait_for_device() {
    local timeout=10 # 设置超时时间为10秒
    local elapsed=0 # 已过时间初始化为0
    local device=$1
    while [ ! -c "$device" ]; do
        if [ $elapsed -ge $timeout ]; then
            return 1 # 设备文件没有出现
        fi
        sleep 1
        ((elapsed++)) # 增加已过时间
    done
    return 0
}

# 写字符设备
echo_device() {
    local at=$1
    local device=$2
    # 如果要写入的目标文件为普通文件,而非字符设备文件,则立刻删除
    if [ -f $device ]; then
        rm -rf $device
    fi
    if wait_for_device $2; then
        echo -e $at >> $device
        sleep 0.5
        return 0
    fi
    return 1 # 设备文件没有出现
}

# 创建指令接收文件,接收来自/dev/ttyUSB*中的消息
# 执行完这个函数,紧接着必须使用$!获取进程pid
# 脚本结束前必须杀死此进程
setup_listen_file() {
    local file_name=$1
    local device=$2
    if [[ -p $file_name || -f $file_name ]]; then
        rm $file_name
    fi
    touch $file_name
    cat $device >> $file_name & disown
}
# 启动关键字监听,返回监听到的消息,并清空被监听文件的缓存
# 如监听"\"usbmode\","关键字,那么返回值有可能为"+SYSNV: "usbmode",5"
start_grep() {
    local file=$1
    local pattern=$2
    grep -m 1 "$pattern" < "$file"
    echo "" > "$file" #清空文件
}

echo "$(date) -- find: $vid:$pid" >> /tmp/usb-log.txt #debug
echo "$(date) -- device: $device_name" >> /tmp/usb-log.txt #debug

# 等待option驱动创建ttyUSB设备文件,不等的话会导致驱动报错
# 一般都会有/dev/ttyUSB0,所以以此为基准
wait_for_device $ttyusb

if [[ "$vid" == "0525" && "$pid" == "a4a7" ]]; then
    stty -F $ttyusb raw -echo
    echo_device "AT+SYSNV=1,\"usbmode\",5\r" $ttyusb
    echo_device "AT+TRB\r" $ttyusb
    exit 1
fi

if [[ "$vid" == "2dee" && "$pid" == "4d41" ]]; then
    stty -F $ttyusb raw -echo
    echo_device "AT+SYSNV=1,\"usbmode\",5\r" $ttyusb
    echo_device "AT+TRB\r" $ttyusb
    exit 1
fi

if [[ "$vid" == "2dee" && "$pid" == "4d43" ]]; then
    echo_device "AT+SYSNV=1,\"usbmode\",5\r" $ttyusb
    echo_device "AT+TRB\r" $ttyusb
    exit 1
fi

# SLM320的RNDIS与ECM的vid、pid都相同,所以需要额外判断
if [[ "$vid" == "2dee" && "$pid" == "4d42" ]]; then
    stty -F $ttyusb raw -echo
    timeout 1 cat $ttyusb #输出剩下的字符,确保缓存为空
    
    # 创建监听文件
    file="/tmp/ATbuf_$$"
    setup_listen_file $file $ttyusb
    # 获取监听进程PID
    listener_pid=$!
    sleep 1

    # 检查SIM卡状态
    echo_device "AT+CPIN?\r" $ttyusb
    sleep 0.5

    # 查找"READY"关键字,判断SIM卡是否就绪
    buf=$(start_grep "$file" '+CPIN: READY')
    echo "$(date) -- $buf" >> /tmp/usb-log.txt #debug
    if [ -z "$buf" ]; then
        echo "$(date) -- usbmode not ready." >> /tmp/usb-log.txt #debug
        exit 1
    fi

    # 查询当前拨号模式
    echo_device "AT+SYSNV=0,\"usbmode\"\r" $ttyusb
    sleep 0.5

    # 查找"+SYSNV: \"usbmode\""关键字,判断拨号状态
    buf=$(start_grep "$file" '+SYSNV: "usbmode",')
    echo "$(date) -- $buf" >> /tmp/usb-log.txt #debug

    # 先判断是否能找到关键字
    if [ ! -z "$buf" ]; then
        if [[ "$buf" == *"\"usbmode\",3"* || "$buf" == *"\"usbmode\",5"* ]]; then
            echo_device "AT+CGACT=1\r" $ttyusb         # 激活上下文
            echo_device "AT+VERCTRL=0,1\r" $ttyusb     # 自动激活网络
            echo_device "AT^NDISDUP=1,1\r" $ttyusb     # 拨号
            # 对所有usb模块执行udhcpc获取ip,超时时间60秒
            for interface in /sys/class/net/usb*; do
                iface=$(basename $interface)
                timeout 60 udhcpc -i $iface > /dev/null 2>&1 &
            done
            echo "$(date) -- okay." >> /tmp/usb-log.txt #debug
        fi
    else
        echo "$(date) -- usbmode error." >> /tmp/usb-log.txt #debug
        echo_device "AT+SYSNV=1,\"usbmode\",5\r" $ttyusb
        echo_device "AT+TRB\r" $ttyusb
    fi

    # 清理监听进程
    kill -9 "$listener_pid" &> /dev/null
    # 删除监听文件
    rm "$file"

    # else
    #     echo "Command Failed or Timed Out."
    #     #     echo_device "AT+SYSNV=1,\"usbmode\",5\r" $ttyusb
    #     #     echo_device "AT+TRB\r" $ttyusb
    #     #     exit 1
    # fi

    exit 0
fi

) 320>$lock_file

# udev生效测试:
# echo -e "AT+SYSNV=1,\"usbmode\",1\r" > /dev/ttyUSB0
# echo -e "AT+TRB\r" > /dev/ttyUSB0

首先此脚本使用了flock机制,为了使脚本在同一时间内只被执行一次,防止udev连续触发而导致脚本重复执行:

lock_file="/var/lock/usb-SLM320-inserted.lock"
(
# 尝试获取锁
flock -n 320 || exit 1

# 业务代码
...

) 320>$lock_file

初始化变量,指定SLM320默认AT通信设备为/dev/ttyUSB0:

vid="$1"
pid="$2"
device_name="$3"
# 默认为ttyUSB0
ttyusb="/dev/ttyUSB0"

然后这里还判断一下触发此脚本的是什么设备,只有设备为网卡设备usb*或者ttyUSB*设备才能继续执行脚本,其他设备触发该脚本的一律不与执行:

# 检查$3是否为网卡名称
if [ ! -d "/sys/class/net/$device_name" ]; then
    if [ ! -c "/dev/$device_name" ]; then
        echo "$(date) -- $device_name is not device name." >> /tmp/usb-log.txt #debug
        exit 1
    fi
fi

在脚本中,会多处看到如下日志代码,以便debug:

echo "$(date) -- $device_name is not device name." >> /tmp/usb-log.txt #debug

然后就是四个函数:

# 等待字符设备文件的出现
wait_for_device()

# 写字符设备
echo_device()

# 创建指令接收文件,接收来自/dev/ttyUSB*中的消息
# 执行完这个函数,紧接着必须使用$!获取进程pid
# 脚本结束前必须杀死此进程
setup_listen_file()

# 启动关键字监听,返回监听到的消息,并清空被监听文件的缓存
# 如监听"\"usbmode\","关键字,那么返回值有可能为"+SYSNV: "usbmode",5"
start_grep()

wait_for_device()函数是为了等待option驱动完成ttyUSB*设备的创建而存在的,其函数会在10秒内轮询检查指定的设备文件是否存在,找到返回0,等待超时返回1。

这个函数的最大作用是为了保证在使用echo发送AT指令前保证目标设备文件存在,

echo -e "AT+SYSNV=1,\"usbmode\",1\r" > /dev/ttyUSB0

否则在重定向时,就会在/dev下创建一个名为ttyUSB0的普通文本文件,导致option驱动无法创建字符设备文件而出错。

echo_device()函数就是对echo -e指令重新封装,在发送AT指令前调用wait_for_device()函数,保证目标字符设备被成功初始化后再进行发送,发送完成后延时0.5秒保证AT指令在4G模块中生效。

setup_listen_file()函数会创建一个文本文件,用于持续接收/dev/ttyUSB0中返回的指令,因为如果不先缓存下了的话,发送完AT指令后,返回值会因为接收不及时而被刷走,其核心就是创建一个cat /dev/ttyUSB0重定向到一个文本文件的后台进程,该文本文件在脚本中也称为监听文件。

start_grep()用于在对/dev/ttyUSB0发送指令后,获取其返回值的函数,其原理就是在setup_listen_file()函数创建的监听文件中搜索关键字,然后返回,再在外面进行逻辑判断处理,在搜索完成后,会清空监听文件中的内容,以防止干扰下一次搜索。

定义完这些通用函数后,接下来就是关键业务代码的实现,每个型号的4G模块可能略有差异,但总体逻辑框架都是一样的,最终目的也是一样的,

首先判断4G模块当前是否工作在ECM拨号模式下,如果不是,就修改4G模块为ECM模式,并重启4G模块,然后退出脚本,直到模块重启完成后再次触发udev调用该脚本,

在确保模式为ECM模式后,对模块进行简单拨号设置后直接拨号,最后使用udhcpc从4G模块中获取本地IP。

具体如下:

首先等待/dev/ttyUSB0设备的出现,以确保设备驱动完成初始化:

# 等待option驱动创建ttyUSB设备文件,不等的话会导致驱动报错
# 一般都会有/dev/ttyUSB0,所以以此为基准
wait_for_device $ttyusb

然后对所有可能的其他工作模式进行重新设置为ECM模式:

if [[ "$vid" == "0525" && "$pid" == "a4a7" ]]; then
    stty -F $ttyusb raw -echo
    echo_device "AT+SYSNV=1,\"usbmode\",5\r" $ttyusb
    echo_device "AT+TRB\r" $ttyusb
    exit 1
fi

if [[ "$vid" == "2dee" && "$pid" == "4d41" ]]; then
    stty -F $ttyusb raw -echo
    echo_device "AT+SYSNV=1,\"usbmode\",5\r" $ttyusb
    echo_device "AT+TRB\r" $ttyusb
    exit 1
fi

if [[ "$vid" == "2dee" && "$pid" == "4d43" ]]; then
    echo_device "AT+SYSNV=1,\"usbmode\",5\r" $ttyusb
    echo_device "AT+TRB\r" $ttyusb
    exit 1
fi

如果当前为ECM模式对应的vid、pid,则开始拨号流程:

if [[ "$vid" == "2dee" && "$pid" == "4d42" ]]; then
# 拨号流程
...

首先清空/dev/ttyUSB0中的缓存,防止干扰接下来的操作,由于字符设备本身可能缓存为空,直接cat会导致挂起阻塞,所以添加1秒的超时限制,超时后杀死cat进程:

timeout 1 cat $ttyusb #输出剩下的字符,确保缓存为空

然后创建一个临时的监听文件,并开始监听:

    # 创建监听文件
    file="/tmp/ATbuf_$$"
    setup_listen_file $file $ttyusb
    # 获取监听进程PID
    listener_pid=$!
    sleep 1

"/tmp/ATbuf_"表示当前进程的id,以区分开不同进程创建的监听文件。

这里要使用$!来获取setup_listen_file()函数中在后台cat进程的id,以便脚本结束时杀死该进程。

创建好监听文件和监听进程后,就可以执行业务代码发送AT指令了,

SLM320模块可以发送“AT+CPIN?”检查SIM卡是否就绪以保证模块完成初始化及SIM卡已插入:

    # 检查SIM卡状态
    echo_device "AT+CPIN?\r" $ttyusb
    sleep 0.5

使用start_grep()函数搜索检查SIM卡指令的返回值,如果找不到就表示SIM卡异常,不予拨号并退出脚本:

    # 查找"READY"关键字,判断SIM卡是否就绪
    buf=$(start_grep "$file" '+CPIN: READY')
    echo "$(date) -- $buf" >> /tmp/usb-log.txt #debug
    if [ -z "$buf" ]; then
        echo "$(date) -- usbmode not ready." >> /tmp/usb-log.txt #debug
        exit 1
    fi

然后这里为了确保SLM320工作模式为ECM模式或RNDIS模式,再次发送指令进行判断,由于两种模式都可以正常拨号使用,所以不做区分,如果并非这两种模式其中之一,则发送修改模式的AT指令并重启模块且退出脚本:

    # 查找"+SYSNV: \"usbmode\""关键字,判断拨号状态
    buf=$(start_grep "$file" '+SYSNV: "usbmode",')
    echo "$(date) -- $buf" >> /tmp/usb-log.txt #debug

    # 先判断是否能找到关键字
    if [ ! -z "$buf" ]; then
        if [[ "$buf" == *"\"usbmode\",3"* || "$buf" == *"\"usbmode\",5"* ]]; then
# 拨号
...
# 拨号完成
    else
        echo "$(date) -- usbmode error." >> /tmp/usb-log.txt #debug
        echo_device "AT+SYSNV=1,\"usbmode\",5\r" $ttyusb
        echo_device "AT+TRB\r" $ttyusb
    fi

如果模式正确,则开始拨号,这里每个模块的拨号指令也有所不同:

# 拨号
echo_device "AT+CGACT=1\r" $ttyusb         # 激活上下文
            echo_device "AT+VERCTRL=0,1\r" $ttyusb     # 自动激活网络
            echo_device "AT^NDISDUP=1,1\r" $ttyusb     # 拨号
            # 对所有usb模块执行udhcpc获取ip,超时时间60秒
            for interface in /sys/class/net/usb*; do
                iface=$(basename $interface)
                timeout 60 udhcpc -i $iface > /dev/null 2>&1 &
            done
            echo "$(date) -- okay." >> /tmp/usb-log.txt #debug
# 拨号完成

拨号后,使用for循环,对所有usb*网卡设备都使用udhcpc获取IP,因为不确定是usb0还是usb1还是其他,udhcpc会对已获取到IP的网卡设备进行忽略。

至此完成拨号,最后清理监听文件和监听进程,退出脚本:

    # 清理监听进程
    kill -9 "$listener_pid" &> /dev/null
    # 删除监听文件
    rm "$file"
exit 0

脚本拨号完成后就能在ifconfig中看到网卡已经能获取到本地IP,并且直接就能上网:

不过也有特殊情况,就像EC200模块,只有一种vid、pid,那就只能通过AT指令判断当前工作模式了:

# EC200G
ACTION=="add", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0904", RUN+="/usr/local/bin/usb-EC200G-inserted.sh 2c7c 0904 %k"

...
if [[ "$vid" == "2c7c" && "$pid" == "0904" ]]; then
    stty -F $ttyusb raw -echo
    timeout 1 cat $ttyusb #输出剩下的字符,确保缓存为空

    # 创建监听文件
    file="/tmp/ATbuf_$$"
    setup_listen_file $file $ttyusb
    # 获取监听进程PID
    listener_pid=$!
    sleep 1

    # 检查拨号模式
    echo_device "AT+QCFG=\"usbnet\"\r" $ttyusb
    sleep 0.5

    # 查找""usbnet","关键字,判断是否为ECM模式
    buf=$(start_grep "$file" '+QCFG: "usbnet",')
    echo "$(date) -- $buf" >> /tmp/usb-log.txt #debug
    if [[ "$buf" != *"\"usbnet\",1"* ]]; then
        echo_device "AT+QCFG=\"usbnet\",1\r" $ttyusb
        echo_device "AT+RESET\r" $ttyusb
        exit 1
    fi
...

如果是ECM模式,向模块发送AT+QCFG="usbnet"指令则模块会返回+QCFG: "usbnet",1

如果是其他模式,则后面的数字就不是1了,借此可判断接下来应该修改模式还是继续拨号。

已知bug

  1. 对于存在多个同一或不同型号的4G模块,udev自动拨号只对第一个模块拨号,因为目前脚本无法对因存在多个模块而往后递增的/dev/ttyUSB*字符设备文件进行自动操作。

其他平台移植

首先在移植前,需要保证自身平台的内核已完成对虚拟usb网卡的适配支持,并且支持ECM、RNDIS拨号,

在插入4G模块后,存在/dev/ttyUSB*字符设备,并且在ifconfig能看到该网卡。

然后在文件系统构建中在/etc/udev/rules.d/目录下创建名为11-usb-autodial.rules的文件,并赋予足够的执行权限:

sudo touch /etc/udev/rules.d/11-usb-autodial.rules
sudo  chmod a+rx /etc/udev/rules.d/11-usb-autodial.rules

在文件中加入所有4G模块的udev匹配规则:

# SLM320
ACTION=="add", ATTRS{idVendor}=="0525", ATTRS{idProduct}=="a4a7", RUN+="/usr/local/bin/usb-SLM320-inserted.sh 0525 a4a7 %k"
ACTION=="add", ATTRS{idVendor}=="2dee", ATTRS{idProduct}=="4d41", RUN+="/usr/local/bin/usb-SLM320-inserted.sh 2dee 4d41 %k"
ACTION=="add", ATTRS{idVendor}=="2dee", ATTRS{idProduct}=="4d42", RUN+="/usr/local/bin/usb-SLM320-inserted.sh 2dee 4d42 %k"

# SLM750
ACTION=="add", ATTRS{idVendor}=="05c6", ATTRS{idProduct}=="f601", RUN+="/usr/local/bin/usb-SLM750-inserted.sh 05c6 f601 %k"
ACTION=="add", ATTRS{idVendor}=="05c6", ATTRS{idProduct}=="f602", RUN+="/usr/local/bin/usb-SLM750-inserted.sh 05c6 f602 %k"
ACTION=="add", ATTRS{idVendor}=="05c6", ATTRS{idProduct}=="f603", RUN+="/usr/local/bin/usb-SLM750-inserted.sh 05c6 f603 %k"
ACTION=="add", ATTRS{idVendor}=="05c6", ATTRS{idProduct}=="f604", RUN+="/usr/local/bin/usb-SLM750-inserted.sh 05c6 f604 %k"
ACTION=="add", ATTRS{idVendor}=="05e3", ATTRS{idProduct}=="f610", RUN+="/usr/local/bin/usb-SLM750-inserted.sh 05e3 f610 %k"

# N720
ACTION=="add", ATTRS{idVendor}=="2949", ATTRS{idProduct}=="8241", RUN+="/usr/local/bin/usb-N720-inserted.sh 2949 8241 %k"
ACTION=="add", ATTRS{idVendor}=="2949", ATTRS{idProduct}=="8242", RUN+="/usr/local/bin/usb-N720-inserted.sh 2949 8242 %k"
ACTION=="add", ATTRS{idVendor}=="2949", ATTRS{idProduct}=="8243", RUN+="/usr/local/bin/usb-N720-inserted.sh 2949 8243 %k"

# EC200G
ACTION=="add", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0904", RUN+="/usr/local/bin/usb-EC200G-inserted.sh 2c7c 0904 %k"
# EC200U
ACTION=="add", ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0901", RUN+="/usr/local/bin/usb-EC200G-inserted.sh 2c7c 0904 %k"

在/usr/local/bin/下放入以下脚本文件:

usb-SLM320-inserted.sh

然后给予这些脚本文件的执行权限:

sudo  chmod a+rx /usr/local/bin/usb-*

如果是直接在开发板上完成以上步骤,则需重启开发板或输入以下指令使udev规则生效:

sudo /etc/init.d/udev restart

至此移植完成,插入模块后稍等片刻既可上网,如果出现问题,可以查看拨号脚本中生成的日志进行分析:

cat /tmp/usb-log.txt

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值