OpenWrt Hotplug 浅析

Hotplug,直译就是热插拔。在OpenWrt中,无论何时一个设备从系统中增删,都产生一个“热插拔事件”。

hotplug机制是linux内核提供的一种消息通知机制,主要用来实现内核态事件向用户态传递。OpenWrt下的ubus也类似。ubus是用户态的,而hotplug是内核态的,其功能都是用来消息传递。

插拔一个设备,我们一般要作两方面的开发。
- 内核侧,底层驱动mapping相应的中断,然后处理中断及上报事件
- 用户侧,上层应用收到内核上报的事件,根据需求做不同的处理

从上述可以看出,我们的定制开发主要涉及两方面,内核驱动相关中断/事件的上报;用户层相关应用的对中断/事件的处理。目前经过多年的演进,这两方面的开发也趋于简单。

  • 针对板子设计创建或者修改DTS文件
  • 针对不同的设备在/etc/hotplug.d或者/etc/rc.button下添加或者修改设备事件处理脚本

下面以WPS为例,描述整个过程,

1) DTS文件

内核侧使用DTS,不用改代码,直接修改文件就可以根据自己的板子定义不同GPIO pin以及其他属性。下面是

        gpio-keys {

                compatible = "gpio-keys";

                autorepeat;

                btn_reset {

                        label = "btn,reset";

                        linux,code = <KEY_RESTART>;

                        gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;

                };

                btn_wps {

                        label = "btn,wps";        //按键标签

                        linux,code = <KEY_WPS_BUTTON>;   // 按键对应的Linux输入子系统的键值代码

                        gpios = <&gpio3 7 GPIO_ACTIVE_LOW>;  //指定GPIO端口和引脚编号

                };

                btn_wifi {

                        label = "btn,wifi";

                        linux,code = <KEY_WLAN>;

                        gpios = <&gpio3 3 GPIO_ACTIVE_LOW>;

                };

        };

2)gpiobutton-hotplug库  

在OpenWrt系统中,gpio-button-hotplug库用于简化和优化基于通用输入输出(GPIO)的按钮事件处理。它提供了一种机制,允许开发者方便地在内核级别捕获来自物理按键的事件。这种处理机制不仅增加了按键事件处理的灵活性和可靠性,还减少了开发者在底层硬件交互方面的工作量。

在gpio-button-hotplug库,两种主要的按键状态检测方式:轮询(Polling)和中断(Interrupt)。

中断方式

  • 实现方式:中断方式依赖于GPIO引脚能够产生硬件中断。当按键状态发生变化时,如被按下或释放,硬件中断会被触发,从而立即通知系统处理按键事件。
  • 适用场合:这种方式适用于对按键响应时间有严格要求的应用,如紧急停止按钮或高交互性的控制按键。

KEY_WPS_BUTTON  //gpio-button-hotplug/src/gpio-button-hotplug.c                    

gpio_keys_probe   //interupt process way

   gpio_keys_button_probe   //get platform data

         gpio_keys_irq_work_func  //delay debounce time then process this irq

                  gpio_keys_handle_button

                         button_hotplug_create_event  //create an event

                                  button_hotplug_work 

                                          button_hotplug_fill_event  //construct message content

                                          broadcast_uevent           //send uevent to user space

                                                   netlink_broadcast   //lib/kobject_uevent.c

轮询方式

  • 实现方式:在轮询模式下,系统定期检查每个按键的状态。这种方式不依赖于硬件中断,因此对于无法生成硬件中断的GPIO按键状态的检测非常适用。
  • 适用场合:轮询方式通常用于那些对实时性要求不高的场合,或是在硬件资源有限的情况下,如简单的用户界面交互。

gpio_keys_polled_probe  //poll process way

    gpio_keys_button_probe

         gpio_keys_polled_poll

              gpio_keys_handle_button  //create an event and send to user space via uevent

              gpio_keys_polled_queue_work  //delay poll interval

      在gpio-button-hotplug库,内核使用netlink socket以广播的方式把uevent事件发送给用户层。uevent 事件是以json格式的附带信息内容。事件包含的信息一般包含HOME ,PATH,SUBSYSTEM,ACTION,BUTTON,SEEN,SEQNUM等。

具体事件内容填写见button_hotplug_fill_event。下面是一个例子,

  1. procd: rule_handle_command(360): Message:
  2. procd: rule_handle_command(362):  HOME=/
  3. procd: rule_handle_command(362):  PATH=/sbin:/bin:/usr/sbin:/usr/bin
  4. procd: rule_handle_command(362):  SUBSYSTEM=button
  5. procd: rule_handle_command(362):  ACTION=pressed
  6. procd: rule_handle_command(362):  BUTTON=wps
  7. procd: rule_handle_command(362):  SEEN=862
  8. procd: rule_handle_command(362):  SEQNUM=593

3)Procd hotplug

procd/plug/hotplug.c中, 创建一个 PF_NETLINK 套接字来监听内核 netlink_broadcast() 发出的 uevent. 收到 uevent 之后, 再根据 /etc/hotplug.json 里的描述定位到对应的执行函数来处理。

通常情况下,/etc/hotplug.json会调用/sbin/hotplug-call来处理uevent,它根据uevent的$SUBSYSTEM变量来分别调用/etc/hotplug.d下不同目录中的脚本

/etc/hotplug.d目录中,您会发现一些目录block、iface、net和ntp。当触发器事件触发时,procd 将按字母顺序执行该触发器目录中的所有脚本。/sbin/hotplug-call脚本会完成这部分工作,依次执行相应目录下的所有脚本

目录                                              描述

block                             块设备事件:设备连接/断开

button:     默认情况下不创建,看到/etc/rc.button代替

dhcp                              DHCP相关事件

firewall         防火墙相关事件

iface:                         LAN / WAN /etc。连接/断开

neigh                             邻居发现

net                                 网络相关事件

ntp                                 时间同步事件:时间步长、时间服务器层变化

tftp                TFTP相关事件

USB                                                USB 设备,如 3g 调制解调器和 tty*

比如/etc/hotplug.d/iface下有4个脚本,会按顺序执行这4个脚本。

root@AONT:/etc/hotplug.d/iface# ls

00-netstate      05-led_inet      10-rgwstack  20-firewall

对于button,则执行 /etc/rc.button/ 下的 %BUTTON% 脚本来处理. 每个button对应不同的处理脚本。

以wps为例,hotplug.json内容如下,最终会调用/etc/rc.button/wps

               [ "if",

                                [ "and",

                                                [ "has", "BUTTON" ],

                                                [ "eq", "SUBSYSTEM", "button" ]

                                ],

                                [ "button", "/etc/rc.button/%BUTTON%" ]

                ],

代码走读,

procd初始化的时候将hotplug运行起来,procd_state_next-->state_enter-->hotplug

hotplug 会创建PF_NETLINK socket,接收NETLINK_KOBJECT_UEVENT。将这个socket加入uloop,其callback是hotplug_handler

hotplug_handler 会接收uevent事件,并解析json 格式的事件报文。然后调用json_script_run

json_script_run根据etc/hotplug.json中预先定义的JSON内容匹配条件,定位到对应的执行函数。

handlers[] = {
	[HANDLER_MKDEV] = {
		.name = "makedev",
		.atomic = 1,
		.handler = handle_makedev,
	},
	[HANDLER_RM] = {
		.name = "rm",
		.atomic = 1,
		.handler = handle_rm,
	},
	[HANDLER_EXEC] = {
		.name = "exec",
		.handler = handle_exec,
	},
	[HANDLER_BUTTON] = {
		.name = "button",
		.handler = handle_exec,  //执行相应的脚本
		.start = handle_button_start,
		.complete = handle_button_complete,
	},
	[HANDLER_FW] = {
		.name = "load-firmware",
		.handler = handle_firmware,
	},
	[HANDLER_START_CONSOLE] = {
		.name = "start-console",
		.handler = handle_start_console,
	},
};

rule_handle_command

       handle_exec

       handle_makedev

       handle_rm

       handle_firmware

       handle_start_console

这些函数的入参是(struct blob_attr *msg, struct blob_attr *data),其中msg表示来自kernel JSON uevent;data表示来自hotplug.json中匹配的规则。

参考文献:

OpenWRT Hotplug原理分析-CSDN博客

openwrt热插拔HotPlug_openwrt hotplug-CSDN博客

openwrt hotplug_openwrt uevent事件-CSDN博客

基础指南:在OpenWrt中使用GPIO按键 - 知乎 (zhihu.com)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值