aboutsummaryrefslogtreecommitdiff
path: root/drivers/hid
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/bpf/hid_bpf_dispatch.c16
-rw-r--r--drivers/hid/hid-core.c16
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);