You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取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;
}

关键细节说明

  1. 父设备遍历逻辑:udev设备的父子关系是分层的,hidraw设备的直接父是hid设备,再往上才是usb_interface节点,所以需要循环遍历直到找到目标类型;
  2. 热插拔适配:如果要监听设备热插拔事件,只需把枚举逻辑替换为udev监视器(udev_monitor_new_from_netlink),在设备添加/移除事件触发时调用get_usb_interface_path函数即可,逻辑和枚举时完全一致;
  3. 属性可靠性:busnum和devnum是USB设备的核心标识属性,只要设备正常枚举就一定会存在,无需额外容错处理。

编译与运行

编译时需要链接libudev库:

gcc -o hidraw_usb_map hidraw_usb_map.c -ludev

运行后就能看到每个hidraw设备对应的USB接口路径了。

内容的提问来源于stack exchange,提问作者David Gabbay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:48:58