From: Benjamin Tissoires <bentiss@kernel.org>
To: Jiri Kosina <jikos@kernel.org>,
Peter Hutterer <peter.hutterer@who-t.net>,
Vicki Pfau <vi@endrift.com>, Shuah Khan <shuah@kernel.org>,
Jonathan Corbet <corbet@lwn.net>
Cc: linux-input@vger.kernel.org, linux-kselftest@vger.kernel.org,
linux-kernel@vger.kernel.org, bpf@vger.kernel.org,
linux-doc@vger.kernel.org,
Benjamin Tissoires <bentiss@kernel.org>
Subject: [PATCH HID 4/7] HID: bpf: allow BPF programs to force using hid-generic
Date: Tue, 03 Sep 2024 01:14:34 +0900 [thread overview]
Message-ID: <20240903-hid-bpf-hid-generic-v1-4-9511a565b2da@kernel.org> (raw)
In-Reply-To: <20240903-hid-bpf-hid-generic-v1-0-9511a565b2da@kernel.org>
The use case is when we fix a device through HID-BPF, 99% of the cases
we want the device to use hid-generic now instead of a dedicated device.
That's because the dedicated device might also want to change the report
descriptor, or will be handling the device in a different way the new
fixed device is using.
In hid-core, after matching for the device (so that we only call this new
hook on compatible drivers), we call for `.hid_bpf_driver_probe`.
The function can not communicate with the device because it is not yet
started, but it can make educated guesses and decide to:
- let hid-core decide by itself
- force the use of this driver (by comparing the provided name with
"hid-generic" for instance)
- force hid-core to ignore this driver for this device.
For API stability, we don't rely on a bitfield or a return value for
chosing hid-core behavior. We simply have a couple of writeable fields
in the new struct hid_bpf_driver, and then hid-core can make its educated
decision.
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
---
Documentation/hid/hid-bpf.rst | 2 +-
drivers/hid/bpf/hid_bpf_dispatch.c | 31 ++++++++++++++++++++++++++++
drivers/hid/bpf/hid_bpf_struct_ops.c | 3 +++
drivers/hid/hid-core.c | 6 ++++++
include/linux/hid_bpf.h | 40 ++++++++++++++++++++++++++++++++++++
5 files changed, 81 insertions(+), 1 deletion(-)
diff --git a/Documentation/hid/hid-bpf.rst b/Documentation/hid/hid-bpf.rst
index 5939eeafb361..05a43f11cdab 100644
--- a/Documentation/hid/hid-bpf.rst
+++ b/Documentation/hid/hid-bpf.rst
@@ -190,7 +190,7 @@ User API data structures available in programs:
-----------------------------------------------
.. kernel-doc:: include/linux/hid_bpf.h
- :identifiers: hid_bpf_ctx
+ :identifiers: hid_bpf_ctx hid_bpf_driver
Available API that can be used in all HID-BPF struct_ops programs:
------------------------------------------------------------------
diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c
index a272a086c950..2df136d64152 100644
--- a/drivers/hid/bpf/hid_bpf_dispatch.c
+++ b/drivers/hid/bpf/hid_bpf_dispatch.c
@@ -189,6 +189,37 @@ u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *s
}
EXPORT_SYMBOL_GPL(call_hid_bpf_rdesc_fixup);
+int call_hid_bpf_driver_probe(struct hid_device *hdev, struct hid_driver *hdrv,
+ const struct hid_device_id *id)
+{
+ struct hid_bpf_driver drv = { 0 };
+ struct hid_bpf_ops *e;
+ int idx;
+
+ if (strscpy(drv.name, hdrv->name, sizeof(drv.name)) < 0)
+ return 0;
+
+ idx = srcu_read_lock(&hdev->bpf.srcu);
+ list_for_each_entry_srcu(e, &hdev->bpf.prog_list, list,
+ srcu_read_lock_held(&hdev->bpf.srcu)) {
+ if (!e->hid_driver_probe)
+ continue;
+
+ e->hid_driver_probe(hdev, &drv, id);
+ }
+
+ srcu_read_unlock(&hdev->bpf.srcu, idx);
+
+ if (drv.force_driver)
+ return 1;
+
+ if (drv.ignore_driver)
+ return -1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(call_hid_bpf_driver_probe);
+
static int device_match_id(struct device *dev, const void *id)
{
struct hid_device *hdev = to_hid_device(dev);
diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c
index cd696c59ba0f..1e13a22f73a1 100644
--- a/drivers/hid/bpf/hid_bpf_struct_ops.c
+++ b/drivers/hid/bpf/hid_bpf_struct_ops.c
@@ -46,6 +46,7 @@ static int hid_bpf_ops_check_member(const struct btf_type *t,
case offsetof(struct hid_bpf_ops, hid_rdesc_fixup):
case offsetof(struct hid_bpf_ops, hid_hw_request):
case offsetof(struct hid_bpf_ops, hid_hw_output_report):
+ case offsetof(struct hid_bpf_ops, hid_driver_probe):
break;
default:
if (prog->sleepable)
@@ -79,6 +80,8 @@ static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log,
WRITE_RANGE(hid_device, name, true),
WRITE_RANGE(hid_device, uniq, true),
WRITE_RANGE(hid_device, phys, true),
+ WRITE_RANGE(hid_bpf_driver, force_driver, false),
+ WRITE_RANGE(hid_bpf_driver, ignore_driver, false),
};
#undef WRITE_RANGE
const struct btf_type *state = NULL;
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 988d0acbdf04..7845f0a789ec 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2639,10 +2639,16 @@ static bool hid_check_device_match(struct hid_device *hdev,
struct hid_driver *hdrv,
const struct hid_device_id **id)
{
+ int ret;
+
*id = hid_match_device(hdev, hdrv);
if (!*id)
return false;
+ ret = call_hid_bpf_driver_probe(hdev, hdrv, *id);
+ if (ret)
+ return ret > 0;
+
if (hdrv->match)
return hdrv->match(hdev, hid_ignore_special_drivers);
diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h
index d4d063cf63b5..20693c218857 100644
--- a/include/linux/hid_bpf.h
+++ b/include/linux/hid_bpf.h
@@ -9,6 +9,7 @@
#include <uapi/linux/hid.h>
struct hid_device;
+struct hid_driver;
/*
* The following is the user facing HID BPF API.
@@ -80,6 +81,22 @@ struct hid_ops {
extern struct hid_ops *hid_ops;
+/**
+ * struct hid_bpf_driver - User accessible data for the ``hid_bpf_probe``
+ * struct_ops
+ *
+ * @name: the name of the driver currently being treated
+ * @force_driver: set this to ``true`` to force hid-core to use this driver,
+ * bypassing any further decision made by this driver
+ * @ignore_driver: set this to ``true`` to force hid-core to ignore this driver,
+ * bypassing any further decision made by this driver
+ */
+struct hid_bpf_driver {
+ __u8 name[64];
+ bool force_driver;
+ bool ignore_driver;
+};
+
/**
* struct hid_bpf_ops - A BPF struct_ops of callbacks allowing to attach HID-BPF
* programs to a HID device
@@ -178,6 +195,25 @@ struct hid_bpf_ops {
*/
int (*hid_hw_output_report)(struct hid_bpf_ctx *ctx, u64 source);
+ /**
+ * @hid_driver_probe: called before the kernel ``.probe()`` function
+ *
+ * It has the following arguments:
+ *
+ * ``hdev``: The HID device kernel representation
+ *
+ * ``hdrv``: A BPF partially writeable representation of a HID driver
+ *
+ * ``id``: The device match structure found in the driver
+ *
+ * Note that the device has not been started yet, and thus kfuncs like
+ * ``hid_hw_output_report`` will likely fail.
+ *
+ * This function is useful to force/ignore a given supported HID driver,
+ * by writing ``true`` in ``hdrv->force_driver`` or ``hdrv->ignore_driver``
+ */
+ void (*hid_driver_probe)(struct hid_device *hdev, struct hid_bpf_driver *hdrv,
+ const struct hid_device_id *id);
/* private: do not show up in the docs */
struct hid_device *hdev;
@@ -213,6 +249,8 @@ void hid_bpf_disconnect_device(struct hid_device *hdev);
void hid_bpf_destroy_device(struct hid_device *hid);
int hid_bpf_device_init(struct hid_device *hid);
u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size);
+int call_hid_bpf_driver_probe(struct hid_device *hdev, struct hid_driver *hdrv,
+ const struct hid_device_id *id);
#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,
@@ -228,6 +266,8 @@ static inline int hid_bpf_connect_device(struct hid_device *hdev) { return 0; }
static inline void hid_bpf_disconnect_device(struct hid_device *hdev) {}
static inline void hid_bpf_destroy_device(struct hid_device *hid) {}
static inline int hid_bpf_device_init(struct hid_device *hid) { return 0; }
+static inline int call_hid_bpf_driver_probe(struct hid_device *hdev, struct hid_driver *hdrv,
+ const struct hid_device_id *id) { return 0; }
/*
* This specialized allocator has to be a macro for its allocations to be
* accounted separately (to have a separate alloc_tag). The typecast is
--
2.46.0
next prev parent reply other threads:[~2024-09-02 16:15 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-09-02 16:14 [PATCH HID 0/7] HID: bpf: add a new hook to control hid-generic Benjamin Tissoires
2024-09-02 16:14 ` [PATCH HID 1/7] selftests/hid: add dependency on hid_common.h Benjamin Tissoires
2024-09-02 16:14 ` [PATCH HID 2/7] selftests/hid: cleanup C tests by adding a common struct uhid_device Benjamin Tissoires
2024-09-02 16:14 ` [PATCH HID 3/7] selftests/hid: allow to parametrize bus/vid/pid/rdesc on the test device Benjamin Tissoires
2024-09-02 16:14 ` Benjamin Tissoires [this message]
2024-09-03 5:36 ` [PATCH HID 4/7] HID: bpf: allow BPF programs to force using hid-generic Peter Hutterer
2024-09-03 15:05 ` Benjamin Tissoires
2024-09-04 9:28 ` Benjamin Tissoires
2024-09-02 16:14 ` [PATCH HID 5/7] selftests/hid: add test for assigning a given device to hid-generic Benjamin Tissoires
2024-09-02 16:14 ` [PATCH HID 6/7] HID: bpf: Allow to control the connect mask of hid-generic from BPF Benjamin Tissoires
2024-09-03 5:57 ` Peter Hutterer
2024-09-03 16:32 ` Benjamin Tissoires
2024-09-02 16:14 ` [PATCH HID 7/7] selftests/hid: add test to disable hid-input Benjamin Tissoires
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240903-hid-bpf-hid-generic-v1-4-9511a565b2da@kernel.org \
--to=bentiss@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=corbet@lwn.net \
--cc=jikos@kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=peter.hutterer@who-t.net \
--cc=shuah@kernel.org \
--cc=vi@endrift.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).