xref: /openbmc/linux/samples/hid/hid_mouse.c (revision c4c3c32d)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2022 Benjamin Tissoires
3  *
4  * This is a pure HID-BPF example, and should be considered as such:
5  * on the Etekcity Scroll 6E, the X and Y axes will be swapped and
6  * inverted. On any other device... Not sure what this will do.
7  *
8  * This C main file is generic though. To adapt the code and test, users
9  * must amend only the .bpf.c file, which this program will load any
10  * eBPF program it finds.
11  */
12 
13 #include <assert.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <libgen.h>
17 #include <signal.h>
18 #include <stdbool.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/resource.h>
23 #include <unistd.h>
24 
25 #include <linux/bpf.h>
26 #include <linux/errno.h>
27 
28 #include <bpf/bpf.h>
29 #include <bpf/libbpf.h>
30 
31 #include "hid_mouse.skel.h"
32 #include "hid_bpf_attach.h"
33 
34 static bool running = true;
35 
36 static void int_exit(int sig)
37 {
38 	running = false;
39 	exit(0);
40 }
41 
42 static void usage(const char *prog)
43 {
44 	fprintf(stderr,
45 		"%s: %s /sys/bus/hid/devices/0BUS:0VID:0PID:00ID\n\n",
46 		__func__, prog);
47 	fprintf(stderr,
48 		"This program will upload and attach a HID-BPF program to the given device.\n"
49 		"On the Etekcity Scroll 6E, the X and Y axis will be inverted, but on any other\n"
50 		"device, chances are high that the device will not be working anymore\n\n"
51 		"consider this as a demo and adapt the eBPF program to your needs\n"
52 		"Hit Ctrl-C to unbind the program and reset the device\n");
53 }
54 
55 static int get_hid_id(const char *path)
56 {
57 	const char *str_id, *dir;
58 	char uevent[1024];
59 	int fd;
60 
61 	memset(uevent, 0, sizeof(uevent));
62 	snprintf(uevent, sizeof(uevent) - 1, "%s/uevent", path);
63 
64 	fd = open(uevent, O_RDONLY | O_NONBLOCK);
65 	if (fd < 0)
66 		return -ENOENT;
67 
68 	close(fd);
69 
70 	dir = basename((char *)path);
71 
72 	str_id = dir + sizeof("0003:0001:0A37.");
73 	return (int)strtol(str_id, NULL, 16);
74 }
75 
76 int main(int argc, char **argv)
77 {
78 	struct hid_mouse *skel;
79 	struct bpf_program *prog;
80 	int err;
81 	const char *optstr = "";
82 	const char *sysfs_path;
83 	int opt, hid_id, attach_fd;
84 	struct attach_prog_args args = {
85 		.retval = -1,
86 	};
87 	DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattr,
88 			    .ctx_in = &args,
89 			    .ctx_size_in = sizeof(args),
90 	);
91 
92 	while ((opt = getopt(argc, argv, optstr)) != -1) {
93 		switch (opt) {
94 		default:
95 			usage(basename(argv[0]));
96 			return 1;
97 		}
98 	}
99 
100 	if (optind == argc) {
101 		usage(basename(argv[0]));
102 		return 1;
103 	}
104 
105 	sysfs_path = argv[optind];
106 	if (!sysfs_path) {
107 		perror("sysfs");
108 		return 1;
109 	}
110 
111 	skel = hid_mouse__open_and_load();
112 	if (!skel) {
113 		fprintf(stderr, "%s  %s:%d", __func__, __FILE__, __LINE__);
114 		return -1;
115 	}
116 
117 	hid_id = get_hid_id(sysfs_path);
118 
119 	if (hid_id < 0) {
120 		fprintf(stderr, "can not open HID device: %m\n");
121 		return 1;
122 	}
123 	args.hid = hid_id;
124 
125 	attach_fd = bpf_program__fd(skel->progs.attach_prog);
126 	if (attach_fd < 0) {
127 		fprintf(stderr, "can't locate attach prog: %m\n");
128 		return 1;
129 	}
130 
131 	bpf_object__for_each_program(prog, *skel->skeleton->obj) {
132 		/* ignore syscalls */
133 		if (bpf_program__get_type(prog) != BPF_PROG_TYPE_TRACING)
134 			continue;
135 
136 		args.retval = -1;
137 		args.prog_fd = bpf_program__fd(prog);
138 		err = bpf_prog_test_run_opts(attach_fd, &tattr);
139 		if (err) {
140 			fprintf(stderr, "can't attach prog to hid device %d: %m (err: %d)\n",
141 				hid_id, err);
142 			return 1;
143 		}
144 	}
145 
146 	signal(SIGINT, int_exit);
147 	signal(SIGTERM, int_exit);
148 
149 	while (running)
150 		sleep(1);
151 
152 	hid_mouse__destroy(skel);
153 
154 	return 0;
155 }
156