1a56a2569SBenjamin Tissoires // SPDX-License-Identifier: GPL-2.0-only
2a56a2569SBenjamin Tissoires /* Copyright (c) 2022 Benjamin Tissoires
3a56a2569SBenjamin Tissoires *
4a56a2569SBenjamin Tissoires * This program will morph the Microsoft Surface Dial into a mouse,
5a56a2569SBenjamin Tissoires * and depending on the chosen resolution enable or not the haptic feedback:
6a56a2569SBenjamin Tissoires * - a resolution (-r) of 3600 will report 3600 "ticks" in one full rotation
7*2364b506SColin Ian King * without haptic feedback
8a56a2569SBenjamin Tissoires * - any other resolution will report N "ticks" in a full rotation with haptic
9a56a2569SBenjamin Tissoires * feedback
10a56a2569SBenjamin Tissoires *
11a56a2569SBenjamin Tissoires * A good default for low resolution haptic scrolling is 72 (1 "tick" every 5
12a56a2569SBenjamin Tissoires * degrees), and set to 3600 for smooth scrolling.
13a56a2569SBenjamin Tissoires */
14a56a2569SBenjamin Tissoires
15a56a2569SBenjamin Tissoires #include <assert.h>
16a56a2569SBenjamin Tissoires #include <errno.h>
17a56a2569SBenjamin Tissoires #include <fcntl.h>
18a56a2569SBenjamin Tissoires #include <libgen.h>
19a56a2569SBenjamin Tissoires #include <signal.h>
20a56a2569SBenjamin Tissoires #include <stdbool.h>
21a56a2569SBenjamin Tissoires #include <stdio.h>
22a56a2569SBenjamin Tissoires #include <stdlib.h>
23a56a2569SBenjamin Tissoires #include <string.h>
24a56a2569SBenjamin Tissoires #include <sys/resource.h>
25a56a2569SBenjamin Tissoires #include <unistd.h>
26a56a2569SBenjamin Tissoires
27a56a2569SBenjamin Tissoires #include <linux/bpf.h>
28a56a2569SBenjamin Tissoires #include <linux/errno.h>
29a56a2569SBenjamin Tissoires
30a56a2569SBenjamin Tissoires #include <bpf/bpf.h>
31a56a2569SBenjamin Tissoires #include <bpf/libbpf.h>
32a56a2569SBenjamin Tissoires
33a56a2569SBenjamin Tissoires #include "hid_surface_dial.skel.h"
34a56a2569SBenjamin Tissoires #include "hid_bpf_attach.h"
35a56a2569SBenjamin Tissoires
36a56a2569SBenjamin Tissoires static bool running = true;
37a56a2569SBenjamin Tissoires
38a56a2569SBenjamin Tissoires struct haptic_syscall_args {
39a56a2569SBenjamin Tissoires unsigned int hid;
40a56a2569SBenjamin Tissoires int retval;
41a56a2569SBenjamin Tissoires };
42a56a2569SBenjamin Tissoires
int_exit(int sig)43a56a2569SBenjamin Tissoires static void int_exit(int sig)
44a56a2569SBenjamin Tissoires {
45a56a2569SBenjamin Tissoires running = false;
46a56a2569SBenjamin Tissoires exit(0);
47a56a2569SBenjamin Tissoires }
48a56a2569SBenjamin Tissoires
usage(const char * prog)49a56a2569SBenjamin Tissoires static void usage(const char *prog)
50a56a2569SBenjamin Tissoires {
51a56a2569SBenjamin Tissoires fprintf(stderr,
52a56a2569SBenjamin Tissoires "%s: %s [OPTIONS] /sys/bus/hid/devices/0BUS:0VID:0PID:00ID\n\n"
53a56a2569SBenjamin Tissoires " OPTIONS:\n"
54a56a2569SBenjamin Tissoires " -r N\t set the given resolution to the device (number of ticks per 360°)\n\n",
55a56a2569SBenjamin Tissoires __func__, prog);
56a56a2569SBenjamin Tissoires fprintf(stderr,
57a56a2569SBenjamin Tissoires "This program will morph the Microsoft Surface Dial into a mouse,\n"
58a56a2569SBenjamin Tissoires "and depending on the chosen resolution enable or not the haptic feedback:\n"
59a56a2569SBenjamin Tissoires "- a resolution (-r) of 3600 will report 3600 'ticks' in one full rotation\n"
60*2364b506SColin Ian King " without haptic feedback\n"
61a56a2569SBenjamin Tissoires "- any other resolution will report N 'ticks' in a full rotation with haptic\n"
62a56a2569SBenjamin Tissoires " feedback\n"
63a56a2569SBenjamin Tissoires "\n"
64a56a2569SBenjamin Tissoires "A good default for low resolution haptic scrolling is 72 (1 'tick' every 5\n"
65a56a2569SBenjamin Tissoires "degrees), and set to 3600 for smooth scrolling.\n");
66a56a2569SBenjamin Tissoires }
67a56a2569SBenjamin Tissoires
get_hid_id(const char * path)68a56a2569SBenjamin Tissoires static int get_hid_id(const char *path)
69a56a2569SBenjamin Tissoires {
70a56a2569SBenjamin Tissoires const char *str_id, *dir;
71a56a2569SBenjamin Tissoires char uevent[1024];
72a56a2569SBenjamin Tissoires int fd;
73a56a2569SBenjamin Tissoires
74a56a2569SBenjamin Tissoires memset(uevent, 0, sizeof(uevent));
75a56a2569SBenjamin Tissoires snprintf(uevent, sizeof(uevent) - 1, "%s/uevent", path);
76a56a2569SBenjamin Tissoires
77a56a2569SBenjamin Tissoires fd = open(uevent, O_RDONLY | O_NONBLOCK);
78a56a2569SBenjamin Tissoires if (fd < 0)
79a56a2569SBenjamin Tissoires return -ENOENT;
80a56a2569SBenjamin Tissoires
81a56a2569SBenjamin Tissoires close(fd);
82a56a2569SBenjamin Tissoires
83a56a2569SBenjamin Tissoires dir = basename((char *)path);
84a56a2569SBenjamin Tissoires
85a56a2569SBenjamin Tissoires str_id = dir + sizeof("0003:0001:0A37.");
86a56a2569SBenjamin Tissoires return (int)strtol(str_id, NULL, 16);
87a56a2569SBenjamin Tissoires }
88a56a2569SBenjamin Tissoires
attach_prog(struct hid_surface_dial * skel,struct bpf_program * prog,int hid_id)89a56a2569SBenjamin Tissoires static int attach_prog(struct hid_surface_dial *skel, struct bpf_program *prog, int hid_id)
90a56a2569SBenjamin Tissoires {
91a56a2569SBenjamin Tissoires struct attach_prog_args args = {
92a56a2569SBenjamin Tissoires .hid = hid_id,
93a56a2569SBenjamin Tissoires .retval = -1,
94a56a2569SBenjamin Tissoires };
95a56a2569SBenjamin Tissoires int attach_fd, err;
96a56a2569SBenjamin Tissoires DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattr,
97a56a2569SBenjamin Tissoires .ctx_in = &args,
98a56a2569SBenjamin Tissoires .ctx_size_in = sizeof(args),
99a56a2569SBenjamin Tissoires );
100a56a2569SBenjamin Tissoires
101a56a2569SBenjamin Tissoires attach_fd = bpf_program__fd(skel->progs.attach_prog);
102a56a2569SBenjamin Tissoires if (attach_fd < 0) {
103a56a2569SBenjamin Tissoires fprintf(stderr, "can't locate attach prog: %m\n");
104a56a2569SBenjamin Tissoires return 1;
105a56a2569SBenjamin Tissoires }
106a56a2569SBenjamin Tissoires
107a56a2569SBenjamin Tissoires args.prog_fd = bpf_program__fd(prog);
108a56a2569SBenjamin Tissoires err = bpf_prog_test_run_opts(attach_fd, &tattr);
109a56a2569SBenjamin Tissoires if (err) {
110a56a2569SBenjamin Tissoires fprintf(stderr, "can't attach prog to hid device %d: %m (err: %d)\n",
111a56a2569SBenjamin Tissoires hid_id, err);
112a56a2569SBenjamin Tissoires return 1;
113a56a2569SBenjamin Tissoires }
114a56a2569SBenjamin Tissoires return 0;
115a56a2569SBenjamin Tissoires }
116a56a2569SBenjamin Tissoires
set_haptic(struct hid_surface_dial * skel,int hid_id)117a56a2569SBenjamin Tissoires static int set_haptic(struct hid_surface_dial *skel, int hid_id)
118a56a2569SBenjamin Tissoires {
119a56a2569SBenjamin Tissoires struct haptic_syscall_args args = {
120a56a2569SBenjamin Tissoires .hid = hid_id,
121a56a2569SBenjamin Tissoires .retval = -1,
122a56a2569SBenjamin Tissoires };
123a56a2569SBenjamin Tissoires int haptic_fd, err;
124a56a2569SBenjamin Tissoires DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattr,
125a56a2569SBenjamin Tissoires .ctx_in = &args,
126a56a2569SBenjamin Tissoires .ctx_size_in = sizeof(args),
127a56a2569SBenjamin Tissoires );
128a56a2569SBenjamin Tissoires
129a56a2569SBenjamin Tissoires haptic_fd = bpf_program__fd(skel->progs.set_haptic);
130a56a2569SBenjamin Tissoires if (haptic_fd < 0) {
131a56a2569SBenjamin Tissoires fprintf(stderr, "can't locate haptic prog: %m\n");
132a56a2569SBenjamin Tissoires return 1;
133a56a2569SBenjamin Tissoires }
134a56a2569SBenjamin Tissoires
135a56a2569SBenjamin Tissoires err = bpf_prog_test_run_opts(haptic_fd, &tattr);
136a56a2569SBenjamin Tissoires if (err) {
137a56a2569SBenjamin Tissoires fprintf(stderr, "can't set haptic configuration to hid device %d: %m (err: %d)\n",
138a56a2569SBenjamin Tissoires hid_id, err);
139a56a2569SBenjamin Tissoires return 1;
140a56a2569SBenjamin Tissoires }
141a56a2569SBenjamin Tissoires return 0;
142a56a2569SBenjamin Tissoires }
143a56a2569SBenjamin Tissoires
main(int argc,char ** argv)144a56a2569SBenjamin Tissoires int main(int argc, char **argv)
145a56a2569SBenjamin Tissoires {
146a56a2569SBenjamin Tissoires struct hid_surface_dial *skel;
147a56a2569SBenjamin Tissoires struct bpf_program *prog;
148a56a2569SBenjamin Tissoires const char *optstr = "r:";
149a56a2569SBenjamin Tissoires const char *sysfs_path;
150a56a2569SBenjamin Tissoires int opt, hid_id, resolution = 72;
151a56a2569SBenjamin Tissoires
152a56a2569SBenjamin Tissoires while ((opt = getopt(argc, argv, optstr)) != -1) {
153a56a2569SBenjamin Tissoires switch (opt) {
154a56a2569SBenjamin Tissoires case 'r':
155a56a2569SBenjamin Tissoires {
156a56a2569SBenjamin Tissoires char *endp = NULL;
157a56a2569SBenjamin Tissoires long l = -1;
158a56a2569SBenjamin Tissoires
159a56a2569SBenjamin Tissoires if (optarg) {
160a56a2569SBenjamin Tissoires l = strtol(optarg, &endp, 10);
161a56a2569SBenjamin Tissoires if (endp && *endp)
162a56a2569SBenjamin Tissoires l = -1;
163a56a2569SBenjamin Tissoires }
164a56a2569SBenjamin Tissoires
165a56a2569SBenjamin Tissoires if (l < 0) {
166a56a2569SBenjamin Tissoires fprintf(stderr,
167a56a2569SBenjamin Tissoires "invalid r option %s - expecting a number\n",
168a56a2569SBenjamin Tissoires optarg ? optarg : "");
169a56a2569SBenjamin Tissoires exit(EXIT_FAILURE);
170a56a2569SBenjamin Tissoires };
171a56a2569SBenjamin Tissoires
172a56a2569SBenjamin Tissoires resolution = (int) l;
173a56a2569SBenjamin Tissoires break;
174a56a2569SBenjamin Tissoires }
175a56a2569SBenjamin Tissoires default:
176a56a2569SBenjamin Tissoires usage(basename(argv[0]));
177a56a2569SBenjamin Tissoires return 1;
178a56a2569SBenjamin Tissoires }
179a56a2569SBenjamin Tissoires }
180a56a2569SBenjamin Tissoires
181a56a2569SBenjamin Tissoires if (optind == argc) {
182a56a2569SBenjamin Tissoires usage(basename(argv[0]));
183a56a2569SBenjamin Tissoires return 1;
184a56a2569SBenjamin Tissoires }
185a56a2569SBenjamin Tissoires
186a56a2569SBenjamin Tissoires sysfs_path = argv[optind];
187a56a2569SBenjamin Tissoires if (!sysfs_path) {
188a56a2569SBenjamin Tissoires perror("sysfs");
189a56a2569SBenjamin Tissoires return 1;
190a56a2569SBenjamin Tissoires }
191a56a2569SBenjamin Tissoires
192a56a2569SBenjamin Tissoires skel = hid_surface_dial__open_and_load();
193a56a2569SBenjamin Tissoires if (!skel) {
194a56a2569SBenjamin Tissoires fprintf(stderr, "%s %s:%d", __func__, __FILE__, __LINE__);
195a56a2569SBenjamin Tissoires return -1;
196a56a2569SBenjamin Tissoires }
197a56a2569SBenjamin Tissoires
198a56a2569SBenjamin Tissoires hid_id = get_hid_id(sysfs_path);
199a56a2569SBenjamin Tissoires if (hid_id < 0) {
200a56a2569SBenjamin Tissoires fprintf(stderr, "can not open HID device: %m\n");
201a56a2569SBenjamin Tissoires return 1;
202a56a2569SBenjamin Tissoires }
203a56a2569SBenjamin Tissoires
204a56a2569SBenjamin Tissoires skel->data->resolution = resolution;
205a56a2569SBenjamin Tissoires skel->data->physical = (int)(resolution / 72);
206a56a2569SBenjamin Tissoires
207a56a2569SBenjamin Tissoires bpf_object__for_each_program(prog, *skel->skeleton->obj) {
208a56a2569SBenjamin Tissoires /* ignore syscalls */
209a56a2569SBenjamin Tissoires if (bpf_program__get_type(prog) != BPF_PROG_TYPE_TRACING)
210a56a2569SBenjamin Tissoires continue;
211a56a2569SBenjamin Tissoires
212a56a2569SBenjamin Tissoires attach_prog(skel, prog, hid_id);
213a56a2569SBenjamin Tissoires }
214a56a2569SBenjamin Tissoires
215a56a2569SBenjamin Tissoires signal(SIGINT, int_exit);
216a56a2569SBenjamin Tissoires signal(SIGTERM, int_exit);
217a56a2569SBenjamin Tissoires
218a56a2569SBenjamin Tissoires set_haptic(skel, hid_id);
219a56a2569SBenjamin Tissoires
220a56a2569SBenjamin Tissoires while (running)
221a56a2569SBenjamin Tissoires sleep(1);
222a56a2569SBenjamin Tissoires
223a56a2569SBenjamin Tissoires hid_surface_dial__destroy(skel);
224a56a2569SBenjamin Tissoires
225a56a2569SBenjamin Tissoires return 0;
226a56a2569SBenjamin Tissoires }
227