| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- // SPDX-License-Identifier: GPL-2.0-only
- /* Copyright (c) 2022 Benjamin Tissoires
- *
- * This program will morph the Microsoft Surface Dial into a mouse,
- * and depending on the chosen resolution enable or not the haptic feedback:
- * - a resolution (-r) of 3600 will report 3600 "ticks" in one full rotation
- * without haptic feedback
- * - any other resolution will report N "ticks" in a full rotation with haptic
- * feedback
- *
- * A good default for low resolution haptic scrolling is 72 (1 "tick" every 5
- * degrees), and set to 3600 for smooth scrolling.
- */
- #include <assert.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <libgen.h>
- #include <signal.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/resource.h>
- #include <unistd.h>
- #include <linux/bpf.h>
- #include <linux/errno.h>
- #include <bpf/bpf.h>
- #include <bpf/libbpf.h>
- #include "hid_surface_dial.skel.h"
- static bool running = true;
- struct haptic_syscall_args {
- unsigned int hid;
- int retval;
- };
- static void int_exit(int sig)
- {
- running = false;
- exit(0);
- }
- static void usage(const char *prog)
- {
- fprintf(stderr,
- "%s: %s [OPTIONS] /sys/bus/hid/devices/0BUS:0VID:0PID:00ID\n\n"
- " OPTIONS:\n"
- " -r N\t set the given resolution to the device (number of ticks per 360°)\n\n",
- __func__, prog);
- fprintf(stderr,
- "This program will morph the Microsoft Surface Dial into a mouse,\n"
- "and depending on the chosen resolution enable or not the haptic feedback:\n"
- "- a resolution (-r) of 3600 will report 3600 'ticks' in one full rotation\n"
- " without haptic feedback\n"
- "- any other resolution will report N 'ticks' in a full rotation with haptic\n"
- " feedback\n"
- "\n"
- "A good default for low resolution haptic scrolling is 72 (1 'tick' every 5\n"
- "degrees), and set to 3600 for smooth scrolling.\n");
- }
- static int get_hid_id(const char *path)
- {
- const char *str_id, *dir;
- char uevent[1024];
- int fd;
- memset(uevent, 0, sizeof(uevent));
- snprintf(uevent, sizeof(uevent) - 1, "%s/uevent", path);
- fd = open(uevent, O_RDONLY | O_NONBLOCK);
- if (fd < 0)
- return -ENOENT;
- close(fd);
- dir = basename((char *)path);
- str_id = dir + sizeof("0003:0001:0A37.");
- return (int)strtol(str_id, NULL, 16);
- }
- static int set_haptic(struct hid_surface_dial *skel, int hid_id)
- {
- struct haptic_syscall_args args = {
- .hid = hid_id,
- .retval = -1,
- };
- int haptic_fd, err;
- DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattr,
- .ctx_in = &args,
- .ctx_size_in = sizeof(args),
- );
- haptic_fd = bpf_program__fd(skel->progs.set_haptic);
- if (haptic_fd < 0) {
- fprintf(stderr, "can't locate haptic prog: %m\n");
- return 1;
- }
- err = bpf_prog_test_run_opts(haptic_fd, &tattr);
- if (err) {
- fprintf(stderr, "can't set haptic configuration to hid device %d: %m (err: %d)\n",
- hid_id, err);
- return 1;
- }
- return 0;
- }
- int main(int argc, char **argv)
- {
- struct hid_surface_dial *skel;
- const char *optstr = "r:";
- struct bpf_link *link;
- const char *sysfs_path;
- int err, opt, hid_id, resolution = 72;
- while ((opt = getopt(argc, argv, optstr)) != -1) {
- switch (opt) {
- case 'r':
- {
- char *endp = NULL;
- long l = -1;
- if (optarg) {
- l = strtol(optarg, &endp, 10);
- if (endp && *endp)
- l = -1;
- }
- if (l < 0) {
- fprintf(stderr,
- "invalid r option %s - expecting a number\n",
- optarg ? optarg : "");
- exit(EXIT_FAILURE);
- };
- resolution = (int) l;
- break;
- }
- default:
- usage(basename(argv[0]));
- return 1;
- }
- }
- if (optind == argc) {
- usage(basename(argv[0]));
- return 1;
- }
- sysfs_path = argv[optind];
- if (!sysfs_path) {
- perror("sysfs");
- return 1;
- }
- skel = hid_surface_dial__open();
- if (!skel) {
- fprintf(stderr, "%s %s:%d", __func__, __FILE__, __LINE__);
- return -1;
- }
- hid_id = get_hid_id(sysfs_path);
- if (hid_id < 0) {
- fprintf(stderr, "can not open HID device: %m\n");
- return 1;
- }
- 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);
- 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);
- signal(SIGTERM, int_exit);
- set_haptic(skel, hid_id);
- while (running)
- sleep(1);
- hid_surface_dial__destroy(skel);
- return 0;
- }
|