Commit 6e504d2c authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-linus-2024071601' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid

Pull HID updates from Benjamin Tissoires:

 - rewrite of the HID-BPF internal implementation to use bpf struct_ops
   instead of a tracing endpoint (Benjamin Tissoires)

 - add two new HID-BPF hooks to be able to intercept userspace calls
   targeting a HID device and filtering them (Benjamin Tissoires)

 - add support for various new devices through HID-BPF filters (Benjamin
   Tissoires)

 - add support for the magic keyboard backlight (Orlando Chamberlain)

 - add the missing MODULE_DESCRIPTION() macros in HID drivers (Jeff
   Johnson)

 - use of kvzalloc in case memory gets too fragmented (Hailong Liu)

 - retrieve the device firmware node in the child HID device (Danny
   Kaehn)

 - some hid-uclogic improvements (José Expósito)

 - some more typos, trivial fixes, kernel doctext and unused functions
   cleanups

* tag 'for-linus-2024071601' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (60 commits)
  HID: hid-steam: Fix typo in goto label
  HID: mcp2221: Remove unnecessary semicolon
  HID: Fix spelling mistakes "Kensigton" -> "Kensington"
  HID: add more missing MODULE_DESCRIPTION() macros
  HID: samples: fix the 2 struct_ops definitions
  HID: fix for amples in for-6.11/bpf
  HID: apple: Add support for magic keyboard backlight on T2 Macs
  HID: bpf: Thrustmaster TCA Yoke Boeing joystick fix
  HID: bpf: Add Huion Dial 2 bpf fixup
  HID: bpf: Add support for the XP-PEN Deco Mini 4
  HID: bpf: move the BIT() macro to hid_bpf_helpers.h
  HID: bpf: add a driver for the Huion Inspiroy 2S (H641P)
  HID: bpf: Add a HID report composition helper macros
  HID: bpf: doc fixes for hid_hw_request() hooks
  HID: bpf: doc fixes for hid_hw_request() hooks
  HID: bpf: fix gcc warning and unify __u64 into u64
  selftests/hid: ensure CKI can compile our new tests on old kernels
  selftests/hid: add an infinite loop test for hid_bpf_try_input_report
  selftests/hid: add another test for injecting an event from an event hook
  HID: bpf: allow hid_device_event hooks to inject input reports on self
  ...
parents 221fd1e1 30b86641
This diff is collapsed.
......@@ -154,10 +154,8 @@ obj-$(CONFIG_HID_WINWING) += hid-winwing.o
obj-$(CONFIG_HID_SENSOR_HUB) += hid-sensor-hub.o
obj-$(CONFIG_HID_SENSOR_CUSTOM_SENSOR) += hid-sensor-custom.o
hid-uclogic-test-objs := hid-uclogic-rdesc.o \
hid-uclogic-params.o \
hid-uclogic-rdesc-test.o
obj-$(CONFIG_HID_KUNIT_TEST) += hid-uclogic-test.o
hid-uclogic-test-objs := hid-uclogic-rdesc-test.o
obj-$(CONFIG_HID_KUNIT_TEST) += hid-uclogic.o hid-uclogic-test.o
obj-$(CONFIG_USB_HID) += usbhid/
obj-$(CONFIG_USB_MOUSE) += usbhid/
......
......@@ -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 bpf_link *link)
{
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 bpf_link *link)
{
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.
......@@ -163,4 +163,5 @@ static struct hid_driver a4_driver = {
};
module_hid_driver(a4_driver);
MODULE_DESCRIPTION("HID driver for some a4tech \"special\" devices");
MODULE_LICENSE("GPL");
......@@ -8,6 +8,8 @@
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
* Copyright (c) 2019 Paul Pawlowski <paul@mrarm.io>
* Copyright (c) 2023 Orlando Chamberlain <orlandoch.dev@gmail.com>
* Copyright (c) 2024 Aditya Garg <gargaditya08@live.com>
*/
/*
......@@ -23,6 +25,7 @@
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/leds.h>
#include <dt-bindings/leds/common.h>
#include "hid-ids.h"
......@@ -38,12 +41,17 @@
#define APPLE_RDESC_BATTERY BIT(9)
#define APPLE_BACKLIGHT_CTL BIT(10)
#define APPLE_IS_NON_APPLE BIT(11)
#define APPLE_MAGIC_BACKLIGHT BIT(12)
#define APPLE_FLAG_FKEY 0x01
#define HID_COUNTRY_INTERNATIONAL_ISO 13
#define APPLE_BATTERY_TIMEOUT_MS 60000
#define HID_USAGE_MAGIC_BL 0xff00000f
#define APPLE_MAGIC_REPORT_ID_POWER 3
#define APPLE_MAGIC_REPORT_ID_BRIGHTNESS 1
static unsigned int fnmode = 3;
module_param(fnmode, uint, 0644);
MODULE_PARM_DESC(fnmode, "Mode of fn key on Apple keyboards (0 = disabled, "
......@@ -81,6 +89,12 @@ struct apple_sc_backlight {
struct hid_device *hdev;
};
struct apple_magic_backlight {
struct led_classdev cdev;
struct hid_report *brightness;
struct hid_report *power;
};
struct apple_sc {
struct hid_device *hdev;
unsigned long quirks;
......@@ -822,6 +836,66 @@ static int apple_backlight_init(struct hid_device *hdev)
return ret;
}
static void apple_magic_backlight_report_set(struct hid_report *rep, s32 value, u8 rate)
{
rep->field[0]->value[0] = value;
rep->field[1]->value[0] = 0x5e; /* Mimic Windows */
rep->field[1]->value[0] |= rate << 8;
hid_hw_request(rep->device, rep, HID_REQ_SET_REPORT);
}
static void apple_magic_backlight_set(struct apple_magic_backlight *backlight,
int brightness, char rate)
{
apple_magic_backlight_report_set(backlight->power, brightness ? 1 : 0, rate);
if (brightness)
apple_magic_backlight_report_set(backlight->brightness, brightness, rate);
}
static int apple_magic_backlight_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct apple_magic_backlight *backlight = container_of(led_cdev,
struct apple_magic_backlight, cdev);
apple_magic_backlight_set(backlight, brightness, 1);
return 0;
}
static int apple_magic_backlight_init(struct hid_device *hdev)
{
struct apple_magic_backlight *backlight;
struct hid_report_enum *report_enum;
/*
* Ensure this usb endpoint is for the keyboard backlight, not touchbar
* backlight.
*/
if (hdev->collection[0].usage != HID_USAGE_MAGIC_BL)
return -ENODEV;
backlight = devm_kzalloc(&hdev->dev, sizeof(*backlight), GFP_KERNEL);
if (!backlight)
return -ENOMEM;
report_enum = &hdev->report_enum[HID_FEATURE_REPORT];
backlight->brightness = report_enum->report_id_hash[APPLE_MAGIC_REPORT_ID_BRIGHTNESS];
backlight->power = report_enum->report_id_hash[APPLE_MAGIC_REPORT_ID_POWER];
if (!backlight->brightness || !backlight->power)
return -ENODEV;
backlight->cdev.name = ":white:" LED_FUNCTION_KBD_BACKLIGHT;
backlight->cdev.max_brightness = backlight->brightness->field[0]->logical_maximum;
backlight->cdev.brightness_set_blocking = apple_magic_backlight_led_set;
apple_magic_backlight_set(backlight, 0, 0);
return devm_led_classdev_register(&hdev->dev, &backlight->cdev);
}
static int apple_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
......@@ -860,7 +934,18 @@ static int apple_probe(struct hid_device *hdev,
if (quirks & APPLE_BACKLIGHT_CTL)
apple_backlight_init(hdev);
if (quirks & APPLE_MAGIC_BACKLIGHT) {
ret = apple_magic_backlight_init(hdev);
if (ret)
goto out_err;
}
return 0;
out_err:
del_timer_sync(&asc->battery_timer);
hid_hw_stop(hdev);
return ret;
}
static void apple_remove(struct hid_device *hdev)
......@@ -1073,6 +1158,8 @@ static const struct hid_device_id apple_devices[] = {
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY },
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021),
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT),
.driver_data = APPLE_MAGIC_BACKLIGHT },
{ }
};
......@@ -1091,4 +1178,5 @@ static struct hid_driver apple_driver = {
};
module_hid_driver(apple_driver);
MODULE_DESCRIPTION("Apple USB HID quirks support for Linux");
MODULE_LICENSE("GPL");
......@@ -41,4 +41,5 @@ static struct hid_driver aureal_driver = {
};
module_hid_driver(aureal_driver);
MODULE_DESCRIPTION("HID driver for Aureal Cy se W-01RN USB_V3.1 devices");
MODULE_LICENSE("GPL");
......@@ -85,4 +85,5 @@ static struct hid_driver belkin_driver = {
};
module_hid_driver(belkin_driver);
MODULE_DESCRIPTION("HID driver for some belkin \"special\" devices");
MODULE_LICENSE("GPL");
......@@ -162,4 +162,5 @@ static struct hid_driver betop_driver = {
};
module_hid_driver(betop_driver);
MODULE_DESCRIPTION("Force feedback support for Betop based devices");
MODULE_LICENSE("GPL");
......@@ -490,4 +490,5 @@ static struct hid_driver bigben_driver = {
};
module_hid_driver(bigben_driver);
MODULE_DESCRIPTION("LED & force feedback support for BigBen Interactive");
MODULE_LICENSE("GPL");
......@@ -68,4 +68,5 @@ static struct hid_driver ch_driver = {
};
module_hid_driver(ch_driver);
MODULE_DESCRIPTION("HID driver for some cherry \"special\" devices");
MODULE_LICENSE("GPL");
......@@ -152,4 +152,5 @@ static struct hid_driver ch_driver = {
};
module_hid_driver(ch_driver);
MODULE_DESCRIPTION("HID driver for some chicony \"special\" devices");
MODULE_LICENSE("GPL");
......@@ -95,9 +95,9 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
return NULL;
}
field = kzalloc((sizeof(struct hid_field) +
usages * sizeof(struct hid_usage) +
3 * usages * sizeof(unsigned int)), GFP_KERNEL);
field = kvzalloc((sizeof(struct hid_field) +
usages * sizeof(struct hid_usage) +
3 * usages * sizeof(unsigned int)), GFP_KERNEL);
if (!field)
return NULL;
......@@ -661,7 +661,7 @@ static void hid_free_report(struct hid_report *report)
kfree(report->field_entries);
for (n = 0; n < report->maxfield; n++)
kfree(report->field[n]);
kvfree(report->field[n]);
kfree(report);
}
......@@ -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();
......@@ -3024,4 +3072,5 @@ module_exit(hid_exit);
MODULE_AUTHOR("Andreas Gal");
MODULE_AUTHOR("Vojtech Pavlik");
MODULE_AUTHOR("Jiri Kosina");
MODULE_DESCRIPTION("HID support for Linux");
MODULE_LICENSE("GPL");
......@@ -176,4 +176,5 @@ static struct hid_driver cp_driver = {
};
module_hid_driver(cp_driver);
MODULE_DESCRIPTION("HID driver for some cypress \"special\" devices");
MODULE_LICENSE("GPL");
......@@ -316,4 +316,5 @@ static struct hid_driver dr_driver = {
};
module_hid_driver(dr_driver);
MODULE_DESCRIPTION("Force feedback support for DragonRise Inc. game controllers");
MODULE_LICENSE("GPL");
......@@ -136,4 +136,5 @@ static struct hid_driver elecom_driver = {
};
module_hid_driver(elecom_driver);
MODULE_DESCRIPTION("HID driver for ELECOM devices");
MODULE_LICENSE("GPL");
......@@ -313,4 +313,5 @@ static void __exit elo_driver_exit(void)
module_exit(elo_driver_exit);
MODULE_AUTHOR("Jiri Slaby <jslaby@suse.cz>");
MODULE_DESCRIPTION("HID driver for ELO usb touchscreen 4000/4500");
MODULE_LICENSE("GPL");
......@@ -144,5 +144,6 @@ static struct hid_driver ems_driver = {
};
module_hid_driver(ems_driver);
MODULE_DESCRIPTION("Force feedback support for EMS Trio Linker Plus II");
MODULE_LICENSE("GPL");
......@@ -50,4 +50,5 @@ static struct hid_driver evision_driver = {
};
module_hid_driver(evision_driver);
MODULE_DESCRIPTION("HID driver for EVision devices");
MODULE_LICENSE("GPL");
......@@ -75,4 +75,5 @@ static struct hid_driver ez_driver = {
};
module_hid_driver(ez_driver);
MODULE_DESCRIPTION("HID driver for some ezkey \"special\" devices");
MODULE_LICENSE("GPL");
......@@ -169,4 +169,5 @@ static struct hid_driver ga_driver = {
};
module_hid_driver(ga_driver);
MODULE_DESCRIPTION("Force feedback support for GreenAsia (Product ID 0x12) based devices");
MODULE_LICENSE("GPL");
......@@ -641,4 +641,5 @@ static void __exit hammer_exit(void)
}
module_exit(hammer_exit);
MODULE_DESCRIPTION("HID driver for Google Hammer device.");
MODULE_LICENSE("GPL");
......@@ -155,4 +155,5 @@ static struct hid_driver stadia_driver = {
};
module_hid_driver(stadia_driver);
MODULE_DESCRIPTION("Google Stadia controller rumble support.");
MODULE_LICENSE("GPL");
......@@ -87,4 +87,5 @@ static struct hid_driver gyration_driver = {
};
module_hid_driver(gyration_driver);
MODULE_DESCRIPTION("HID driver for some gyration \"special\" devices");
MODULE_LICENSE("GPL");
......@@ -180,4 +180,5 @@ static struct hid_driver holtek_kbd_driver = {
};
module_hid_driver(holtek_kbd_driver);
MODULE_DESCRIPTION("HID driver for Holtek keyboard");
MODULE_LICENSE("GPL");
......@@ -110,4 +110,5 @@ static struct hid_driver holtek_mouse_driver = {
};
module_hid_driver(holtek_mouse_driver);
MODULE_DESCRIPTION("HID driver for Holtek gaming mice");
MODULE_LICENSE("GPL");
......@@ -141,4 +141,5 @@ static struct hid_driver ite_driver = {
module_hid_driver(ite_driver);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("HID driver for some ITE \"special\" devices");
MODULE_LICENSE("GPL");
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* HID driver for Kensigton Slimblade Trackball
* HID driver for Kensington Slimblade Trackball
*
* Copyright (c) 2009 Jiri Kosina
*/
......@@ -46,4 +46,5 @@ static struct hid_driver ks_driver = {
};
module_hid_driver(ks_driver);
MODULE_DESCRIPTION("HID driver for Kensington Slimblade Trackball");
MODULE_LICENSE("GPL");
......@@ -48,5 +48,6 @@ static struct hid_driver keytouch_driver = {
};
module_hid_driver(keytouch_driver);
MODULE_DESCRIPTION("HID driver for Keytouch devices not fully compliant with HID standard");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jiri Kosina");
......@@ -671,4 +671,5 @@ static struct hid_driver kye_driver = {
};
module_hid_driver(kye_driver);
MODULE_DESCRIPTION("HID driver for Kye/Genius devices not fully compliant with HID standard");
MODULE_LICENSE("GPL");
......@@ -53,4 +53,5 @@ static struct hid_driver ts_driver = {
};
module_hid_driver(ts_driver);
MODULE_DESCRIPTION("HID driver for LC Power Model RC1000MCE");
MODULE_LICENSE("GPL");
......@@ -1442,4 +1442,5 @@ static struct hid_driver lenovo_driver = {
};
module_hid_driver(lenovo_driver);
MODULE_DESCRIPTION("HID driver for IBM/Lenovo");
MODULE_LICENSE("GPL");
......@@ -319,4 +319,5 @@ static struct hid_driver letsketch_driver = {
module_hid_driver(letsketch_driver);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("Driver for the LetSketch / VSON WP9620N drawing tablet");
MODULE_LICENSE("GPL");
......@@ -954,4 +954,5 @@ static struct hid_driver lg_g15_driver = {
module_hid_driver(lg_g15_driver);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("HID driver for gaming keys on Logitech gaming keyboards");
MODULE_LICENSE("GPL");
......@@ -942,4 +942,5 @@ module_param_named(lg4ff_no_autoswitch, lg4ff_no_autoswitch, int, S_IRUGO);
MODULE_PARM_DESC(lg4ff_no_autoswitch, "Do not switch multimode wheels to their native mode automatically");
#endif
MODULE_DESCRIPTION("HID driver for some logitech \"special\" devices");
MODULE_LICENSE("GPL");
......@@ -2047,6 +2047,7 @@ static struct hid_driver logi_djreceiver_driver = {
module_hid_driver(logi_djreceiver_driver);
MODULE_DESCRIPTION("HID driver for Logitech receivers");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Logitech");
MODULE_AUTHOR("Nestor Lopez Casado");
......
......@@ -968,4 +968,5 @@ static struct hid_driver magicmouse_driver = {
};
module_hid_driver(magicmouse_driver);
MODULE_DESCRIPTION("Apple \"Magic\" Wireless Mouse driver");
MODULE_LICENSE("GPL");
......@@ -162,4 +162,5 @@ static struct hid_driver maltron_driver = {
};
module_hid_driver(maltron_driver);
MODULE_DESCRIPTION("HID driver for Maltron L90");
MODULE_LICENSE("GPL");
......@@ -1048,7 +1048,7 @@ static int mcp_iio_channels(struct mcp2221 *mcp)
break;
default:
continue;
};
}
chan->type = IIO_VOLTAGE;
chan->indexed = 1;
......
......@@ -122,4 +122,5 @@ static struct hid_driver mwctrl_driver = {
};
module_hid_driver(mwctrl_driver);
MODULE_DESCRIPTION("Vibration support for Mega World controllers");
MODULE_LICENSE("GPL");
......@@ -166,4 +166,5 @@ static struct hid_driver mf_driver = {
};
module_hid_driver(mf_driver);
MODULE_DESCRIPTION("Force feedback support for Mayflash game controller adapters.");
MODULE_LICENSE("GPL");
......@@ -475,4 +475,5 @@ static struct hid_driver ms_driver = {
};
module_hid_driver(ms_driver);
MODULE_DESCRIPTION("HID driver for some microsoft \"special\" devices");
MODULE_LICENSE("GPL");
......@@ -62,4 +62,5 @@ static struct hid_driver mr_driver = {
};
module_hid_driver(mr_driver);
MODULE_DESCRIPTION("HID driver for some monterey \"special\" devices");
MODULE_LICENSE("GPL");
......@@ -658,7 +658,6 @@ struct joycon_ctlr {
(ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCR || \
ctlr->ctlr_type == JOYCON_CTLR_TYPE_PRO)
/*
* Controller device helpers
*
......@@ -669,31 +668,11 @@ struct joycon_ctlr {
* These helpers are most useful early during the HID probe or in conjunction
* with the capability helpers below.
*/
static inline bool joycon_device_is_procon(struct joycon_ctlr *ctlr)
{
return ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_PROCON;
}
static inline bool joycon_device_is_chrggrip(struct joycon_ctlr *ctlr)
{
return ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_CHRGGRIP;
}
static inline bool joycon_device_is_snescon(struct joycon_ctlr *ctlr)
{
return ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_SNESCON;
}
static inline bool joycon_device_is_gencon(struct joycon_ctlr *ctlr)
{
return ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_GENCON;
}
static inline bool joycon_device_is_n64con(struct joycon_ctlr *ctlr)
{
return ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_N64CON;
}
/*
* Controller type helpers
*
......
......@@ -1029,4 +1029,5 @@ static struct hid_driver ntrig_driver = {
};
module_hid_driver(ntrig_driver);
MODULE_DESCRIPTION("HID driver for N-Trig touchscreens");
MODULE_LICENSE("GPL");
......@@ -51,4 +51,5 @@ static struct hid_driver ortek_driver = {
};
module_hid_driver(ortek_driver);
MODULE_DESCRIPTION("HID driver for Ortek PKB-1700/WKB-2000/Skycable wireless keyboard and mouse trackpad");
MODULE_LICENSE("GPL");
......@@ -102,4 +102,5 @@ static struct hid_driver pl_driver = {
};
module_hid_driver(pl_driver);
MODULE_DESCRIPTION("HID driver for some petalynx \"special\" devices");
MODULE_LICENSE("GPL");
......@@ -219,4 +219,5 @@ static struct hid_driver pl_driver = {
};
module_hid_driver(pl_driver);
MODULE_DESCRIPTION("Force feedback support for PantherLord/GreenAsia based devices");
MODULE_LICENSE("GPL");
......@@ -70,4 +70,5 @@ static struct hid_driver px_driver = {
module_hid_driver(px_driver);
MODULE_AUTHOR("Terry Lambert <tlambert@google.com>");
MODULE_DESCRIPTION("HID driver for primax and similar keyboards with in-band modifiers");
MODULE_LICENSE("GPL");
......@@ -862,4 +862,5 @@ static struct hid_driver pk_driver = {
};
module_hid_driver(pk_driver);
MODULE_DESCRIPTION("HID driver for the Prodikeys PC-MIDI Keyboard");
MODULE_LICENSE("GPL");
......@@ -122,4 +122,5 @@ static struct hid_driver razer_driver = {
module_hid_driver(razer_driver);
MODULE_AUTHOR("Jelle van der Waa <jvanderwaa@redhat.com>");
MODULE_DESCRIPTION("HID driver for gaming keys on Razer Blackwidow gaming keyboards");
MODULE_LICENSE("GPL");
......@@ -59,4 +59,5 @@ static struct hid_driver redragon_driver = {
module_hid_driver(redragon_driver);
MODULE_DESCRIPTION("HID driver for Redragon keyboards");
MODULE_LICENSE("GPL");
......@@ -94,4 +94,5 @@ static struct hid_driver retrode_driver = {
module_hid_driver(retrode_driver);
MODULE_DESCRIPTION("HID driver for Retrode 2 controller adapter and plug-in extensions");
MODULE_LICENSE("GPL");
......@@ -204,4 +204,5 @@ static struct hid_driver saitek_driver = {
};
module_hid_driver(saitek_driver);
MODULE_DESCRIPTION("HID driver for Saitek devices.");
MODULE_LICENSE("GPL");
......@@ -561,4 +561,5 @@ static struct hid_driver samsung_driver = {
};
module_hid_driver(samsung_driver);
MODULE_DESCRIPTION("HID driver for some samsung \"special\" devices");
MODULE_LICENSE("GPL");
......@@ -37,4 +37,5 @@ static struct hid_driver semitek_driver = {
};
module_hid_driver(semitek_driver);
MODULE_DESCRIPTION("HID driver for Semitek keyboards");
MODULE_LICENSE("GPL");
......@@ -168,6 +168,7 @@ static struct hid_driver sjoy_driver = {
};
module_hid_driver(sjoy_driver);
MODULE_DESCRIPTION("Force feedback support for SmartJoy PLUS PS2->USB adapter");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jussi Kivilinna");
......@@ -2308,4 +2308,5 @@ static void __exit sony_exit(void)
module_init(sony_init);
module_exit(sony_exit);
MODULE_DESCRIPTION("HID driver for Sony / PS2 / PS3 / PS4 BD devices");
MODULE_LICENSE("GPL");
......@@ -75,4 +75,5 @@ static struct hid_driver speedlink_driver = {
};
module_hid_driver(speedlink_driver);
MODULE_DESCRIPTION("HID driver for Speedlink Vicious and Divine Cezanne (USB mouse)");
MODULE_LICENSE("GPL");
......@@ -45,6 +45,7 @@
#include <linux/power_supply.h>
#include "hid-ids.h"
MODULE_DESCRIPTION("HID driver for Valve Steam Controller");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>");
......@@ -1267,7 +1268,7 @@ static int steam_probe(struct hid_device *hdev,
steam->client_hdev = steam_create_client_hid(hdev);
if (IS_ERR(steam->client_hdev)) {
ret = PTR_ERR(steam->client_hdev);
goto err_stream_unregister;
goto err_steam_unregister;
}
steam->client_hdev->driver_data = steam;
......@@ -1279,7 +1280,7 @@ static int steam_probe(struct hid_device *hdev,
err_destroy:
hid_destroy_device(steam->client_hdev);
err_stream_unregister:
err_steam_unregister:
if (steam->connected)
steam_unregister(steam);
err_hw_close:
......
......@@ -662,6 +662,7 @@ static struct hid_driver steelseries_driver = {
};
module_hid_driver(steelseries_driver);
MODULE_DESCRIPTION("HID driver for Steelseries devices");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
MODULE_AUTHOR("Simon Wood <simon@mungewell.org>");
......@@ -62,4 +62,5 @@ static struct hid_driver sp_driver = {
};
module_hid_driver(sp_driver);
MODULE_DESCRIPTION("HID driver for some sunplus \"special\" devices");
MODULE_LICENSE("GPL");
......@@ -73,5 +73,6 @@ static struct hid_driver tivo_driver = {
};
module_hid_driver(tivo_driver);
MODULE_DESCRIPTION("HID driver for TiVo Slide Bluetooth remote");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
......@@ -265,4 +265,5 @@ static struct hid_driver tm_driver = {
};
module_hid_driver(tm_driver);
MODULE_DESCRIPTION("Force feedback support for various HID compliant devices by ThrustMaster");
MODULE_LICENSE("GPL");
......@@ -78,4 +78,5 @@ static struct hid_driver ts_driver = {
};
module_hid_driver(ts_driver);
MODULE_DESCRIPTION("HID driver for TopSeed Cyberlink remote");
MODULE_LICENSE("GPL");
......@@ -131,4 +131,5 @@ static struct hid_driver twinhan_driver = {
};
module_hid_driver(twinhan_driver);
MODULE_DESCRIPTION("HID driver for TwinHan IR remote control");
MODULE_LICENSE("GPL");
......@@ -567,7 +567,9 @@ module_hid_driver(uclogic_driver);
MODULE_AUTHOR("Martin Rusko");
MODULE_AUTHOR("Nikolai Kondrashov");
MODULE_DESCRIPTION("HID driver for UC-Logic devices not fully compliant with HID standard");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("HID driver for UC-Logic devices not fully compliant with HID standard");
#ifdef CONFIG_HID_KUNIT_TEST
#include "hid-uclogic-core-test.c"
......
......@@ -9,6 +9,8 @@
#include <kunit/test.h>
#include "./hid-uclogic-rdesc.h"
MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING);
struct uclogic_template_case {
const char *name;
const __u8 *template;
......
......@@ -17,6 +17,7 @@
#include "hid-uclogic-rdesc.h"
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <kunit/visibility.h>
/* Fixed WP4030U report descriptor */
__u8 uclogic_rdesc_wp4030u_fixed_arr[] = {
......@@ -689,10 +690,10 @@ const size_t uclogic_rdesc_v2_pen_template_size =
0xA0, /* Collection (Physical), */ \
0x05, 0x09, /* Usage Page (Button), */ \
0x19, 0x01, /* Usage Minimum (01h), */ \
0x29, 0x03, /* Usage Maximum (03h), */ \
0x95, 0x03, /* Report Count (3), */ \
0x29, 0x0A, /* Usage Maximum (0Ah), */ \
0x95, 0x0A, /* Report Count (10), */ \
0x81, 0x02, /* Input (Variable), */ \
0x95, ((_size) * 8 - 45), \
0x95, ((_size) * 8 - 52), \
/* Report Count (padding), */ \
0x81, 0x01, /* Input (Constant), */ \
0xC0, /* End Collection, */ \
......@@ -789,7 +790,8 @@ const __u8 uclogic_rdesc_v2_frame_touch_strip_arr[] = {
0x95, 0x01, /* Report Count (1), */
0x81, 0x02, /* Input (Variable), */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x38, /* Usage (Wheel), */
0x09, 0x33, /* Usage (Rx), */
0x09, 0x34, /* Usage (Ry), */
0x95, 0x01, /* Report Count (1), */
0x15, 0x00, /* Logical Minimum (0), */
0x25, 0x07, /* Logical Maximum (7), */
......@@ -1242,3 +1244,4 @@ __u8 *uclogic_rdesc_template_apply(const __u8 *template_ptr,
return rdesc_ptr;
}
EXPORT_SYMBOL_IF_KUNIT(uclogic_rdesc_template_apply);
......@@ -102,4 +102,5 @@ static struct hid_driver viewsonic_driver = {
};
module_hid_driver(viewsonic_driver);
MODULE_DESCRIPTION("HID driver for ViewSonic devices not fully compliant with HID standard");
MODULE_LICENSE("GPL");
......@@ -138,4 +138,5 @@ const struct attribute_group *vivaldi_attribute_groups[] = {
};
EXPORT_SYMBOL_GPL(vivaldi_attribute_groups);
MODULE_DESCRIPTION("Helpers for ChromeOS HID Vivaldi keyboards");
MODULE_LICENSE("GPL");
......@@ -742,4 +742,5 @@ static struct hid_driver waltop_driver = {
};
module_hid_driver(waltop_driver);
MODULE_DESCRIPTION("HID driver for Waltop devices not fully compliant with HID standard");
MODULE_LICENSE("GPL");
......@@ -223,4 +223,5 @@ static struct hid_driver winwing_driver = {
};
module_hid_driver(winwing_driver);
MODULE_DESCRIPTION("HID driver for WinWing Orion 2 throttle");
MODULE_LICENSE("GPL");
......@@ -56,4 +56,5 @@ static struct hid_driver xinmo_driver = {
};
module_hid_driver(xinmo_driver);
MODULE_DESCRIPTION("HID driver for Xin-Mo devices");
MODULE_LICENSE("GPL");
......@@ -138,4 +138,5 @@ static struct hid_driver zp_driver = {
};
module_hid_driver(zp_driver);
MODULE_DESCRIPTION("Force feedback support for Zeroplus based devices");
MODULE_LICENSE("GPL");
......@@ -205,4 +205,5 @@ static struct hid_driver zc_driver = {
};
module_hid_driver(zc_driver);
MODULE_DESCRIPTION("HID driver for zydacron remote control");
MODULE_LICENSE("GPL");
......@@ -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;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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