203 lines · 4.4 KB · C
RawDownload
// 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;}