LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

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

 

posted on 2025-06-17 23:59  ArnoldLu  阅读(205)  评论(0)    收藏  举报

导航