1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2022 Benjamin Tissoires
4 * This program will morph the Microsoft Surface Dial into a mouse,
5 * and depending on the chosen resolution enable or not the haptic feedback:
6 * - a resolution (-r) of 3600 will report 3600 "ticks" in one full rotation
7 * without haptic feedback
8 * - any other resolution will report N "ticks" in a full rotation with haptic
11 * A good default for low resolution haptic scrolling is 72 (1 "tick" every 5
12 * degrees), and set to 3600 for smooth scrolling.
24 #include <sys/resource.h>
27 #include <linux/bpf.h>
28 #include <linux/errno.h>
31 #include <bpf/libbpf.h>
33 #include "hid_surface_dial.skel.h"
35 static bool running
= true;
37 struct haptic_syscall_args
{
42 static void int_exit(int sig
)
48 static void usage(const char *prog
)
51 "%s: %s [OPTIONS] /sys/bus/hid/devices/0BUS:0VID:0PID:00ID\n\n"
53 " -r N\t set the given resolution to the device (number of ticks per 360°)\n\n",
56 "This program will morph the Microsoft Surface Dial into a mouse,\n"
57 "and depending on the chosen resolution enable or not the haptic feedback:\n"
58 "- a resolution (-r) of 3600 will report 3600 'ticks' in one full rotation\n"
59 " without haptic feedback\n"
60 "- any other resolution will report N 'ticks' in a full rotation with haptic\n"
63 "A good default for low resolution haptic scrolling is 72 (1 'tick' every 5\n"
64 "degrees), and set to 3600 for smooth scrolling.\n");
67 static int get_hid_id(const char *path
)
69 const char *str_id
, *dir
;
73 memset(uevent
, 0, sizeof(uevent
));
74 snprintf(uevent
, sizeof(uevent
) - 1, "%s/uevent", path
);
76 fd
= open(uevent
, O_RDONLY
| O_NONBLOCK
);
82 dir
= basename((char *)path
);
84 str_id
= dir
+ sizeof("0003:0001:0A37.");
85 return (int)strtol(str_id
, NULL
, 16);
88 static int set_haptic(struct hid_surface_dial
*skel
, int hid_id
)
90 struct haptic_syscall_args args
= {
95 DECLARE_LIBBPF_OPTS(bpf_test_run_opts
, tattr
,
97 .ctx_size_in
= sizeof(args
),
100 haptic_fd
= bpf_program__fd(skel
->progs
.set_haptic
);
102 fprintf(stderr
, "can't locate haptic prog: %m\n");
106 err
= bpf_prog_test_run_opts(haptic_fd
, &tattr
);
108 fprintf(stderr
, "can't set haptic configuration to hid device %d: %m (err: %d)\n",
115 int main(int argc
, char **argv
)
117 struct hid_surface_dial
*skel
;
118 const char *optstr
= "r:";
119 struct bpf_link
*link
;
120 const char *sysfs_path
;
121 int err
, opt
, hid_id
, resolution
= 72;
123 while ((opt
= getopt(argc
, argv
, optstr
)) != -1) {
131 l
= strtol(optarg
, &endp
, 10);
138 "invalid r option %s - expecting a number\n",
139 optarg
? optarg
: "");
143 resolution
= (int) l
;
147 usage(basename(argv
[0]));
152 if (optind
== argc
) {
153 usage(basename(argv
[0]));
157 sysfs_path
= argv
[optind
];
163 skel
= hid_surface_dial__open();
165 fprintf(stderr
, "%s %s:%d", __func__
, __FILE__
, __LINE__
);
169 hid_id
= get_hid_id(sysfs_path
);
171 fprintf(stderr
, "can not open HID device: %m\n");
175 skel
->struct_ops
.surface_dial
->hid_id
= hid_id
;
177 err
= hid_surface_dial__load(skel
);
179 fprintf(stderr
, "can not load HID-BPF program: %m\n");
183 skel
->data
->resolution
= resolution
;
184 skel
->data
->physical
= (int)(resolution
/ 72);
186 link
= bpf_map__attach_struct_ops(skel
->maps
.surface_dial
);
188 fprintf(stderr
, "can not attach HID-BPF program: %m\n");
192 signal(SIGINT
, int_exit
);
193 signal(SIGTERM
, int_exit
);
195 set_haptic(skel
, hid_id
);
200 hid_surface_dial__destroy(skel
);