如何获取libudev下hidraw设备对应的USB子系统路径?
关于hidraw与USB子系统的对应关系及路径获取方案
首先明确回答你的问题:USB子系统和hidraw子系统是强关联的——每个hidraw设备本质上都是USB HID设备在用户空间的访问入口,完全对应到USB总线上的某个特定USB接口。你完全可以通过libudev从hidraw设备反向追溯到它对应的USB接口设备路径。
接下来我会给你具体的实现思路和代码示例,和你当前的C/C+++libudev技术栈完全匹配:
核心实现思路
要从hidraw设备找到对应的USB接口路径,核心是通过udev的设备父子关系链向上追溯:
- 枚举所有hidraw设备(这部分你已经在做了);
- 对每个hidraw设备,遍历它的父设备,直到找到属于
usb子系统、设备类型为usb_interface的节点; - 从这个USB接口节点中提取总线号(
busnum)和设备号(devnum),拼接出/dev/bus/usb/<busnum>/<devnum>格式的路径。
完整代码示例
以下是包含热插拔适配的C语言实现代码:
#include <libudev.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> // 从hidraw设备反向获取对应的USB接口路径 char* get_usb_interface_path(struct udev_device *hidraw_dev) { struct udev_device *current_dev = hidraw_dev; // 向上遍历父设备链,定位usb_interface类型的节点 while (current_dev != NULL) { const char *subsystem = udev_device_get_subsystem(current_dev); const char *devtype = udev_device_get_devtype(current_dev); if (subsystem && strcmp(subsystem, "usb") == 0 && devtype && strcmp(devtype, "usb_interface") == 0) { // 读取总线号和设备号属性 const char *busnum = udev_device_get_sysattr_value(current_dev, "busnum"); const char *devnum = udev_device_get_sysattr_value(current_dev, "devnum"); if (busnum && devnum) { // 拼接目标路径 char *path = malloc(64); snprintf(path, 64, "/dev/bus/usb/%s/%s", busnum, devnum); return path; } break; } current_dev = udev_device_get_parent(current_dev); } return NULL; } int main() { struct udev *udev; struct udev_enumerate *enumerate; struct udev_list_entry *devices, *dev_list_entry; struct udev_device *dev; // 初始化udev上下文 udev = udev_new(); if (!udev) { fprintf(stderr, "Failed to create udev context\n"); return 1; } // 创建枚举器,过滤hidraw子系统设备 enumerate = udev_enumerate_new(udev); udev_enumerate_add_match_subsystem(enumerate, "hidraw"); udev_enumerate_scan_devices(enumerate); devices = udev_enumerate_get_list_entry(enumerate); // 遍历所有已连接的hidraw设备 udev_list_entry_foreach(dev_list_entry, devices) { const char *syspath = udev_list_entry_get_name(dev_list_entry); dev = udev_device_new_from_syspath(udev, syspath); if (dev) { const char *hidraw_path = udev_device_get_devnode(dev); printf("Found hidraw device: %s\n", hidraw_path); // 获取对应的USB接口路径 char *usb_path = get_usb_interface_path(dev); if (usb_path) { printf("Corresponding USB interface path: %s\n", usb_path); free(usb_path); } else { printf("Failed to find matching USB interface\n"); } udev_device_unref(dev); } } // 清理资源 udev_enumerate_unref(enumerate); udev_unref(udev); return 0; }
关键细节说明
- 父设备遍历逻辑:udev设备的父子关系是分层的,hidraw设备的直接父是hid设备,再往上才是usb_interface节点,所以需要循环遍历直到找到目标类型;
- 热插拔适配:如果要监听设备热插拔事件,只需把枚举逻辑替换为udev监视器(
udev_monitor_new_from_netlink),在设备添加/移除事件触发时调用get_usb_interface_path函数即可,逻辑和枚举时完全一致; - 属性可靠性:
busnum和devnum是USB设备的核心标识属性,只要设备正常枚举就一定会存在,无需额外容错处理。
编译与运行
编译时需要链接libudev库:
gcc -o hidraw_usb_map hidraw_usb_map.c -ludev
运行后就能看到每个hidraw设备对应的USB接口路径了。
内容的提问来源于stack exchange,提问作者David Gabbay
相关产品推荐
相关产品推荐

