| // SPDX-License-Identifier: GPL-2.0 |
| /* Copyright (c) 2022 Red hat */ |
| #include "hid_bpf_helpers.h" |
| |
| char _license[] SEC("license") = "GPL"; |
| |
| struct attach_prog_args { |
| int prog_fd; |
| unsigned int hid; |
| int retval; |
| int insert_head; |
| }; |
| |
| __u64 callback_check = 52; |
| __u64 callback2_check = 52; |
| |
| SEC("?fmod_ret/hid_bpf_device_event") |
| int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx) |
| { |
| __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */); |
| |
| if (!rw_data) |
| return 0; /* EPERM check */ |
| |
| callback_check = rw_data[1]; |
| |
| rw_data[2] = rw_data[1] + 5; |
| |
| return hid_ctx->size; |
| } |
| |
| SEC("?fmod_ret/hid_bpf_device_event") |
| int BPF_PROG(hid_second_event, struct hid_bpf_ctx *hid_ctx) |
| { |
| __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); |
| |
| if (!rw_data) |
| return 0; /* EPERM check */ |
| |
| rw_data[3] = rw_data[2] + 5; |
| |
| return hid_ctx->size; |
| } |
| |
| SEC("?fmod_ret/hid_bpf_device_event") |
| int BPF_PROG(hid_change_report_id, struct hid_bpf_ctx *hid_ctx) |
| { |
| __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */); |
| |
| if (!rw_data) |
| return 0; /* EPERM check */ |
| |
| rw_data[0] = 2; |
| |
| return 9; |
| } |
| |
| SEC("syscall") |
| int attach_prog(struct attach_prog_args *ctx) |
| { |
| ctx->retval = hid_bpf_attach_prog(ctx->hid, |
| ctx->prog_fd, |
| ctx->insert_head ? HID_BPF_FLAG_INSERT_HEAD : |
| HID_BPF_FLAG_NONE); |
| return 0; |
| } |
| |
| struct hid_hw_request_syscall_args { |
| /* data needs to come at offset 0 so we can use it in calls */ |
| __u8 data[10]; |
| unsigned int hid; |
| int retval; |
| size_t size; |
| enum hid_report_type type; |
| __u8 request_type; |
| }; |
| |
| SEC("syscall") |
| int hid_user_raw_request(struct hid_hw_request_syscall_args *args) |
| { |
| struct hid_bpf_ctx *ctx; |
| const size_t size = args->size; |
| int i, ret = 0; |
| |
| if (size > sizeof(args->data)) |
| return -7; /* -E2BIG */ |
| |
| ctx = hid_bpf_allocate_context(args->hid); |
| if (!ctx) |
| return -1; /* EPERM check */ |
| |
| ret = hid_bpf_hw_request(ctx, |
| args->data, |
| size, |
| args->type, |
| args->request_type); |
| args->retval = ret; |
| |
| hid_bpf_release_context(ctx); |
| |
| return 0; |
| } |
| |
| static const __u8 rdesc[] = { |
| 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ |
| 0x09, 0x32, /* USAGE (Z) */ |
| 0x95, 0x01, /* REPORT_COUNT (1) */ |
| 0x81, 0x06, /* INPUT (Data,Var,Rel) */ |
| |
| 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ |
| 0x19, 0x01, /* USAGE_MINIMUM (1) */ |
| 0x29, 0x03, /* USAGE_MAXIMUM (3) */ |
| 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ |
| 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ |
| 0x95, 0x03, /* REPORT_COUNT (3) */ |
| 0x75, 0x01, /* REPORT_SIZE (1) */ |
| 0x91, 0x02, /* Output (Data,Var,Abs) */ |
| 0x95, 0x01, /* REPORT_COUNT (1) */ |
| 0x75, 0x05, /* REPORT_SIZE (5) */ |
| 0x91, 0x01, /* Output (Cnst,Var,Abs) */ |
| |
| 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ |
| 0x19, 0x06, /* USAGE_MINIMUM (6) */ |
| 0x29, 0x08, /* USAGE_MAXIMUM (8) */ |
| 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ |
| 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ |
| 0x95, 0x03, /* REPORT_COUNT (3) */ |
| 0x75, 0x01, /* REPORT_SIZE (1) */ |
| 0xb1, 0x02, /* Feature (Data,Var,Abs) */ |
| 0x95, 0x01, /* REPORT_COUNT (1) */ |
| 0x75, 0x05, /* REPORT_SIZE (5) */ |
| 0x91, 0x01, /* Output (Cnst,Var,Abs) */ |
| |
| 0xc0, /* END_COLLECTION */ |
| 0xc0, /* END_COLLECTION */ |
| }; |
| |
| SEC("?fmod_ret/hid_bpf_rdesc_fixup") |
| int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx) |
| { |
| __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4096 /* size */); |
| |
| if (!data) |
| return 0; /* EPERM check */ |
| |
| callback2_check = data[4]; |
| |
| /* insert rdesc at offset 73 */ |
| __builtin_memcpy(&data[73], rdesc, sizeof(rdesc)); |
| |
| /* Change Usage Vendor globally */ |
| data[4] = 0x42; |
| |
| return sizeof(rdesc) + 73; |
| } |
| |
| SEC("?fmod_ret/hid_bpf_device_event") |
| int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx) |
| { |
| __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); |
| |
| if (!data) |
| return 0; /* EPERM check */ |
| |
| /* we need to be run first */ |
| if (data[2] || data[3]) |
| return -1; |
| |
| data[1] = 1; |
| |
| return 0; |
| } |
| |
| SEC("?fmod_ret/hid_bpf_device_event") |
| int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx) |
| { |
| __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); |
| |
| if (!data) |
| return 0; /* EPERM check */ |
| |
| /* after insert0 and before insert2 */ |
| if (!data[1] || data[3]) |
| return -1; |
| |
| data[2] = 2; |
| |
| return 0; |
| } |
| |
| SEC("?fmod_ret/hid_bpf_device_event") |
| int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx) |
| { |
| __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */); |
| |
| if (!data) |
| return 0; /* EPERM check */ |
| |
| /* at the end */ |
| if (!data[1] || !data[2]) |
| return -1; |
| |
| data[3] = 3; |
| |
| return 0; |
| } |