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 结构的大小字段设置为从 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 端点,则此操作会较慢,但为了完整性而添加。通常,这用于在应用程序通过常规设备 write() 接口发送更新之前,设置设备的输出报告的初始状态。使用此报告发出的缓冲区的格式与 HIDIOCSFEATURE 的格式相同。

HIDIOCGOUTPUT(len)

获取输出报告

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

示例

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

文档作者

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