linux使用udev实现hdmi设备插拔事件

背景

技术需求,在插入hdmi接口的时候打开扩展屏应用程序,在拔出的时候关闭应用程序

udev

udev介绍

使用udev查询触发哪个事件

运行udev实时监视命令,然后拔插设备,观察输出信息

udevadm monitor

在这里插入图片描述
插入hdmi:
在这里插入图片描述
拔出hdmi:
在这里插入图片描述
观察输出可以发现,插入和拔出内核都是发送的change事件,而不是add/remove事件,容易踩坑。同时可以从后面看出系统显示设备的路径,这里截取后面的路径即/drm/card1,因此可以得到该显示驱动路径:/sys/class/drm/card1
一般显示设备都会在/sys/class/drm路径下:
在这里插入图片描述
不确定的话可以去某个文件下用cat读取status文件可以获取其状态,在插拔一下确认状态是否正确改变:
在这里插入图片描述

通过udev查询详细信息

使用上面确认后的设备路径,执行下面命令:

udevadm info --attribute-walk --path=$(udevadm info --query=path --path=/sys/class/drm/card1-HDMI-A-1)

在这里插入图片描述
通过命令的输出可以确认KERNELS和SUBSYSTEMS的值,于是编写udev的规则文件99-custom-hdmi.rules

sudo vim /etc/udev/rules.d/99-custom-hdmi.rules

加入如下内容:

ACTION=="change", KERNEL=="card1", SUBSYSTEMS=="drm", RUN+="/path/to/your/script.sh"

其中,捕获事件设置为change,KERNEL值设置为card1-HDMI-A-1,但实测这个值不起作用,于是设置为其父设备的值,SUBSYSTEMS的值设为drm,RUN中为事件响应时执行的脚本。
然后使配置文件生效:

udevadm trigger

这里我也设置不起作用,于是还是重启试试。

事件响应脚本

在响应脚本中执行你要处理的事情,我的脚本是启动扩展屏程序。
由于设备的插入或者拔出都是触发的change事件,所以需要在响应脚本中手动判断是插入或拔出。我的判断逻辑是读取设备文件的状态文件/sys/class/drm/card1/card1-HDMI-A-1/status,插入时该状态是connected,拔出时为disconnected,代码:

#!/bin/bash  

# 读取HDMI连接状态
status=$(cat /sys/class/drm/card1-HDMI-A-1/status)
tty=/dev/pts/0
# 拔出事件
if [ "$status" = "disconnected" ]; then 
    echo "HDMI disconnected..." > $tty
# 插入事件
else   
    echo "HDMI connected..." > $tty
fi

由于脚本不在tty中执行,因为需要指定tty才能接收到输出信息,可以在终端中使用tty命令查看当前终端位置:
在这里插入图片描述
ok,这下插拔时就会有相应的响应了,上面脚本只是示例脚本,实际我在插入事件中启动我的应用,在拔出事件中杀掉应用。
注意:*.rules规则文件修改后必须重启生效,而响应脚本修改后不需要重启即生效。

problem:在脚本中执行我的程序,事件响应后会直接杀掉该程序

无论我的启动方式是后台还是挂起,脚本执行完毕后都会杀掉程序
我们打印执行前后的进程树:pstree -up
执行事件处理时:
在这里插入图片描述
执行事件处理后:
在这里插入图片描述
也是通过这一步才发现原来脚本执行后就会杀掉程序,不知道什么原因,但是这种处理方式肯定不行。
后面想到了把应用程序注册为服务,在响应脚本中启动服务,这样应用程序的进程与响应脚本的进程独立,不受影响。

第一次写,记录一下工作中遇到的问题,文笔不好切莫责怪,感谢支持~~~

  • 24
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简单的 C 语言实现程序,用于监测 udev 插拔设备: ```c #include <libudev.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(void) { struct udev *udev; struct udev_monitor *mon; int fd; udev = udev_new(); if (!udev) { printf("Failed to create udev\n"); return 1; } mon = udev_monitor_new_from_netlink(udev, "udev"); udev_monitor_filter_add_match_subsystem_devtype(mon, "usb", "usb_device"); udev_monitor_enable_receiving(mon); fd = udev_monitor_get_fd(mon); while (1) { fd_set fds; struct timeval tv; int ret; FD_ZERO(&fds); FD_SET(fd, &fds); tv.tv_sec = 1; tv.tv_usec = 0; ret = select(fd + 1, &fds, NULL, NULL, &tv); if (ret > 0 && FD_ISSET(fd, &fds)) { struct udev_device *dev = udev_monitor_receive_device(mon); if (dev) { const char *action = udev_device_get_action(dev); const char *devnode = udev_device_get_devnode(dev); const char *vendor = udev_device_get_property_value(dev, "ID_VENDOR_FROM_DATABASE"); const char *product = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE"); if (action && devnode && vendor && product) { printf("Device %s (%s %s) %s\n", devnode, vendor, product, action); } udev_device_unref(dev); } } } udev_monitor_unref(mon); udev_unref(udev); return 0; } ``` 该程序首先创建了一个 `udev` 对象,然后创建一个 `udev_monitor` 对象,并添加一个匹配 `usb` 子系统和 `usb_device` 设备类型的规则。接下来,程序启用接收功能并获取文件描述符。接下来,程序进入一个无限循环,调用 `select()` 函数阻塞等待事件,并在收到事件时处理设备变更。程序获取 `udev_device` 对象,并打印设备节点路径、设备制造商和产品信息以及设备行为。最后,程序释放相关资源并退出。 请注意,此程序仅用于演示目的,实际使用时可能需要添加更多的错误处理和异常情况处理。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值