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