Merge branch 'for-6.13/bpf' into for-linus

- improvement of the way hid-bpf coexists with specific drivers (others than
  hid-generic) that are already bound to devices (Benjamin Tissoires)
This commit is contained in:
Jiri Kosina 2024-11-18 22:02:17 +01:00
commit 903796855b
17 changed files with 708 additions and 201 deletions

View File

@ -148,7 +148,7 @@ out:
} }
EXPORT_SYMBOL_GPL(dispatch_hid_bpf_output_report); EXPORT_SYMBOL_GPL(dispatch_hid_bpf_output_report);
u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size) const u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size)
{ {
int ret; int ret;
struct hid_bpf_ctx_kern ctx_kern = { struct hid_bpf_ctx_kern ctx_kern = {
@ -183,7 +183,7 @@ u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned
ignore_bpf: ignore_bpf:
kfree(ctx_kern.data); kfree(ctx_kern.data);
return kmemdup(rdesc, *size, GFP_KERNEL); return rdesc;
} }
EXPORT_SYMBOL_GPL(call_hid_bpf_rdesc_fixup); EXPORT_SYMBOL_GPL(call_hid_bpf_rdesc_fixup);
@ -260,8 +260,11 @@ int hid_bpf_allocate_event_data(struct hid_device *hdev)
int hid_bpf_reconnect(struct hid_device *hdev) int hid_bpf_reconnect(struct hid_device *hdev)
{ {
if (!test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status)) if (!test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status)) {
/* trigger call to call_hid_bpf_rdesc_fixup() during the next probe */
hdev->bpf_rsize = 0;
return device_reprobe(&hdev->dev); return device_reprobe(&hdev->dev);
}
return 0; return 0;
} }

View File

@ -79,6 +79,7 @@ static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log,
WRITE_RANGE(hid_device, name, true), WRITE_RANGE(hid_device, name, true),
WRITE_RANGE(hid_device, uniq, true), WRITE_RANGE(hid_device, uniq, true),
WRITE_RANGE(hid_device, phys, true), WRITE_RANGE(hid_device, phys, true),
WRITE_RANGE(hid_device, quirks, false),
}; };
#undef WRITE_RANGE #undef WRITE_RANGE
const struct btf_type *state = NULL; const struct btf_type *state = NULL;

View File

@ -214,7 +214,8 @@ static const __u8 fixed_rdesc_pad[] = {
CollectionApplication( CollectionApplication(
// -- Byte 0 in report // -- Byte 0 in report
ReportId(PAD_REPORT_ID) ReportId(PAD_REPORT_ID)
LogicalRange_i8(0, 1) LogicalMaximum_i8(0)
LogicalMaximum_i8(1)
UsagePage_Digitizers UsagePage_Digitizers
Usage_Dig_TabletFunctionKeys Usage_Dig_TabletFunctionKeys
CollectionPhysical( CollectionPhysical(
@ -234,14 +235,17 @@ static const __u8 fixed_rdesc_pad[] = {
Input(Var|Abs) Input(Var|Abs)
// Byte 4 in report is the dial // Byte 4 in report is the dial
Usage_GD_Wheel Usage_GD_Wheel
LogicalRange_i8(-1, 1) LogicalMinimum_i8(-1)
LogicalMaximum_i8(1)
ReportCount(1) ReportCount(1)
ReportSize(8) ReportSize(8)
Input(Var|Rel) Input(Var|Rel)
// Byte 5 is the button state // Byte 5 is the button state
UsagePage_Button UsagePage_Button
UsageRange_i8(0x01, 0x8) UsageMinimum_i8(0x01)
LogicalRange_i8(0x0, 0x1) UsageMaximum_i8(0x08)
LogicalMinimum_i8(0x0)
LogicalMaximum_i8(0x1)
ReportCount(7) ReportCount(7)
ReportSize(1) ReportSize(1)
Input(Var|Abs) Input(Var|Abs)
@ -265,7 +269,8 @@ static const __u8 fixed_rdesc_pen[] = {
Usage_Dig_TipSwitch Usage_Dig_TipSwitch
Usage_Dig_BarrelSwitch Usage_Dig_BarrelSwitch
Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2 Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2
LogicalRange_i8(0, 1) LogicalMinimum_i8(0)
LogicalMaximum_i8(1)
ReportSize(1) ReportSize(1)
ReportCount(3) ReportCount(3)
Input(Var|Abs) Input(Var|Abs)
@ -280,22 +285,28 @@ static const __u8 fixed_rdesc_pen[] = {
UsagePage_GenericDesktop UsagePage_GenericDesktop
Unit(cm) Unit(cm)
UnitExponent(-1) UnitExponent(-1)
PhysicalRange_i16(0, 266) PhysicalMinimum_i16(0)
LogicalRange_i16(0, 32767) PhysicalMaximum_i16(266)
LogicalMinimum_i16(0)
LogicalMaximum_i16(32767)
Usage_GD_X Usage_GD_X
Input(Var|Abs) // Bytes 2+3 Input(Var|Abs) // Bytes 2+3
PhysicalRange_i16(0, 166) PhysicalMinimum_i16(0)
LogicalRange_i16(0, 32767) PhysicalMaximum_i16(166)
LogicalMinimum_i16(0)
LogicalMaximum_i16(32767)
Usage_GD_Y Usage_GD_Y
Input(Var|Abs) // Bytes 4+5 Input(Var|Abs) // Bytes 4+5
) )
UsagePage_Digitizers UsagePage_Digitizers
Usage_Dig_TipPressure Usage_Dig_TipPressure
LogicalRange_i16(0, 8191) LogicalMinimum_i16(0)
LogicalMaximum_i16(8191)
Input(Var|Abs) // Byte 6+7 Input(Var|Abs) // Byte 6+7
ReportSize(8) ReportSize(8)
ReportCount(2) ReportCount(2)
LogicalRange_i8(-60, 60) LogicalMinimum_i8(-60)
LogicalMaximum_i8(60)
Usage_Dig_XTilt Usage_Dig_XTilt
Usage_Dig_YTilt Usage_Dig_YTilt
Input(Var|Abs) // Byte 8+9 Input(Var|Abs) // Byte 8+9
@ -313,7 +324,8 @@ static const __u8 fixed_rdesc_vendor[] = {
Usage_Dig_Pen Usage_Dig_Pen
CollectionPhysical( CollectionPhysical(
// Byte 1 are the buttons // Byte 1 are the buttons
LogicalRange_i8(0, 1) LogicalMinimum_i8(0)
LogicalMaximum_i8(1)
ReportSize(1) ReportSize(1)
Usage_Dig_TipSwitch Usage_Dig_TipSwitch
Usage_Dig_BarrelSwitch Usage_Dig_BarrelSwitch
@ -333,25 +345,31 @@ static const __u8 fixed_rdesc_vendor[] = {
UnitExponent(-1) UnitExponent(-1)
// Note: reported logical range differs // Note: reported logical range differs
// from the pen report ID for x and y // from the pen report ID for x and y
LogicalRange_i16(0, 53340) LogicalMinimum_i16(0)
PhysicalRange_i16(0, 266) LogicalMaximum_i16(53340)
PhysicalMinimum_i16(0)
PhysicalMaximum_i16(266)
// Bytes 2/3 in report // Bytes 2/3 in report
Usage_GD_X Usage_GD_X
Input(Var|Abs) Input(Var|Abs)
LogicalRange_i16(0, 33340) LogicalMinimum_i16(0)
PhysicalRange_i16(0, 166) LogicalMaximum_i16(33340)
PhysicalMinimum_i16(0)
PhysicalMaximum_i16(166)
// Bytes 4/5 in report // Bytes 4/5 in report
Usage_GD_Y Usage_GD_Y
Input(Var|Abs) Input(Var|Abs)
) )
// Bytes 6/7 in report // Bytes 6/7 in report
LogicalRange_i16(0, 8191) LogicalMinimum_i16(0)
LogicalMaximum_i16(8191)
Usage_Dig_TipPressure Usage_Dig_TipPressure
Input(Var|Abs) Input(Var|Abs)
// Bytes 8/9 in report // Bytes 8/9 in report
ReportCount(1) // Padding ReportCount(1) // Padding
Input(Const) Input(Const)
LogicalRange_i8(-60, 60) LogicalMinimum_i8(-60)
LogicalMaximum_i8(60)
// Byte 10 in report // Byte 10 in report
Usage_Dig_XTilt Usage_Dig_XTilt
// Byte 11 in report // Byte 11 in report
@ -366,7 +384,8 @@ static const __u8 fixed_rdesc_vendor[] = {
CollectionApplication( CollectionApplication(
// Byte 0 // Byte 0
ReportId(PAD_REPORT_ID) ReportId(PAD_REPORT_ID)
LogicalRange_i8(0, 1) LogicalMinimum_i8(0)
LogicalMaximum_i8(1)
UsagePage_Digitizers UsagePage_Digitizers
Usage_Dig_TabletFunctionKeys Usage_Dig_TabletFunctionKeys
CollectionPhysical( CollectionPhysical(
@ -386,15 +405,18 @@ static const __u8 fixed_rdesc_vendor[] = {
Input(Var|Abs) Input(Var|Abs)
// Byte 4 is the button state // Byte 4 is the button state
UsagePage_Button UsagePage_Button
UsageRange_i8(0x01, 0x8) UsageMinimum_i8(0x1)
LogicalRange_i8(0x0, 0x1) UsageMaximum_i8(0x8)
LogicalMinimum_i8(0x0)
LogicalMaximum_i8(0x1)
ReportCount(8) ReportCount(8)
ReportSize(1) ReportSize(1)
Input(Var|Abs) Input(Var|Abs)
// Byte 5 is the top dial // Byte 5 is the top dial
UsagePage_GenericDesktop UsagePage_GenericDesktop
Usage_GD_Wheel Usage_GD_Wheel
LogicalRange_i8(-1, 1) LogicalMinimum_i8(-1)
LogicalMaximum_i8(1)
ReportCount(1) ReportCount(1)
ReportSize(8) ReportSize(8)
Input(Var|Rel) Input(Var|Rel)

View File

@ -170,7 +170,8 @@ static const __u8 fixed_rdesc_pad[] = {
CollectionApplication( CollectionApplication(
// -- Byte 0 in report // -- Byte 0 in report
ReportId(PAD_REPORT_ID) ReportId(PAD_REPORT_ID)
LogicalRange_i8(0, 1) LogicalMinimum_i8(0)
LogicalMaximum_i8(1)
UsagePage_Digitizers UsagePage_Digitizers
Usage_Dig_TabletFunctionKeys Usage_Dig_TabletFunctionKeys
CollectionPhysical( CollectionPhysical(
@ -190,14 +191,17 @@ static const __u8 fixed_rdesc_pad[] = {
Input(Var|Abs) Input(Var|Abs)
// Byte 4 in report is the wheel // Byte 4 in report is the wheel
Usage_GD_Wheel Usage_GD_Wheel
LogicalRange_i8(-1, 1) LogicalMinimum_i8(-1)
LogicalMaximum_i8(1)
ReportCount(1) ReportCount(1)
ReportSize(8) ReportSize(8)
Input(Var|Rel) Input(Var|Rel)
// Byte 5 is the button state // Byte 5 is the button state
UsagePage_Button UsagePage_Button
UsageRange_i8(0x01, 0x6) UsageMinimum_i8(0x1)
LogicalRange_i8(0x01, 0x6) UsageMaximum_i8(0x6)
LogicalMinimum_i8(0x1)
LogicalMaximum_i8(0x6)
ReportCount(1) ReportCount(1)
ReportSize(8) ReportSize(8)
Input(Arr|Abs) Input(Arr|Abs)
@ -219,7 +223,8 @@ static const __u8 fixed_rdesc_pen[] = {
Usage_Dig_TipSwitch Usage_Dig_TipSwitch
Usage_Dig_BarrelSwitch Usage_Dig_BarrelSwitch
Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2 Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2
LogicalRange_i8(0, 1) LogicalMinimum_i8(0)
LogicalMaximum_i8(1)
ReportSize(1) ReportSize(1)
ReportCount(3) ReportCount(3)
Input(Var|Abs) Input(Var|Abs)
@ -234,18 +239,23 @@ static const __u8 fixed_rdesc_pen[] = {
UsagePage_GenericDesktop UsagePage_GenericDesktop
Unit(cm) Unit(cm)
UnitExponent(-1) UnitExponent(-1)
PhysicalRange_i16(0, 160) PhysicalMinimum_i16(0)
LogicalRange_i16(0, 32767) PhysicalMaximum_i16(160)
LogicalMinimum_i16(0)
LogicalMaximum_i16(32767)
Usage_GD_X Usage_GD_X
Input(Var|Abs) // Bytes 2+3 Input(Var|Abs) // Bytes 2+3
PhysicalRange_i16(0, 100) PhysicalMinimum_i16(0)
LogicalRange_i16(0, 32767) PhysicalMaximum_i16(100)
LogicalMinimum_i16(0)
LogicalMaximum_i16(32767)
Usage_GD_Y Usage_GD_Y
Input(Var|Abs) // Bytes 4+5 Input(Var|Abs) // Bytes 4+5
) )
UsagePage_Digitizers UsagePage_Digitizers
Usage_Dig_TipPressure Usage_Dig_TipPressure
LogicalRange_i16(0, 8191) LogicalMinimum_i16(0)
LogicalMaximum_i16(8191)
Input(Var|Abs) // Byte 6+7 Input(Var|Abs) // Byte 6+7
// Two bytes padding so we don't need to change the report at all // Two bytes padding so we don't need to change the report at all
ReportSize(8) ReportSize(8)
@ -265,7 +275,8 @@ static const __u8 fixed_rdesc_vendor[] = {
Usage_Dig_Pen Usage_Dig_Pen
CollectionPhysical( CollectionPhysical(
// Byte 1 are the buttons // Byte 1 are the buttons
LogicalRange_i8(0, 1) LogicalMinimum_i8(0)
LogicalMaximum_i8(1)
ReportSize(1) ReportSize(1)
Usage_Dig_TipSwitch Usage_Dig_TipSwitch
Usage_Dig_BarrelSwitch Usage_Dig_BarrelSwitch
@ -285,19 +296,24 @@ static const __u8 fixed_rdesc_vendor[] = {
UnitExponent(-1) UnitExponent(-1)
// Note: reported logical range differs // Note: reported logical range differs
// from the pen report ID for x and y // from the pen report ID for x and y
LogicalRange_i16(0, 32000) LogicalMinimum_i16(0)
PhysicalRange_i16(0, 160) LogicalMaximum_i16(32000)
PhysicalMinimum_i16(0)
PhysicalMaximum_i16(160)
// Bytes 2/3 in report // Bytes 2/3 in report
Usage_GD_X Usage_GD_X
Input(Var|Abs) Input(Var|Abs)
LogicalRange_i16(0, 20000) LogicalMinimum_i16(0)
PhysicalRange_i16(0, 100) LogicalMaximum_i16(20000)
PhysicalMinimum_i16(0)
PhysicalMaximum_i16(100)
// Bytes 4/5 in report // Bytes 4/5 in report
Usage_GD_Y Usage_GD_Y
Input(Var|Abs) Input(Var|Abs)
) )
// Bytes 6/7 in report // Bytes 6/7 in report
LogicalRange_i16(0, 8192) LogicalMinimum_i16(0)
LogicalMaximum_i16(8192)
Usage_Dig_TipPressure Usage_Dig_TipPressure
Input(Var|Abs) Input(Var|Abs)
) )
@ -307,7 +323,8 @@ static const __u8 fixed_rdesc_vendor[] = {
CollectionApplication( CollectionApplication(
// Byte 0 // Byte 0
ReportId(PAD_REPORT_ID) ReportId(PAD_REPORT_ID)
LogicalRange_i8(0, 1) LogicalMinimum_i8(0)
LogicalMaximum_i8(1)
UsagePage_Digitizers UsagePage_Digitizers
Usage_Dig_TabletFunctionKeys Usage_Dig_TabletFunctionKeys
CollectionPhysical( CollectionPhysical(
@ -327,8 +344,10 @@ static const __u8 fixed_rdesc_vendor[] = {
Input(Var|Abs) Input(Var|Abs)
// Byte 4 is the button state // Byte 4 is the button state
UsagePage_Button UsagePage_Button
UsageRange_i8(0x01, 0x6) UsageMinimum_i8(0x1)
LogicalRange_i8(0x0, 0x1) UsageMaximum_i8(0x6)
LogicalMinimum_i8(0x0)
LogicalMaximum_i8(0x1)
ReportCount(6) ReportCount(6)
ReportSize(1) ReportSize(1)
Input(Var|Abs) Input(Var|Abs)
@ -337,7 +356,8 @@ static const __u8 fixed_rdesc_vendor[] = {
// Byte 5 is the wheel // Byte 5 is the wheel
UsagePage_GenericDesktop UsagePage_GenericDesktop
Usage_GD_Wheel Usage_GD_Wheel
LogicalRange_i8(-1, 1) LogicalMinimum_i8(-1)
LogicalMaximum_i8(1)
ReportCount(1) ReportCount(1)
ReportSize(8) ReportSize(8)
Input(Var|Rel) Input(Var|Rel)

View File

@ -0,0 +1,154 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2024 Tatsuyuki Ishi
*/
#include "vmlinux.h"
#include "hid_bpf.h"
#include "hid_bpf_helpers.h"
#include <bpf/bpf_tracing.h>
#define VID_HOLTEK 0x04D9
#define PID_MD770 0x0339
#define RDESC_SIZE 203
HID_BPF_CONFIG(
HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_HOLTEK, PID_MD770)
);
/*
* The Mistel MD770 keyboard reports the first 6 simultaneous key presses
* through the first interface, and anything beyond that through a second
* interface. Unfortunately, the second interface's report descriptor has an
* error, causing events to be malformed and ignored. This HID-BPF driver
* fixes the descriptor to allow NKRO to work again.
*
* For reference, this is the original report descriptor:
*
* 0x05, 0x01, // Usage Page (Generic Desktop) 0
* 0x09, 0x80, // Usage (System Control) 2
* 0xa1, 0x01, // Collection (Application) 4
* 0x85, 0x01, // Report ID (1) 6
* 0x19, 0x81, // Usage Minimum (129) 8
* 0x29, 0x83, // Usage Maximum (131) 10
* 0x15, 0x00, // Logical Minimum (0) 12
* 0x25, 0x01, // Logical Maximum (1) 14
* 0x95, 0x03, // Report Count (3) 16
* 0x75, 0x01, // Report Size (1) 18
* 0x81, 0x02, // Input (Data,Var,Abs) 20
* 0x95, 0x01, // Report Count (1) 22
* 0x75, 0x05, // Report Size (5) 24
* 0x81, 0x01, // Input (Cnst,Arr,Abs) 26
* 0xc0, // End Collection 28
* 0x05, 0x0c, // Usage Page (Consumer Devices) 29
* 0x09, 0x01, // Usage (Consumer Control) 31
* 0xa1, 0x01, // Collection (Application) 33
* 0x85, 0x02, // Report ID (2) 35
* 0x15, 0x00, // Logical Minimum (0) 37
* 0x25, 0x01, // Logical Maximum (1) 39
* 0x95, 0x12, // Report Count (18) 41
* 0x75, 0x01, // Report Size (1) 43
* 0x0a, 0x83, 0x01, // Usage (AL Consumer Control Config) 45
* 0x0a, 0x8a, 0x01, // Usage (AL Email Reader) 48
* 0x0a, 0x92, 0x01, // Usage (AL Calculator) 51
* 0x0a, 0x94, 0x01, // Usage (AL Local Machine Browser) 54
* 0x09, 0xcd, // Usage (Play/Pause) 57
* 0x09, 0xb7, // Usage (Stop) 59
* 0x09, 0xb6, // Usage (Scan Previous Track) 61
* 0x09, 0xb5, // Usage (Scan Next Track) 63
* 0x09, 0xe2, // Usage (Mute) 65
* 0x09, 0xea, // Usage (Volume Down) 67
* 0x09, 0xe9, // Usage (Volume Up) 69
* 0x0a, 0x21, 0x02, // Usage (AC Search) 71
* 0x0a, 0x23, 0x02, // Usage (AC Home) 74
* 0x0a, 0x24, 0x02, // Usage (AC Back) 77
* 0x0a, 0x25, 0x02, // Usage (AC Forward) 80
* 0x0a, 0x26, 0x02, // Usage (AC Stop) 83
* 0x0a, 0x27, 0x02, // Usage (AC Refresh) 86
* 0x0a, 0x2a, 0x02, // Usage (AC Bookmarks) 89
* 0x81, 0x02, // Input (Data,Var,Abs) 92
* 0x95, 0x01, // Report Count (1) 94
* 0x75, 0x0e, // Report Size (14) 96
* 0x81, 0x01, // Input (Cnst,Arr,Abs) 98
* 0xc0, // End Collection 100
* 0x05, 0x01, // Usage Page (Generic Desktop) 101
* 0x09, 0x02, // Usage (Mouse) 103
* 0xa1, 0x01, // Collection (Application) 105
* 0x09, 0x01, // Usage (Pointer) 107
* 0xa1, 0x00, // Collection (Physical) 109
* 0x85, 0x03, // Report ID (3) 111
* 0x05, 0x09, // Usage Page (Button) 113
* 0x19, 0x01, // Usage Minimum (1) 115
* 0x29, 0x08, // Usage Maximum (8) 117
* 0x15, 0x00, // Logical Minimum (0) 119
* 0x25, 0x01, // Logical Maximum (1) 121
* 0x75, 0x01, // Report Size (1) 123
* 0x95, 0x08, // Report Count (8) 125
* 0x81, 0x02, // Input (Data,Var,Abs) 127
* 0x05, 0x01, // Usage Page (Generic Desktop) 129
* 0x09, 0x30, // Usage (X) 131
* 0x09, 0x31, // Usage (Y) 133
* 0x16, 0x01, 0x80, // Logical Minimum (-32767) 135
* 0x26, 0xff, 0x7f, // Logical Maximum (32767) 138
* 0x75, 0x10, // Report Size (16) 141
* 0x95, 0x02, // Report Count (2) 143
* 0x81, 0x06, // Input (Data,Var,Rel) 145
* 0x09, 0x38, // Usage (Wheel) 147
* 0x15, 0x81, // Logical Minimum (-127) 149
* 0x25, 0x7f, // Logical Maximum (127) 151
* 0x75, 0x08, // Report Size (8) 153
* 0x95, 0x01, // Report Count (1) 155
* 0x81, 0x06, // Input (Data,Var,Rel) 157
* 0x05, 0x0c, // Usage Page (Consumer Devices) 159
* 0x0a, 0x38, 0x02, // Usage (AC Pan) 161
* 0x95, 0x01, // Report Count (1) 164
* 0x81, 0x06, // Input (Data,Var,Rel) 166
* 0xc0, // End Collection 168
* 0xc0, // End Collection 169
* 0x05, 0x01, // Usage Page (Generic Desktop) 170
* 0x09, 0x06, // Usage (Keyboard) 172
* 0xa1, 0x01, // Collection (Application) 174
* 0x85, 0x04, // Report ID (4) 176
* 0x05, 0x07, // Usage Page (Keyboard) 178
* 0x95, 0x01, // Report Count (1) 180
* 0x75, 0x08, // Report Size (8) 182
* 0x81, 0x03, // Input (Cnst,Var,Abs) 184
* 0x95, 0xe8, // Report Count (232) 186
* 0x75, 0x01, // Report Size (1) 188
* 0x15, 0x00, // Logical Minimum (0) 190
* 0x25, 0x01, // Logical Maximum (1) 192
* 0x05, 0x07, // Usage Page (Keyboard) 194
* 0x19, 0x00, // Usage Minimum (0) 196
* 0x29, 0xe7, // Usage Maximum (231) 198
* 0x81, 0x00, // Input (Data,Arr,Abs) 200 <- change to 0x81, 0x02 (Data,Var,Abs)
* 0xc0, // End Collection 202
*/
SEC(HID_BPF_RDESC_FIXUP)
int BPF_PROG(hid_rdesc_fixup_mistel_md770, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0, HID_MAX_DESCRIPTOR_SIZE);
if (!data)
return 0; /* EPERM check */
if (data[201] == 0x00)
data[201] = 0x02;
return 0;
}
HID_BPF_OPS(mistel_md770) = {
.hid_rdesc_fixup = (void *)hid_rdesc_fixup_mistel_md770,
};
SEC("syscall")
int probe(struct hid_bpf_probe_args *ctx)
{
ctx->retval = ctx->rdesc_size != RDESC_SIZE;
if (ctx->retval)
ctx->retval = -EINVAL;
return 0;
}
char _license[] SEC("license") = "GPL";

View File

@ -0,0 +1,148 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2024 José Expósito
*/
#include "vmlinux.h"
#include "hid_bpf.h"
#include "hid_bpf_helpers.h"
#include <bpf/bpf_tracing.h>
#define VID_RAPOO 0x24AE
#define PID_M50 0x2015
#define RDESC_SIZE 186
HID_BPF_CONFIG(
HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_RAPOO, PID_M50)
);
/*
* The Rapoo M50 Plus Silent mouse has 2 side buttons in addition to the left,
* right and middle buttons. However, its original HID descriptor has a Usage
* Maximum of 3, preventing the side buttons to work. This HID-BPF driver
* changes that usage to 5.
*
* For reference, this is the original report descriptor:
*
* 0x05, 0x01, // Usage Page (Generic Desktop) 0
* 0x09, 0x02, // Usage (Mouse) 2
* 0xa1, 0x01, // Collection (Application) 4
* 0x85, 0x01, // Report ID (1) 6
* 0x09, 0x01, // Usage (Pointer) 8
* 0xa1, 0x00, // Collection (Physical) 10
* 0x05, 0x09, // Usage Page (Button) 12
* 0x19, 0x01, // Usage Minimum (1) 14
* 0x29, 0x03, // Usage Maximum (3) 16 <- change to 0x05
* 0x15, 0x00, // Logical Minimum (0) 18
* 0x25, 0x01, // Logical Maximum (1) 20
* 0x75, 0x01, // Report Size (1) 22
* 0x95, 0x05, // Report Count (5) 24
* 0x81, 0x02, // Input (Data,Var,Abs) 26
* 0x75, 0x03, // Report Size (3) 28
* 0x95, 0x01, // Report Count (1) 30
* 0x81, 0x01, // Input (Cnst,Arr,Abs) 32
* 0x05, 0x01, // Usage Page (Generic Desktop) 34
* 0x09, 0x30, // Usage (X) 36
* 0x09, 0x31, // Usage (Y) 38
* 0x16, 0x01, 0x80, // Logical Minimum (-32767) 40
* 0x26, 0xff, 0x7f, // Logical Maximum (32767) 43
* 0x75, 0x10, // Report Size (16) 46
* 0x95, 0x02, // Report Count (2) 48
* 0x81, 0x06, // Input (Data,Var,Rel) 50
* 0x09, 0x38, // Usage (Wheel) 52
* 0x15, 0x81, // Logical Minimum (-127) 54
* 0x25, 0x7f, // Logical Maximum (127) 56
* 0x75, 0x08, // Report Size (8) 58
* 0x95, 0x01, // Report Count (1) 60
* 0x81, 0x06, // Input (Data,Var,Rel) 62
* 0xc0, // End Collection 64
* 0xc0, // End Collection 65
* 0x05, 0x0c, // Usage Page (Consumer Devices) 66
* 0x09, 0x01, // Usage (Consumer Control) 68
* 0xa1, 0x01, // Collection (Application) 70
* 0x85, 0x02, // Report ID (2) 72
* 0x75, 0x10, // Report Size (16) 74
* 0x95, 0x01, // Report Count (1) 76
* 0x15, 0x01, // Logical Minimum (1) 78
* 0x26, 0x8c, 0x02, // Logical Maximum (652) 80
* 0x19, 0x01, // Usage Minimum (1) 83
* 0x2a, 0x8c, 0x02, // Usage Maximum (652) 85
* 0x81, 0x00, // Input (Data,Arr,Abs) 88
* 0xc0, // End Collection 90
* 0x05, 0x01, // Usage Page (Generic Desktop) 91
* 0x09, 0x80, // Usage (System Control) 93
* 0xa1, 0x01, // Collection (Application) 95
* 0x85, 0x03, // Report ID (3) 97
* 0x09, 0x82, // Usage (System Sleep) 99
* 0x09, 0x81, // Usage (System Power Down) 101
* 0x09, 0x83, // Usage (System Wake Up) 103
* 0x15, 0x00, // Logical Minimum (0) 105
* 0x25, 0x01, // Logical Maximum (1) 107
* 0x19, 0x01, // Usage Minimum (1) 109
* 0x29, 0x03, // Usage Maximum (3) 111
* 0x75, 0x01, // Report Size (1) 113
* 0x95, 0x03, // Report Count (3) 115
* 0x81, 0x02, // Input (Data,Var,Abs) 117
* 0x95, 0x05, // Report Count (5) 119
* 0x81, 0x01, // Input (Cnst,Arr,Abs) 121
* 0xc0, // End Collection 123
* 0x05, 0x01, // Usage Page (Generic Desktop) 124
* 0x09, 0x00, // Usage (Undefined) 126
* 0xa1, 0x01, // Collection (Application) 128
* 0x85, 0x05, // Report ID (5) 130
* 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 132
* 0x09, 0x01, // Usage (Vendor Usage 1) 135
* 0x15, 0x81, // Logical Minimum (-127) 137
* 0x25, 0x7f, // Logical Maximum (127) 139
* 0x75, 0x08, // Report Size (8) 141
* 0x95, 0x07, // Report Count (7) 143
* 0xb1, 0x02, // Feature (Data,Var,Abs) 145
* 0xc0, // End Collection 147
* 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 148
* 0x09, 0x0e, // Usage (Vendor Usage 0x0e) 151
* 0xa1, 0x01, // Collection (Application) 153
* 0x85, 0xba, // Report ID (186) 155
* 0x95, 0x1f, // Report Count (31) 157
* 0x75, 0x08, // Report Size (8) 159
* 0x26, 0xff, 0x00, // Logical Maximum (255) 161
* 0x15, 0x00, // Logical Minimum (0) 164
* 0x09, 0x01, // Usage (Vendor Usage 1) 166
* 0x91, 0x02, // Output (Data,Var,Abs) 168
* 0x85, 0xba, // Report ID (186) 170
* 0x95, 0x1f, // Report Count (31) 172
* 0x75, 0x08, // Report Size (8) 174
* 0x26, 0xff, 0x00, // Logical Maximum (255) 176
* 0x15, 0x00, // Logical Minimum (0) 179
* 0x09, 0x01, // Usage (Vendor Usage 1) 181
* 0x81, 0x02, // Input (Data,Var,Abs) 183
* 0xc0, // End Collection 185
*/
SEC(HID_BPF_RDESC_FIXUP)
int BPF_PROG(hid_rdesc_fixup_rapoo_m50, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0, HID_MAX_DESCRIPTOR_SIZE);
if (!data)
return 0; /* EPERM check */
if (data[17] == 0x03)
data[17] = 0x05;
return 0;
}
HID_BPF_OPS(rapoo_m50) = {
.hid_rdesc_fixup = (void *)hid_rdesc_fixup_rapoo_m50,
};
SEC("syscall")
int probe(struct hid_bpf_probe_args *ctx)
{
ctx->retval = ctx->rdesc_size != RDESC_SIZE;
if (ctx->retval)
ctx->retval = -EINVAL;
return 0;
}
char _license[] SEC("license") = "GPL";

View File

@ -52,7 +52,8 @@
* Usage_GD_Keyboard * Usage_GD_Keyboard
* CollectionApplication( Open the collection * CollectionApplication( Open the collection
* ReportId(3) * ReportId(3)
* LogicalRange_i8(0, 1) * LogicalMinimum_i8(0)
* LogicalMaximum_i8(1)
* // other fields * // other fields
* ) End EndCollection * ) End EndCollection
* *
@ -74,26 +75,43 @@
#define Arr 0x0 #define Arr 0x0
#define Abs 0x0 #define Abs 0x0
#define Rel 0x4 #define Rel 0x4
#define Null 0x40
#define Buff 0x0100
/* Use like this: Input(Var|Abs) */ /* Use like this: Input(Var|Abs) */
#define Input(i_) 0x081, i8(i_), #define Input(i_) 0x081, i8(i_),
#define Output(i_) 0x091, i8(i_), #define Output(i_) 0x091, i8(i_),
#define Feature(i_) 0x0b1, i8(i_), #define Feature(i_) 0x0b1, i8(i_),
#define Input_i16(i_) 0x082, LE16(i_),
#define Output_i16(i_) 0x092, LE16(i_),
#define Feature_i16(i_) 0x0b2, LE16(i_),
#define ReportId(id_) 0x85, i8(id_), #define ReportId(id_) 0x85, i8(id_),
#define ReportSize(sz_) 0x75, i8(sz_), #define ReportSize(sz_) 0x75, i8(sz_),
#define ReportCount(cnt_) 0x95, i8(cnt_), #define ReportCount(cnt_) 0x95, i8(cnt_),
#define LogicalRange_i8(min_, max_) 0x15, i8(min_), 0x25, i8(max_), #define LogicalMinimum_i8(min_) 0x15, i8(min_),
#define LogicalRange_i16(min_, max_) 0x16, LE16(min_), 0x26, LE16(max_), #define LogicalMinimum_i16(min_) 0x16, LE16(min_),
#define LogicalRange_i32(min_, max_) 0x17, LE32(min_), 0x27, LE32(max_), #define LogicalMinimum_i32(min_) 0x17, LE32(min_),
#define PhysicalRange_i8(min_, max_) 0x35, i8(min_), 0x45, i8(max_), #define LogicalMaximum_i8(max_) 0x25, i8(max_),
#define PhysicalRange_i16(min_, max_) 0x36, LE16(min_), 0x46, LE16(max_), #define LogicalMaximum_i16(max_) 0x26, LE16(max_),
#define PhysicalRange_i32(min_, max_) 0x37, LE32(min_), 0x47, LE32(max_), #define LogicalMaximum_i32(max_) 0x27, LE32(max_),
#define UsageRange_i8(min_, max_) 0x19, i8(min_), 0x29, i8(max_), #define PhysicalMinimum_i8(min_) 0x35, i8(min_),
#define UsageRange_i16(min_, max_) 0x1a, LE16(min_), 0x2a, LE16(max_), #define PhysicalMinimum_i16(min_) 0x36, LE16(min_),
#define PhysicalMinimum_i32(min_) 0x37, LE32(min_),
#define PhysicalMaximum_i8(max_) 0x45, i8(max_),
#define PhysicalMaximum_i16(max_) 0x46, LE16(max_),
#define PhysicalMaximum_i32(max_) 0x47, LE32(max_),
#define UsageMinimum_i8(min_) 0x19, i8(min_),
#define UsageMinimum_i16(min_) 0x1a, LE16(min_),
#define UsageMaximum_i8(max_) 0x29, i8(max_),
#define UsageMaximum_i16(max_) 0x2a, LE16(max_),
#define UsagePage_i8(p_) 0x05, i8(p_), #define UsagePage_i8(p_) 0x05, i8(p_),
#define UsagePage_i16(p_) 0x06, LE16(p_), #define UsagePage_i16(p_) 0x06, LE16(p_),

View File

@ -713,6 +713,13 @@ static void hid_close_report(struct hid_device *device)
INIT_LIST_HEAD(&report_enum->report_list); INIT_LIST_HEAD(&report_enum->report_list);
} }
/*
* If the HID driver had a rdesc_fixup() callback, dev->rdesc
* will be allocated by hid-core and needs to be freed.
* Otherwise, it is either equal to dev_rdesc or bpf_rdesc, in
* which cases it'll be freed later on device removal or destroy.
*/
if (device->rdesc != device->dev_rdesc && device->rdesc != device->bpf_rdesc)
kfree(device->rdesc); kfree(device->rdesc);
device->rdesc = NULL; device->rdesc = NULL;
device->rsize = 0; device->rsize = 0;
@ -726,6 +733,14 @@ static void hid_close_report(struct hid_device *device)
device->status &= ~HID_STAT_PARSED; device->status &= ~HID_STAT_PARSED;
} }
static inline void hid_free_bpf_rdesc(struct hid_device *hdev)
{
/* bpf_rdesc is either equal to dev_rdesc or allocated by call_hid_bpf_rdesc_fixup() */
if (hdev->bpf_rdesc != hdev->dev_rdesc)
kfree(hdev->bpf_rdesc);
hdev->bpf_rdesc = NULL;
}
/* /*
* Free a device structure, all reports, and all fields. * Free a device structure, all reports, and all fields.
*/ */
@ -735,6 +750,7 @@ void hiddev_free(struct kref *ref)
struct hid_device *hid = container_of(ref, struct hid_device, ref); struct hid_device *hid = container_of(ref, struct hid_device, ref);
hid_close_report(hid); hid_close_report(hid);
hid_free_bpf_rdesc(hid);
kfree(hid->dev_rdesc); kfree(hid->dev_rdesc);
kfree(hid); kfree(hid);
} }
@ -1227,7 +1243,6 @@ int hid_open_report(struct hid_device *device)
struct hid_item item; struct hid_item item;
unsigned int size; unsigned int size;
const __u8 *start; const __u8 *start;
__u8 *buf;
const __u8 *end; const __u8 *end;
const __u8 *next; const __u8 *next;
int ret; int ret;
@ -1243,25 +1258,34 @@ int hid_open_report(struct hid_device *device)
if (WARN_ON(device->status & HID_STAT_PARSED)) if (WARN_ON(device->status & HID_STAT_PARSED))
return -EBUSY; return -EBUSY;
start = device->dev_rdesc; start = device->bpf_rdesc;
if (WARN_ON(!start)) if (WARN_ON(!start))
return -ENODEV; return -ENODEV;
size = device->dev_rsize; size = device->bpf_rsize;
if (device->driver->report_fixup) {
/*
* device->driver->report_fixup() needs to work
* on a copy of our report descriptor so it can
* change it.
*/
__u8 *buf = kmemdup(start, size, GFP_KERNEL);
/* call_hid_bpf_rdesc_fixup() ensures we work on a copy of rdesc */
buf = call_hid_bpf_rdesc_fixup(device, start, &size);
if (buf == NULL) if (buf == NULL)
return -ENOMEM; return -ENOMEM;
if (device->driver->report_fixup)
start = device->driver->report_fixup(device, buf, &size); start = device->driver->report_fixup(device, buf, &size);
else
start = buf;
/*
* The second kmemdup is required in case report_fixup() returns
* a static read-only memory, but we have no idea if that memory
* needs to be cleaned up or not at the end.
*/
start = kmemdup(start, size, GFP_KERNEL); start = kmemdup(start, size, GFP_KERNEL);
kfree(buf); kfree(buf);
if (start == NULL) if (start == NULL)
return -ENOMEM; return -ENOMEM;
}
device->rdesc = start; device->rdesc = start;
device->rsize = size; device->rsize = size;
@ -2656,9 +2680,10 @@ static bool hid_check_device_match(struct hid_device *hdev,
/* /*
* hid-generic implements .match(), so we must be dealing with a * hid-generic implements .match(), so we must be dealing with a
* different HID driver here, and can simply check if * different HID driver here, and can simply check if
* hid_ignore_special_drivers is set or not. * hid_ignore_special_drivers or HID_QUIRK_IGNORE_SPECIAL_DRIVER
* are set or not.
*/ */
return !hid_ignore_special_drivers; return !hid_ignore_special_drivers && !(hdev->quirks & HID_QUIRK_IGNORE_SPECIAL_DRIVER);
} }
static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv)
@ -2666,6 +2691,27 @@ static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv)
const struct hid_device_id *id; const struct hid_device_id *id;
int ret; int ret;
if (!hdev->bpf_rsize) {
unsigned int quirks;
/* reset the quirks that has been previously set */
quirks = hid_lookup_quirk(hdev);
hdev->quirks = quirks;
/* in case a bpf program gets detached, we need to free the old one */
hid_free_bpf_rdesc(hdev);
/* keep this around so we know we called it once */
hdev->bpf_rsize = hdev->dev_rsize;
/* call_hid_bpf_rdesc_fixup will always return a valid pointer */
hdev->bpf_rdesc = call_hid_bpf_rdesc_fixup(hdev, hdev->dev_rdesc,
&hdev->bpf_rsize);
if (quirks ^ hdev->quirks)
hid_info(hdev, "HID-BPF toggled quirks on the device: %04x",
quirks ^ hdev->quirks);
}
if (!hid_check_device_match(hdev, hdrv, &id)) if (!hid_check_device_match(hdev, hdrv, &id))
return -ENODEV; return -ENODEV;
@ -2673,8 +2719,6 @@ static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv)
if (!hdev->devres_group_id) if (!hdev->devres_group_id)
return -ENOMEM; return -ENOMEM;
/* reset the quirks that has been previously set */
hdev->quirks = hid_lookup_quirk(hdev);
hdev->driver = hdrv; hdev->driver = hdrv;
if (hdrv->probe) { if (hdrv->probe) {
@ -2922,9 +2966,11 @@ static void hid_remove_device(struct hid_device *hdev)
hid_debug_unregister(hdev); hid_debug_unregister(hdev);
hdev->status &= ~HID_STAT_ADDED; hdev->status &= ~HID_STAT_ADDED;
} }
hid_free_bpf_rdesc(hdev);
kfree(hdev->dev_rdesc); kfree(hdev->dev_rdesc);
hdev->dev_rdesc = NULL; hdev->dev_rdesc = NULL;
hdev->dev_rsize = 0; hdev->dev_rsize = 0;
hdev->bpf_rsize = 0;
} }
/** /**

View File

@ -40,6 +40,9 @@ static bool hid_generic_match(struct hid_device *hdev,
if (ignore_special_driver) if (ignore_special_driver)
return true; return true;
if (hdev->quirks & HID_QUIRK_IGNORE_SPECIAL_DRIVER)
return true;
if (hdev->quirks & HID_QUIRK_HAVE_SPECIAL_DRIVER) if (hdev->quirks & HID_QUIRK_HAVE_SPECIAL_DRIVER)
return false; return false;

View File

@ -359,6 +359,7 @@ struct hid_item {
* | @HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP: * | @HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP:
* | @HID_QUIRK_HAVE_SPECIAL_DRIVER: * | @HID_QUIRK_HAVE_SPECIAL_DRIVER:
* | @HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE: * | @HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE:
* | @HID_QUIRK_IGNORE_SPECIAL_DRIVER
* | @HID_QUIRK_FULLSPEED_INTERVAL: * | @HID_QUIRK_FULLSPEED_INTERVAL:
* | @HID_QUIRK_NO_INIT_REPORTS: * | @HID_QUIRK_NO_INIT_REPORTS:
* | @HID_QUIRK_NO_IGNORE: * | @HID_QUIRK_NO_IGNORE:
@ -384,6 +385,7 @@ struct hid_item {
#define HID_QUIRK_HAVE_SPECIAL_DRIVER BIT(19) #define HID_QUIRK_HAVE_SPECIAL_DRIVER BIT(19)
#define HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE BIT(20) #define HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE BIT(20)
#define HID_QUIRK_NOINVERT BIT(21) #define HID_QUIRK_NOINVERT BIT(21)
#define HID_QUIRK_IGNORE_SPECIAL_DRIVER BIT(22)
#define HID_QUIRK_FULLSPEED_INTERVAL BIT(28) #define HID_QUIRK_FULLSPEED_INTERVAL BIT(28)
#define HID_QUIRK_NO_INIT_REPORTS BIT(29) #define HID_QUIRK_NO_INIT_REPORTS BIT(29)
#define HID_QUIRK_NO_IGNORE BIT(30) #define HID_QUIRK_NO_IGNORE BIT(30)
@ -599,15 +601,17 @@ enum hid_battery_status {
struct hid_driver; struct hid_driver;
struct hid_ll_driver; struct hid_ll_driver;
struct hid_device { /* device report descriptor */ struct hid_device {
const __u8 *dev_rdesc; const __u8 *dev_rdesc; /* device report descriptor */
unsigned dev_rsize; const __u8 *bpf_rdesc; /* bpf modified report descriptor, if any */
const __u8 *rdesc; const __u8 *rdesc; /* currently used report descriptor */
unsigned rsize; unsigned int dev_rsize;
unsigned int bpf_rsize;
unsigned int rsize;
unsigned int collection_size; /* Number of allocated hid_collections */
struct hid_collection *collection; /* List of HID collections */ struct hid_collection *collection; /* List of HID collections */
unsigned collection_size; /* Number of allocated hid_collections */ unsigned int maxcollection; /* Number of parsed collections */
unsigned maxcollection; /* Number of parsed collections */ unsigned int maxapplication; /* Number of applications */
unsigned maxapplication; /* Number of applications */
__u16 bus; /* BUS ID */ __u16 bus; /* BUS ID */
__u16 group; /* Report group */ __u16 group; /* Report group */
__u32 vendor; /* Vendor ID */ __u32 vendor; /* Vendor ID */

View File

@ -212,7 +212,7 @@ int hid_bpf_connect_device(struct hid_device *hdev);
void hid_bpf_disconnect_device(struct hid_device *hdev); void hid_bpf_disconnect_device(struct hid_device *hdev);
void hid_bpf_destroy_device(struct hid_device *hid); void hid_bpf_destroy_device(struct hid_device *hid);
int hid_bpf_device_init(struct hid_device *hid); int hid_bpf_device_init(struct hid_device *hid);
u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size); const u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size);
#else /* CONFIG_HID_BPF */ #else /* CONFIG_HID_BPF */
static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type,
u8 *data, u32 *size, int interrupt, u8 *data, u32 *size, int interrupt,
@ -228,13 +228,8 @@ static inline int hid_bpf_connect_device(struct hid_device *hdev) { return 0; }
static inline void hid_bpf_disconnect_device(struct hid_device *hdev) {} static inline void hid_bpf_disconnect_device(struct hid_device *hdev) {}
static inline void hid_bpf_destroy_device(struct hid_device *hid) {} static inline void hid_bpf_destroy_device(struct hid_device *hid) {}
static inline int hid_bpf_device_init(struct hid_device *hid) { return 0; } static inline int hid_bpf_device_init(struct hid_device *hid) { return 0; }
/* static inline const u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc,
* This specialized allocator has to be a macro for its allocations to be unsigned int *size) { return rdesc; }
* accounted separately (to have a separate alloc_tag). The typecast is
* intentional to enforce typesafety.
*/
#define call_hid_bpf_rdesc_fixup(_hdev, _rdesc, _size) \
((u8 *)kmemdup(_rdesc, *(_size), GFP_KERNEL))
#endif /* CONFIG_HID_BPF */ #endif /* CONFIG_HID_BPF */

View File

@ -232,7 +232,7 @@ $(BPF_SKELS): %.skel.h: %.bpf.o $(BPFTOOL) | $(OUTPUT)
$(Q)$(BPFTOOL) gen object $(<:.o=.linked1.o) $< $(Q)$(BPFTOOL) gen object $(<:.o=.linked1.o) $<
$(Q)$(BPFTOOL) gen skeleton $(<:.o=.linked1.o) name $(notdir $(<:.bpf.o=)) > $@ $(Q)$(BPFTOOL) gen skeleton $(<:.o=.linked1.o) name $(notdir $(<:.bpf.o=)) > $@
$(OUTPUT)/%.o: %.c $(BPF_SKELS) $(OUTPUT)/%.o: %.c $(BPF_SKELS) hid_common.h
$(call msg,CC,,$@) $(call msg,CC,,$@)
$(Q)$(CC) $(CFLAGS) -c $(filter %.c,$^) $(LDLIBS) -o $@ $(Q)$(CC) $(CFLAGS) -c $(filter %.c,$^) $(LDLIBS) -o $@

View File

@ -4,13 +4,6 @@
#include "hid_common.h" #include "hid_common.h"
#include <bpf/bpf.h> #include <bpf/bpf.h>
struct attach_prog_args {
int prog_fd;
unsigned int hid;
int retval;
int insert_head;
};
struct hid_hw_request_syscall_args { struct hid_hw_request_syscall_args {
__u8 data[10]; __u8 data[10];
unsigned int hid; unsigned int hid;
@ -21,11 +14,8 @@ struct hid_hw_request_syscall_args {
}; };
FIXTURE(hid_bpf) { FIXTURE(hid_bpf) {
int dev_id; struct uhid_device hid;
int uhid_fd;
int hidraw_fd; int hidraw_fd;
int hid_id;
pthread_t tid;
struct hid *skel; struct hid *skel;
struct bpf_link *hid_links[3]; /* max number of programs loaded in a single test */ struct bpf_link *hid_links[3]; /* max number of programs loaded in a single test */
}; };
@ -54,35 +44,52 @@ static void detach_bpf(FIXTURE_DATA(hid_bpf) * self)
FIXTURE_TEARDOWN(hid_bpf) { FIXTURE_TEARDOWN(hid_bpf) {
void *uhid_err; void *uhid_err;
uhid_destroy(_metadata, self->uhid_fd); uhid_destroy(_metadata, &self->hid);
detach_bpf(self); detach_bpf(self);
pthread_join(self->tid, &uhid_err); pthread_join(self->hid.tid, &uhid_err);
} }
#define TEARDOWN_LOG(fmt, ...) do { \ #define TEARDOWN_LOG(fmt, ...) do { \
TH_LOG(fmt, ##__VA_ARGS__); \ TH_LOG(fmt, ##__VA_ARGS__); \
hid_bpf_teardown(_metadata, self, variant); \ hid_bpf_teardown(_metadata, self, variant); \
} while (0) } while (0)
struct specific_device {
const char test_name[64];
__u16 bus;
__u32 vid;
__u32 pid;
};
FIXTURE_SETUP(hid_bpf) FIXTURE_SETUP(hid_bpf)
{ {
time_t t; const struct specific_device *match = NULL;
int err; int err;
/* initialize random number generator */ const struct specific_device devices[] = {
srand((unsigned int)time(&t)); {
.test_name = "test_hid_driver_probe",
.bus = BUS_BLUETOOTH,
.vid = 0x05ac, /* USB_VENDOR_ID_APPLE */
.pid = 0x022c, /* USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI */
}, {
.test_name = "*",
.bus = BUS_USB,
.vid = 0x0001,
.pid = 0x0a36,
}};
self->dev_id = rand() % 1024; for (int i = 0; i < ARRAY_SIZE(devices); i++) {
match = &devices[i];
if (!strncmp(_metadata->name, devices[i].test_name, sizeof(devices[i].test_name)))
break;
}
self->uhid_fd = setup_uhid(_metadata, self->dev_id); ASSERT_OK_PTR(match);
/* locate the uev, self, variant);ent file of the created device */ err = setup_uhid(_metadata, &self->hid, match->bus, match->vid, match->pid,
self->hid_id = get_hid_id(self->dev_id); rdesc, sizeof(rdesc));
ASSERT_GT(self->hid_id, 0) ASSERT_OK(err);
TEARDOWN_LOG("Could not locate uhid device id: %d", self->hid_id);
err = uhid_start_listener(_metadata, &self->tid, self->uhid_fd);
ASSERT_EQ(0, err) TEARDOWN_LOG("could not start udev listener: %d", err);
} }
struct test_program { struct test_program {
@ -129,7 +136,7 @@ static void load_programs(const struct test_program programs[],
ops_hid_id = bpf_map__initial_value(map, NULL); ops_hid_id = bpf_map__initial_value(map, NULL);
ASSERT_OK_PTR(ops_hid_id) TH_LOG("unable to retrieve struct_ops data"); ASSERT_OK_PTR(ops_hid_id) TH_LOG("unable to retrieve struct_ops data");
*ops_hid_id = self->hid_id; *ops_hid_id = self->hid.hid_id;
} }
/* we disable the auto-attach feature of all maps because we /* we disable the auto-attach feature of all maps because we
@ -157,7 +164,7 @@ static void load_programs(const struct test_program programs[],
hid__attach(self->skel); hid__attach(self->skel);
self->hidraw_fd = open_hidraw(self->dev_id); self->hidraw_fd = open_hidraw(&self->hid);
ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
} }
@ -192,7 +199,7 @@ TEST_F(hid_bpf, raw_event)
/* inject one event */ /* inject one event */
buf[0] = 1; buf[0] = 1;
buf[1] = 42; buf[1] = 42;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* check that hid_first_event() was executed */ /* check that hid_first_event() was executed */
ASSERT_EQ(self->skel->data->callback_check, 42) TH_LOG("callback_check1"); ASSERT_EQ(self->skel->data->callback_check, 42) TH_LOG("callback_check1");
@ -208,7 +215,7 @@ TEST_F(hid_bpf, raw_event)
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
buf[0] = 1; buf[0] = 1;
buf[1] = 47; buf[1] = 47;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* check that hid_first_event() was executed */ /* check that hid_first_event() was executed */
ASSERT_EQ(self->skel->data->callback_check, 47) TH_LOG("callback_check1"); ASSERT_EQ(self->skel->data->callback_check, 47) TH_LOG("callback_check1");
@ -239,7 +246,7 @@ TEST_F(hid_bpf, subprog_raw_event)
/* inject one event */ /* inject one event */
buf[0] = 1; buf[0] = 1;
buf[1] = 42; buf[1] = 42;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* read the data from hidraw */ /* read the data from hidraw */
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -252,7 +259,7 @@ TEST_F(hid_bpf, subprog_raw_event)
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
buf[0] = 1; buf[0] = 1;
buf[1] = 47; buf[1] = 47;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* read the data from hidraw */ /* read the data from hidraw */
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -303,7 +310,7 @@ TEST_F(hid_bpf, test_attach_detach)
/* inject one event */ /* inject one event */
buf[0] = 1; buf[0] = 1;
buf[1] = 42; buf[1] = 42;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* read the data from hidraw */ /* read the data from hidraw */
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -326,14 +333,14 @@ TEST_F(hid_bpf, test_attach_detach)
/* detach the program */ /* detach the program */
detach_bpf(self); detach_bpf(self);
self->hidraw_fd = open_hidraw(self->dev_id); self->hidraw_fd = open_hidraw(&self->hid);
ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
/* inject another event */ /* inject another event */
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
buf[0] = 1; buf[0] = 1;
buf[1] = 47; buf[1] = 47;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* read the data from hidraw */ /* read the data from hidraw */
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -352,7 +359,7 @@ TEST_F(hid_bpf, test_attach_detach)
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
buf[0] = 1; buf[0] = 1;
buf[1] = 42; buf[1] = 42;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* read the data from hidraw */ /* read the data from hidraw */
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -382,7 +389,7 @@ TEST_F(hid_bpf, test_hid_change_report)
/* inject one event */ /* inject one event */
buf[0] = 1; buf[0] = 1;
buf[1] = 42; buf[1] = 42;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* read the data from hidraw */ /* read the data from hidraw */
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -412,7 +419,7 @@ TEST_F(hid_bpf, test_hid_user_input_report_call)
LOAD_BPF; LOAD_BPF;
args.hid = self->hid_id; args.hid = self->hid.hid_id;
args.data[0] = 1; /* report ID */ args.data[0] = 1; /* report ID */
args.data[1] = 2; /* report ID */ args.data[1] = 2; /* report ID */
args.data[2] = 42; /* report ID */ args.data[2] = 42; /* report ID */
@ -458,7 +465,7 @@ TEST_F(hid_bpf, test_hid_user_output_report_call)
LOAD_BPF; LOAD_BPF;
args.hid = self->hid_id; args.hid = self->hid.hid_id;
args.data[0] = 1; /* report ID */ args.data[0] = 1; /* report ID */
args.data[1] = 2; /* report ID */ args.data[1] = 2; /* report ID */
args.data[2] = 42; /* report ID */ args.data[2] = 42; /* report ID */
@ -506,7 +513,7 @@ TEST_F(hid_bpf, test_hid_user_raw_request_call)
LOAD_BPF; LOAD_BPF;
args.hid = self->hid_id; args.hid = self->hid.hid_id;
args.data[0] = 1; /* report ID */ args.data[0] = 1; /* report ID */
prog_fd = bpf_program__fd(self->skel->progs.hid_user_raw_request); prog_fd = bpf_program__fd(self->skel->progs.hid_user_raw_request);
@ -539,7 +546,7 @@ TEST_F(hid_bpf, test_hid_filter_raw_request_call)
/* inject one event */ /* inject one event */
buf[0] = 1; buf[0] = 1;
buf[1] = 42; buf[1] = 42;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* read the data from hidraw */ /* read the data from hidraw */
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -565,7 +572,7 @@ TEST_F(hid_bpf, test_hid_filter_raw_request_call)
/* detach the program */ /* detach the program */
detach_bpf(self); detach_bpf(self);
self->hidraw_fd = open_hidraw(self->dev_id); self->hidraw_fd = open_hidraw(&self->hid);
ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf); err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf);
@ -641,7 +648,7 @@ TEST_F(hid_bpf, test_hid_filter_output_report_call)
/* inject one event */ /* inject one event */
buf[0] = 1; buf[0] = 1;
buf[1] = 42; buf[1] = 42;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* read the data from hidraw */ /* read the data from hidraw */
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -667,7 +674,7 @@ TEST_F(hid_bpf, test_hid_filter_output_report_call)
/* detach the program */ /* detach the program */
detach_bpf(self); detach_bpf(self);
self->hidraw_fd = open_hidraw(self->dev_id); self->hidraw_fd = open_hidraw(&self->hid);
ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
err = write(self->hidraw_fd, buf, 3); err = write(self->hidraw_fd, buf, 3);
@ -742,7 +749,7 @@ TEST_F(hid_bpf, test_multiply_events_wq)
/* inject one event */ /* inject one event */
buf[0] = 1; buf[0] = 1;
buf[1] = 42; buf[1] = 42;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* read the data from hidraw */ /* read the data from hidraw */
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -780,7 +787,7 @@ TEST_F(hid_bpf, test_multiply_events)
/* inject one event */ /* inject one event */
buf[0] = 1; buf[0] = 1;
buf[1] = 42; buf[1] = 42;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* read the data from hidraw */ /* read the data from hidraw */
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -816,7 +823,7 @@ TEST_F(hid_bpf, test_hid_infinite_loop_input_report_call)
buf[1] = 2; buf[1] = 2;
buf[2] = 42; buf[2] = 42;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* read the data from hidraw */ /* read the data from hidraw */
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -867,7 +874,7 @@ TEST_F(hid_bpf, test_hid_attach_flags)
/* inject one event */ /* inject one event */
buf[0] = 1; buf[0] = 1;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* read the data from hidraw */ /* read the data from hidraw */
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -878,6 +885,54 @@ TEST_F(hid_bpf, test_hid_attach_flags)
ASSERT_EQ(buf[3], 3); ASSERT_EQ(buf[3], 3);
} }
static bool is_using_driver(struct __test_metadata *_metadata, struct uhid_device *hid,
const char *driver)
{
char driver_line[512];
char uevent[1024];
char temp[512];
int fd, nread;
bool found = false;
sprintf(uevent, "/sys/bus/hid/devices/%04X:%04X:%04X.%04X/uevent",
hid->bus, hid->vid, hid->pid, hid->hid_id);
fd = open(uevent, O_RDONLY | O_NONBLOCK);
if (fd < 0) {
TH_LOG("couldn't open '%s': %d, %d", uevent, fd, errno);
return false;
}
sprintf(driver_line, "DRIVER=%s", driver);
nread = read(fd, temp, ARRAY_SIZE(temp));
if (nread > 0 && (strstr(temp, driver_line)) != NULL)
found = true;
close(fd);
return found;
}
/*
* Attach hid_driver_probe to the given uhid device,
* check that the device is now using hid-generic.
*/
TEST_F(hid_bpf, test_hid_driver_probe)
{
const struct test_program progs[] = {
{
.name = "hid_test_driver_probe",
},
};
ASSERT_TRUE(is_using_driver(_metadata, &self->hid, "apple"));
LOAD_PROGRAMS(progs);
ASSERT_TRUE(is_using_driver(_metadata, &self->hid, "hid-generic"));
}
/* /*
* Attach hid_rdesc_fixup to the given uhid device, * Attach hid_rdesc_fixup to the given uhid device,
* retrieve and open the matching hidraw node, * retrieve and open the matching hidraw node,

View File

@ -19,6 +19,16 @@
__typeof__(b) _b = (b); \ __typeof__(b) _b = (b); \
_a < _b ? _a : _b; }) _a < _b ? _a : _b; })
struct uhid_device {
int dev_id; /* uniq (random) number to identify the device */
int uhid_fd;
int hid_id; /* HID device id in the system */
__u16 bus;
__u32 vid;
__u32 pid;
pthread_t tid; /* thread for reading uhid events */
};
static unsigned char rdesc[] = { static unsigned char rdesc[] = {
0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
0x09, 0x21, /* Usage (Vendor Usage 0x21) */ 0x09, 0x21, /* Usage (Vendor Usage 0x21) */
@ -122,7 +132,9 @@ static int uhid_write(struct __test_metadata *_metadata, int fd, const struct uh
} }
} }
static int uhid_create(struct __test_metadata *_metadata, int fd, int rand_nb) static int uhid_create(struct __test_metadata *_metadata, int fd, int rand_nb,
__u16 bus, __u32 vid, __u32 pid, __u8 *rdesc,
size_t rdesc_size)
{ {
struct uhid_event ev; struct uhid_event ev;
char buf[25]; char buf[25];
@ -133,10 +145,10 @@ static int uhid_create(struct __test_metadata *_metadata, int fd, int rand_nb)
ev.type = UHID_CREATE; ev.type = UHID_CREATE;
strcpy((char *)ev.u.create.name, buf); strcpy((char *)ev.u.create.name, buf);
ev.u.create.rd_data = rdesc; ev.u.create.rd_data = rdesc;
ev.u.create.rd_size = sizeof(rdesc); ev.u.create.rd_size = rdesc_size;
ev.u.create.bus = BUS_USB; ev.u.create.bus = bus;
ev.u.create.vendor = 0x0001; ev.u.create.vendor = vid;
ev.u.create.product = 0x0a37; ev.u.create.product = pid;
ev.u.create.version = 0; ev.u.create.version = 0;
ev.u.create.country = 0; ev.u.create.country = 0;
@ -146,14 +158,14 @@ static int uhid_create(struct __test_metadata *_metadata, int fd, int rand_nb)
return uhid_write(_metadata, fd, &ev); return uhid_write(_metadata, fd, &ev);
} }
static void uhid_destroy(struct __test_metadata *_metadata, int fd) static void uhid_destroy(struct __test_metadata *_metadata, struct uhid_device *hid)
{ {
struct uhid_event ev; struct uhid_event ev;
memset(&ev, 0, sizeof(ev)); memset(&ev, 0, sizeof(ev));
ev.type = UHID_DESTROY; ev.type = UHID_DESTROY;
uhid_write(_metadata, fd, &ev); uhid_write(_metadata, hid->uhid_fd, &ev);
} }
static int uhid_event(struct __test_metadata *_metadata, int fd) static int uhid_event(struct __test_metadata *_metadata, int fd)
@ -281,7 +293,8 @@ static int uhid_start_listener(struct __test_metadata *_metadata, pthread_t *tid
return 0; return 0;
} }
static int uhid_send_event(struct __test_metadata *_metadata, int fd, __u8 *buf, size_t size) static int uhid_send_event(struct __test_metadata *_metadata, struct uhid_device *hid,
__u8 *buf, size_t size)
{ {
struct uhid_event ev; struct uhid_event ev;
@ -294,36 +307,20 @@ static int uhid_send_event(struct __test_metadata *_metadata, int fd, __u8 *buf,
memcpy(ev.u.input2.data, buf, size); memcpy(ev.u.input2.data, buf, size);
return uhid_write(_metadata, fd, &ev); return uhid_write(_metadata, hid->uhid_fd, &ev);
} }
static int setup_uhid(struct __test_metadata *_metadata, int rand_nb) static bool match_sysfs_device(struct uhid_device *hid, const char *workdir, struct dirent *dir)
{ {
int fd; char target[20] = "";
const char *path = "/dev/uhid";
int ret;
fd = open(path, O_RDWR | O_CLOEXEC);
ASSERT_GE(fd, 0) TH_LOG("open uhid-cdev failed; %d", fd);
ret = uhid_create(_metadata, fd, rand_nb);
ASSERT_EQ(0, ret) {
TH_LOG("create uhid device failed: %d", ret);
close(fd);
}
return fd;
}
static bool match_sysfs_device(int dev_id, const char *workdir, struct dirent *dir)
{
const char *target = "0003:0001:0A37.*";
char phys[512]; char phys[512];
char uevent[1024]; char uevent[1024];
char temp[512]; char temp[512];
int fd, nread; int fd, nread;
bool found = false; bool found = false;
snprintf(target, sizeof(target), "%04X:%04X:%04X.*", hid->bus, hid->vid, hid->pid);
if (fnmatch(target, dir->d_name, 0)) if (fnmatch(target, dir->d_name, 0))
return false; return false;
@ -334,7 +331,7 @@ static bool match_sysfs_device(int dev_id, const char *workdir, struct dirent *d
if (fd < 0) if (fd < 0)
return false; return false;
sprintf(phys, "PHYS=%d", dev_id); sprintf(phys, "PHYS=%d", hid->dev_id);
nread = read(fd, temp, ARRAY_SIZE(temp)); nread = read(fd, temp, ARRAY_SIZE(temp));
if (nread > 0 && (strstr(temp, phys)) != NULL) if (nread > 0 && (strstr(temp, phys)) != NULL)
@ -345,7 +342,7 @@ static bool match_sysfs_device(int dev_id, const char *workdir, struct dirent *d
return found; return found;
} }
static int get_hid_id(int dev_id) static int get_hid_id(struct uhid_device *hid)
{ {
const char *workdir = "/sys/devices/virtual/misc/uhid"; const char *workdir = "/sys/devices/virtual/misc/uhid";
const char *str_id; const char *str_id;
@ -360,10 +357,10 @@ static int get_hid_id(int dev_id)
d = opendir(workdir); d = opendir(workdir);
if (d) { if (d) {
while ((dir = readdir(d)) != NULL) { while ((dir = readdir(d)) != NULL) {
if (!match_sysfs_device(dev_id, workdir, dir)) if (!match_sysfs_device(hid, workdir, dir))
continue; continue;
str_id = dir->d_name + sizeof("0003:0001:0A37."); str_id = dir->d_name + sizeof("0000:0000:0000.");
found = (int)strtol(str_id, NULL, 16); found = (int)strtol(str_id, NULL, 16);
break; break;
@ -377,7 +374,7 @@ static int get_hid_id(int dev_id)
return found; return found;
} }
static int get_hidraw(int dev_id) static int get_hidraw(struct uhid_device *hid)
{ {
const char *workdir = "/sys/devices/virtual/misc/uhid"; const char *workdir = "/sys/devices/virtual/misc/uhid";
char sysfs[1024]; char sysfs[1024];
@ -394,7 +391,7 @@ static int get_hidraw(int dev_id)
continue; continue;
while ((dir = readdir(d)) != NULL) { while ((dir = readdir(d)) != NULL) {
if (!match_sysfs_device(dev_id, workdir, dir)) if (!match_sysfs_device(hid, workdir, dir))
continue; continue;
sprintf(sysfs, "%s/%s/hidraw", workdir, dir->d_name); sprintf(sysfs, "%s/%s/hidraw", workdir, dir->d_name);
@ -421,12 +418,12 @@ static int get_hidraw(int dev_id)
return found; return found;
} }
static int open_hidraw(int dev_id) static int open_hidraw(struct uhid_device *hid)
{ {
int hidraw_number; int hidraw_number;
char hidraw_path[64] = { 0 }; char hidraw_path[64] = { 0 };
hidraw_number = get_hidraw(dev_id); hidraw_number = get_hidraw(hid);
if (hidraw_number < 0) if (hidraw_number < 0)
return hidraw_number; return hidraw_number;
@ -434,3 +431,44 @@ static int open_hidraw(int dev_id)
sprintf(hidraw_path, "/dev/hidraw%d", hidraw_number); sprintf(hidraw_path, "/dev/hidraw%d", hidraw_number);
return open(hidraw_path, O_RDWR | O_NONBLOCK); return open(hidraw_path, O_RDWR | O_NONBLOCK);
} }
static int setup_uhid(struct __test_metadata *_metadata, struct uhid_device *hid,
__u16 bus, __u32 vid, __u32 pid, const __u8 *rdesc, size_t rdesc_size)
{
const char *path = "/dev/uhid";
time_t t;
int ret;
/* initialize random number generator */
srand((unsigned int)time(&t));
hid->dev_id = rand() % 1024;
hid->bus = bus;
hid->vid = vid;
hid->pid = pid;
hid->uhid_fd = open(path, O_RDWR | O_CLOEXEC);
ASSERT_GE(hid->uhid_fd, 0) TH_LOG("open uhid-cdev failed; %d", hid->uhid_fd);
ret = uhid_create(_metadata, hid->uhid_fd, hid->dev_id, bus, vid, pid,
(__u8 *)rdesc, rdesc_size);
ASSERT_EQ(0, ret) {
TH_LOG("create uhid device failed: %d", ret);
close(hid->uhid_fd);
return ret;
}
/* locate the uevent file of the created device */
hid->hid_id = get_hid_id(hid);
ASSERT_GT(hid->hid_id, 0)
TH_LOG("Could not locate uhid device id: %d", hid->hid_id);
ret = uhid_start_listener(_metadata, &hid->tid, hid->uhid_fd);
ASSERT_EQ(0, ret) {
TH_LOG("could not start udev listener: %d", ret);
close(hid->uhid_fd);
return ret;
}
return 0;
}

View File

@ -9,11 +9,8 @@
#endif /* HIDIOCREVOKE */ #endif /* HIDIOCREVOKE */
FIXTURE(hidraw) { FIXTURE(hidraw) {
int dev_id; struct uhid_device hid;
int uhid_fd;
int hidraw_fd; int hidraw_fd;
int hid_id;
pthread_t tid;
}; };
static void close_hidraw(FIXTURE_DATA(hidraw) * self) static void close_hidraw(FIXTURE_DATA(hidraw) * self)
{ {
@ -25,10 +22,10 @@ static void close_hidraw(FIXTURE_DATA(hidraw) * self)
FIXTURE_TEARDOWN(hidraw) { FIXTURE_TEARDOWN(hidraw) {
void *uhid_err; void *uhid_err;
uhid_destroy(_metadata, self->uhid_fd); uhid_destroy(_metadata, &self->hid);
close_hidraw(self); close_hidraw(self);
pthread_join(self->tid, &uhid_err); pthread_join(self->hid.tid, &uhid_err);
} }
#define TEARDOWN_LOG(fmt, ...) do { \ #define TEARDOWN_LOG(fmt, ...) do { \
TH_LOG(fmt, ##__VA_ARGS__); \ TH_LOG(fmt, ##__VA_ARGS__); \
@ -37,25 +34,12 @@ FIXTURE_TEARDOWN(hidraw) {
FIXTURE_SETUP(hidraw) FIXTURE_SETUP(hidraw)
{ {
time_t t;
int err; int err;
/* initialize random number generator */ err = setup_uhid(_metadata, &self->hid, BUS_USB, 0x0001, 0x0a37, rdesc, sizeof(rdesc));
srand((unsigned int)time(&t)); ASSERT_OK(err);
self->dev_id = rand() % 1024; self->hidraw_fd = open_hidraw(&self->hid);
self->uhid_fd = setup_uhid(_metadata, self->dev_id);
/* locate the uev, self, variant);ent file of the created device */
self->hid_id = get_hid_id(self->dev_id);
ASSERT_GT(self->hid_id, 0)
TEARDOWN_LOG("Could not locate uhid device id: %d", self->hid_id);
err = uhid_start_listener(_metadata, &self->tid, self->uhid_fd);
ASSERT_EQ(0, err) TEARDOWN_LOG("could not start udev listener: %d", err);
self->hidraw_fd = open_hidraw(self->dev_id);
ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
} }
@ -79,7 +63,7 @@ TEST_F(hidraw, raw_event)
/* inject one event */ /* inject one event */
buf[0] = 1; buf[0] = 1;
buf[1] = 42; buf[1] = 42;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* read the data from hidraw */ /* read the data from hidraw */
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -101,7 +85,7 @@ TEST_F(hidraw, raw_event_revoked)
/* inject one event */ /* inject one event */
buf[0] = 1; buf[0] = 1;
buf[1] = 42; buf[1] = 42;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* read the data from hidraw */ /* read the data from hidraw */
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -117,7 +101,7 @@ TEST_F(hidraw, raw_event_revoked)
/* inject one other event */ /* inject one other event */
buf[0] = 1; buf[0] = 1;
buf[1] = 43; buf[1] = 43;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
/* read the data from hidraw */ /* read the data from hidraw */
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
@ -161,7 +145,7 @@ TEST_F(hidraw, poll_revoked)
/* inject one event */ /* inject one event */
buf[0] = 1; buf[0] = 1;
buf[1] = 42; buf[1] = 42;
uhid_send_event(_metadata, self->uhid_fd, buf, 6); uhid_send_event(_metadata, &self->hid, buf, 6);
while (true) { while (true) {
ready = poll(pfds, 1, 5000); ready = poll(pfds, 1, 5000);

View File

@ -598,3 +598,15 @@ SEC(".struct_ops.link")
struct hid_bpf_ops test_infinite_loop_input_report = { struct hid_bpf_ops test_infinite_loop_input_report = {
.hid_device_event = (void *)hid_test_infinite_loop_input_report, .hid_device_event = (void *)hid_test_infinite_loop_input_report,
}; };
SEC("?struct_ops.s/hid_rdesc_fixup")
int BPF_PROG(hid_test_driver_probe, struct hid_bpf_ctx *hid_ctx)
{
hid_ctx->hid->quirks |= HID_QUIRK_IGNORE_SPECIAL_DRIVER;
return 0;
}
SEC(".struct_ops.link")
struct hid_bpf_ops test_driver_probe = {
.hid_rdesc_fixup = (void *)hid_test_driver_probe,
};

View File

@ -84,10 +84,14 @@ struct hid_bpf_ops {
struct hid_device *hdev; struct hid_device *hdev;
}; };
#define BIT(n) (1U << n)
#ifndef BPF_F_BEFORE #ifndef BPF_F_BEFORE
#define BPF_F_BEFORE (1U << 3) #define BPF_F_BEFORE BIT(3)
#endif #endif
#define HID_QUIRK_IGNORE_SPECIAL_DRIVER BIT(22)
/* following are kfuncs exported by HID for HID-BPF */ /* following are kfuncs exported by HID for HID-BPF */
extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx,
unsigned int offset, unsigned int offset,