Commit 30b86641 authored by Benjamin Tissoires's avatar Benjamin Tissoires

Merge branch 'for-6.11/bpf' into for-linus

- Rewrite of HID-BPF internal implementation to use bpf struct_ops
  instead of tracing (Benjamin Tissoires)
- Add new HID-BPF hooks to be able to intercept userspace calls
  targetting a HID device and filtering them (Benjamin Tissoires)
- Add support for various new devices through HID-BPF filters (Benjamin
  Tissoires)
parents e518f368 a67a1deb
This diff is collapsed.
......@@ -8,4 +8,4 @@ LIBBPF_INCLUDE = $(srctree)/tools/lib
obj-$(CONFIG_HID_BPF) += hid_bpf.o
CFLAGS_hid_bpf_dispatch.o += -I$(LIBBPF_INCLUDE)
CFLAGS_hid_bpf_jmp_table.o += -I$(LIBBPF_INCLUDE)
hid_bpf-objs += hid_bpf_dispatch.o hid_bpf_jmp_table.o
hid_bpf-objs += hid_bpf_dispatch.o hid_bpf_struct_ops.o
# SPDX-License-Identifier: GPL-2.0
OUTPUT := .output
abs_out := $(abspath $(OUTPUT))
CLANG ?= clang
LLC ?= llc
LLVM_STRIP ?= llvm-strip
TOOLS_PATH := $(abspath ../../../../tools)
BPFTOOL_SRC := $(TOOLS_PATH)/bpf/bpftool
BPFTOOL_OUTPUT := $(abs_out)/bpftool
DEFAULT_BPFTOOL := $(BPFTOOL_OUTPUT)/bootstrap/bpftool
BPFTOOL ?= $(DEFAULT_BPFTOOL)
LIBBPF_SRC := $(TOOLS_PATH)/lib/bpf
LIBBPF_OUTPUT := $(abs_out)/libbpf
LIBBPF_DESTDIR := $(LIBBPF_OUTPUT)
LIBBPF_INCLUDE := $(LIBBPF_DESTDIR)/include
BPFOBJ := $(LIBBPF_OUTPUT)/libbpf.a
INCLUDES := -I$(OUTPUT) -I$(LIBBPF_INCLUDE) -I$(TOOLS_PATH)/include/uapi
CFLAGS := -g -Wall
VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux) \
$(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux) \
../../../../vmlinux \
/sys/kernel/btf/vmlinux \
/boot/vmlinux-$(shell uname -r)
VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))
ifeq ($(VMLINUX_BTF),)
$(error Cannot find a vmlinux for VMLINUX_BTF at any of "$(VMLINUX_BTF_PATHS)")
endif
ifeq ($(V),1)
Q =
msg =
else
Q = @
msg = @printf ' %-8s %s%s\n' "$(1)" "$(notdir $(2))" "$(if $(3), $(3))";
MAKEFLAGS += --no-print-directory
submake_extras := feature_display=0
endif
.DELETE_ON_ERROR:
.PHONY: all clean
all: entrypoints.lskel.h
clean:
$(call msg,CLEAN)
$(Q)rm -rf $(OUTPUT) entrypoints
entrypoints.lskel.h: $(OUTPUT)/entrypoints.bpf.o | $(BPFTOOL)
$(call msg,GEN-SKEL,$@)
$(Q)$(BPFTOOL) gen skeleton -L $< > $@
$(OUTPUT)/entrypoints.bpf.o: entrypoints.bpf.c $(OUTPUT)/vmlinux.h $(BPFOBJ) | $(OUTPUT)
$(call msg,BPF,$@)
$(Q)$(CLANG) -g -O2 --target=bpf $(INCLUDES) \
-c $(filter %.c,$^) -o $@ && \
$(LLVM_STRIP) -g $@
$(OUTPUT)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL) | $(INCLUDE_DIR)
ifeq ($(VMLINUX_H),)
$(call msg,GEN,,$@)
$(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $@
else
$(call msg,CP,,$@)
$(Q)cp "$(VMLINUX_H)" $@
endif
$(OUTPUT) $(LIBBPF_OUTPUT) $(BPFTOOL_OUTPUT):
$(call msg,MKDIR,$@)
$(Q)mkdir -p $@
$(BPFOBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(LIBBPF_OUTPUT)
$(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_SRC) \
OUTPUT=$(abspath $(dir $@))/ prefix= \
DESTDIR=$(LIBBPF_DESTDIR) $(abspath $@) install_headers
ifeq ($(CROSS_COMPILE),)
$(DEFAULT_BPFTOOL): $(BPFOBJ) | $(BPFTOOL_OUTPUT)
$(Q)$(MAKE) $(submake_extras) -C $(BPFTOOL_SRC) \
OUTPUT=$(BPFTOOL_OUTPUT)/ \
LIBBPF_BOOTSTRAP_OUTPUT=$(LIBBPF_OUTPUT)/ \
LIBBPF_BOOTSTRAP_DESTDIR=$(LIBBPF_DESTDIR)/ bootstrap
else
$(DEFAULT_BPFTOOL): | $(BPFTOOL_OUTPUT)
$(Q)$(MAKE) $(submake_extras) -C $(BPFTOOL_SRC) \
OUTPUT=$(BPFTOOL_OUTPUT)/ bootstrap
endif
WARNING:
If you change "entrypoints.bpf.c" do "make -j" in this directory to rebuild "entrypoints.skel.h".
Make sure to have clang 10 installed.
See Documentation/bpf/bpf_devel_QA.rst
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2022 Benjamin Tissoires */
#include ".output/vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#define HID_BPF_MAX_PROGS 1024
struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, HID_BPF_MAX_PROGS);
__uint(key_size, sizeof(__u32));
__uint(value_size, sizeof(__u32));
} hid_jmp_table SEC(".maps");
SEC("fmod_ret/__hid_bpf_tail_call")
int BPF_PROG(hid_tail_call, struct hid_bpf_ctx *hctx)
{
bpf_tail_call(ctx, &hid_jmp_table, hctx->index);
return 0;
}
char LICENSE[] SEC("license") = "GPL";
This diff is collapsed.
This diff is collapsed.
......@@ -8,16 +8,13 @@
struct hid_bpf_ctx_kern {
struct hid_bpf_ctx ctx;
u8 *data;
bool from_bpf;
};
int hid_bpf_preload_skel(void);
void hid_bpf_free_links_and_skel(void);
int hid_bpf_get_prog_attach_type(struct bpf_prog *prog);
int __hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_type, int prog_fd,
struct bpf_prog *prog, __u32 flags);
void __hid_bpf_destroy_device(struct hid_device *hdev);
int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type,
struct hid_bpf_ctx_kern *ctx_kern);
struct hid_device *hid_get_device(unsigned int hid_id);
void hid_put_device(struct hid_device *hid);
int hid_bpf_allocate_event_data(struct hid_device *hdev);
void __hid_bpf_ops_destroy_device(struct hid_device *hdev);
int hid_bpf_reconnect(struct hid_device *hdev);
struct bpf_prog;
......
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-only
/*
* HID-BPF support for Linux
*
* Copyright (c) 2024 Benjamin Tissoires
*/
#include <linux/bitops.h>
#include <linux/bpf_verifier.h>
#include <linux/bpf.h>
#include <linux/btf.h>
#include <linux/btf_ids.h>
#include <linux/filter.h>
#include <linux/hid.h>
#include <linux/hid_bpf.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/stddef.h>
#include <linux/workqueue.h>
#include "hid_bpf_dispatch.h"
static struct btf *hid_bpf_ops_btf;
static int hid_bpf_ops_init(struct btf *btf)
{
hid_bpf_ops_btf = btf;
return 0;
}
static bool hid_bpf_ops_is_valid_access(int off, int size,
enum bpf_access_type type,
const struct bpf_prog *prog,
struct bpf_insn_access_aux *info)
{
return bpf_tracing_btf_ctx_access(off, size, type, prog, info);
}
static int hid_bpf_ops_check_member(const struct btf_type *t,
const struct btf_member *member,
const struct bpf_prog *prog)
{
u32 moff = __btf_member_bit_offset(t, member) / 8;
switch (moff) {
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):
break;
default:
if (prog->sleepable)
return -EINVAL;
}
return 0;
}
struct hid_bpf_offset_write_range {
const char *struct_name;
u32 struct_length;
u32 start;
u32 end;
};
static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg,
int off, int size)
{
#define WRITE_RANGE(_name, _field, _is_string) \
{ \
.struct_name = #_name, \
.struct_length = sizeof(struct _name), \
.start = offsetof(struct _name, _field), \
.end = offsetofend(struct _name, _field) - !!(_is_string), \
}
const struct hid_bpf_offset_write_range write_ranges[] = {
WRITE_RANGE(hid_bpf_ctx, retval, false),
WRITE_RANGE(hid_device, name, true),
WRITE_RANGE(hid_device, uniq, true),
WRITE_RANGE(hid_device, phys, true),
};
#undef WRITE_RANGE
const struct btf_type *state = NULL;
const struct btf_type *t;
const char *cur = NULL;
int i;
t = btf_type_by_id(reg->btf, reg->btf_id);
for (i = 0; i < ARRAY_SIZE(write_ranges); i++) {
const struct hid_bpf_offset_write_range *write_range = &write_ranges[i];
s32 type_id;
/* we already found a writeable struct, but there is a
* new one, let's break the loop.
*/
if (t == state && write_range->struct_name != cur)
break;
/* new struct to look for */
if (write_range->struct_name != cur) {
type_id = btf_find_by_name_kind(reg->btf, write_range->struct_name,
BTF_KIND_STRUCT);
if (type_id < 0)
return -EINVAL;
state = btf_type_by_id(reg->btf, type_id);
}
/* this is not the struct we are looking for */
if (t != state) {
cur = write_range->struct_name;
continue;
}
/* first time we see this struct, check for out of bounds */
if (cur != write_range->struct_name &&
off + size > write_range->struct_length) {
bpf_log(log, "write access for struct %s at off %d with size %d\n",
write_range->struct_name, off, size);
return -EACCES;
}
/* now check if we are in our boundaries */
if (off >= write_range->start && off + size <= write_range->end)
return NOT_INIT;
cur = write_range->struct_name;
}
if (t != state)
bpf_log(log, "write access to this struct is not supported\n");
else
bpf_log(log,
"write access at off %d with size %d on read-only part of %s\n",
off, size, cur);
return -EACCES;
}
static const struct bpf_verifier_ops hid_bpf_verifier_ops = {
.get_func_proto = bpf_base_func_proto,
.is_valid_access = hid_bpf_ops_is_valid_access,
.btf_struct_access = hid_bpf_ops_btf_struct_access,
};
static int hid_bpf_ops_init_member(const struct btf_type *t,
const struct btf_member *member,
void *kdata, const void *udata)
{
const struct hid_bpf_ops *uhid_bpf_ops;
struct hid_bpf_ops *khid_bpf_ops;
u32 moff;
uhid_bpf_ops = (const struct hid_bpf_ops *)udata;
khid_bpf_ops = (struct hid_bpf_ops *)kdata;
moff = __btf_member_bit_offset(t, member) / 8;
switch (moff) {
case offsetof(struct hid_bpf_ops, hid_id):
/* For hid_id and flags fields, this function has to copy it
* and return 1 to indicate that the data has been handled by
* the struct_ops type, or the verifier will reject the map if
* the value of those fields is not zero.
*/
khid_bpf_ops->hid_id = uhid_bpf_ops->hid_id;
return 1;
case offsetof(struct hid_bpf_ops, flags):
if (uhid_bpf_ops->flags & ~BPF_F_BEFORE)
return -EINVAL;
khid_bpf_ops->flags = uhid_bpf_ops->flags;
return 1;
}
return 0;
}
static int hid_bpf_reg(void *kdata)
{
struct hid_bpf_ops *ops = kdata;
struct hid_device *hdev;
int count, err = 0;
hdev = hid_get_device(ops->hid_id);
if (IS_ERR(hdev))
return PTR_ERR(hdev);
ops->hdev = hdev;
mutex_lock(&hdev->bpf.prog_list_lock);
count = list_count_nodes(&hdev->bpf.prog_list);
if (count >= HID_BPF_MAX_PROGS_PER_DEV) {
err = -E2BIG;
goto out_unlock;
}
if (ops->hid_rdesc_fixup) {
if (hdev->bpf.rdesc_ops) {
err = -EINVAL;
goto out_unlock;
}
hdev->bpf.rdesc_ops = ops;
}
if (ops->hid_device_event) {
err = hid_bpf_allocate_event_data(hdev);
if (err)
goto out_unlock;
}
if (ops->flags & BPF_F_BEFORE)
list_add_rcu(&ops->list, &hdev->bpf.prog_list);
else
list_add_tail_rcu(&ops->list, &hdev->bpf.prog_list);
synchronize_srcu(&hdev->bpf.srcu);
out_unlock:
mutex_unlock(&hdev->bpf.prog_list_lock);
if (err) {
if (hdev->bpf.rdesc_ops == ops)
hdev->bpf.rdesc_ops = NULL;
hid_put_device(hdev);
} else if (ops->hid_rdesc_fixup) {
hid_bpf_reconnect(hdev);
}
return err;
}
static void hid_bpf_unreg(void *kdata)
{
struct hid_bpf_ops *ops = kdata;
struct hid_device *hdev;
bool reconnect = false;
hdev = ops->hdev;
/* check if __hid_bpf_ops_destroy_device() has been called */
if (!hdev)
return;
mutex_lock(&hdev->bpf.prog_list_lock);
list_del_rcu(&ops->list);
synchronize_srcu(&hdev->bpf.srcu);
reconnect = hdev->bpf.rdesc_ops == ops;
if (reconnect)
hdev->bpf.rdesc_ops = NULL;
mutex_unlock(&hdev->bpf.prog_list_lock);
if (reconnect)
hid_bpf_reconnect(hdev);
hid_put_device(hdev);
}
static int __hid_bpf_device_event(struct hid_bpf_ctx *ctx, enum hid_report_type type, u64 source)
{
return 0;
}
static int __hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx)
{
return 0;
}
static struct hid_bpf_ops __bpf_hid_bpf_ops = {
.hid_device_event = __hid_bpf_device_event,
.hid_rdesc_fixup = __hid_bpf_rdesc_fixup,
};
static struct bpf_struct_ops bpf_hid_bpf_ops = {
.verifier_ops = &hid_bpf_verifier_ops,
.init = hid_bpf_ops_init,
.check_member = hid_bpf_ops_check_member,
.init_member = hid_bpf_ops_init_member,
.reg = hid_bpf_reg,
.unreg = hid_bpf_unreg,
.name = "hid_bpf_ops",
.cfi_stubs = &__bpf_hid_bpf_ops,
.owner = THIS_MODULE,
};
void __hid_bpf_ops_destroy_device(struct hid_device *hdev)
{
struct hid_bpf_ops *e;
rcu_read_lock();
list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) {
hid_put_device(hdev);
e->hdev = NULL;
}
rcu_read_unlock();
}
static int __init hid_bpf_struct_ops_init(void)
{
return register_bpf_struct_ops(&bpf_hid_bpf_ops, hid_bpf_ops);
}
late_initcall(hid_bpf_struct_ops_init);
......@@ -133,7 +133,7 @@ HID_BPF_CONFIG(
* integer. We thus divide it by 30 to match what other joysticks are
* doing
*/
SEC("fmod_ret/hid_bpf_rdesc_fixup")
SEC(HID_BPF_RDESC_FIXUP)
int BPF_PROG(hid_fix_rdesc_raptor_mach_2, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
......@@ -152,7 +152,7 @@ int BPF_PROG(hid_fix_rdesc_raptor_mach_2, struct hid_bpf_ctx *hctx)
* divide it by 30.
* Byte 34 is always null, so it is ignored.
*/
SEC("fmod_ret/hid_bpf_device_event")
SEC(HID_BPF_DEVICE_EVENT)
int BPF_PROG(raptor_mach_2_fix_hat_switch, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 64 /* size */);
......@@ -168,6 +168,11 @@ int BPF_PROG(raptor_mach_2_fix_hat_switch, struct hid_bpf_ctx *hctx)
return 0;
}
HID_BPF_OPS(raptor_mach_2) = {
.hid_rdesc_fixup = (void *)hid_fix_rdesc_raptor_mach_2,
.hid_device_event = (void *)raptor_mach_2_fix_hat_switch,
};
SEC("syscall")
int probe(struct hid_bpf_probe_args *ctx)
{
......
......@@ -30,7 +30,7 @@ HID_BPF_CONFIG(
* pointer.
*/
SEC("fmod_ret/hid_bpf_rdesc_fixup")
SEC(HID_BPF_RDESC_FIXUP)
int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);
......@@ -45,6 +45,10 @@ int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)
return 0;
}
HID_BPF_OPS(hp_elite_presenter) = {
.hid_rdesc_fixup = (void *)hid_fix_rdesc,
};
SEC("syscall")
int probe(struct hid_bpf_probe_args *ctx)
{
......
This diff is collapsed.
This diff is collapsed.
......@@ -191,7 +191,7 @@ static const __u8 fixed_rdesc[] = {
0xc0, // End Collection 327
};
SEC("fmod_ret/hid_bpf_rdesc_fixup")
SEC(HID_BPF_RDESC_FIXUP)
int BPF_PROG(hid_fix_rdesc_huion_kamvas_pro_19, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
......@@ -215,7 +215,7 @@ int BPF_PROG(hid_fix_rdesc_huion_kamvas_pro_19, struct hid_bpf_ctx *hctx)
* - if there was this out-of-proximity event, we are entering
* eraser mode, and we will until the next out-of-proximity.
*/
SEC("fmod_ret/hid_bpf_device_event")
SEC(HID_BPF_DEVICE_EVENT)
int BPF_PROG(kamvas_pro_19_fix_3rd_button, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);
......@@ -255,6 +255,11 @@ int BPF_PROG(kamvas_pro_19_fix_3rd_button, struct hid_bpf_ctx *hctx)
return 0;
}
HID_BPF_OPS(huion_Kamvas_pro_19) = {
.hid_rdesc_fixup = (void *)hid_fix_rdesc_huion_kamvas_pro_19,
.hid_device_event = (void *)kamvas_pro_19_fix_3rd_button,
};
SEC("syscall")
int probe(struct hid_bpf_probe_args *ctx)
{
......
......@@ -21,7 +21,7 @@ HID_BPF_CONFIG(
* We just fix the report descriptor to enable those missing 7 buttons.
*/
SEC("fmod_ret/hid_bpf_rdesc_fixup")
SEC(HID_BPF_RDESC_FIXUP)
int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)
{
const u8 offsets[] = {84, 112, 140};
......@@ -45,6 +45,10 @@ int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)
return 0;
}
HID_BPF_OPS(iogear_kaliber_momentum) = {
.hid_rdesc_fixup = (void *)hid_fix_rdesc,
};
SEC("syscall")
int probe(struct hid_bpf_probe_args *ctx)
{
......
......@@ -56,7 +56,7 @@ clean:
%.bpf.o: %.bpf.c vmlinux.h $(BPFOBJ) | $(OUTPUT)
$(call msg,BPF,$@)
$(Q)$(CLANG) -g -O2 --target=bpf $(INCLUDES) \
$(Q)$(CLANG) -g -O2 --target=bpf -Wall -Werror $(INCLUDES) \
-c $(filter %.c,$^) -o $@ && \
$(LLVM_STRIP) -g $@
......
......@@ -15,20 +15,19 @@ HID_BPF_CONFIG(
);
/*
* When using the XBox Wireless Controller Elite 2 over Bluetooth,
* the device exports the paddle on the back of the device as a single
* When using the Xbox Wireless Controller Elite 2 over Bluetooth,
* the device exports the paddles on the back of the device as a single
* bitfield value of usage "Assign Selection".
*
* The kernel doesn't process those usages properly and report KEY_UNKNOWN
* for it.
* The kernel doesn't process the paddles usage properly and reports KEY_UNKNOWN.
*
* SDL doesn't know how to interprete that KEY_UNKNOWN and thus ignores the paddles.
* SDL doesn't know how to interpret KEY_UNKNOWN and thus ignores the paddles.
*
* Given that over USB the kernel uses BTN_TRIGGER_HAPPY[5-8], we
* can tweak the report descriptor to make the kernel interprete it properly:
* - we need an application collection of gamepad (so we have to close the current
* can tweak the report descriptor to make the kernel interpret it properly:
* - We need an application collection of gamepad (so we have to close the current
* Consumer Control one)
* - we need to change the usage to be buttons from 0x15 to 0x18
* - We need to change the usage to be buttons from 0x15 to 0x18
*/
#define OFFSET_ASSIGN_SELECTION 211
......@@ -93,7 +92,7 @@ _Static_assert(sizeof(rdesc_assign_selection) == sizeof(fixed_rdesc_assign_selec
_Static_assert(sizeof(rdesc_assign_selection) + OFFSET_ASSIGN_SELECTION < ORIGINAL_RDESC_SIZE,
"Rdesc at given offset is too big");
SEC("fmod_ret/hid_bpf_rdesc_fixup")
SEC(HID_BPF_RDESC_FIXUP)
int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);
......@@ -114,6 +113,10 @@ int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)
return 0;
}
HID_BPF_OPS(xbox_elite_2) = {
.hid_rdesc_fixup = (void *)hid_fix_rdesc,
};
SEC("syscall")
int probe(struct hid_bpf_probe_args *ctx)
{
......
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2024 Kumar Swarnam Iyer (kumar.s.iyer65@gmail.com)
*/
#include "vmlinux.h"
#include "hid_bpf.h"
#include "hid_bpf_helpers.h"
#include <bpf/bpf_tracing.h>
#define VID_THRUSTMASTER 0x044F
#define PID_TCA_YOKE_BOEING 0x0409
HID_BPF_CONFIG(
HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_THRUSTMASTER, PID_TCA_YOKE_BOEING)
);
/* The original HID descriptor of the Thrustmaster TCA Yoke Boeing joystick contains
* an Input field that shows up as an axis, ABS_MISC in Linux. But it is not possible
* to assign an actual physical control to this axis as they're all taken up. There
* are 2 vendor-defined inputs where the Input type appears to be defined wrongly.
* This bpf attempts to fix this by changing the Inputs so that it doesn't show up in
* Linux at all.
* This version is the short version fix that only changes 2 fields in the descriptor
* instead of the whole report descriptor.
* For reference, this is the original report descriptor:
*
* 0x05, 0x01, // Usage Page (Generic Desktop) 0
* 0x09, 0x04, // Usage (Joystick) 2
* 0xa1, 0x01, // Collection (Application) 4
* 0x85, 0x01, // Report ID (1) 6
* 0x09, 0x39, // Usage (Hat switch) 8
* 0x15, 0x00, // Logical Minimum (0) 10
* 0x25, 0x07, // Logical Maximum (7) 12
* 0x35, 0x00, // Physical Minimum (0) 14
* 0x46, 0x3b, 0x01, // Physical Maximum (315) 16
* 0x65, 0x14, // Unit (EnglishRotation: deg) 19
* 0x75, 0x04, // Report Size (4) 21
* 0x95, 0x01, // Report Count (1) 23
* 0x81, 0x42, // Input (Data,Var,Abs,Null) 25
* 0x65, 0x00, // Unit (None) 27
* 0x05, 0x09, // Usage Page (Button) 29
* 0x19, 0x01, // Usage Minimum (1) 31
* 0x29, 0x12, // Usage Maximum (18) 33
* 0x15, 0x00, // Logical Minimum (0) 35
* 0x25, 0x01, // Logical Maximum (1) 37
* 0x75, 0x01, // Report Size (1) 39
* 0x95, 0x12, // Report Count (18) 41
* 0x81, 0x02, // Input (Data,Var,Abs) 43
* 0x95, 0x02, // Report Count (2) 45
* 0x81, 0x03, // Input (Cnst,Var,Abs) 47
* 0x05, 0x01, // Usage Page (Generic Desktop) 49
* 0x09, 0x31, // Usage (Y) 51
* 0x09, 0x30, // Usage (X) 53
* 0x09, 0x32, // Usage (Z) 55
* 0x09, 0x34, // Usage (Ry) 57
* 0x09, 0x33, // Usage (Rx) 59
* 0x09, 0x35, // Usage (Rz) 61
* 0x15, 0x00, // Logical Minimum (0) 63
* 0x27, 0xff, 0xff, 0x00, 0x00, // Logical Maximum (65535) 65
* 0x75, 0x10, // Report Size (16) 70
* 0x95, 0x06, // Report Count (6) 72
* 0x81, 0x02, // Input (Data,Var,Abs) 74
* 0x06, 0xf0, 0xff, // Usage Page (Vendor Usage Page 0xfff0) 76
* 0x09, 0x59, // Usage (Vendor Usage 0x59) 79
* 0x15, 0x00, // Logical Minimum (0) 81
* 0x26, 0xff, 0x00, // Logical Maximum (255) 83
* 0x75, 0x08, // Report Size (8) 86
* 0x95, 0x01, // Report Count (1) 88
* 0x81, 0x02, // Input (Data,Var,Abs) 90 --> Needs to be changed
* 0x09, 0x51, // Usage (Vendor Usage 0x51) 92
* 0x15, 0x00, // Logical Minimum (0) 94
* 0x26, 0xff, 0x00, // Logical Maximum (255) 96
* 0x75, 0x08, // Report Size (8) 99
* 0x95, 0x20, // Report Count (32) 101 --> Needs to be changed
* 0x81, 0x02, // Input (Data,Var,Abs) 103
* 0x09, 0x50, // Usage (Vendor Usage 0x50) 105
* 0x15, 0x00, // Logical Minimum (0) 107
* 0x26, 0xff, 0x00, // Logical Maximum (255) 109
* 0x75, 0x08, // Report Size (8) 112
* 0x95, 0x0f, // Report Count (15) 114
* 0x81, 0x03, // Input (Cnst,Var,Abs) 116
* 0x09, 0x47, // Usage (Vendor Usage 0x47) 118
* 0x85, 0xf2, // Report ID (242) 120
* 0x15, 0x00, // Logical Minimum (0) 122
* 0x26, 0xff, 0x00, // Logical Maximum (255) 124
* 0x75, 0x08, // Report Size (8) 127
* 0x95, 0x3f, // Report Count (63) 129
* 0xb1, 0x02, // Feature (Data,Var,Abs) 131
* 0x09, 0x48, // Usage (Vendor Usage 0x48) 133
* 0x85, 0xf3, // Report ID (243) 135
* 0x15, 0x00, // Logical Minimum (0) 137
* 0x26, 0xff, 0x00, // Logical Maximum (255) 139
* 0x75, 0x08, // Report Size (8) 142
* 0x95, 0x3f, // Report Count (63) 144
* 0xb1, 0x02, // Feature (Data,Var,Abs) 146
* 0xc0, // End Collection 148
*/
SEC(HID_BPF_RDESC_FIXUP)
int BPF_PROG(hid_fix_rdesc_tca_yoke, struct hid_bpf_ctx *hctx)
{
const int expected_length = 148;
if (hctx->size != expected_length)
return 0;
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
if (!data)
return 0; /* EPERM */
/* Safety check, our probe() should take care of this though */
if (data[1] != 0x01 /* Generic Desktop */ || data[3] != 0x04 /* Joystick */)
return 0;
/* The report descriptor sets incorrect Input items in 2 places, resulting in a
* non-existing axis showing up.
* This change sets the correct Input which prevents the axis from showing up in Linux.
*/
if (data[90] == 0x81 && /* Input */
data[103] == 0x81) { /* Input */
data[91] = 0x03; /* Input set to 0x03 Constant, Variable Absolute */
data[104] = 0x03; /* Input set to 0X03 Constant, Variable Absolute */
}
return 0;
}
HID_BPF_OPS(tca_yoke) = {
.hid_rdesc_fixup = (void *)hid_fix_rdesc_tca_yoke,
};
SEC("syscall")
int probe(struct hid_bpf_probe_args *ctx)
{
/* ensure the kernel isn't fixed already */
if (ctx->rdesc[91] != 0x02) /* Input for 0x59 Usage type has changed */
ctx->retval = -EINVAL;
return 0;
}
char _license[] SEC("license") = "GPL";
......@@ -101,7 +101,7 @@ static inline __u8 *get_u8(__u8 *data, unsigned int offset)
return (__u8 *)get_bits(data, offset);
}
SEC("fmod_ret/hid_bpf_device_event")
SEC(HID_BPF_DEVICE_EVENT)
int BPF_PROG(artpen_pressure_interpolate, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, PEN_REPORT_LEN /* size */);
......@@ -139,6 +139,10 @@ int BPF_PROG(artpen_pressure_interpolate, struct hid_bpf_ctx *hctx)
return 0;
}
HID_BPF_OPS(wacom_artpen) = {
.hid_device_event = (void *)artpen_pressure_interpolate,
};
SEC("syscall")
int probe(struct hid_bpf_probe_args *ctx)
{
......
......@@ -78,8 +78,6 @@ static const __u8 fixed_rdesc[] = {
0xc0, // End Collection 106
};
#define BIT(n) (1UL << n)
#define TIP_SWITCH BIT(0)
#define BARREL_SWITCH BIT(1)
#define ERASER BIT(2)
......@@ -91,7 +89,7 @@ static const __u8 fixed_rdesc[] = {
#define U16(index) (data[index] | (data[index + 1] << 8))
SEC("fmod_ret/hid_bpf_rdesc_fixup")
SEC(HID_BPF_RDESC_FIXUP)
int BPF_PROG(hid_fix_rdesc_xppen_artist24, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);
......@@ -152,13 +150,12 @@ static __u8 prev_state = 0;
* E: TipSwitch InRange
*
*/
SEC("fmod_ret/hid_bpf_device_event")
SEC(HID_BPF_DEVICE_EVENT)
int BPF_PROG(xppen_24_fix_eraser, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);
__u8 current_state, changed_state;
bool prev_tip;
__u16 tilt;
if (!data)
return 0; /* EPERM check */
......@@ -209,6 +206,11 @@ int BPF_PROG(xppen_24_fix_eraser, struct hid_bpf_ctx *hctx)
return 0;
}
HID_BPF_OPS(xppen_artist_24) = {
.hid_rdesc_fixup = (void *)hid_fix_rdesc_xppen_artist24,
.hid_device_event = (void *)xppen_24_fix_eraser,
};
SEC("syscall")
int probe(struct hid_bpf_probe_args *ctx)
{
......
......@@ -82,7 +82,7 @@ static const __u8 fixed_rdesc[] = {
0xc0, // End Collection 112
};
SEC("fmod_ret/hid_bpf_rdesc_fixup")
SEC(HID_BPF_RDESC_FIXUP)
int BPF_PROG(hid_fix_rdesc_xppen_artistpro16gen2, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);
......@@ -105,8 +105,7 @@ int BPF_PROG(hid_fix_rdesc_xppen_artistpro16gen2, struct hid_bpf_ctx *hctx)
return sizeof(fixed_rdesc);
}
SEC("fmod_ret/hid_bpf_device_event")
int BPF_PROG(xppen_16_fix_eraser, struct hid_bpf_ctx *hctx)
static int xppen_16_fix_eraser(struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);
......@@ -207,8 +206,7 @@ static void compensate_coordinates_by_tilt(__u8 *data, const __u8 idx,
data[idx+1] = coords >> 8;
}
SEC("fmod_ret/hid_bpf_device_event")
int BPF_PROG(xppen_16_fix_angle_offset, struct hid_bpf_ctx *hctx)
static int xppen_16_fix_angle_offset(struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);
......@@ -254,6 +252,22 @@ int BPF_PROG(xppen_16_fix_angle_offset, struct hid_bpf_ctx *hctx)
return 0;
}
SEC(HID_BPF_DEVICE_EVENT)
int BPF_PROG(xppen_artist_pro_16_device_event, struct hid_bpf_ctx *hctx)
{
int ret = xppen_16_fix_angle_offset(hctx);
if (ret)
return ret;
return xppen_16_fix_eraser(hctx);
}
HID_BPF_OPS(xppen_artist_pro_16) = {
.hid_rdesc_fixup = (void *)hid_fix_rdesc_xppen_artistpro16gen2,
.hid_device_event = (void *)xppen_artist_pro_16_device_event,
};
SEC("syscall")
int probe(struct hid_bpf_probe_args *ctx)
{
......
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2024 José Expósito
*/
#include "vmlinux.h"
#include "hid_bpf.h"
#include "hid_bpf_helpers.h"
#include <bpf/bpf_tracing.h>
#define VID_UGEE 0x28BD
#define PID_DECO_MINI_4 0x0929
#define RDESC_SIZE_PAD 177
#define RDESC_SIZE_PEN 109
#define PAD_REPORT_ID 0x06
/*
* XP-Pen devices return a descriptor with the values the driver should use when
* one of its interfaces is queried. For this device the descriptor is:
*
* 0E 03 60 4F 88 3B 06 00 FF 1F D8 13
* ----- ----- ----- -----
* | | | |
* | | | `- Resolution: 5080 (13d8)
* | | `- Maximum pressure: 8191 (1FFF)
* | `- Logical maximum Y: 15240 (3B88)
* `- Logical maximum X: 20320 (4F60)
*
* The physical maximum is calculated as (logical_max * 1000) / resolution.
*/
#define LOGICAL_MAX_X 0x60, 0x4F
#define LOGICAL_MAX_Y 0x88, 0x3B
#define PHYSICAL_MAX_X 0xA0, 0x0F
#define PHYSICAL_MAX_Y 0xB8, 0x0B
#define PRESSURE_MAX 0xFF, 0x1F
HID_BPF_CONFIG(
HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_DECO_MINI_4)
);
/*
* The tablet send these values when the pad buttons are pressed individually:
*
* Buttons released: 06 00 00 00 00 00 00 00
* Button 1: 06 00 05 00 00 00 00 00 -> b
* Button 2: 06 00 08 00 00 00 00 00 -> e
* Button 3: 06 04 00 00 00 00 00 00 -> LAlt
* Button 4: 06 00 2c 00 00 00 00 00 -> Space
* Button 5: 06 01 16 00 00 00 00 00 -> LControl + s
* Button 6: 06 01 1d 00 00 00 00 00 -> LControl + z
*
* When multiple buttons are pressed at the same time, the values used to
* identify the buttons are identical, but they appear in different bytes of the
* record. For example, when button 2 (0x08) and button 1 (0x05) are pressed,
* this is the report:
*
* Buttons 2 and 1: 06 00 08 05 00 00 00 00 -> e + b
*
* Buttons 1, 2, 4, 5 and 6 can be matched by finding their values in the
* report.
*
* Button 3 is pressed when the 3rd bit is 1. For example, pressing buttons 3
* and 5 generates this report:
*
* Buttons 3 and 5: 06 05 16 00 00 00 00 00 -> LControl + LAlt + s
* -- --
* | |
* | `- Button 5 (0x16)
* `- 0x05 = 0101. Button 3 is pressed
* ^
*
* pad_buttons contains a list of buttons that can be matched in
* HID_BPF_DEVICE_EVENT. Button 3 as it has a dedicated bit.
*/
static const __u8 pad_buttons[] = { 0x05, 0x08, 0x00, 0x2C, 0x16, 0x1D };
static const __u8 fixed_pad_rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x07, /* Usage (Keypad), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x06, /* Report ID (6), */
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x39, /* Usage (Tablet Function Keys), */
0xA0, /* Collection (Physical), */
0x05, 0x09, /* Usage Page (Button), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x06, /* Report Count (6), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x06, /* Usage Maximum (06h), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x32, /* Report Count (50), */
0x81, 0x01, /* Input (Constant), */
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
static const __u8 fixed_pen_rdesc[] = {
0x05, 0x0d, /* Usage Page (Digitizers), */
0x09, 0x01, /* Usage (Digitizer), */
0xa1, 0x01, /* Collection (Application), */
0x85, 0x07, /* Report ID (7), */
0x09, 0x20, /* Usage (Stylus), */
0xa1, 0x00, /* Collection (Physical), */
0x09, 0x42, /* Usage (Tip Switch), */
0x09, 0x44, /* Usage (Barrel Switch), */
0x09, 0x46, /* Usage (Tablet Pick), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x03, /* Report Count (3), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x02, /* Report Count (2), */
0x81, 0x03, /* Input (Constant, Variable), */
0x09, 0x32, /* Usage (In Range), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x02, /* Report Count (2), */
0x81, 0x03, /* Input (Constant, Variable), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x01, /* Report Count (1), */
0x35, 0x00, /* Physical Minimum (0), */
0xa4, /* Push, */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x30, /* Usage (X), */
0x65, 0x13, /* Unit (Inch), */
0x55, 0x0d, /* Unit Exponent (-3), */
0x26, LOGICAL_MAX_X, /* Logical Maximum, */
0x46, PHYSICAL_MAX_X, /* Physical Maximum, */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x26, LOGICAL_MAX_Y, /* Logical Maximum, */
0x46, PHYSICAL_MAX_Y, /* Physical Maximum, */
0x81, 0x02, /* Input (Variable), */
0xb4, /* Pop, */
0x09, 0x30, /* Usage (Tip Pressure), */
0x45, 0x00, /* Physical Maximum (0), */
0x26, PRESSURE_MAX, /* Logical Maximum, */
0x75, 0x0D, /* Report Size (13), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x02, /* Input (Variable), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x13, /* Report Count (19), */
0x81, 0x01, /* Input (Constant), */
0xc0, /* End Collection, */
0xc0, /* End Collection */
};
static const size_t fixed_pad_rdesc_size = sizeof(fixed_pad_rdesc);
static const size_t fixed_pen_rdesc_size = sizeof(fixed_pen_rdesc);
SEC(HID_BPF_RDESC_FIXUP)
int BPF_PROG(hid_rdesc_fixup_xppen_deco_mini_4, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0, HID_MAX_DESCRIPTOR_SIZE);
if (!data)
return 0; /* EPERM check */
if (hctx->size == RDESC_SIZE_PAD) {
__builtin_memcpy(data, fixed_pad_rdesc, fixed_pad_rdesc_size);
return fixed_pad_rdesc_size;
} else if (hctx->size == RDESC_SIZE_PEN) {
__builtin_memcpy(data, fixed_pen_rdesc, fixed_pen_rdesc_size);
return fixed_pen_rdesc_size;
}
return 0;
}
SEC(HID_BPF_DEVICE_EVENT)
int BPF_PROG(hid_device_event_xppen_deco_mini_4, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 8 /* size */);
__u8 button_mask = 0;
int d, b;
if (!data)
return 0; /* EPERM check */
if (data[0] != PAD_REPORT_ID)
return 0;
/* data[1] stores the status of BTN_2 in the 3rd bit*/
if (data[1] & BIT(2))
button_mask |= BIT(2);
/* The rest of the descriptor stores the buttons as in pad_buttons */
for (d = 2; d < 8; d++) {
for (b = 0; b < sizeof(pad_buttons); b++) {
if (data[d] != 0 && data[d] == pad_buttons[b])
button_mask |= BIT(b);
}
}
__u8 report[8] = {PAD_REPORT_ID, button_mask, 0x00};
__builtin_memcpy(data, report, sizeof(report));
return 0;
}
HID_BPF_OPS(deco_mini_4) = {
.hid_device_event = (void *)hid_device_event_xppen_deco_mini_4,
.hid_rdesc_fixup = (void *)hid_rdesc_fixup_xppen_deco_mini_4,
};
SEC("syscall")
int probe(struct hid_bpf_probe_args *ctx)
{
/*
* The device has 2 modes: The compatibility mode, enabled by default,
* and the raw mode, that can be activated by sending a buffer of magic
* data to a certain USB endpoint.
*
* Depending on the mode, different interfaces of the device are used:
* - First interface: Pad in compatibility mode
* - Second interface: Pen in compatibility mode
* - Third interface: Only used in raw mode
*
* We'll use the device in compatibility mode.
*/
ctx->retval = ctx->rdesc_size != RDESC_SIZE_PAD &&
ctx->rdesc_size != RDESC_SIZE_PEN;
if (ctx->retval)
ctx->retval = -EINVAL;
return 0;
}
char _license[] SEC("license") = "GPL";
......@@ -5,6 +5,12 @@
#ifndef ____HID_BPF__H
#define ____HID_BPF__H
#define HID_BPF_DEVICE_EVENT "struct_ops/hid_device_event"
#define HID_BPF_RDESC_FIXUP "struct_ops/hid_rdesc_fixup"
#define HID_BPF_OPS(name) SEC(".struct_ops.link") \
struct hid_bpf_ops name
#define hid_set_name(_hdev, _name) __builtin_memcpy(_hdev->name, _name, sizeof(_name))
struct hid_bpf_probe_args {
unsigned int hid;
unsigned int rdesc_size;
......
......@@ -66,6 +66,7 @@ extern int hid_bpf_hw_request(struct hid_bpf_ctx *ctx,
#define HID_VID_ANY 0x0000
#define HID_PID_ANY 0x0000
#define BIT(n) (1UL << (n))
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/* Helper macro to convert (foo, __LINE__) into foo134 so we can use __LINE__ for
......
This diff is collapsed.
......@@ -2025,19 +2025,10 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
}
EXPORT_SYMBOL_GPL(hid_report_raw_event);
/**
* hid_input_report - report data from lower layer (usb, bt...)
*
* @hid: hid device
* @type: HID report type (HID_*_REPORT)
* @data: report contents
* @size: size of data parameter
* @interrupt: distinguish between interrupt and control transfers
*
* This is data entry for lower layers.
*/
int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
int interrupt)
static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
u8 *data, u32 size, int interrupt, u64 source, bool from_bpf,
bool lock_already_taken)
{
struct hid_report_enum *report_enum;
struct hid_driver *hdrv;
......@@ -2047,8 +2038,13 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data
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;
......@@ -2057,7 +2053,7 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data
report_enum = hid->report_enum + type;
hdrv = hid->driver;
data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt);
data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source, from_bpf);
if (IS_ERR(data)) {
ret = PTR_ERR(data);
goto unlock;
......@@ -2089,9 +2085,29 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data
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;
}
/**
* hid_input_report - report data from lower layer (usb, bt...)
*
* @hid: hid device
* @type: HID report type (HID_*_REPORT)
* @data: report contents
* @size: size of data parameter
* @interrupt: distinguish between interrupt and control transfers
*
* This is data entry for lower layers.
*/
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,
false, /* from_bpf */
false /* lock_already_taken */);
}
EXPORT_SYMBOL_GPL(hid_input_report);
bool hid_match_one_id(const struct hid_device *hdev,
......@@ -2392,6 +2408,30 @@ void hid_hw_request(struct hid_device *hdev,
}
EXPORT_SYMBOL_GPL(hid_hw_request);
int __hid_hw_raw_request(struct hid_device *hdev,
unsigned char reportnum, __u8 *buf,
size_t len, enum hid_report_type rtype,
enum hid_class_request reqtype,
u64 source, bool from_bpf)
{
unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE;
int ret;
if (hdev->ll_driver->max_buffer_size)
max_buffer_size = hdev->ll_driver->max_buffer_size;
if (len < 1 || len > max_buffer_size || !buf)
return -EINVAL;
ret = dispatch_hid_bpf_raw_requests(hdev, reportnum, buf, len, rtype,
reqtype, source, from_bpf);
if (ret)
return ret;
return hdev->ll_driver->raw_request(hdev, reportnum, buf, len,
rtype, reqtype);
}
/**
* hid_hw_raw_request - send report request to device
*
......@@ -2409,8 +2449,16 @@ EXPORT_SYMBOL_GPL(hid_hw_request);
int hid_hw_raw_request(struct hid_device *hdev,
unsigned char reportnum, __u8 *buf,
size_t len, enum hid_report_type rtype, enum hid_class_request reqtype)
{
return __hid_hw_raw_request(hdev, reportnum, buf, len, rtype, reqtype, 0, false);
}
EXPORT_SYMBOL_GPL(hid_hw_raw_request);
int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, u64 source,
bool from_bpf)
{
unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE;
int ret;
if (hdev->ll_driver->max_buffer_size)
max_buffer_size = hdev->ll_driver->max_buffer_size;
......@@ -2418,10 +2466,15 @@ int hid_hw_raw_request(struct hid_device *hdev,
if (len < 1 || len > max_buffer_size || !buf)
return -EINVAL;
return hdev->ll_driver->raw_request(hdev, reportnum, buf, len,
rtype, reqtype);
ret = dispatch_hid_bpf_output_report(hdev, buf, len, source, from_bpf);
if (ret)
return ret;
if (hdev->ll_driver->output_report)
return hdev->ll_driver->output_report(hdev, buf, len);
return -ENOSYS;
}
EXPORT_SYMBOL_GPL(hid_hw_raw_request);
/**
* hid_hw_output_report - send output report to device
......@@ -2434,18 +2487,7 @@ EXPORT_SYMBOL_GPL(hid_hw_raw_request);
*/
int hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len)
{
unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE;
if (hdev->ll_driver->max_buffer_size)
max_buffer_size = hdev->ll_driver->max_buffer_size;
if (len < 1 || len > max_buffer_size || !buf)
return -EINVAL;
if (hdev->ll_driver->output_report)
return hdev->ll_driver->output_report(hdev, buf, len);
return -ENOSYS;
return __hid_hw_output_report(hdev, buf, len, 0, false);
}
EXPORT_SYMBOL_GPL(hid_hw_output_report);
......@@ -2854,9 +2896,15 @@ struct hid_device *hid_allocate_device(void)
mutex_init(&hdev->ll_open_lock);
kref_init(&hdev->ref);
hid_bpf_device_init(hdev);
ret = hid_bpf_device_init(hdev);
if (ret)
goto out_err;
return hdev;
out_err:
hid_destroy_device(hdev);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(hid_allocate_device);
......@@ -2970,11 +3018,11 @@ int hid_check_keys_pressed(struct hid_device *hid)
EXPORT_SYMBOL_GPL(hid_check_keys_pressed);
#ifdef CONFIG_HID_BPF
static struct hid_bpf_ops hid_ops = {
static struct hid_ops __hid_ops = {
.hid_get_report = hid_get_report,
.hid_hw_raw_request = hid_hw_raw_request,
.hid_hw_output_report = hid_hw_output_report,
.hid_input_report = hid_input_report,
.hid_hw_raw_request = __hid_hw_raw_request,
.hid_hw_output_report = __hid_hw_output_report,
.hid_input_report = __hid_input_report,
.owner = THIS_MODULE,
.bus_type = &hid_bus_type,
};
......@@ -2991,7 +3039,7 @@ static int __init hid_init(void)
}
#ifdef CONFIG_HID_BPF
hid_bpf_ops = &hid_ops;
hid_ops = &__hid_ops;
#endif
ret = hidraw_init();
......@@ -3010,7 +3058,7 @@ static int __init hid_init(void)
static void __exit hid_exit(void)
{
#ifdef CONFIG_HID_BPF
hid_bpf_ops = NULL;
hid_ops = NULL;
#endif
hid_debug_exit();
hidraw_exit();
......
......@@ -140,7 +140,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
if ((report_type == HID_OUTPUT_REPORT) &&
!(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) {
ret = hid_hw_output_report(dev, buf, count);
ret = __hid_hw_output_report(dev, buf, count, (u64)(long)file, false);
/*
* compatibility with old implementation of USB-HID and I2C-HID:
* if the device does not support receiving output reports,
......@@ -150,8 +150,8 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
goto out_free;
}
ret = hid_hw_raw_request(dev, buf[0], buf, count, report_type,
HID_REQ_SET_REPORT);
ret = __hid_hw_raw_request(dev, buf[0], buf, count, report_type,
HID_REQ_SET_REPORT, (u64)(long)file, false);
out_free:
kfree(buf);
......@@ -227,8 +227,8 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t
goto out_free;
}
ret = hid_hw_raw_request(dev, report_number, buf, count, report_type,
HID_REQ_GET_REPORT);
ret = __hid_hw_raw_request(dev, report_number, buf, count, report_type,
HID_REQ_GET_REPORT, (u64)(long)file, false);
if (ret < 0)
goto out_free;
......
......@@ -1125,6 +1125,13 @@ int __must_check hid_hw_open(struct hid_device *hdev);
void hid_hw_close(struct hid_device *hdev);
void hid_hw_request(struct hid_device *hdev,
struct hid_report *report, enum hid_class_request reqtype);
int __hid_hw_raw_request(struct hid_device *hdev,
unsigned char reportnum, __u8 *buf,
size_t len, enum hid_report_type rtype,
enum hid_class_request reqtype,
__u64 source, bool from_bpf);
int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source,
bool from_bpf);
int hid_hw_raw_request(struct hid_device *hdev,
unsigned char reportnum, __u8 *buf,
size_t len, enum hid_report_type rtype,
......
This diff is collapsed.
......@@ -16,7 +16,6 @@ LIBBPF_DESTDIR = $(LIBBPF_OUTPUT)
LIBBPF_INCLUDE = $(LIBBPF_DESTDIR)/include
LIBBPF = $(LIBBPF_OUTPUT)/libbpf.a
EXTRA_HEADERS := hid_bpf_attach.h
EXTRA_BPF_HEADERS := hid_bpf_helpers.h
hid_mouse-objs := hid_mouse.o
......@@ -207,8 +206,8 @@ $(obj)/%.bpf.o: $(src)/%.bpf.c $(EXTRA_BPF_HEADERS_SRC) $(obj)/vmlinux.h
LINKED_SKELS := hid_mouse.skel.h hid_surface_dial.skel.h
clean-files += $(LINKED_SKELS)
hid_mouse.skel.h-deps := hid_mouse.bpf.o hid_bpf_attach.bpf.o
hid_surface_dial.skel.h-deps := hid_surface_dial.bpf.o hid_bpf_attach.bpf.o
hid_mouse.skel.h-deps := hid_mouse.bpf.o
hid_surface_dial.skel.h-deps := hid_surface_dial.bpf.o
LINKED_BPF_SRCS := $(patsubst %.bpf.o,%.bpf.c,$(foreach skel,$(LINKED_SKELS),$($(skel)-deps)))
......
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2022 Benjamin Tissoires
*/
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "hid_bpf_attach.h"
#include "hid_bpf_helpers.h"
SEC("syscall")
int attach_prog(struct attach_prog_args *ctx)
{
ctx->retval = hid_bpf_attach_prog(ctx->hid,
ctx->prog_fd,
0);
return 0;
}
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2022 Benjamin Tissoires
*/
#ifndef __HID_BPF_ATTACH_H
#define __HID_BPF_ATTACH_H
struct attach_prog_args {
int prog_fd;
unsigned int hid;
int retval;
};
#endif /* __HID_BPF_ATTACH_H */
......@@ -5,8 +5,7 @@
#include <bpf/bpf_tracing.h>
#include "hid_bpf_helpers.h"
SEC("fmod_ret/hid_bpf_device_event")
int BPF_PROG(hid_y_event, struct hid_bpf_ctx *hctx)
static int hid_y_event(struct hid_bpf_ctx *hctx)
{
s16 y;
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 9 /* size */);
......@@ -51,8 +50,7 @@ int BPF_PROG(hid_y_event, struct hid_bpf_ctx *hctx)
return 0;
}
SEC("fmod_ret/hid_bpf_device_event")
int BPF_PROG(hid_x_event, struct hid_bpf_ctx *hctx)
static int hid_x_event(struct hid_bpf_ctx *hctx)
{
s16 x;
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 9 /* size */);
......@@ -69,7 +67,19 @@ int BPF_PROG(hid_x_event, struct hid_bpf_ctx *hctx)
return 0;
}
SEC("fmod_ret/hid_bpf_rdesc_fixup")
SEC("struct_ops/hid_device_event")
int BPF_PROG(hid_event, struct hid_bpf_ctx *hctx, enum hid_report_type type)
{
int ret = hid_y_event(hctx);
if (ret)
return ret;
return hid_x_event(hctx);
}
SEC("struct_ops/hid_rdesc_fixup")
int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);
......@@ -109,4 +119,10 @@ int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hctx)
return 0;
}
SEC(".struct_ops.link")
struct hid_bpf_ops mouse_invert = {
.hid_rdesc_fixup = (void *)hid_rdesc_fixup,
.hid_device_event = (void *)hid_event,
};
char _license[] SEC("license") = "GPL";
......@@ -29,7 +29,6 @@
#include <bpf/libbpf.h>
#include "hid_mouse.skel.h"
#include "hid_bpf_attach.h"
static bool running = true;
......@@ -76,18 +75,11 @@ static int get_hid_id(const char *path)
int main(int argc, char **argv)
{
struct hid_mouse *skel;
struct bpf_program *prog;
struct bpf_link *link;
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),
);
int opt, hid_id;
while ((opt = getopt(argc, argv, optstr)) != -1) {
switch (opt) {
......@@ -108,7 +100,7 @@ int main(int argc, char **argv)
return 1;
}
skel = hid_mouse__open_and_load();
skel = hid_mouse__open();
if (!skel) {
fprintf(stderr, "%s %s:%d", __func__, __FILE__, __LINE__);
return -1;
......@@ -120,27 +112,18 @@ int main(int argc, char **argv)
fprintf(stderr, "can not open HID device: %m\n");
return 1;
}
args.hid = hid_id;
skel->struct_ops.mouse_invert->hid_id = hid_id;
attach_fd = bpf_program__fd(skel->progs.attach_prog);
if (attach_fd < 0) {
fprintf(stderr, "can't locate attach prog: %m\n");
err = hid_mouse__load(skel);
if (err < 0) {
fprintf(stderr, "can not load HID-BPF program: %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;
}
link = bpf_map__attach_struct_ops(skel->maps.mouse_invert);
if (!link) {
fprintf(stderr, "can not attach HID-BPF program: %m\n");
return 1;
}
signal(SIGINT, int_exit);
......
......@@ -10,7 +10,7 @@
#define HID_UP_BUTTON 0x0009
#define HID_GD_WHEEL 0x0038
SEC("fmod_ret/hid_bpf_device_event")
SEC("struct_ops/hid_device_event")
int BPF_PROG(hid_event, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 9 /* size */);
......@@ -101,7 +101,7 @@ int set_haptic(struct haptic_syscall_args *args)
}
/* Convert REL_DIAL into REL_WHEEL */
SEC("fmod_ret/hid_bpf_rdesc_fixup")
SEC("struct_ops/hid_rdesc_fixup")
int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);
......@@ -130,5 +130,11 @@ int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hctx)
return 0;
}
SEC(".struct_ops.link")
struct hid_bpf_ops surface_dial = {
.hid_rdesc_fixup = (void *)hid_rdesc_fixup,
.hid_device_event = (void *)hid_event,
};
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = 1;
......@@ -31,7 +31,6 @@
#include <bpf/libbpf.h>
#include "hid_surface_dial.skel.h"
#include "hid_bpf_attach.h"
static bool running = true;
......@@ -86,34 +85,6 @@ static int get_hid_id(const char *path)
return (int)strtol(str_id, NULL, 16);
}
static int attach_prog(struct hid_surface_dial *skel, struct bpf_program *prog, int hid_id)
{
struct attach_prog_args args = {
.hid = hid_id,
.retval = -1,
};
int attach_fd, err;
DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattr,
.ctx_in = &args,
.ctx_size_in = sizeof(args),
);
attach_fd = bpf_program__fd(skel->progs.attach_prog);
if (attach_fd < 0) {
fprintf(stderr, "can't locate attach prog: %m\n");
return 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;
}
return 0;
}
static int set_haptic(struct hid_surface_dial *skel, int hid_id)
{
struct haptic_syscall_args args = {
......@@ -144,10 +115,10 @@ static int set_haptic(struct hid_surface_dial *skel, int hid_id)
int main(int argc, char **argv)
{
struct hid_surface_dial *skel;
struct bpf_program *prog;
const char *optstr = "r:";
struct bpf_link *link;
const char *sysfs_path;
int opt, hid_id, resolution = 72;
int err, opt, hid_id, resolution = 72;
while ((opt = getopt(argc, argv, optstr)) != -1) {
switch (opt) {
......@@ -189,7 +160,7 @@ int main(int argc, char **argv)
return 1;
}
skel = hid_surface_dial__open_and_load();
skel = hid_surface_dial__open();
if (!skel) {
fprintf(stderr, "%s %s:%d", __func__, __FILE__, __LINE__);
return -1;
......@@ -201,15 +172,21 @@ int main(int argc, char **argv)
return 1;
}
skel->struct_ops.surface_dial->hid_id = hid_id;
err = hid_surface_dial__load(skel);
if (err < 0) {
fprintf(stderr, "can not load HID-BPF program: %m\n");
return 1;
}
skel->data->resolution = resolution;
skel->data->physical = (int)(resolution / 72);
bpf_object__for_each_program(prog, *skel->skeleton->obj) {
/* ignore syscalls */
if (bpf_program__get_type(prog) != BPF_PROG_TYPE_TRACING)
continue;
attach_prog(skel, prog, hid_id);
link = bpf_map__attach_struct_ops(skel->maps.surface_dial);
if (!link) {
fprintf(stderr, "can not attach HID-BPF program: %m\n");
return 1;
}
signal(SIGINT, int_exit);
......
This diff is collapsed.
This diff is collapsed.
......@@ -7,6 +7,7 @@
/* "undefine" structs and enums in vmlinux.h, because we "override" them below */
#define hid_bpf_ctx hid_bpf_ctx___not_used
#define hid_bpf_ops hid_bpf_ops___not_used
#define hid_report_type hid_report_type___not_used
#define hid_class_request hid_class_request___not_used
#define hid_bpf_attach_flags hid_bpf_attach_flags___not_used
......@@ -20,13 +21,11 @@
#define HID_REQ_SET_REPORT HID_REQ_SET_REPORT___not_used
#define HID_REQ_SET_IDLE HID_REQ_SET_IDLE___not_used
#define HID_REQ_SET_PROTOCOL HID_REQ_SET_PROTOCOL___not_used
#define HID_BPF_FLAG_NONE HID_BPF_FLAG_NONE___not_used
#define HID_BPF_FLAG_INSERT_HEAD HID_BPF_FLAG_INSERT_HEAD___not_used
#define HID_BPF_FLAG_MAX HID_BPF_FLAG_MAX___not_used
#include "vmlinux.h"
#undef hid_bpf_ctx
#undef hid_bpf_ops
#undef hid_report_type
#undef hid_class_request
#undef hid_bpf_attach_flags
......@@ -40,9 +39,6 @@
#undef HID_REQ_SET_REPORT
#undef HID_REQ_SET_IDLE
#undef HID_REQ_SET_PROTOCOL
#undef HID_BPF_FLAG_NONE
#undef HID_BPF_FLAG_INSERT_HEAD
#undef HID_BPF_FLAG_MAX
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
......@@ -57,10 +53,8 @@ enum hid_report_type {
};
struct hid_bpf_ctx {
__u32 index;
const struct hid_device *hid;
struct hid_device *hid;
__u32 allocated_size;
enum hid_report_type report_type;
union {
__s32 retval;
__s32 size;
......@@ -76,17 +70,28 @@ enum hid_class_request {
HID_REQ_SET_PROTOCOL = 0x0B,
};
enum hid_bpf_attach_flags {
HID_BPF_FLAG_NONE = 0,
HID_BPF_FLAG_INSERT_HEAD = _BITUL(0),
HID_BPF_FLAG_MAX,
struct hid_bpf_ops {
int hid_id;
u32 flags;
struct list_head list;
int (*hid_device_event)(struct hid_bpf_ctx *ctx, enum hid_report_type report_type,
u64 source);
int (*hid_rdesc_fixup)(struct hid_bpf_ctx *ctx);
int (*hid_hw_request)(struct hid_bpf_ctx *ctx, unsigned char reportnum,
enum hid_report_type rtype, enum hid_class_request reqtype,
u64 source);
int (*hid_hw_output_report)(struct hid_bpf_ctx *ctx, u64 source);
struct hid_device *hdev;
};
#ifndef BPF_F_BEFORE
#define BPF_F_BEFORE (1U << 3)
#endif
/* following are kfuncs exported by HID for HID-BPF */
extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx,
unsigned int offset,
const size_t __sz) __ksym;
extern int hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, u32 flags) __ksym;
extern struct hid_bpf_ctx *hid_bpf_allocate_context(unsigned int hid_id) __ksym;
extern void hid_bpf_release_context(struct hid_bpf_ctx *ctx) __ksym;
extern int hid_bpf_hw_request(struct hid_bpf_ctx *ctx,
......@@ -100,5 +105,18 @@ extern int hid_bpf_input_report(struct hid_bpf_ctx *ctx,
enum hid_report_type type,
__u8 *data,
size_t buf__sz) __ksym;
extern int hid_bpf_try_input_report(struct hid_bpf_ctx *ctx,
enum hid_report_type type,
__u8 *data,
size_t buf__sz) __ksym;
/* bpf_wq implementation */
extern int bpf_wq_init(struct bpf_wq *wq, void *p__map, unsigned int flags) __weak __ksym;
extern int bpf_wq_start(struct bpf_wq *wq, unsigned int flags) __weak __ksym;
extern int bpf_wq_set_callback_impl(struct bpf_wq *wq,
int (callback_fn)(void *map, int *key, struct bpf_wq *wq),
unsigned int flags__k, void *aux__ign) __ksym;
#define bpf_wq_set_callback(timer, cb, flags) \
bpf_wq_set_callback_impl(timer, cb, flags, NULL)
#endif /* __HID_BPF_HELPERS_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment