root/samples/hid/hid_surface_dial.bpf.c
// 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_helpers.h"

#define HID_UP_BUTTON           0x0009
#define HID_GD_WHEEL            0x0038

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 */);

        if (!data)
                return 0; /* EPERM check */

        /* Touch */
        data[1] &= 0xfd;

        /* X */
        data[4] = 0;
        data[5] = 0;

        /* Y */
        data[6] = 0;
        data[7] = 0;

        return 0;
}

/* 72 == 360 / 5 -> 1 report every 5 degrees */
int resolution = 72;
int physical = 5;

struct haptic_syscall_args {
        unsigned int hid;
        int retval;
};

static __u8 haptic_data[8];

SEC("syscall")
int set_haptic(struct haptic_syscall_args *args)
{
        struct hid_bpf_ctx *ctx;
        const size_t size = sizeof(haptic_data);
        u16 *res;
        int ret;

        if (size > sizeof(haptic_data))
                return -7; /* -E2BIG */

        ctx = hid_bpf_allocate_context(args->hid);
        if (!ctx)
                return -1; /* EPERM check */

        haptic_data[0] = 1;  /* report ID */

        ret = hid_bpf_hw_request(ctx, haptic_data, size, HID_FEATURE_REPORT, HID_REQ_GET_REPORT);

        bpf_printk("probed/remove event ret value: %d", ret);
        bpf_printk("buf: %02x %02x %02x",
                   haptic_data[0],
                   haptic_data[1],
                   haptic_data[2]);
        bpf_printk("     %02x %02x %02x",
                   haptic_data[3],
                   haptic_data[4],
                   haptic_data[5]);
        bpf_printk("     %02x %02x",
                   haptic_data[6],
                   haptic_data[7]);

        /* whenever resolution multiplier is not 3600, we have the fixed report descriptor */
        res = (u16 *)&haptic_data[1];
        if (*res != 3600) {
//              haptic_data[1] = 72; /* resolution multiplier */
//              haptic_data[2] = 0;  /* resolution multiplier */
//              haptic_data[3] = 0;  /* Repeat Count */
                haptic_data[4] = 3;  /* haptic Auto Trigger */
//              haptic_data[5] = 5;  /* Waveform Cutoff Time */
//              haptic_data[6] = 80; /* Retrigger Period */
//              haptic_data[7] = 0;  /* Retrigger Period */
        } else {
                haptic_data[4] = 0;
        }

        ret = hid_bpf_hw_request(ctx, haptic_data, size, HID_FEATURE_REPORT, HID_REQ_SET_REPORT);

        bpf_printk("set haptic ret value: %d -> %d", ret, haptic_data[4]);

        args->retval = ret;

        hid_bpf_release_context(ctx);

        return 0;
}

/* Convert REL_DIAL into REL_WHEEL */
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 */);
        __u16 *res, *phys;

        if (!data)
                return 0; /* EPERM check */

        /* Convert TOUCH into a button */
        data[31] = HID_UP_BUTTON;
        data[33] = 2;

        /* Convert REL_DIAL into REL_WHEEL */
        data[45] = HID_GD_WHEEL;

        /* Change Resolution Multiplier */
        phys = (__u16 *)&data[61];
        *phys = physical;
        res = (__u16 *)&data[66];
        *res = resolution;

        /* Convert X,Y from Abs to Rel */
        data[88] = 0x06;
        data[98] = 0x06;

        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;