OpenWRT(22):hotplug处理流程
procd同时负责hotplug处理,根据hotplug.json或hotplug-preinit.json中的规格进行处理。
1 procd中处理热插拔流程
procd对hotplug的出流程如下:
代码流程如下:
main
->procd_state_next
->state_enter
->hotplug
->strdup--复制规则文件路径,即/etc/hotplut.json。
->socket--创建用于接收内核uevent的netlink socket。
->bind--将socket绑定到指定netlink地址。
->setsockopt--设置接收数据缓冲区大小。
->json_script_init--初始化JSON规则引擎。
->uloop_fd_add--添加监听读事件到循环。
->hotplug_handler--读事件回调函数。
->recv--从socket接收内核发送的uevent原始数据。
->hotplug_handler_debug--打印接收到的uevent内容。
->json_script_run--执行JSON规格匹配并处理。
一个典型的uevent时间处理流程如下:
2 /etc/hotplug-preinit.json和/etc/hotplug.json
/etc/hotplug-preinit.json用于系统preinit 阶段:
- 处理系统启动过程中发生的早期热插拔事件
- 配置基础设备(如网络接口、存储设备)的初始化行为
- 为系统关键服务(如网络栈)提供前置条件
[ [ "case", "ACTION", { -- 根据ACTION变量的值进行分支处理 "add": [ -- 当ACTION为"add"时执行以下操作。 [ "if", -- 条件判断 [ "has", "FIRMWARE" ], -- 如果存在FIRMWARE环境变量。 [ -- 则执行以下动作序列。 [ "exec", "/sbin/hotplug-call", "%SUBSYSTEM%" ], -- 调用子系统hotplug脚本。 [ "load-firmware", "/lib/firmware" ], -- 从/lib/firmware加载固件 [ "return" ] -- 终止后续处理 ] ] ] } ], [ "if", -- 另一个独立规则 [ "eq", "SUBSYSTEM", "button" ], -- 如果SUBSYSTEM是"button"。 [ "exec", "/etc/rc.button/failsafe" ] -- 执行安全模式按钮脚本。 ] ]
/etc/hotplug.json用于系统完全启动后(正常运行阶段):
- 处理用户空间的热插拔事件
- 实现设备动态管理功能
- 响应交互式设备操作
[ [ "case", "ACTION", { -- 根据ACTION变量分支。 "add": [ -- 设备添加事件处理。 [ "if", -- 条件判断 [ "and", -- 逻辑与条件 [ "has", "MAJOR" ], -- 存在MAJOR变量 [ "has", "MINOR" ] -- 存在MINOR变量 ], [ -- 条件成立时执行。 [ "if", -- 嵌套条件1 [ "eq", "DEVNAME", -- 判断DEVNAME是否为特殊设备 [ "null", "full", "ptmx", "zero", "tty", "net", "random", "urandom" ] -- 特殊设备列表 ], [ -- 特殊设备处理 [ "makedev", "/dev/%DEVNAME%", "0666" ], -- 创建设备节点(所有用户可读写) [ "return" ] -- 终止后续处理 ] ], [ "if", -- 嵌套条件2 [ "regex", "DEVNAME", "^snd" ], -- 声卡设备(正则匹配) [ "makedev", "/dev/%DEVNAME%", "0660", "audio" ] -- 创建设备节点(音频组) ], [ "if", -- 嵌套条件3 [ "regex", "DEVNAME", "^tty" ], -- 串口设备 [ "makedev", "/dev/%DEVNAME%", "0660", "dialout" ] -- 创建设备节点(dialout组) ], [ "if", -- 嵌套条件4 [ "has", "DEVNAME" ], -- 存在DEVNAME变量 [ "makedev", "/dev/%DEVNAME%", "0600" ] -- 创建设备节点(仅root可访问) ] ] ], [ "if", -- 另一个条件分支 [ "has", "FIRMWARE" ], -- 存在固件需要加载 [ -- 固件加载处理 [ "exec", "/sbin/hotplug-call", "%SUBSYSTEM%" ], -- 调用子系统脚本 [ "load-firmware", "/lib/firmware" ], -- 加载固件 [ "return" ] -- 终止后续处理 ] ], [ "if", -- 最后一个条件分支 [ "regex", "DEVNAME", "^ttyGS" ], -- USB gadget串口设备 [ "start-console", "%DEVNAME%" ] -- 启动控制台服务 ] ], "remove" : [ -- 设备移除事件处理。 [ "if", -- 条件判断 [ "and", -- 逻辑与条件 [ "has", "DEVNAME" ], -- 存在DEVNAME [ "has", "MAJOR" ], -- 存在MAJOR [ "has", "MINOR" ] -- 存在MINOR ], [ "rm", "/dev/%DEVNAME%" ] -- 删除设备节点 ] ] } ], [ "if", -- 按钮事件处理规则。 [ "and", -- 逻辑与条件 [ "has", "BUTTON" ], -- 存在BUTTON变量。 [ "eq", "SUBSYSTEM", "button" ] -- SUBSYSTEM是button。 ], [ "button", "/etc/rc.button/%BUTTON%" ] -- 执行对应按钮脚本。 ], [ "if", -- USB串口设备处理规则 [ "and", -- 逻辑与条件 [ "eq", "SUBSYSTEM", "usb-serial" ], -- SUBSYSTEM是usb-serial [ "regex", "DEVNAME", -- DEVNAME匹配以下正则 [ "^ttyUSB", "^ttyACM" ] -- ttyUSB或ttyACM设备 ] ], [ "exec", "/sbin/hotplug-call", "tty" ], -- 调用tty子系统脚本 [ "if", -- 如果上面的and条件不满足,处理其他子系统热插拔。 [ "isdir", "/etc/hotplug.d/%SUBSYSTEM%" ], -- 检查子系统目录是否存在 [ "exec", "/sbin/hotplug-call", "%SUBSYSTEM%" ] -- 存在则调用子系统脚本 ] ] ]
/sbin/hotplug-call脚本用于处理对应子系统目录下面的脚本:
#!/bin/sh # Copyright (C) 2006-2016 OpenWrt.org export HOTPLUG_TYPE="$1"--导出热插拔事件类型为第一个参数(如 usb/net/block)。 . /lib/functions.sh PATH="/usr/sbin:/usr/bin:/sbin:/bin" LOGNAME=root USER=root export PATH LOGNAME USER export DEVICENAME="${DEVPATH##*/}" if [ \! -z "$1" -a -d /etc/hotplug.d/$1 ]; then--检查参数$1非空且对应目录存在。 for script in $(ls /etc/hotplug.d/$1/* 2>&-); do (--遍历事件类型目录的所有脚本。 [ -f $script ] && . $script--如果是文件则加载执行(在子shell中运行)。 ); done fi
hotplug-call执行流程:
3 添加自己的热插拔规则
在/etc/hotplug.json中添加新设备规则:
[ // ... 现有规则 ... [ "if", [ "and", [ "eq", "SUBSYSTEM", "usb" ], // 子系统类型 [ "regex", "PRODUCT", "abcd/1234/.*" ], // 设备标识符 [ "case", "ACTION", { // 根据事件类型分支 "add": [], "remove": [], "change": [] }] ], [ // 设备事件处理 [ "exec", "/sbin/hotplug-call", "mydevice" ] // 调用自定义处理 ] ] ]
创建自定义子系统目录:
mkdir -p /etc/hotplug.d/mydevice
为不同类型创建处理脚本:
#!/bin/sh # /etc/hotplug.d/mydevice/10-add [ "$ACTION" = "add" ] || exit 0 logger -t mydevice "设备已添加: $PRODUCT" echo "新设备 $DEVNAME 已连接" > /dev/console # 加载专用驱动 modprobe mydevice_driver # 创建设备节点 mknod /dev/mydevice c $(cat /sys/$DEVPATH/dev | tr : ' ') 0666 # 启动相关服务 /etc/init.d/mydevice_service start
联系方式:arnoldlu@qq.com