mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
HID: bpf: export hid_hw_output_report as a BPF kfunc
We currently only export hid_hw_raw_request() as a BPF kfunc. However, some devices require an explicit write on the Output Report instead of the use of the control channel. So also export hid_hw_output_report to BPF Link: https://lore.kernel.org/r/20240315-b4-hid-bpf-new-funcs-v4-2-079c282469d3@kernel.org Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
This commit is contained in:
parent
4171954f56
commit
5599f80196
|
|
@ -179,7 +179,7 @@ Available API that can be used in syscall HID-BPF programs:
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
|
|
||||||
.. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c
|
.. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c
|
||||||
:functions: hid_bpf_attach_prog hid_bpf_hw_request hid_bpf_allocate_context hid_bpf_release_context
|
:functions: hid_bpf_attach_prog hid_bpf_hw_request hid_bpf_hw_output_report hid_bpf_allocate_context hid_bpf_release_context
|
||||||
|
|
||||||
General overview of a HID-BPF program
|
General overview of a HID-BPF program
|
||||||
=====================================
|
=====================================
|
||||||
|
|
|
||||||
|
|
@ -376,6 +376,46 @@ hid_bpf_release_context(struct hid_bpf_ctx *ctx)
|
||||||
put_device(&hid->dev);
|
put_device(&hid->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
__hid_bpf_hw_check_params(struct hid_bpf_ctx *ctx, __u8 *buf, size_t *buf__sz,
|
||||||
|
enum hid_report_type rtype)
|
||||||
|
{
|
||||||
|
struct hid_report_enum *report_enum;
|
||||||
|
struct hid_report *report;
|
||||||
|
struct hid_device *hdev;
|
||||||
|
u32 report_len;
|
||||||
|
|
||||||
|
/* check arguments */
|
||||||
|
if (!ctx || !hid_bpf_ops || !buf)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
switch (rtype) {
|
||||||
|
case HID_INPUT_REPORT:
|
||||||
|
case HID_OUTPUT_REPORT:
|
||||||
|
case HID_FEATURE_REPORT:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*buf__sz < 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
hdev = (struct hid_device *)ctx->hid; /* discard const */
|
||||||
|
|
||||||
|
report_enum = hdev->report_enum + rtype;
|
||||||
|
report = hid_bpf_ops->hid_get_report(report_enum, buf);
|
||||||
|
if (!report)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
report_len = hid_report_len(report);
|
||||||
|
|
||||||
|
if (*buf__sz > report_len)
|
||||||
|
*buf__sz = report_len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hid_bpf_hw_request - Communicate with a HID device
|
* hid_bpf_hw_request - Communicate with a HID device
|
||||||
*
|
*
|
||||||
|
|
@ -392,24 +432,14 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
|
||||||
enum hid_report_type rtype, enum hid_class_request reqtype)
|
enum hid_report_type rtype, enum hid_class_request reqtype)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev;
|
struct hid_device *hdev;
|
||||||
struct hid_report *report;
|
size_t size = buf__sz;
|
||||||
struct hid_report_enum *report_enum;
|
|
||||||
u8 *dma_data;
|
u8 *dma_data;
|
||||||
u32 report_len;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* check arguments */
|
/* check arguments */
|
||||||
if (!ctx || !hid_bpf_ops || !buf)
|
ret = __hid_bpf_hw_check_params(ctx, buf, &size, rtype);
|
||||||
return -EINVAL;
|
if (ret)
|
||||||
|
return ret;
|
||||||
switch (rtype) {
|
|
||||||
case HID_INPUT_REPORT:
|
|
||||||
case HID_OUTPUT_REPORT:
|
|
||||||
case HID_FEATURE_REPORT:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (reqtype) {
|
switch (reqtype) {
|
||||||
case HID_REQ_GET_REPORT:
|
case HID_REQ_GET_REPORT:
|
||||||
|
|
@ -423,29 +453,16 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf__sz < 1)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
hdev = (struct hid_device *)ctx->hid; /* discard const */
|
hdev = (struct hid_device *)ctx->hid; /* discard const */
|
||||||
|
|
||||||
report_enum = hdev->report_enum + rtype;
|
dma_data = kmemdup(buf, size, GFP_KERNEL);
|
||||||
report = hid_bpf_ops->hid_get_report(report_enum, buf);
|
|
||||||
if (!report)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
report_len = hid_report_len(report);
|
|
||||||
|
|
||||||
if (buf__sz > report_len)
|
|
||||||
buf__sz = report_len;
|
|
||||||
|
|
||||||
dma_data = kmemdup(buf, buf__sz, GFP_KERNEL);
|
|
||||||
if (!dma_data)
|
if (!dma_data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = hid_bpf_ops->hid_hw_raw_request(hdev,
|
ret = hid_bpf_ops->hid_hw_raw_request(hdev,
|
||||||
dma_data[0],
|
dma_data[0],
|
||||||
dma_data,
|
dma_data,
|
||||||
buf__sz,
|
size,
|
||||||
rtype,
|
rtype,
|
||||||
reqtype);
|
reqtype);
|
||||||
|
|
||||||
|
|
@ -455,6 +472,42 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
|
||||||
kfree(dma_data);
|
kfree(dma_data);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hid_bpf_hw_output_report - Send an output report to a HID device
|
||||||
|
*
|
||||||
|
* @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context()
|
||||||
|
* @buf: a %PTR_TO_MEM buffer
|
||||||
|
* @buf__sz: the size of the data to transfer
|
||||||
|
*
|
||||||
|
* Returns the number of bytes transferred on success, a negative error code otherwise.
|
||||||
|
*/
|
||||||
|
__bpf_kfunc int
|
||||||
|
hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz)
|
||||||
|
{
|
||||||
|
struct hid_device *hdev;
|
||||||
|
size_t size = buf__sz;
|
||||||
|
u8 *dma_data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* check arguments */
|
||||||
|
ret = __hid_bpf_hw_check_params(ctx, buf, &size, HID_OUTPUT_REPORT);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
hdev = (struct hid_device *)ctx->hid; /* discard const */
|
||||||
|
|
||||||
|
dma_data = kmemdup(buf, size, GFP_KERNEL);
|
||||||
|
if (!dma_data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = hid_bpf_ops->hid_hw_output_report(hdev,
|
||||||
|
dma_data,
|
||||||
|
size);
|
||||||
|
|
||||||
|
kfree(dma_data);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
__bpf_kfunc_end_defs();
|
__bpf_kfunc_end_defs();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -488,6 +541,7 @@ BTF_ID_FLAGS(func, hid_bpf_attach_prog)
|
||||||
BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL)
|
BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL)
|
||||||
BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE)
|
BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE)
|
||||||
BTF_ID_FLAGS(func, hid_bpf_hw_request)
|
BTF_ID_FLAGS(func, hid_bpf_hw_request)
|
||||||
|
BTF_ID_FLAGS(func, hid_bpf_hw_output_report)
|
||||||
BTF_KFUNCS_END(hid_bpf_syscall_kfunc_ids)
|
BTF_KFUNCS_END(hid_bpf_syscall_kfunc_ids)
|
||||||
|
|
||||||
static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = {
|
static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = {
|
||||||
|
|
|
||||||
|
|
@ -2974,6 +2974,7 @@ EXPORT_SYMBOL_GPL(hid_check_keys_pressed);
|
||||||
static struct hid_bpf_ops hid_ops = {
|
static struct hid_bpf_ops hid_ops = {
|
||||||
.hid_get_report = hid_get_report,
|
.hid_get_report = hid_get_report,
|
||||||
.hid_hw_raw_request = hid_hw_raw_request,
|
.hid_hw_raw_request = hid_hw_raw_request,
|
||||||
|
.hid_hw_output_report = hid_hw_output_report,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.bus_type = &hid_bus_type,
|
.bus_type = &hid_bus_type,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,7 @@ struct hid_bpf_ops {
|
||||||
unsigned char reportnum, __u8 *buf,
|
unsigned char reportnum, __u8 *buf,
|
||||||
size_t len, enum hid_report_type rtype,
|
size_t len, enum hid_report_type rtype,
|
||||||
enum hid_class_request reqtype);
|
enum hid_class_request reqtype);
|
||||||
|
int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len);
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
const struct bus_type *bus_type;
|
const struct bus_type *bus_type;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user