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