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
35struct pad_led_group {
36	struct libinput_tablet_pad_mode_group base;
37	struct list led_list;
38	struct list toggle_button_list;
39};
40
41struct pad_mode_toggle_button {
42	struct list link;
43	unsigned int button_index;
44};
45
46struct 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
54static inline struct pad_mode_toggle_button *
55pad_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
68static inline void
69pad_mode_toggle_button_destroy(struct pad_mode_toggle_button* button)
70{
71	list_remove(&button->link);
72	free(button);
73}
74
75static inline int
76pad_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
104static inline void
105pad_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
115static inline struct pad_mode_led *
116pad_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
148error:
149	pad_led_destroy(libinput, led);
150	return NULL;
151}
152#endif /* HAVE_LIBWACOM */
153
154static void
155pad_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
170static struct pad_led_group *
171pad_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
191static inline bool
192is_litest_device(struct evdev_device *device)
193{
194	return !!udev_device_get_property_value(device->udev_device,
195						"LIBINPUT_TEST_DEVICE");
196}
197
198static inline struct pad_led_group *
199pad_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
232error:
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
242static inline bool
243pad_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
277static int
278pad_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
341static inline struct libinput_tablet_pad_mode_group *
342pad_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
356static inline int
357pad_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
380static int
381pad_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
437static void
438pad_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
464static void
465pad_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
491static int
492pad_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
521out:
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
534static int
535pad_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
556int
557pad_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
581void
582pad_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
590void
591pad_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
609int
610evdev_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
625struct libinput_tablet_pad_mode_group *
626evdev_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