当前位置:

首页 > 编程开发 > c++如何读取Linux下的HID设备原始报告描述符【深度】

c++如何读取Linux下的HID设备原始报告描述符【深度】

C++如何读取Linux下的HID设备原始报告描述符【深度】 什么是HID原始报告描述符,为什么不能直接用 read() 读 在Linux环境下,想直接通过 read() 系统调用从 /dev/hidraw0 这类设备文件里读取HID原始报告描述符?这条路走不通。原因在于,原始报告描述符本质上属于设

本文目录

    C++如何读取Linux下的HID设备原始报告描述符【深度】

    c++如何读取Linux下的HID设备原始报告描述符【深度】

    什么是HID原始报告描述符,为什么不能直接用 read() 读

    在Linux环境下,想直接通过 read() 系统调用从 /dev/hidraw0 这类设备文件里读取HID原始报告描述符?这条路走不通。原因在于,原始报告描述符本质上属于设备的“身份档案”和“通信协议说明书”,是静态的元信息,而非动态传输的数据流。

    内核在设备枚举阶段就已经完成了对描述符的解析和缓存。因此,当你对 /dev/hidrawX 执行 read() 时,获取到的只能是设备实时上报的输入报告数据,永远碰不到那份底层的描述符二进制数据。

    那么正确的入口在哪里?答案是 sysfs 文件系统。只有通过它提供的只读属性文件接口,才能触及到这份原始的、未经内核处理的描述符数据。

    从 /sys/class/hidraw/ 定位设备并读取 report_descriptor

    每个被内核识别并创建的 hidraw 设备,都会在 sysfs 中拥有一个对应的目录。你需要找到的,正是这个目录下的 device/report_descriptor 文件。其完整路径通常形如 /sys/class/hidraw/hidraw0/device/report_descriptor。

    这个文件以二进制格式、小端字节序存储着完整的原始报告描述符,其文件大小就是描述符的实际字节长度。

    实际操作时,有几个关键点必须注意:

    立即学习“C++免费学习笔记(深入)”;

    • 权限是敲门砖:访问该文件需要 root 权限,或者用户属于 hidraw 组。否则,open() 函数会直接返回权限错误(-EACCES)。
    • 只读且不可映射:该文件属性为只读,不支持写入,也不支持 mmap 内存映射。唯一的方式就是传统的 open() 加 read()。
    • 动态处理文件大小:读取前,先用 stat() 获取文件大小(st_size)来分配缓冲区是个好习惯。但要小心,在一些较旧的内核版本(例如4.15及之前)中,这个大小可能返回为0。此时就需要采用循环读取的策略,直到 read() 返回0为止。
    • 注意设备热插拔:设备被拔掉后,对应的 sysfs 路径会失效。健壮的程序需要监听内核的 uevent 事件,或者在每次操作前重新枚举 /sys/class/hidraw/ 目录。

    下面是一个结合了上述要点的C++17示例代码片段,包含了基本的错误处理:

    #include 
    #include 
    #include 
    #include 
    
    std::vector read_report_descriptor(const std::string& hidraw_dev) {
        std::string path = "/sys/class/hidraw/" + hidraw_dev + "/device/report_descriptor";
        int fd = open(path.c_str(), O_RDONLY);
        if (fd == -1) return {};
    
        struct stat st;
        if (fstat(fd, &st) == 0 && st.st_size > 0) {
            std::vector buf(st.st_size);
            ssize_t n = read(fd, buf.data(), buf.size());
            close(fd);
            if (n > 0) buf.resize(n);
            return buf;
        }
    
        // fallback: size unknown
        std::vector buf(4096);
        ssize_t n = read(fd, buf.data(), buf.size());
        close(fd);
        if (n > 0) {
            buf.resize(n);
            return buf;
        }
        return {};
    }

    用 libudev 动态发现 hidraw 设备并关联到物理路径

    在真实的应用场景中,硬编码 hidraw0 这样的设备名是不可靠的。设备节点序号可能变化,更通用的方法是根据设备的供应商ID(VID)、产品ID(PID)或制造商字符串来动态定位目标设备。

    这就需要借助 libudev 库的力量。核心思路是遍历 hidraw 子系统下的所有设备,然后向上查找其父设备(通常是 usb 或 bluetooth 子系统),从而提取出关键的识别属性。

    关键步骤分解如下:

    • 定位父设备:使用 udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device") 函数,找到对应的USB父设备节点。
    • 提取VID/PID:从找到的USB父设备节点中,读取 idVendor 和 idProduct 属性。注意,它们是以十六进制字符串形式存储的,需要使用 strtol(..., nullptr, 16) 进行转换。
    • 获取关键路径:同时,从 hidraw 设备节点本身获取 devnode(即 /dev/hidraw2 这样的路径)和 syspath(用于拼接出 report_descriptor 文件的完整路径)。
    • 处理非USB设备:并非所有HID设备都通过USB连接,例如蓝牙HID-over-GATT(HOGP)设备。对于这类设备,需要回退到从 hid 子系统的 HID_ID 属性中解析VID/PID,其格式通常为 0003:00001234:00005678,其中后两组数字分别对应VID和PID。

    常见坑:内核版本差异与描述符截断问题

    Linux内核在不同版本中对 report_descriptor 文件的处理方式存在差异,这是开发中最容易踩坑的地方之一:

    • 内核版本 ≥ 5.3:行为最理想。描述符被完整导出,通过 stat() 获取的文件大小是准确的。
    • 内核版本 4.15 – 5.2:可能出现描述符截断问题。尤其当描述符长度超过1024字节时,read() 可能只返回前1024字节,并且 stat() 得到的 st_size 很可能为0。
    • 内核版本 < 4.15:情况更复杂。某些内核配置下,sysfs 中可能根本不存在 report_descriptor 这个文件(访问返回 ENOENT)。此时,必须换用 ioctl 方式,通过 HIDIOCGRAWINFO、HIDIOCGRDESCSIZE 和 HIDIOCGRDESC 这些命令来获取描述符。但请注意,这种方式要求目标 /dev/hidrawX 设备文件已经被打开,并且内核编译时启用了 HID_RAW 支持。

    如果你发现读取出来的描述符只有几十个字节,明显不完整,第一步应该检查内核版本,并直接用命令行工具验证:cat /sys/class/hidraw/hidraw0/device/report_descriptor | hexdump -C,看看输出长度是否正常。

    一旦确认是内核导致的截断问题,最健壮的解决方案要么是升级内核到较新版本,要么就是转向使用更复杂但功能完整的 ioctl 接口。

    最后必须强调,HID报告描述符本身是一种非常紧凑的二进制格式,没有固定的长度头。解析时必须严格按照HID规范逐项(item)进行,任何对标签(tag)的误判或读取越界,都会导致后续解析完全混乱。这就是为什么强烈不建议手动编写解析器,而应该使用像 libhidapi 这样成熟的库。它提供的诸如 hid_get_manufacturer_string() 等接口,也能很好地辅助进行设备的交叉验证工作。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Linux C++
    相关文章 更多
    linux删除用户与用户组的方法及命令教程
    linux删除用户与用户组的方法及命令教程

    掌握Linux中删除用户和用户组的正确方法,避免残留文件和进程冲突。本文深入解析userdel和groupdel命令的参数用法,提供处理“用户仍在使用”等常见错误的实战方案。

    Linux如何开启端口号?开放端口命令详解
    Linux如何开启端口号?开放端口命令详解

    详细讲解Linux如何开启端口号以及常用的开放端口命令。

    crossover卸载软件下载及重新安装教程
    crossover卸载软件下载及重新安装教程

    遇到CrossOver运行错误或需要更新版本时,如何彻底卸载旧版并干净重装?本教程详解macOS和Linux下的卸载步骤、残留文件清理方法及官方下载渠道,确保软件环境纯净稳定。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。