• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2014 Red Hat, Inc.
4  */
5 
6 #include "config.h"
7 
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <libgen.h>
11 #include <limits.h>
12 #include <poll.h>
13 #include <signal.h>
14 #include <stdint.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 
20 #include "libevdev/libevdev.h"
21 
22 #define min(a, b) (((a) < (b)) ? (a) : (b))
23 #define max(a, b) (((a) > (b)) ? (a) : (b))
24 
25 static int signalled = 0;
26 
27 struct measurements {
28 	int distance;
29 	double max_frequency;
30 	double *frequencies;
31 	size_t frequencies_sz;
32 	size_t nfrequencies;
33 	uint64_t us;
34 };
35 
36 static int
usage(const char * progname)37 usage(const char *progname) {
38 	printf("Usage: %s /dev/input/event0\n", progname);
39 	printf("\n");
40 	printf("This tool reads relative events from the kernel and calculates\n"
41 	       "the distance covered and maximum frequency of the incoming events.\n"
42 	       "Some mouse devices provide dynamic frequencies, it is\n"
43 	       "recommended to measure multiple times to obtain the highest value.\n");
44 	return 1;
45 }
46 
47 static inline double
get_frequency(uint64_t last,uint64_t current)48 get_frequency(uint64_t last, uint64_t current)
49 {
50 	return 1000000.0/(current - last);
51 }
52 
53 static inline void
push_frequency(struct measurements * m,double freq)54 push_frequency(struct measurements *m, double freq)
55 {
56 	if (m->nfrequencies == m->frequencies_sz) {
57 		m->frequencies_sz += 100;
58 		m->frequencies = realloc(m->frequencies,
59 					 m->frequencies_sz * sizeof *m->frequencies);
60 		if (!m->frequencies)
61 			abort();
62 	}
63 
64 	m->frequencies[m->nfrequencies] = freq;
65 	m->nfrequencies++;
66 }
67 
68 static int
print_current_values(const struct measurements * m)69 print_current_values(const struct measurements *m)
70 {
71 	static int progress = 0;
72 	char status = 0;
73 
74 	switch (progress) {
75 		case 0: status = '|'; break;
76 		case 1: status = '/'; break;
77 		case 2: status = '-'; break;
78 		case 3: status = '\\'; break;
79 		default:
80 			status = '?';
81 			break;
82 	}
83 
84 	progress = (progress + 1) % 4;
85 
86 	printf("\rCovered distance in device units: %8d at frequency %3.1fHz 	%c",
87 	       abs(m->distance), m->max_frequency, status);
88 
89 	return 0;
90 }
91 
92 static int
handle_event(struct measurements * m,const struct input_event * ev)93 handle_event(struct measurements *m, const struct input_event *ev)
94 {
95 	if (ev->type == EV_SYN) {
96 		const int idle_reset = 3000000; /* us */
97 		uint64_t last_us = m->us;
98 
99 		m->us = ev->input_event_sec * 1000000 + ev->input_event_usec;
100 
101 		/* reset after pause */
102 		if (last_us + idle_reset < m->us) {
103 			m->max_frequency = 0.0;
104 			m->distance = 0;
105 		} else {
106 			double freq = get_frequency(last_us, m->us);
107 			push_frequency(m, freq);
108 			m->max_frequency = max(freq, m->max_frequency);
109 			return print_current_values(m);
110 		}
111 
112 		return 0;
113 	}
114 
115 	if (ev->type != EV_REL)
116 		return 0;
117 
118 	switch(ev->code) {
119 		case REL_X:
120 			m->distance += ev->value;
121 			break;
122 	}
123 
124 	return 0;
125 }
126 
127 static void
signal_handler(int signal)128 signal_handler(__attribute__((__unused__)) int signal)
129 {
130 	signalled++;
131 }
132 
133 static int
mainloop(struct libevdev * dev,struct measurements * m)134 mainloop(struct libevdev *dev, struct measurements *m) {
135 	struct pollfd fds;
136 
137 	fds.fd = libevdev_get_fd(dev);
138 	fds.events = POLLIN;
139 
140 	signal(SIGINT, signal_handler);
141 
142 	while (poll(&fds, 1, -1)) {
143 		struct input_event ev;
144 		int rc;
145 
146 		if (signalled)
147 			break;
148 
149 		do {
150 			rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
151 			if (rc == LIBEVDEV_READ_STATUS_SYNC) {
152 				fprintf(stderr, "Error: cannot keep up\n");
153 				return 1;
154 			}
155 
156 			if (rc != -EAGAIN && rc < 0) {
157 				fprintf(stderr, "Error: %s\n", strerror(-rc));
158 				return 1;
159 			}
160 
161 			if (rc == LIBEVDEV_READ_STATUS_SUCCESS)
162 				handle_event(m, &ev);
163 		} while (rc != -EAGAIN);
164 	}
165 
166 	return 0;
167 }
168 
169 static inline double
mean_frequency(struct measurements * m)170 mean_frequency(struct measurements *m)
171 {
172 	int idx;
173 
174 	idx = m->nfrequencies/2;
175 	return m->frequencies[idx];
176 }
177 
178 static inline const char*
bustype(int bustype)179 bustype(int bustype)
180 {
181 	const char *bus;
182 
183 	switch(bustype) {
184 		case BUS_PCI: bus = "pci"; break;
185 		case BUS_ISAPNP: bus = "isapnp"; break;
186 		case BUS_USB: bus = "usb"; break;
187 		case BUS_HIL: bus = "hil"; break;
188 		case BUS_BLUETOOTH: bus = "bluetooth"; break;
189 		case BUS_VIRTUAL: bus = "virtual"; break;
190 		default: bus = "unknown bus type"; break;
191 	}
192 
193 	return bus;
194 }
195 
196 static void
print_summary(struct libevdev * dev,struct measurements * m)197 print_summary(struct libevdev *dev, struct measurements *m)
198 {
199 	int res;
200 	int max_freq, mean_freq;
201 
202 	if (m->nfrequencies == 0) {
203 		fprintf(stderr, "Error: no matching events received.\n");
204 		return;
205 	}
206 
207 	max_freq = (int)m->max_frequency;
208 	mean_freq = (int)mean_frequency(m);
209 
210 	printf("Estimated sampling frequency: %dHz (mean %dHz)\n",
211 	       max_freq, mean_freq);
212 
213 	if (max_freq > mean_freq * 1.3)
214 		printf("WARNING: Max frequency is more than 30%% higher "
215 		       "than mean frequency. Manual verification required!\n");
216 
217 	printf("To calculate resolution, measure physical distance covered\n"
218 	       "and look up the matching resolution in the table below\n");
219 
220 	m->distance = abs(m->distance);
221 
222 	/* If the mouse has more than 2500dpi, the manufacturer usually
223 	   shows off on their website anyway */
224 	for (res = 400; res <= 2500; res += 200) {
225 		double inch = m->distance/(double)res;
226 		printf("%8dmm	%8.2fin	%8ddpi\n",
227 		       (int)(inch * 25.4), inch, res);
228 	}
229 	printf("If your resolution is not in the list, calculate it with:\n"
230 	       "\tresolution=%d/inches, or\n"
231 	       "\tresolution=%d * 25.4/mm\n", m->distance, m->distance);
232 
233 	printf("\n");
234 	printf("Entry for hwdb match (replace XXX with the resolution in DPI):\n"
235 	       "mouse:%s:v%04xp%04x:name:%s:\n"
236 	       " MOUSE_DPI=XXX@%d\n",
237 	       bustype(libevdev_get_id_bustype(dev)),
238 	       libevdev_get_id_vendor(dev),
239 	       libevdev_get_id_product(dev),
240 	       libevdev_get_name(dev),
241 	       (int)m->max_frequency);
242 }
243 
244 int
main(int argc,char ** argv)245 main (int argc, char **argv) {
246 	int rc;
247 	int fd;
248 	const char *path;
249 	struct libevdev *dev;
250 	struct measurements measurements = {0};
251 
252 	if (argc < 2)
253 		return usage(basename(argv[0]));
254 
255 	path = argv[1];
256 	if (path[0] == '-')
257 		return usage(basename(argv[0]));
258 
259 	fd = open(path, O_RDONLY|O_NONBLOCK);
260 	if (fd < 0) {
261 		fprintf(stderr, "Error opening the device: %s\n", strerror(errno));
262 		return 1;
263 	}
264 
265 	rc = libevdev_new_from_fd(fd, &dev);
266 	if (rc != 0) {
267 		fprintf(stderr, "Error fetching the device info: %s\n", strerror(-rc));
268 		return 1;
269 	}
270 
271 	if (libevdev_grab(dev, LIBEVDEV_GRAB) != 0) {
272 		fprintf(stderr, "Error: cannot grab the device, something else is grabbing it.\n");
273 		fprintf(stderr, "Use 'fuser -v %s' to find processes with an open fd\n", path);
274 		return 1;
275 	}
276 	libevdev_grab(dev, LIBEVDEV_UNGRAB);
277 
278 	printf("Mouse %s on %s\n", libevdev_get_name(dev), path);
279 	printf("Move the device 250mm/10in or more along the x-axis.\n");
280 	printf("Pause 3 seconds before movement to reset, Ctrl+C to exit.\n");
281 	setbuf(stdout, NULL);
282 
283 	rc = mainloop(dev, &measurements);
284 
285 	printf("\n");
286 
287 	print_summary(dev, &measurements);
288 
289 	libevdev_free(dev);
290 	close(fd);
291 
292 	return rc;
293 }
294