We have 2 cases of usage here: - either n <= 32: we are addressing individual bits at the given offset - either n > 32: we are using a memcpy to transmit the data to the caller, meaning that we need to be byte-aligned. Signed-off-by: Benjamin Tissoires <benjamin.tissoires@xxxxxxxxxx> --- changes in v2: - split the series by bpf/libbpf/hid/selftests and samples - allow for n > 32, by relying on memcpy --- drivers/hid/hid-bpf.c | 68 ++++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-core.c | 4 +-- include/linux/hid.h | 2 ++ 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-bpf.c b/drivers/hid/hid-bpf.c index 510e24f4307c..8ae247fba5bc 100644 --- a/drivers/hid/hid-bpf.c +++ b/drivers/hid/hid-bpf.c @@ -105,6 +105,72 @@ static void hid_bpf_array_detached(struct hid_device *hdev, enum bpf_hid_attach_ } } +int hid_bpf_get_data(struct hid_device *hdev, u8 *buf, size_t buf_size, u64 offset, u32 n, + u8 *data, u64 data_size) +{ + u32 *value = (u32 *)data; + + if (((offset + n) >> 3) >= buf_size) + return -E2BIG; + + if (n <= 32) { + /* data must be a pointer to a u32 */ + if (data_size != 4) + return -EINVAL; + + *value = hid_field_extract(hdev, buf, offset, n); + return 4; + } + + /* if n > 32, use memcpy, but ensure we are dealing with full bytes */ + if ((n | offset) & 0x7) + return -EINVAL; + + /* work on bytes now */ + offset = offset >> 3; + n = n >> 3; + + if (n > data_size) + return -EINVAL; + + memcpy(data, buf + offset, n); + + return n; +} + +int hid_bpf_set_data(struct hid_device *hdev, u8 *buf, size_t buf_size, u64 offset, u32 n, + u8 *data, u64 data_size) +{ + u32 *value = (u32 *)data; + + if (((offset + n) >> 3) >= buf_size) + return -E2BIG; + + if (n <= 32) { + /* data must be a pointer to a u32 */ + if (data_size != 4) + return -EINVAL; + + implement(hdev, buf, offset, n, *value); + return 4; + } + + /* if n > 32, use memcpy, but ensure we are dealing with full bytes */ + if ((n | offset) & 0x7) + return -EINVAL; + + /* work on bytes now */ + offset = offset >> 3; + n = n >> 3; + + if (n > data_size) + return -EINVAL; + + memcpy(buf + offset, data, n); + + return n; +} + static int hid_bpf_run_progs(struct hid_device *hdev, enum bpf_hid_attach_type type, struct hid_bpf_ctx *ctx, u8 *data, int size) { @@ -204,6 +270,8 @@ int __init hid_bpf_module_init(void) .link_attach = hid_bpf_link_attach, .link_attached = hid_bpf_link_attached, .array_detached = hid_bpf_array_detached, + .hid_get_data = hid_bpf_get_data, + .hid_set_data = hid_bpf_set_data, }; bpf_hid_set_hooks(&hooks); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 0eb8189faaee..d3f4499ee4cd 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1416,8 +1416,8 @@ static void __implement(u8 *report, unsigned offset, int n, u32 value) } } -static void implement(const struct hid_device *hid, u8 *report, - unsigned offset, unsigned n, u32 value) +void implement(const struct hid_device *hid, u8 *report, unsigned int offset, unsigned int n, + u32 value) { if (unlikely(n > 32)) { hid_warn(hid, "%s() called with n (%d) > 32! (%s)\n", diff --git a/include/linux/hid.h b/include/linux/hid.h index 66d949d10b78..7454e844324c 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -944,6 +944,8 @@ bool hid_compare_device_paths(struct hid_device *hdev_a, s32 hid_snto32(__u32 value, unsigned n); __u32 hid_field_extract(const struct hid_device *hid, __u8 *report, unsigned offset, unsigned n); +void implement(const struct hid_device *hid, u8 *report, unsigned int offset, unsigned int n, + u32 value); #ifdef CONFIG_PM int hid_driver_suspend(struct hid_device *hdev, pm_message_t state); -- 2.35.1