diff mbox series

[bpf-next,v9,21/23] samples/bpf: add new hid_mouse example

Message ID 20220824134055.1328882-22-benjamin.tissoires@redhat.com (mailing list archive)
State New, archived
Headers show
Series None | expand

Commit Message

Benjamin Tissoires Aug. 24, 2022, 1:40 p.m. UTC
Everything should be available in the selftest part of the tree, but
providing an example without uhid and hidraw will be more easy to
follow for users.

This example will probably ever only work on the Etekcity Scroll 6E
because we need to adapt the various raw values to the actual device.

On that device, the X and Y axis will be swapped and inverted, and on
any other device, chances are high that the device will not work until
Ctrl-C is hit.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v9:
- amended the usage part

no changes in v8

changes in v7:
- remove unnecessary __must_check definition

changes in v6:
- clean up code by removing old comments

changes in v5:
- bring back same features than v3, with the new API

changes in v4:
- dropped the not-yet-implemented rdesc_fixup
- use the new API

changes in v3:
- use the new hid_get_data API
- add a comment for the report descriptor fixup to explain what is done

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples

fix hid_mouse
---
 samples/bpf/.gitignore      |   1 +
 samples/bpf/Makefile        |  23 ++++++
 samples/bpf/hid_mouse.bpf.c | 134 ++++++++++++++++++++++++++++++
 samples/bpf/hid_mouse.c     | 161 ++++++++++++++++++++++++++++++++++++
 4 files changed, 319 insertions(+)
 create mode 100644 samples/bpf/hid_mouse.bpf.c
 create mode 100644 samples/bpf/hid_mouse.c

Comments

Benjamin Tissoires Aug. 24, 2022, 7:30 p.m. UTC | #1
On Wed, Aug 24, 2022 at 3:42 PM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
>
> Everything should be available in the selftest part of the tree, but
> providing an example without uhid and hidraw will be more easy to
> follow for users.
>
> This example will probably ever only work on the Etekcity Scroll 6E
> because we need to adapt the various raw values to the actual device.
>
> On that device, the X and Y axis will be swapped and inverted, and on
> any other device, chances are high that the device will not work until
> Ctrl-C is hit.
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
>
> ---


Sorry, I realized that there are two 21/23 and 22/23...
This one should be disregarded, as there are minor improvements in the
other 21/23 :(

The good ones are the 2 starting with "samples/bpf: HID:"

Cheers,
Benjamin


>
>
> changes in v9:
> - amended the usage part
>
> no changes in v8
>
> changes in v7:
> - remove unnecessary __must_check definition
>
> changes in v6:
> - clean up code by removing old comments
>
> changes in v5:
> - bring back same features than v3, with the new API
>
> changes in v4:
> - dropped the not-yet-implemented rdesc_fixup
> - use the new API
>
> changes in v3:
> - use the new hid_get_data API
> - add a comment for the report descriptor fixup to explain what is done
>
> changes in v2:
> - split the series by bpf/libbpf/hid/selftests and samples
>
> fix hid_mouse
> ---
>  samples/bpf/.gitignore      |   1 +
>  samples/bpf/Makefile        |  23 ++++++
>  samples/bpf/hid_mouse.bpf.c | 134 ++++++++++++++++++++++++++++++
>  samples/bpf/hid_mouse.c     | 161 ++++++++++++++++++++++++++++++++++++
>  4 files changed, 319 insertions(+)
>  create mode 100644 samples/bpf/hid_mouse.bpf.c
>  create mode 100644 samples/bpf/hid_mouse.c
>
> diff --git a/samples/bpf/.gitignore b/samples/bpf/.gitignore
> index 0e7bfdbff80a..65440bd618b2 100644
> --- a/samples/bpf/.gitignore
> +++ b/samples/bpf/.gitignore
> @@ -2,6 +2,7 @@
>  cpustat
>  fds_example
>  hbm
> +hid_mouse
>  ibumad
>  lathist
>  lwt_len_hist
> diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
> index 727da3c5879b..a965bbfaca47 100644
> --- a/samples/bpf/Makefile
> +++ b/samples/bpf/Makefile
> @@ -57,6 +57,8 @@ tprogs-y += xdp_redirect_map
>  tprogs-y += xdp_redirect
>  tprogs-y += xdp_monitor
>
> +tprogs-y += hid_mouse
> +
>  # Libbpf dependencies
>  LIBBPF_SRC = $(TOOLS_PATH)/lib/bpf
>  LIBBPF_OUTPUT = $(abspath $(BPF_SAMPLES_PATH))/libbpf
> @@ -119,6 +121,8 @@ xdp_redirect-objs := xdp_redirect_user.o $(XDP_SAMPLE)
>  xdp_monitor-objs := xdp_monitor_user.o $(XDP_SAMPLE)
>  xdp_router_ipv4-objs := xdp_router_ipv4_user.o $(XDP_SAMPLE)
>
> +hid_mouse-objs := hid_mouse.o
> +
>  # Tell kbuild to always build the programs
>  always-y := $(tprogs-y)
>  always-y += sockex1_kern.o
> @@ -338,6 +342,8 @@ $(obj)/hbm_out_kern.o: $(src)/hbm.h $(src)/hbm_kern.h
>  $(obj)/hbm.o: $(src)/hbm.h
>  $(obj)/hbm_edt_kern.o: $(src)/hbm.h $(src)/hbm_kern.h
>
> +$(obj)/hid_mouse.o: $(obj)/hid_mouse.skel.h
> +
>  # Override includes for xdp_sample_user.o because $(srctree)/usr/include in
>  # TPROGS_CFLAGS causes conflicts
>  XDP_SAMPLE_CFLAGS += -Wall -O2 \
> @@ -422,6 +428,23 @@ $(BPF_SKELS_LINKED): $(BPF_OBJS_LINKED) $(BPFTOOL)
>         @echo "  BPF GEN-SKEL" $(@:.skel.h=)
>         $(Q)$(BPFTOOL) gen skeleton $(@:.skel.h=.lbpf.o) name $(notdir $(@:.skel.h=)) > $@
>
> +# Generate BPF skeletons for non XDP progs
> +OTHER_BPF_SKELS := hid_mouse.skel.h
> +
> +hid_mouse.skel.h-deps := hid_mouse.bpf.o
> +
> +OTHER_BPF_SRCS_LINKED := $(patsubst %.skel.h,%.bpf.c, $(OTHER_BPF_SKELS))
> +OTHER_BPF_OBJS_LINKED := $(patsubst %.bpf.c,$(obj)/%.bpf.o, $(OTHER_BPF_SRCS_LINKED))
> +OTHER_BPF_SKELS_LINKED := $(addprefix $(obj)/,$(OTHER_BPF_SKELS))
> +
> +$(OTHER_BPF_SKELS_LINKED): $(OTHER_BPF_OBJS_LINKED) $(BPFTOOL)
> +       @echo "  BPF GEN-OBJ " $(@:.skel.h=)
> +       $(Q)$(BPFTOOL) gen object $(@:.skel.h=.lbpf.o) $(addprefix $(obj)/,$($(@F)-deps))
> +       @echo "  BPF GEN-SKEL" $(@:.skel.h=)
> +       $(Q)$(BPFTOOL) gen skeleton $(@:.skel.h=.lbpf.o) name $(notdir $(@:.skel.h=_lskel)) > $@
> +#      $(call msg,GEN-SKEL,$@)
> +#      $(Q)$(BPFTOOL) gen skeleton $< > $@
> +
>  # asm/sysreg.h - inline assembly used by it is incompatible with llvm.
>  # But, there is no easy way to fix it, so just exclude it since it is
>  # useless for BPF samples.
> diff --git a/samples/bpf/hid_mouse.bpf.c b/samples/bpf/hid_mouse.bpf.c
> new file mode 100644
> index 000000000000..0113e603f7a7
> --- /dev/null
> +++ b/samples/bpf/hid_mouse.bpf.c
> @@ -0,0 +1,134 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include "vmlinux.h"
> +#include <bpf/bpf_helpers.h>
> +#include <bpf/bpf_tracing.h>
> +
> +/* following are kfuncs exported by HID for HID-BPF */
> +extern int hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, u32 flags) __ksym;
> +extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx,
> +                             unsigned int offset,
> +                             const size_t __sz) __ksym;
> +extern void hid_bpf_data_release(__u8 *data) __ksym;
> +extern int hid_bpf_hw_request(struct hid_bpf_ctx *ctx) __ksym;
> +
> +struct attach_prog_args {
> +       int prog_fd;
> +       unsigned int hid;
> +       int retval;
> +};
> +
> +SEC("syscall")
> +int attach_prog(struct attach_prog_args *ctx)
> +{
> +       ctx->retval = hid_bpf_attach_prog(ctx->hid,
> +                                         ctx->prog_fd,
> +                                         0);
> +       return 0;
> +}
> +
> +SEC("fmod_ret/hid_bpf_device_event")
> +int BPF_PROG(hid_y_event, struct hid_bpf_ctx *hctx)
> +{
> +       s16 y;
> +       __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 9 /* size */);
> +
> +       if (!data)
> +               return 0; /* EPERM check */
> +
> +       bpf_printk("event: size: %d", hctx->size);
> +       bpf_printk("incoming event: %02x %02x %02x",
> +                  data[0],
> +                  data[1],
> +                  data[2]);
> +       bpf_printk("                %02x %02x %02x",
> +                  data[3],
> +                  data[4],
> +                  data[5]);
> +       bpf_printk("                %02x %02x %02x",
> +                  data[6],
> +                  data[7],
> +                  data[8]);
> +
> +       y = data[3] | (data[4] << 8);
> +
> +       y = -y;
> +
> +       data[3] = y & 0xFF;
> +       data[4] = (y >> 8) & 0xFF;
> +
> +       bpf_printk("modified event: %02x %02x %02x",
> +                  data[0],
> +                  data[1],
> +                  data[2]);
> +       bpf_printk("                %02x %02x %02x",
> +                  data[3],
> +                  data[4],
> +                  data[5]);
> +       bpf_printk("                %02x %02x %02x",
> +                  data[6],
> +                  data[7],
> +                  data[8]);
> +
> +       return 0;
> +}
> +
> +SEC("fmod_ret/hid_bpf_device_event")
> +int BPF_PROG(hid_x_event, struct hid_bpf_ctx *hctx)
> +{
> +       s16 x;
> +       __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 9 /* size */);
> +
> +       if (!data)
> +               return 0; /* EPERM check */
> +
> +       x = data[1] | (data[2] << 8);
> +
> +       x = -x;
> +
> +       data[1] = x & 0xFF;
> +       data[2] = (x >> 8) & 0xFF;
> +       return 0;
> +}
> +
> +SEC("fmod_ret/hid_bpf_rdesc_fixup")
> +int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hctx)
> +{
> +       __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);
> +
> +       if (!data)
> +               return 0; /* EPERM check */
> +
> +       bpf_printk("rdesc: %02x %02x %02x",
> +                  data[0],
> +                  data[1],
> +                  data[2]);
> +       bpf_printk("       %02x %02x %02x",
> +                  data[3],
> +                  data[4],
> +                  data[5]);
> +       bpf_printk("       %02x %02x %02x ...",
> +                  data[6],
> +                  data[7],
> +                  data[8]);
> +
> +       /*
> +        * The original report descriptor contains:
> +        *
> +        * 0x05, 0x01,                    //   Usage Page (Generic Desktop)      30
> +        * 0x16, 0x01, 0x80,              //   Logical Minimum (-32767)          32
> +        * 0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           35
> +        * 0x09, 0x30,                    //   Usage (X)                         38
> +        * 0x09, 0x31,                    //   Usage (Y)                         40
> +        *
> +        * So byte 39 contains Usage X and byte 41 Usage Y.
> +        *
> +        * We simply swap the axes here.
> +        */
> +       data[39] = 0x31;
> +       data[41] = 0x30;
> +
> +       return 0;
> +}
> +
> +char _license[] SEC("license") = "GPL";
> diff --git a/samples/bpf/hid_mouse.c b/samples/bpf/hid_mouse.c
> new file mode 100644
> index 000000000000..bea3650787c5
> --- /dev/null
> +++ b/samples/bpf/hid_mouse.c
> @@ -0,0 +1,161 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (c) 2022 Benjamin Tissoires
> + *
> + * This is a pure HID-BPF example, and should be considered as such:
> + * on the Etekcity Scroll 6E, the X and Y axes will be swapped and
> + * inverted. On any other device... Not sure what this will do.
> + *
> + * This C main file is generic though. To adapt the code and test, users
> + * must amend only the .bpf.c file, which this program will load any
> + * eBPF program it finds.
> + */
> +
> +#include <assert.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <libgen.h>
> +#include <signal.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/resource.h>
> +#include <unistd.h>
> +
> +#include <linux/bpf.h>
> +#include <linux/errno.h>
> +
> +#include "bpf_util.h"
> +#include <bpf/bpf.h>
> +#include <bpf/libbpf.h>
> +
> +#include "hid_mouse.skel.h"
> +
> +static bool running = true;
> +
> +struct attach_prog_args {
> +       int prog_fd;
> +       unsigned int hid;
> +       int retval;
> +};
> +
> +static void int_exit(int sig)
> +{
> +       running = false;
> +       exit(0);
> +}
> +
> +static void usage(const char *prog)
> +{
> +       fprintf(stderr,
> +               "%s: %s /sys/bus/hid/devices/0BUS:0VID:0PID:00ID\n\n",
> +               __func__, prog);
> +       fprintf(stderr,
> +               "This program will upload and attach a HID-BPF program to the given device.\n"
> +               "On the Etekcity Scroll 6E, the X and Y axis will be inverted, but on any other\n"
> +               "device, chances are high that the device will not be working anymore\n\n"
> +               "consider this as a demo and adapt the eBPF program to your needs\n"
> +               "Hit Ctrl-C to unbind the program and reset the device\n");
> +}
> +
> +static int get_hid_id(const char *path)
> +{
> +       const char *str_id, *dir;
> +       char uevent[1024];
> +       int fd;
> +
> +       memset(uevent, 0, sizeof(uevent));
> +       snprintf(uevent, sizeof(uevent) - 1, "%s/uevent", path);
> +
> +       fd = open(uevent, O_RDONLY | O_NONBLOCK);
> +       if (fd < 0)
> +               return -ENOENT;
> +
> +       close(fd);
> +
> +       dir = basename((char *)path);
> +
> +       str_id = dir + sizeof("0003:0001:0A37.");
> +       return (int)strtol(str_id, NULL, 16);
> +}
> +
> +int main(int argc, char **argv)
> +{
> +       struct hid_mouse_lskel *skel;
> +       struct bpf_program *prog;
> +       int err;
> +       const char *optstr = "";
> +       const char *sysfs_path;
> +       int opt, hid_id, attach_fd;
> +       struct attach_prog_args args = {
> +               .retval = -1,
> +       };
> +       DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattr,
> +                           .ctx_in = &args,
> +                           .ctx_size_in = sizeof(args),
> +       );
> +
> +       while ((opt = getopt(argc, argv, optstr)) != -1) {
> +               switch (opt) {
> +               default:
> +                       usage(basename(argv[0]));
> +                       return 1;
> +               }
> +       }
> +
> +       if (optind == argc) {
> +               usage(basename(argv[0]));
> +               return 1;
> +       }
> +
> +       sysfs_path = argv[optind];
> +       if (!sysfs_path) {
> +               perror("sysfs");
> +               return 1;
> +       }
> +
> +       skel = hid_mouse_lskel__open_and_load();
> +       if (!skel) {
> +               fprintf(stderr, "%s  %s:%d", __func__, __FILE__, __LINE__);
> +               return -1;
> +       }
> +
> +       hid_id = get_hid_id(sysfs_path);
> +
> +       if (hid_id < 0) {
> +               fprintf(stderr, "can not open HID device: %m\n");
> +               return 1;
> +       }
> +       args.hid = hid_id;
> +
> +       attach_fd = bpf_program__fd(skel->progs.attach_prog);
> +       if (attach_fd < 0) {
> +               fprintf(stderr, "can't locate attach prog: %m\n");
> +               return 1;
> +       }
> +
> +       bpf_object__for_each_program(prog, *skel->skeleton->obj) {
> +               /* ignore syscalls */
> +               if (bpf_program__get_type(prog) != BPF_PROG_TYPE_TRACING)
> +                       continue;
> +
> +               args.retval = -1;
> +               args.prog_fd = bpf_program__fd(prog);
> +               err = bpf_prog_test_run_opts(attach_fd, &tattr);
> +               if (err) {
> +                       fprintf(stderr, "can't attach prog to hid device %d: %m (err: %d)\n",
> +                               hid_id, err);
> +                       return 1;
> +               }
> +       }
> +
> +       signal(SIGINT, int_exit);
> +       signal(SIGTERM, int_exit);
> +
> +       while (running)
> +               sleep(1);
> +
> +       hid_mouse_lskel__destroy(skel);
> +
> +       return 0;
> +}
> --
> 2.36.1
>
diff mbox series

Patch

diff --git a/samples/bpf/.gitignore b/samples/bpf/.gitignore
index 0e7bfdbff80a..65440bd618b2 100644
--- a/samples/bpf/.gitignore
+++ b/samples/bpf/.gitignore
@@ -2,6 +2,7 @@ 
 cpustat
 fds_example
 hbm
+hid_mouse
 ibumad
 lathist
 lwt_len_hist
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 727da3c5879b..a965bbfaca47 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -57,6 +57,8 @@  tprogs-y += xdp_redirect_map
 tprogs-y += xdp_redirect
 tprogs-y += xdp_monitor
 
+tprogs-y += hid_mouse
+
 # Libbpf dependencies
 LIBBPF_SRC = $(TOOLS_PATH)/lib/bpf
 LIBBPF_OUTPUT = $(abspath $(BPF_SAMPLES_PATH))/libbpf
@@ -119,6 +121,8 @@  xdp_redirect-objs := xdp_redirect_user.o $(XDP_SAMPLE)
 xdp_monitor-objs := xdp_monitor_user.o $(XDP_SAMPLE)
 xdp_router_ipv4-objs := xdp_router_ipv4_user.o $(XDP_SAMPLE)
 
+hid_mouse-objs := hid_mouse.o
+
 # Tell kbuild to always build the programs
 always-y := $(tprogs-y)
 always-y += sockex1_kern.o
@@ -338,6 +342,8 @@  $(obj)/hbm_out_kern.o: $(src)/hbm.h $(src)/hbm_kern.h
 $(obj)/hbm.o: $(src)/hbm.h
 $(obj)/hbm_edt_kern.o: $(src)/hbm.h $(src)/hbm_kern.h
 
+$(obj)/hid_mouse.o: $(obj)/hid_mouse.skel.h
+
 # Override includes for xdp_sample_user.o because $(srctree)/usr/include in
 # TPROGS_CFLAGS causes conflicts
 XDP_SAMPLE_CFLAGS += -Wall -O2 \
@@ -422,6 +428,23 @@  $(BPF_SKELS_LINKED): $(BPF_OBJS_LINKED) $(BPFTOOL)
 	@echo "  BPF GEN-SKEL" $(@:.skel.h=)
 	$(Q)$(BPFTOOL) gen skeleton $(@:.skel.h=.lbpf.o) name $(notdir $(@:.skel.h=)) > $@
 
+# Generate BPF skeletons for non XDP progs
+OTHER_BPF_SKELS := hid_mouse.skel.h
+
+hid_mouse.skel.h-deps := hid_mouse.bpf.o
+
+OTHER_BPF_SRCS_LINKED := $(patsubst %.skel.h,%.bpf.c, $(OTHER_BPF_SKELS))
+OTHER_BPF_OBJS_LINKED := $(patsubst %.bpf.c,$(obj)/%.bpf.o, $(OTHER_BPF_SRCS_LINKED))
+OTHER_BPF_SKELS_LINKED := $(addprefix $(obj)/,$(OTHER_BPF_SKELS))
+
+$(OTHER_BPF_SKELS_LINKED): $(OTHER_BPF_OBJS_LINKED) $(BPFTOOL)
+	@echo "  BPF GEN-OBJ " $(@:.skel.h=)
+	$(Q)$(BPFTOOL) gen object $(@:.skel.h=.lbpf.o) $(addprefix $(obj)/,$($(@F)-deps))
+	@echo "  BPF GEN-SKEL" $(@:.skel.h=)
+	$(Q)$(BPFTOOL) gen skeleton $(@:.skel.h=.lbpf.o) name $(notdir $(@:.skel.h=_lskel)) > $@
+#	$(call msg,GEN-SKEL,$@)
+#	$(Q)$(BPFTOOL) gen skeleton $< > $@
+
 # asm/sysreg.h - inline assembly used by it is incompatible with llvm.
 # But, there is no easy way to fix it, so just exclude it since it is
 # useless for BPF samples.
diff --git a/samples/bpf/hid_mouse.bpf.c b/samples/bpf/hid_mouse.bpf.c
new file mode 100644
index 000000000000..0113e603f7a7
--- /dev/null
+++ b/samples/bpf/hid_mouse.bpf.c
@@ -0,0 +1,134 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+/* following are kfuncs exported by HID for HID-BPF */
+extern int hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, u32 flags) __ksym;
+extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx,
+			      unsigned int offset,
+			      const size_t __sz) __ksym;
+extern void hid_bpf_data_release(__u8 *data) __ksym;
+extern int hid_bpf_hw_request(struct hid_bpf_ctx *ctx) __ksym;
+
+struct attach_prog_args {
+	int prog_fd;
+	unsigned int hid;
+	int retval;
+};
+
+SEC("syscall")
+int attach_prog(struct attach_prog_args *ctx)
+{
+	ctx->retval = hid_bpf_attach_prog(ctx->hid,
+					  ctx->prog_fd,
+					  0);
+	return 0;
+}
+
+SEC("fmod_ret/hid_bpf_device_event")
+int BPF_PROG(hid_y_event, struct hid_bpf_ctx *hctx)
+{
+	s16 y;
+	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 9 /* size */);
+
+	if (!data)
+		return 0; /* EPERM check */
+
+	bpf_printk("event: size: %d", hctx->size);
+	bpf_printk("incoming event: %02x %02x %02x",
+		   data[0],
+		   data[1],
+		   data[2]);
+	bpf_printk("                %02x %02x %02x",
+		   data[3],
+		   data[4],
+		   data[5]);
+	bpf_printk("                %02x %02x %02x",
+		   data[6],
+		   data[7],
+		   data[8]);
+
+	y = data[3] | (data[4] << 8);
+
+	y = -y;
+
+	data[3] = y & 0xFF;
+	data[4] = (y >> 8) & 0xFF;
+
+	bpf_printk("modified event: %02x %02x %02x",
+		   data[0],
+		   data[1],
+		   data[2]);
+	bpf_printk("                %02x %02x %02x",
+		   data[3],
+		   data[4],
+		   data[5]);
+	bpf_printk("                %02x %02x %02x",
+		   data[6],
+		   data[7],
+		   data[8]);
+
+	return 0;
+}
+
+SEC("fmod_ret/hid_bpf_device_event")
+int BPF_PROG(hid_x_event, struct hid_bpf_ctx *hctx)
+{
+	s16 x;
+	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 9 /* size */);
+
+	if (!data)
+		return 0; /* EPERM check */
+
+	x = data[1] | (data[2] << 8);
+
+	x = -x;
+
+	data[1] = x & 0xFF;
+	data[2] = (x >> 8) & 0xFF;
+	return 0;
+}
+
+SEC("fmod_ret/hid_bpf_rdesc_fixup")
+int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hctx)
+{
+	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);
+
+	if (!data)
+		return 0; /* EPERM check */
+
+	bpf_printk("rdesc: %02x %02x %02x",
+		   data[0],
+		   data[1],
+		   data[2]);
+	bpf_printk("       %02x %02x %02x",
+		   data[3],
+		   data[4],
+		   data[5]);
+	bpf_printk("       %02x %02x %02x ...",
+		   data[6],
+		   data[7],
+		   data[8]);
+
+	/*
+	 * The original report descriptor contains:
+	 *
+	 * 0x05, 0x01,                    //   Usage Page (Generic Desktop)      30
+	 * 0x16, 0x01, 0x80,              //   Logical Minimum (-32767)          32
+	 * 0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           35
+	 * 0x09, 0x30,                    //   Usage (X)                         38
+	 * 0x09, 0x31,                    //   Usage (Y)                         40
+	 *
+	 * So byte 39 contains Usage X and byte 41 Usage Y.
+	 *
+	 * We simply swap the axes here.
+	 */
+	data[39] = 0x31;
+	data[41] = 0x30;
+
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/samples/bpf/hid_mouse.c b/samples/bpf/hid_mouse.c
new file mode 100644
index 000000000000..bea3650787c5
--- /dev/null
+++ b/samples/bpf/hid_mouse.c
@@ -0,0 +1,161 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2022 Benjamin Tissoires
+ *
+ * This is a pure HID-BPF example, and should be considered as such:
+ * on the Etekcity Scroll 6E, the X and Y axes will be swapped and
+ * inverted. On any other device... Not sure what this will do.
+ *
+ * This C main file is generic though. To adapt the code and test, users
+ * must amend only the .bpf.c file, which this program will load any
+ * eBPF program it finds.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include <linux/bpf.h>
+#include <linux/errno.h>
+
+#include "bpf_util.h"
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+
+#include "hid_mouse.skel.h"
+
+static bool running = true;
+
+struct attach_prog_args {
+	int prog_fd;
+	unsigned int hid;
+	int retval;
+};
+
+static void int_exit(int sig)
+{
+	running = false;
+	exit(0);
+}
+
+static void usage(const char *prog)
+{
+	fprintf(stderr,
+		"%s: %s /sys/bus/hid/devices/0BUS:0VID:0PID:00ID\n\n",
+		__func__, prog);
+	fprintf(stderr,
+		"This program will upload and attach a HID-BPF program to the given device.\n"
+		"On the Etekcity Scroll 6E, the X and Y axis will be inverted, but on any other\n"
+		"device, chances are high that the device will not be working anymore\n\n"
+		"consider this as a demo and adapt the eBPF program to your needs\n"
+		"Hit Ctrl-C to unbind the program and reset the device\n");
+}
+
+static int get_hid_id(const char *path)
+{
+	const char *str_id, *dir;
+	char uevent[1024];
+	int fd;
+
+	memset(uevent, 0, sizeof(uevent));
+	snprintf(uevent, sizeof(uevent) - 1, "%s/uevent", path);
+
+	fd = open(uevent, O_RDONLY | O_NONBLOCK);
+	if (fd < 0)
+		return -ENOENT;
+
+	close(fd);
+
+	dir = basename((char *)path);
+
+	str_id = dir + sizeof("0003:0001:0A37.");
+	return (int)strtol(str_id, NULL, 16);
+}
+
+int main(int argc, char **argv)
+{
+	struct hid_mouse_lskel *skel;
+	struct bpf_program *prog;
+	int err;
+	const char *optstr = "";
+	const char *sysfs_path;
+	int opt, hid_id, attach_fd;
+	struct attach_prog_args args = {
+		.retval = -1,
+	};
+	DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattr,
+			    .ctx_in = &args,
+			    .ctx_size_in = sizeof(args),
+	);
+
+	while ((opt = getopt(argc, argv, optstr)) != -1) {
+		switch (opt) {
+		default:
+			usage(basename(argv[0]));
+			return 1;
+		}
+	}
+
+	if (optind == argc) {
+		usage(basename(argv[0]));
+		return 1;
+	}
+
+	sysfs_path = argv[optind];
+	if (!sysfs_path) {
+		perror("sysfs");
+		return 1;
+	}
+
+	skel = hid_mouse_lskel__open_and_load();
+	if (!skel) {
+		fprintf(stderr, "%s  %s:%d", __func__, __FILE__, __LINE__);
+		return -1;
+	}
+
+	hid_id = get_hid_id(sysfs_path);
+
+	if (hid_id < 0) {
+		fprintf(stderr, "can not open HID device: %m\n");
+		return 1;
+	}
+	args.hid = hid_id;
+
+	attach_fd = bpf_program__fd(skel->progs.attach_prog);
+	if (attach_fd < 0) {
+		fprintf(stderr, "can't locate attach prog: %m\n");
+		return 1;
+	}
+
+	bpf_object__for_each_program(prog, *skel->skeleton->obj) {
+		/* ignore syscalls */
+		if (bpf_program__get_type(prog) != BPF_PROG_TYPE_TRACING)
+			continue;
+
+		args.retval = -1;
+		args.prog_fd = bpf_program__fd(prog);
+		err = bpf_prog_test_run_opts(attach_fd, &tattr);
+		if (err) {
+			fprintf(stderr, "can't attach prog to hid device %d: %m (err: %d)\n",
+				hid_id, err);
+			return 1;
+		}
+	}
+
+	signal(SIGINT, int_exit);
+	signal(SIGTERM, int_exit);
+
+	while (running)
+		sleep(1);
+
+	hid_mouse_lskel__destroy(skel);
+
+	return 0;
+}