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