• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2016 Red Hat, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include "config.h"
25 
26 #include <limits.h>
27 #include <fcntl.h>
28 
29 #include "evdev-tablet-pad.h"
30 
31 #if HAVE_LIBWACOM
32 #include <libwacom/libwacom.h>
33 #endif
34 
35 struct pad_led_group {
36 	struct libinput_tablet_pad_mode_group base;
37 	struct list led_list;
38 	struct list toggle_button_list;
39 };
40 
41 struct pad_mode_toggle_button {
42 	struct list link;
43 	unsigned int button_index;
44 };
45 
46 struct pad_mode_led {
47 	struct list link;
48 	/* /sys/devices/..../input1235/input1235::wacom-led_0.1/brightness */
49 	int brightness_fd;
50 	int mode_idx;
51 };
52 
53 static inline struct pad_mode_toggle_button *
pad_mode_toggle_button_new(struct pad_dispatch * pad,struct libinput_tablet_pad_mode_group * group,unsigned int button_index)54 pad_mode_toggle_button_new(struct pad_dispatch *pad,
55 			   struct libinput_tablet_pad_mode_group *group,
56 			   unsigned int button_index)
57 {
58 	struct pad_mode_toggle_button *button;
59 
60 	button = zalloc(sizeof *button);
61 	button->button_index = button_index;
62 
63 	return button;
64 }
65 
66 static inline void
pad_mode_toggle_button_destroy(struct pad_mode_toggle_button * button)67 pad_mode_toggle_button_destroy(struct pad_mode_toggle_button* button)
68 {
69 	list_remove(&button->link);
70 	free(button);
71 }
72 
73 static inline int
pad_led_group_get_mode(struct pad_led_group * group)74 pad_led_group_get_mode(struct pad_led_group *group)
75 {
76 	char buf[4] = {0};
77 	int rc;
78 	unsigned int brightness;
79 	struct pad_mode_led *led;
80 
81 	list_for_each(led, &group->led_list, link) {
82 		rc = lseek(led->brightness_fd, 0, SEEK_SET);
83 		if (rc == -1)
84 			return -errno;
85 
86 		rc = read(led->brightness_fd, buf, sizeof(buf) - 1);
87 		if (rc == -1)
88 			return -errno;
89 
90 		rc = sscanf(buf, "%u\n", &brightness);
91 		if (rc != 1)
92 			return -EINVAL;
93 
94 		/* Assumption: only one LED lit up at any time */
95 		if (brightness != 0)
96 			return led->mode_idx;
97 	}
98 
99 	return -EINVAL;
100 }
101 
102 static inline void
pad_led_destroy(struct libinput * libinput,struct pad_mode_led * led)103 pad_led_destroy(struct libinput *libinput,
104 		struct pad_mode_led *led)
105 {
106 	list_remove(&led->link);
107 	if (led->brightness_fd != -1)
108 		close_restricted(libinput, led->brightness_fd);
109 	free(led);
110 }
111 
112 static inline struct pad_mode_led *
pad_led_new(struct libinput * libinput,const char * prefix,int group,int mode)113 pad_led_new(struct libinput *libinput, const char *prefix, int group, int mode)
114 {
115 	struct pad_mode_led *led;
116 	char path[PATH_MAX];
117 	int rc, fd;
118 
119 	led = zalloc(sizeof *led);
120 	led->brightness_fd = -1;
121 	led->mode_idx = mode;
122 	list_init(&led->link);
123 
124 	/* /sys/devices/..../input1235/input1235::wacom-0.1/brightness,
125 	 * where 0 and 1 are group and mode index. */
126 	rc = snprintf(path,
127 		      sizeof(path),
128 		      "%s%d.%d/brightness",
129 		      prefix,
130 		      group,
131 		      mode);
132 	if (rc == -1)
133 		goto error;
134 
135 	fd = open_restricted(libinput, path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
136 	if (fd < 0) {
137 		errno = -fd;
138 		goto error;
139 	}
140 
141 	led->brightness_fd = fd;
142 
143 	return led;
144 
145 error:
146 	pad_led_destroy(libinput, led);
147 	return NULL;
148 }
149 
150 static void
pad_led_group_destroy(struct libinput_tablet_pad_mode_group * g)151 pad_led_group_destroy(struct libinput_tablet_pad_mode_group *g)
152 {
153 	struct pad_led_group *group = (struct pad_led_group *)g;
154 	struct pad_mode_toggle_button *button;
155 	struct pad_mode_led *led;
156 
157 	list_for_each_safe(button, &group->toggle_button_list, link)
158 		pad_mode_toggle_button_destroy(button);
159 
160 	list_for_each_safe(led, &group->led_list, link)
161 		pad_led_destroy(g->device->seat->libinput, led);
162 
163 	free(group);
164 }
165 
166 static struct pad_led_group *
pad_group_new_basic(struct pad_dispatch * pad,unsigned int group_index,int nleds)167 pad_group_new_basic(struct pad_dispatch *pad,
168 		    unsigned int group_index,
169 		    int nleds)
170 {
171 	struct pad_led_group *group;
172 
173 	group = zalloc(sizeof *group);
174 	group->base.device = &pad->device->base;
175 	group->base.refcount = 1;
176 	group->base.index = group_index;
177 	group->base.current_mode = 0;
178 	group->base.num_modes = nleds;
179 	group->base.destroy = pad_led_group_destroy;
180 	list_init(&group->toggle_button_list);
181 	list_init(&group->led_list);
182 
183 	return group;
184 }
185 
186 static inline bool
is_litest_device(struct evdev_device * device)187 is_litest_device(struct evdev_device *device)
188 {
189 	return !!udev_device_get_property_value(device->udev_device,
190 						"LIBINPUT_TEST_DEVICE");
191 }
192 
193 static inline struct pad_led_group *
pad_group_new(struct pad_dispatch * pad,unsigned int group_index,int nleds,const char * syspath)194 pad_group_new(struct pad_dispatch *pad,
195 	      unsigned int group_index,
196 	      int nleds,
197 	      const char *syspath)
198 {
199 	struct libinput *libinput = pad->device->base.seat->libinput;
200 	struct pad_led_group *group;
201 	int rc;
202 
203 	group = pad_group_new_basic(pad, group_index, nleds);
204 	if (!group)
205 		return NULL;
206 
207 	while (nleds--) {
208 		struct pad_mode_led *led;
209 
210 		led = pad_led_new(libinput, syspath, group_index, nleds);
211 		if (!led)
212 			goto error;
213 
214 		list_insert(&group->led_list, &led->link);
215 	}
216 
217 	rc = pad_led_group_get_mode(group);
218 	if (rc < 0) {
219 		errno = -rc;
220 		goto error;
221 	}
222 
223 	group->base.current_mode = rc;
224 
225 	return group;
226 
227 error:
228 	if (!is_litest_device(pad->device))
229 		evdev_log_error(pad->device,
230 				"unable to init LED group: %s\n",
231 				strerror(errno));
232 	pad_led_group_destroy(&group->base);
233 
234 	return NULL;
235 }
236 
237 static inline bool
pad_led_get_sysfs_base_path(struct evdev_device * device,char * path_out,size_t path_out_sz)238 pad_led_get_sysfs_base_path(struct evdev_device *device,
239 			    char *path_out,
240 			    size_t path_out_sz)
241 {
242 	struct udev_device *parent, *udev_device;
243 	const char *test_path;
244 	int rc;
245 
246 	udev_device = device->udev_device;
247 
248 	/* For testing purposes only allow for a base path set through a
249 	 * udev rule. We still expect the normal directory hierarchy inside */
250 	test_path = udev_device_get_property_value(udev_device,
251 						   "LIBINPUT_TEST_TABLET_PAD_SYSFS_PATH");
252 	if (test_path) {
253 		rc = snprintf(path_out, path_out_sz, "%s", test_path);
254 		return rc != -1;
255 	}
256 
257 	parent = udev_device_get_parent_with_subsystem_devtype(udev_device,
258 							       "input",
259 							       NULL);
260 	if (!parent)
261 		return false;
262 
263 	rc = snprintf(path_out,
264 		      path_out_sz,
265 		      "%s/%s::wacom-",
266 		      udev_device_get_syspath(parent),
267 		      udev_device_get_sysname(parent));
268 
269 	return rc != -1;
270 }
271 
272 #if HAVE_LIBWACOM
273 static int
pad_init_led_groups(struct pad_dispatch * pad,struct evdev_device * device,WacomDevice * wacom)274 pad_init_led_groups(struct pad_dispatch *pad,
275 		    struct evdev_device *device,
276 		    WacomDevice *wacom)
277 {
278 	const WacomStatusLEDs *leds;
279 	int nleds, nmodes;
280 	int i;
281 	struct pad_led_group *group;
282 	char syspath[PATH_MAX];
283 
284 	leds = libwacom_get_status_leds(wacom, &nleds);
285 	if (nleds == 0)
286 		return 1;
287 
288 	/* syspath is /sys/class/leds/input1234/input12345::wacom-" and
289 	   only needs the group + mode appended */
290 	if (!pad_led_get_sysfs_base_path(device, syspath, sizeof(syspath)))
291 		return 1;
292 
293 	for (i = 0; i < nleds; i++) {
294 		switch(leds[i]) {
295 		case WACOM_STATUS_LED_UNAVAILABLE:
296 			evdev_log_bug_libinput(device,
297 					       "Invalid led type %d\n",
298 					       leds[i]);
299 			return 1;
300 		case WACOM_STATUS_LED_RING:
301 			nmodes = libwacom_get_ring_num_modes(wacom);
302 			group = pad_group_new(pad, i, nmodes, syspath);
303 			if (!group)
304 				return 1;
305 			list_insert(&pad->modes.mode_group_list, &group->base.link);
306 			break;
307 		case WACOM_STATUS_LED_RING2:
308 			nmodes = libwacom_get_ring2_num_modes(wacom);
309 			group = pad_group_new(pad, i, nmodes, syspath);
310 			if (!group)
311 				return 1;
312 			list_insert(&pad->modes.mode_group_list, &group->base.link);
313 			break;
314 		case WACOM_STATUS_LED_TOUCHSTRIP:
315 			nmodes = libwacom_get_strips_num_modes(wacom);
316 			group = pad_group_new(pad, i, nmodes, syspath);
317 			if (!group)
318 				return 1;
319 			list_insert(&pad->modes.mode_group_list, &group->base.link);
320 			break;
321 		case WACOM_STATUS_LED_TOUCHSTRIP2:
322 			/* there is no get_strips2_... */
323 			nmodes = libwacom_get_strips_num_modes(wacom);
324 			group = pad_group_new(pad, i, nmodes, syspath);
325 			if (!group)
326 				return 1;
327 			list_insert(&pad->modes.mode_group_list, &group->base.link);
328 			break;
329 		}
330 	}
331 
332 	return 0;
333 }
334 
335 #endif
336 
337 static inline struct libinput_tablet_pad_mode_group *
pad_get_mode_group(struct pad_dispatch * pad,unsigned int index)338 pad_get_mode_group(struct pad_dispatch *pad, unsigned int index)
339 {
340 	struct libinput_tablet_pad_mode_group *group;
341 
342 	list_for_each(group, &pad->modes.mode_group_list, link) {
343 		if (group->index == index)
344 			return group;
345 	}
346 
347 	return NULL;
348 }
349 
350 #if HAVE_LIBWACOM
351 
352 static inline int
pad_find_button_group(WacomDevice * wacom,int button_index,WacomButtonFlags button_flags)353 pad_find_button_group(WacomDevice *wacom,
354 		      int button_index,
355 		      WacomButtonFlags button_flags)
356 {
357 	int i;
358 	WacomButtonFlags flags;
359 
360 	for (i = 0; i < libwacom_get_num_buttons(wacom); i++) {
361 		if (i == button_index)
362 			continue;
363 
364 		flags = libwacom_get_button_flag(wacom, 'A' + i);
365 		if ((flags & WACOM_BUTTON_MODESWITCH) == 0)
366 			continue;
367 
368 		if ((flags & WACOM_BUTTON_DIRECTION) ==
369 			(button_flags & WACOM_BUTTON_DIRECTION))
370 			return libwacom_get_button_led_group(wacom, 'A' + i);
371 	}
372 
373 	return -1;
374 }
375 
376 static int
pad_init_mode_buttons(struct pad_dispatch * pad,WacomDevice * wacom)377 pad_init_mode_buttons(struct pad_dispatch *pad,
378 		      WacomDevice *wacom)
379 {
380 	struct libinput_tablet_pad_mode_group *group;
381 	unsigned int group_idx;
382 	int i;
383 	WacomButtonFlags flags;
384 
385 	/* libwacom numbers buttons as 'A', 'B', etc. We number them with 0,
386 	 * 1, ...
387 	 */
388 	for (i = 0; i < libwacom_get_num_buttons(wacom); i++) {
389 		group_idx = libwacom_get_button_led_group(wacom, 'A' + i);
390 		flags = libwacom_get_button_flag(wacom, 'A' + i);
391 
392 		/* If this button is not a mode toggle button, find the mode
393 		 * toggle button with the same position flags and take that
394 		 * button's group idx */
395 		if ((int)group_idx == -1) {
396 			group_idx = pad_find_button_group(wacom, i, flags);
397 		}
398 
399 		if ((int)group_idx == -1) {
400 			evdev_log_bug_libinput(pad->device,
401 					       "unhandled position for button %i\n",
402 					       i);
403 			return 1;
404 		}
405 
406 		group = pad_get_mode_group(pad, group_idx);
407 		if (!group) {
408 			evdev_log_bug_libinput(pad->device,
409 					       "Failed to find group %d for button %i\n",
410 					       group_idx,
411 					 i);
412 			return 1;
413 		}
414 
415 		group->button_mask |= 1 << i;
416 
417 		if (flags & WACOM_BUTTON_MODESWITCH) {
418 			struct pad_mode_toggle_button *b;
419 			struct pad_led_group *g;
420 
421 			b = pad_mode_toggle_button_new(pad, group, i);
422 			if (!b)
423 				return 1;
424 			g = (struct pad_led_group*)group;
425 			list_insert(&g->toggle_button_list, &b->link);
426 			group->toggle_button_mask |= 1 << i;
427 		}
428 	}
429 
430 	return 0;
431 }
432 
433 static void
pad_init_mode_rings(struct pad_dispatch * pad,WacomDevice * wacom)434 pad_init_mode_rings(struct pad_dispatch *pad, WacomDevice *wacom)
435 {
436 	struct libinput_tablet_pad_mode_group *group;
437 	const WacomStatusLEDs *leds;
438 	int i, nleds;
439 
440 	leds = libwacom_get_status_leds(wacom, &nleds);
441 	if (nleds == 0)
442 		return;
443 
444 	for (i = 0; i < nleds; i++) {
445 		switch(leds[i]) {
446 		case WACOM_STATUS_LED_RING:
447 			group = pad_get_mode_group(pad, i);
448 			group->ring_mask |= 0x1;
449 			break;
450 		case WACOM_STATUS_LED_RING2:
451 			group = pad_get_mode_group(pad, i);
452 			group->ring_mask |= 0x2;
453 			break;
454 		default:
455 			break;
456 		}
457 	}
458 }
459 
460 static void
pad_init_mode_strips(struct pad_dispatch * pad,WacomDevice * wacom)461 pad_init_mode_strips(struct pad_dispatch *pad, WacomDevice *wacom)
462 {
463 	struct libinput_tablet_pad_mode_group *group;
464 	const WacomStatusLEDs *leds;
465 	int i, nleds;
466 
467 	leds = libwacom_get_status_leds(wacom, &nleds);
468 	if (nleds == 0)
469 		return;
470 
471 	for (i = 0; i < nleds; i++) {
472 		switch(leds[i]) {
473 		case WACOM_STATUS_LED_TOUCHSTRIP:
474 			group = pad_get_mode_group(pad, i);
475 			group->strip_mask |= 0x1;
476 			break;
477 		case WACOM_STATUS_LED_TOUCHSTRIP2:
478 			group = pad_get_mode_group(pad, i);
479 			group->strip_mask |= 0x2;
480 			break;
481 		default:
482 			break;
483 		}
484 	}
485 }
486 
487 static int
pad_init_leds_from_libwacom(struct pad_dispatch * pad,struct evdev_device * device)488 pad_init_leds_from_libwacom(struct pad_dispatch *pad,
489 			    struct evdev_device *device)
490 {
491 	struct libinput *li = pad_libinput_context(pad);
492 	WacomDeviceDatabase *db = NULL;
493 	WacomDevice *wacom = NULL;
494 	int rc = 1;
495 
496 	db = libinput_libwacom_ref(li);
497 	if (!db)
498 		goto out;
499 
500 	wacom = libwacom_new_from_path(db,
501 				       udev_device_get_devnode(device->udev_device),
502 				       WFALLBACK_NONE,
503 				       NULL);
504 	if (!wacom)
505 		goto out;
506 
507 	rc = pad_init_led_groups(pad, device, wacom);
508 	if (rc != 0)
509 		goto out;
510 
511 	if ((rc = pad_init_mode_buttons(pad, wacom)) != 0)
512 		goto out;
513 
514 	pad_init_mode_rings(pad, wacom);
515 	pad_init_mode_strips(pad, wacom);
516 
517 out:
518 	if (wacom)
519 		libwacom_destroy(wacom);
520 	if (db)
521 		libinput_libwacom_unref(li);
522 
523 	if (rc != 0)
524 		pad_destroy_leds(pad);
525 
526 	return rc;
527 }
528 #endif /* HAVE_LIBWACOM */
529 
530 static int
pad_init_fallback_group(struct pad_dispatch * pad)531 pad_init_fallback_group(struct pad_dispatch *pad)
532 {
533 	struct pad_led_group *group;
534 
535 	group = pad_group_new_basic(pad, 0, 1);
536 	if (!group)
537 		return 1;
538 
539 	/* If we only have one group, all buttons/strips/rings are part of
540 	 * that group. We rely on the other layers to filter out invalid
541 	 * indices */
542 	group->base.button_mask = -1;
543 	group->base.strip_mask = -1;
544 	group->base.ring_mask = -1;
545 	group->base.toggle_button_mask = 0;
546 
547 	list_insert(&pad->modes.mode_group_list, &group->base.link);
548 
549 	return 0;
550 }
551 
552 int
pad_init_leds(struct pad_dispatch * pad,struct evdev_device * device)553 pad_init_leds(struct pad_dispatch *pad,
554 	      struct evdev_device *device)
555 {
556 	int rc = 1;
557 
558 	list_init(&pad->modes.mode_group_list);
559 
560 	if (pad->nbuttons > 32) {
561 		evdev_log_bug_libinput(pad->device,
562 				       "Too many pad buttons for modes %d\n",
563 				       pad->nbuttons);
564 		return rc;
565 	}
566 
567 	/* If libwacom fails, we init one fallback group anyway */
568 #if HAVE_LIBWACOM
569 	rc = pad_init_leds_from_libwacom(pad, device);
570 #endif
571 	if (rc != 0)
572 		rc = pad_init_fallback_group(pad);
573 
574 	return rc;
575 }
576 
577 void
pad_destroy_leds(struct pad_dispatch * pad)578 pad_destroy_leds(struct pad_dispatch *pad)
579 {
580 	struct libinput_tablet_pad_mode_group *group;
581 
582 	list_for_each_safe(group, &pad->modes.mode_group_list, link)
583 		libinput_tablet_pad_mode_group_unref(group);
584 }
585 
586 void
pad_button_update_mode(struct libinput_tablet_pad_mode_group * g,unsigned int button_index,enum libinput_button_state state)587 pad_button_update_mode(struct libinput_tablet_pad_mode_group *g,
588 		       unsigned int button_index,
589 		       enum libinput_button_state state)
590 {
591 	struct pad_led_group *group = (struct pad_led_group*)g;
592 	int rc;
593 
594 	if (state != LIBINPUT_BUTTON_STATE_PRESSED)
595 		return;
596 
597 	if (!libinput_tablet_pad_mode_group_button_is_toggle(g, button_index))
598 		return;
599 
600 	rc = pad_led_group_get_mode(group);
601 	if (rc >= 0)
602 		group->base.current_mode = rc;
603 }
604 
605 int
evdev_device_tablet_pad_get_num_mode_groups(struct evdev_device * device)606 evdev_device_tablet_pad_get_num_mode_groups(struct evdev_device *device)
607 {
608 	struct pad_dispatch *pad = (struct pad_dispatch*)device->dispatch;
609 	struct libinput_tablet_pad_mode_group *group;
610 	int num_groups = 0;
611 
612 	if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD))
613 		return -1;
614 
615 	list_for_each(group, &pad->modes.mode_group_list, link)
616 		num_groups++;
617 
618 	return num_groups;
619 }
620 
621 struct libinput_tablet_pad_mode_group *
evdev_device_tablet_pad_get_mode_group(struct evdev_device * device,unsigned int index)622 evdev_device_tablet_pad_get_mode_group(struct evdev_device *device,
623 				       unsigned int index)
624 {
625 	struct pad_dispatch *pad = (struct pad_dispatch*)device->dispatch;
626 
627 	if (!(device->seat_caps & EVDEV_DEVICE_TABLET_PAD))
628 		return NULL;
629 
630 	if (index >=
631 	    (unsigned int)evdev_device_tablet_pad_get_num_mode_groups(device))
632 		return NULL;
633 
634 	return pad_get_mode_group(pad, index);
635 }
636