我正在构建一个 Linux 内核模块,该模块需要读取 HID 设备,以使用 RPC 通信将数据发送到虚拟机管理程序。这些假设是必要的,因为我正在嵌入式 Linux 中工作,并且我正在开发的 SoC 需要内核模块来发送此类数据并通过注册的回调发回数据。
我知道有像
libusb
/ hidapi
这样的库可以简单地发送 HID 命令并从用户空间读取 HID 数据,如下所示:
hidapitester --vidpid 2752:0012 -l 8 --open --send-output 0x03,0x53,0x02,0x58 --read-input
ID 将指向设备,我们可以根据制造商规范发送和读取字节。我们还可以使用它们的库函数编写程序来做到这一点,这些库函数最终基于 HIDRAW。
问题是,这些是在用户空间上工作的库,但是不可能用用户空间函数编译内核模块,因此这些库将无法工作。 HIDRAW 主要依赖于 IOCTL,因此尝试使用
ioctl()
调用,但也没有成功。
如何写入和读取我从 Linux 内核模块中知道其 ID 的 HID 设备?
我尝试过使用
hidapi
和 hidraw
函数,或者只是从内核模块直接调用 ioctl()
,但我得到:
error: implicit declaration of function 'ioctl'
或与库函数相当的东西,经过一番研究,我意识到这些是用户空间函数。
我设法使用内核模块发送 HID 报告/命令。
为此,必须创建一个模块(如果您不熟悉,请阅读 Linux 内核模块结构)并使用 struct hid_driver 结构。这里有必要注册一些函数,一旦我们的驱动程序被识别为适合给定设备的驱动程序,这些函数就会被调用,例如
static struct hid_driver my_hid_driver = {
.name = "my-hid-receiver",
.id_table = my_id_table,
.probe = my_probe_function,
.remove = my_remove_function,
.raw_event = my_raw_event
};
为了让我们的驱动程序在连接 HID 设备时被调用,我们使用 id_table,如下所示:
static const struct hid_device_id my_id_table[] = {
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, <ID OF THE MANUFACTURER>, <ID OF THE PRODUCT>) },
{}
};
其他驱动程序执行更多特异性的文档可在 Linux 源代码中的 /drivers/hid/ 中找到。
之后 .probe 将是一个很好的起点,因为当驱动程序首次分配给设备时会调用它。调用一些必要的函数(如 hid_parse、hid_hw_start 和 hid_hw_open)后,我们就可以发送报告了。
这里我们可以使用 hid_hw_request,来自 hid-core 的函数,用于此目的 如果我们想发送以下 5 个字节的数据 [0x01,0xff,0x01,0x33,0x0f] 我们可以这样做:
struct hid_report *report;
struct hid_report_enum *output_report_enum;
output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT]; // Getting the report from the hid_device hdev
report = output_report_enum->report_id_hash[<ID>]; // hid_device hdev is passed to probe as argument
report->field[0]->value[0] = 0x01; // Actual data to be sent to the device
report->field[0]->value[1] = 0xFF;
report->field[0]->value[2] = 0x01;
report->field[0]->value[3] = 0x33;
report->field[0]->value[4] = 0x0F;
hid_hw_request(hdev, report, HID_REQ_SET_REPORT); // request to set the data
请记住,ID 和字段特定于您的 HID 设备的工作方式,我在这里仅使用了最简单的配置。
如果我们想进行读取,我们可以使用 HID_INPUT_REPORT 作为枚举,使用 HID_REQ_GET_REPORT 作为参数 hid_hw_request。
我们可以查看linux源代码中的/include/linux/hid.h、hid-core和/drivers/hid/中的文档,找到实际示例以及不同的公开函数使用 HID 层。
另外 https://www.kernel.org/doc/html/latest/hid/hid-transport.html 如果要执行更多低级操作,也很有用。