xref: /openbmc/linux/samples/hid/hid_surface_dial.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
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