HIDRAW - 原始访问 USB 和蓝牙人机接口设备

hidraw 驱动程序提供了一个到 USB 和蓝牙人机接口设备 (HID) 的原始接口。它与 hiddev 的不同之处在于,发送和接收的报告不会被 HID 解析器解析,而是以未修改的形式发送到设备并从设备接收。

如果用户空间应用程序确切地知道如何与硬件设备通信,并且能够手动构建 HID 报告,则应使用 Hidraw。对于为自定义 HID 设备制作用户空间驱动程序时,通常是这种情况。

Hidraw 对于与不符合规范的 HID 设备通信也很有用,这些设备以与其报告描述符不一致的方式发送和接收数据。由于 hiddev 会解析通过它发送和接收的报告,并根据设备的报告描述符检查它们,因此使用 hiddev 无法与这些不符合规范的设备进行此类通信。 对于这些不符合规范的设备,Hidraw 是唯一的替代方案,除非编写自定义内核驱动程序。

hidraw 的一个优点是用户空间应用程序对其的使用独立于底层硬件类型。 目前,hidraw 已经为 USB 和蓝牙实现。 未来,随着开发使用 HID 规范的新硬件总线类型,hidraw 将扩展以添加对这些新总线类型的支持。

Hidraw 使用动态主设备号,这意味着应该依靠 udev 来创建 hidraw 设备节点。 Udev 通常会在 /dev 下直接创建设备节点(例如:/dev/hidraw0)。 由于此位置依赖于发行版和 udev 规则,因此应用程序应使用 libudev 来定位连接到系统的 hidraw 设备。 libudev 上有一个包含工作示例的教程,网址为

http://www.signal11.us/oss/udev/
https://web.archive.org/web/2019*/www.signal11.us

HIDRAW API

read()

read() 将读取从 HID 设备接收的排队报告。 在 USB 设备上,使用 read() 读取的报告是从设备在 INTERRUPT IN 端点上发送的报告。 默认情况下,read() 将阻塞,直到有报告可供读取。 可以通过将 O_NONBLOCK 标志传递给 open() 或使用 fcntl() 设置 O_NONBLOCK 标志来使 read() 变为非阻塞。

在使用编号报告的设备上,返回数据的第一个字节将是报告编号;报告数据紧随其后,从第二个字节开始。 对于不使用编号报告的设备,报告数据将从第一个字节开始。

write()

write() 函数会将报告写入设备。 对于 USB 设备,如果设备具有 INTERRUPT OUT 端点,则将在该端点上发送报告。 如果没有,将使用 SET_REPORT 传输通过控制端点发送报告。

传递给 write() 的缓冲区的第一个字节应设置为报告编号。 如果设备不使用编号报告,则第一个字节应设置为 0。 报告数据本身应从第二个字节开始。

ioctl()

Hidraw 支持以下 ioctl

HIDIOCGRDESCSIZE

获取报告描述符大小

此 ioctl 将获取设备报告描述符的大小。

HIDIOCGRDESC

获取报告描述符

此 ioctl 使用 hidraw_report_descriptor 结构返回设备的报告描述符。 请确保将 hidraw_report_descriptor 结构的 size 字段设置为从 HIDIOCGRDESCSIZE 返回的大小。

HIDIOCGRAWINFO

获取原始信息

此 ioctl 将返回一个 hidraw_devinfo 结构,其中包含设备的总线类型、供应商 ID (VID) 和产品 ID (PID)。 总线类型可以是以下之一

- BUS_USB
- BUS_HIL
- BUS_BLUETOOTH
- BUS_VIRTUAL

这些定义在 uapi/linux/input.h 中。

HIDIOCGRAWNAME(len)

获取原始名称

此 ioctl 返回一个字符串,其中包含设备的供应商和产品字符串。 返回的字符串是 Unicode,UTF-8 编码。

HIDIOCGRAWPHYS(len)

获取物理地址

此 ioctl 返回一个字符串,表示设备的物理地址。 对于 USB 设备,该字符串包含设备的物理路径(USB 控制器、集线器、端口等)。 对于蓝牙设备,该字符串包含设备的硬件 (MAC) 地址。

HIDIOCSFEATURE(len)

发送特性报告

此 ioctl 将向设备发送特性报告。 按照 HID 规范,特性报告始终使用控制端点发送。 将提供的缓冲区的第一个字节设置为报告编号。 对于不使用编号报告的设备,将第一个字节设置为 0。 报告数据从第二个字节开始。 请确保相应地设置 len,比报告的长度多一个(以说明报告编号)。

HIDIOCGFEATURE(len)

获取特性报告

此 ioctl 将使用控制端点从设备请求特性报告。 提供的缓冲区的第一个字节应设置为请求报告的报告编号。 对于不使用编号报告的设备,将第一个字节设置为 0。 返回的报告缓冲区将包含第一个字节中的报告编号,后跟从设备读取的报告数据。 对于不使用编号报告的设备,报告数据将从返回缓冲区的第一个字节开始。

HIDIOCSINPUT(len)

发送输入报告

此 ioctl 将使用控制端点向设备发送输入报告。 在大多数情况下,在设备上设置输入 HID 报告是没有意义且没有效果的,但某些设备可能会选择使用它来设置或重置报告的初始状态。 使用此报告发出的缓冲区的格式与 HIDIOCSFEATURE 的格式相同。

HIDIOCGINPUT(len)

获取输入报告

此 ioctl 将使用控制端点从设备请求输入报告。 在大多数设备上,对于存在用于常规输入报告的专用 In 端点,这会比较慢,但它允许主机请求特定报告编号的值。 通常,这用于请求设备的输入报告的初始状态,然后应用程序通过常规设备 read() 接口侦听正常报告。 使用此报告发出的缓冲区的格式与 HIDIOCGFEATURE 的格式相同。

HIDIOCSOUTPUT(len)

发送输出报告

此 ioctl 将使用控制端点向设备发送输出报告。 在大多数设备上,对于存在用于常规输出报告的专用 Out 端点,这会比较慢,但为了完整性而添加了它。 通常,这用于设置设备的输出报告的初始状态,然后应用程序通过 HIDIOCSOUTPUT 请求或常规设备 write() 接口发送更新。 使用此报告发出的缓冲区的格式与 HIDIOCSFEATURE 的格式相同。

HIDIOCGOUTPUT(len)

获取输出报告

此 ioctl 将使用控制端点从设备请求输出报告。 通常,这用于检索设备的输出报告的初始状态,然后应用程序根据需要通过 HIDIOCSOUTPUT 请求或常规设备 write() 接口对其进行更新。 使用此报告发出的缓冲区的格式与 HIDIOCGFEATURE 的格式相同。

示例

在 samples/ 中,找到 hid-example.c,其中显示了 read()、write() 和 hidraw 的所有 ioctl 的示例。 任何人都可以将该代码用于任何目的,并且可以作为使用 hidraw 开发应用程序的起点。

文档作者

Alan Ott <alan@signal11.us>, Signal 11 Software