On Wed, Aug 24, 2022 at 3:42 PM Benjamin Tissoires <benjamin.tissoires@xxxxxxxxxx> 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@xxxxxxxxxx> > > --- 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 >