u8 *
dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data,
- u32 *size, int interrupt, u64 source)
+ u32 *size, int interrupt, u64 source, bool from_bpf)
{
struct hid_bpf_ctx_kern ctx_kern = {
.ctx = {
.size = *size,
},
.data = hdev->bpf.device_data,
+ .from_bpf = from_bpf,
};
struct hid_bpf_ops *e;
int ret;
return ret;
}
+static int
+__hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf,
+ size_t size, bool lock_already_taken)
+{
+ struct hid_bpf_ctx_kern *ctx_kern;
+ int ret;
+
+ ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
+ if (ctx_kern->from_bpf)
+ return -EDEADLOCK;
+
+ /* check arguments */
+ ret = __hid_bpf_hw_check_params(ctx, buf, &size, type);
+ if (ret)
+ return ret;
+
+ return hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (__u64)ctx, true,
+ lock_already_taken);
+}
+
+/**
+ * hid_bpf_try_input_report - Inject a HID report in the kernel from a HID device
+ *
+ * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context()
+ * @type: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT)
+ * @buf: a %PTR_TO_MEM buffer
+ * @buf__sz: the size of the data to transfer
+ *
+ * Returns %0 on success, a negative error code otherwise. This function will immediately
+ * fail if the device is not available, thus can be safely used in IRQ context.
+ */
+__bpf_kfunc int
+hid_bpf_try_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf,
+ const size_t buf__sz)
+{
+ struct hid_bpf_ctx_kern *ctx_kern;
+ bool from_hid_event_hook;
+
+ ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx);
+ from_hid_event_hook = ctx_kern->data && ctx_kern->data == ctx->hid->bpf.device_data;
+
+ return __hid_bpf_input_report(ctx, type, buf, buf__sz, from_hid_event_hook);
+}
+
/**
* hid_bpf_input_report - Inject a HID report in the kernel from a HID device
*
hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf,
const size_t buf__sz)
{
- size_t size = buf__sz;
int ret;
ret = down_interruptible(&ctx->hid->driver_input_lock);
return ret;
/* check arguments */
- ret = __hid_bpf_hw_check_params(ctx, buf, &size, type);
- if (ret)
- return ret;
-
- ret = hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (__u64)ctx,
- true /* lock_already_taken */);
+ ret = __hid_bpf_input_report(ctx, type, buf, buf__sz, true /* lock_already_taken */);
up(&ctx->hid->driver_input_lock);
BTF_ID_FLAGS(func, hid_bpf_hw_request, KF_SLEEPABLE)
BTF_ID_FLAGS(func, hid_bpf_hw_output_report, KF_SLEEPABLE)
BTF_ID_FLAGS(func, hid_bpf_input_report, KF_SLEEPABLE)
+BTF_ID_FLAGS(func, hid_bpf_try_input_report)
BTF_KFUNCS_END(hid_bpf_kfunc_ids)
static const struct btf_kfunc_id_set hid_bpf_kfunc_set = {
static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
- u8 *data, u32 size, int interrupt, u64 source,
+ u8 *data, u32 size, int interrupt, u64 source, bool from_bpf,
bool lock_already_taken)
{
struct hid_report_enum *report_enum;
report_enum = hid->report_enum + type;
hdrv = hid->driver;
- data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source);
+ data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source, from_bpf);
if (IS_ERR(data)) {
ret = PTR_ERR(data);
goto unlock;
int interrupt)
{
return __hid_input_report(hid, type, data, size, interrupt, 0,
+ false, /* from_bpf */
false /* lock_already_taken */);
}
EXPORT_SYMBOL_GPL(hid_input_report);
int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len,
__u64 source, bool from_bpf);
int (*hid_input_report)(struct hid_device *hid, enum hid_report_type type,
- u8 *data, u32 size, int interrupt, u64 source,
+ u8 *data, u32 size, int interrupt, u64 source, bool from_bpf,
bool lock_already_taken);
struct module *owner;
const struct bus_type *bus_type;
#ifdef CONFIG_HID_BPF
u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data,
- u32 *size, int interrupt, u64 source);
+ u32 *size, int interrupt, u64 source, bool from_bpf);
int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
unsigned char reportnum, __u8 *buf,
u32 size, enum hid_report_type rtype,
#else /* CONFIG_HID_BPF */
static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type,
u8 *data, u32 *size, int interrupt,
- u64 source) { return data; }
+ u64 source, bool from_bpf) { return data; }
static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
unsigned char reportnum, u8 *buf,
u32 size, enum hid_report_type rtype,