diff options
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/bpf/hid_bpf_dispatch.c | 16 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 16 |
2 files changed, 24 insertions, 8 deletions
diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 68a9a2fcbc06..5174dc6d9d18 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -496,24 +496,32 @@ hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz) * @buf: a %PTR_TO_MEM buffer * @buf__sz: the size of the data to transfer * - * Returns %0 on success, a negative error code otherwise. + * Returns %0 on success, a negative error code otherwise. This function will wait for the + * device to be available before injecting the event, thus needs to be called in sleepable + * context. */ __bpf_kfunc int hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf, const size_t buf__sz) { - struct hid_device *hdev; size_t size = buf__sz; int ret; + ret = down_interruptible(&ctx->hid->driver_input_lock); + if (ret) + return ret; + /* check arguments */ ret = __hid_bpf_hw_check_params(ctx, buf, &size, type); if (ret) return ret; - hdev = (struct hid_device *)ctx->hid; /* discard const */ + ret = hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (__u64)ctx, + true /* lock_already_taken */); + + up(&ctx->hid->driver_input_lock); - return hid_ops->hid_input_report(hdev, type, buf, size, 0, (__u64)ctx); + return ret; } __bpf_kfunc_end_defs(); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index bb6f334f05bd..e9b5f44683fd 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2027,7 +2027,8 @@ EXPORT_SYMBOL_GPL(hid_report_raw_event); 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 lock_already_taken) { struct hid_report_enum *report_enum; struct hid_driver *hdrv; @@ -2037,8 +2038,13 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, if (!hid) return -ENODEV; - if (down_trylock(&hid->driver_input_lock)) + ret = down_trylock(&hid->driver_input_lock); + if (lock_already_taken && !ret) { + up(&hid->driver_input_lock); + return -EINVAL; + } else if (!lock_already_taken && ret) { return -EBUSY; + } if (!hid->driver) { ret = -ENODEV; @@ -2079,7 +2085,8 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, ret = hid_report_raw_event(hid, type, data, size, interrupt); unlock: - up(&hid->driver_input_lock); + if (!lock_already_taken) + up(&hid->driver_input_lock); return ret; } @@ -2097,7 +2104,8 @@ unlock: int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, int interrupt) { - return __hid_input_report(hid, type, data, size, interrupt, 0); + return __hid_input_report(hid, type, data, size, interrupt, 0, + false /* lock_already_taken */); } EXPORT_SYMBOL_GPL(hid_input_report); |