如何从Linux内核模块发送HID命令和读取HID数据?

问题描述 投票:0回答:1

我正在构建一个 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'

或与库函数相当的东西,经过一番研究,我意识到这些是用户空间函数。

usb linux-device-driver embedded-linux hid ioctl
1个回答
0
投票

我设法使用内核模块发送 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_parsehid_hw_starthid_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.hhid-core/drivers/hid/中的文档,找到实际示例以及不同的公开函数使用 HID 层。

另外 https://www.kernel.org/doc/html/latest/hid/hid-transport.html 如果要执行更多低级操作,也很有用。

© www.soinside.com 2019 - 2024. All rights reserved.