Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: wacom - add Intuos4 LED and OLED control

This commit enables control of the LEDs and OLED displays found on the
Wacom Intuos4 M, L, and XL. For this purpose, a new "wacom_led" attribute
group is added to the sysfs entry of the USB device.

This "wacom_led" group only shows up when the correct device (M, L, or XL)
is detected. The attributes are described in
Documentation/ABI/testing/sysfs-wacom

Signed-off-by: Eduard Hasenleithner <eduard@hasenleithner.at>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

authored by

Eduard Hasenleithner and committed by
Dmitry Torokhov
5d7e7d47 7e66eaf1

+341 -40
+64
Documentation/ABI/testing/sysfs-driver-wacom
··· 1 + What: /sys/class/hidraw/hidraw*/device/speed 2 + Date: April 2010 3 + Kernel Version: 2.6.35 4 + Contact: linux-bluetooth@vger.kernel.org 5 + Description: 6 + The /sys/class/hidraw/hidraw*/device/speed file controls 7 + reporting speed of Wacom bluetooth tablet. Reading from 8 + this file returns 1 if tablet reports in high speed mode 9 + or 0 otherwise. Writing to this file one of these values 10 + switches reporting speed. 11 + 12 + What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/led 13 + Date: August 2011 14 + Contact: linux-input@vger.kernel.org 15 + Description: 16 + Attribute group for control of the status LEDs and the OLED 17 + displays found on the Wacom Intuos 4 M, L, and XL tablets. This 18 + attribute group is not available for other Wacom tablets. 19 + Therefore its presence implicitly signifies the presence of 20 + said LEDs and OLED displays on the tablet device. 21 + 22 + What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/status0_luminance 23 + Date: August 2011 24 + Contact: linux-input@vger.kernel.org 25 + Description: 26 + Writing to this file sets the status LED luminance (0..127) 27 + when the stylus does not touch the tablet surface, and no 28 + button is pressed on the stylus. 29 + 30 + What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/status1_luminance 31 + Date: August 2011 32 + Contact: linux-input@vger.kernel.org 33 + Description: 34 + Writing to this file sets the status LED luminance (0..127) 35 + when the stylus touches the tablet surface, or any button is 36 + pressed on the stylus. 37 + 38 + What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/status_led_select 39 + Date: August 2011 40 + Contact: linux-input@vger.kernel.org 41 + Description: 42 + Writing to this file sets which one of the four status LEDs is 43 + active (0..3). The other three LEDs are always inactive. By 44 + means of specifying "-1" it is possible to set all status LEDs 45 + to inactive. 46 + 47 + What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/buttons_luminance 48 + Date: August 2011 49 + Contact: linux-input@vger.kernel.org 50 + Description: 51 + Writing to this file sets the overall luminance level (0..15) 52 + of all eight button OLED displays. 53 + 54 + What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/button<n>_rawimg 55 + Date: August 2011 56 + Contact: linux-input@vger.kernel.org 57 + Description: 58 + When writing a 1024 byte raw image in Wacom Intuos 4 59 + interleaving format to the file, the image shows up on Button N 60 + of the device. The image is a 64x32 pixel 4-bit gray image. The 61 + 1024 byte binary is split up into 16x 64 byte chunks. Each 64 62 + byte chunk encodes the image data for two consecutive lines on 63 + the display. The low nibble of each byte contains the first 64 + line, and the high nibble contains the second line.
-10
Documentation/ABI/testing/sysfs-wacom
··· 1 - What: /sys/class/hidraw/hidraw*/device/speed 2 - Date: April 2010 3 - Kernel Version: 2.6.35 4 - Contact: linux-bluetooth@vger.kernel.org 5 - Description: 6 - The /sys/class/hidraw/hidraw*/device/speed file controls 7 - reporting speed of wacom bluetooth tablet. Reading from 8 - this file returns 1 if tablet reports in high speed mode 9 - or 0 otherwise. Writing to this file one of these values 10 - switches reporting speed.
+6
drivers/input/tablet/wacom.h
··· 114 114 struct mutex lock; 115 115 bool open; 116 116 char phys[32]; 117 + struct wacom_led { 118 + u8 select; /* status led selector (0..3, -1=none) */ 119 + u8 llv; /* status led brightness no button */ 120 + u8 hlv; /* status led brightness button pressed */ 121 + u8 img_lum; /* OLED matrix display brightness */ 122 + } led; 117 123 }; 118 124 119 125 extern const struct usb_device_id wacom_ids[];
+271 -30
drivers/input/tablet/wacom_sys.c
··· 48 48 /* defines to get/set USB message */ 49 49 #define USB_REQ_GET_REPORT 0x01 50 50 #define USB_REQ_SET_REPORT 0x09 51 + 51 52 #define WAC_HID_FEATURE_REPORT 0x03 52 53 #define WAC_MSG_RETRIES 5 53 54 54 - static int usb_get_report(struct usb_interface *intf, unsigned char type, 55 - unsigned char id, void *buf, int size) 55 + #define WAC_CMD_LED_CONTROL 0x20 56 + #define WAC_CMD_ICON_START 0x21 57 + #define WAC_CMD_ICON_XFER 0x23 58 + #define WAC_CMD_RETRIES 10 59 + 60 + static int wacom_get_report(struct usb_interface *intf, u8 type, u8 id, 61 + void *buf, size_t size, unsigned int retries) 56 62 { 57 - return usb_control_msg(interface_to_usbdev(intf), 58 - usb_rcvctrlpipe(interface_to_usbdev(intf), 0), 59 - USB_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 60 - (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber, 61 - buf, size, 100); 63 + struct usb_device *dev = interface_to_usbdev(intf); 64 + int retval; 65 + 66 + do { 67 + retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 68 + USB_REQ_GET_REPORT, 69 + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 70 + (type << 8) + id, 71 + intf->altsetting[0].desc.bInterfaceNumber, 72 + buf, size, 100); 73 + } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries); 74 + 75 + return retval; 62 76 } 63 77 64 - static int usb_set_report(struct usb_interface *intf, unsigned char type, 65 - unsigned char id, void *buf, int size) 78 + static int wacom_set_report(struct usb_interface *intf, u8 type, u8 id, 79 + void *buf, size_t size, unsigned int retries) 66 80 { 67 - return usb_control_msg(interface_to_usbdev(intf), 68 - usb_sndctrlpipe(interface_to_usbdev(intf), 0), 69 - USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 70 - (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber, 71 - buf, size, 1000); 81 + struct usb_device *dev = interface_to_usbdev(intf); 82 + int retval; 83 + 84 + do { 85 + retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 86 + USB_REQ_SET_REPORT, 87 + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 88 + (type << 8) + id, 89 + intf->altsetting[0].desc.bInterfaceNumber, 90 + buf, size, 1000); 91 + } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries); 92 + 93 + return retval; 72 94 } 73 95 74 96 static void wacom_sys_irq(struct urb *urb) ··· 355 333 rep_data[2] = 0; 356 334 rep_data[3] = 0; 357 335 report_id = 3; 358 - error = usb_set_report(intf, WAC_HID_FEATURE_REPORT, 359 - report_id, rep_data, 4); 336 + error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT, 337 + report_id, rep_data, 4, 1); 360 338 if (error >= 0) 361 - error = usb_get_report(intf, 362 - WAC_HID_FEATURE_REPORT, report_id, 363 - rep_data, 4); 339 + error = wacom_get_report(intf, 340 + WAC_HID_FEATURE_REPORT, 341 + report_id, rep_data, 4, 1); 364 342 } while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES); 365 343 } else if (features->type != TABLETPC) { 366 344 do { 367 345 rep_data[0] = 2; 368 346 rep_data[1] = 2; 369 - error = usb_set_report(intf, WAC_HID_FEATURE_REPORT, 370 - report_id, rep_data, 2); 347 + error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT, 348 + report_id, rep_data, 2, 1); 371 349 if (error >= 0) 372 - error = usb_get_report(intf, 373 - WAC_HID_FEATURE_REPORT, report_id, 374 - rep_data, 2); 350 + error = wacom_get_report(intf, 351 + WAC_HID_FEATURE_REPORT, 352 + report_id, rep_data, 2, 1); 375 353 } while ((error < 0 || rep_data[1] != 2) && limit++ < WAC_MSG_RETRIES); 376 354 } 377 355 ··· 490 468 } 491 469 } 492 470 471 + static int wacom_led_control(struct wacom *wacom) 472 + { 473 + unsigned char *buf; 474 + int retval; 475 + 476 + buf = kzalloc(9, GFP_KERNEL); 477 + if (!buf) 478 + return -ENOMEM; 479 + 480 + buf[0] = WAC_CMD_LED_CONTROL; 481 + buf[1] = wacom->led.select >= 0 ? wacom->led.select | 4 : 0; 482 + buf[2] = wacom->led.llv; 483 + buf[3] = wacom->led.hlv; 484 + buf[4] = wacom->led.img_lum; 485 + 486 + retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_LED_CONTROL, 487 + buf, 9, WAC_CMD_RETRIES); 488 + kfree(buf); 489 + 490 + return retval; 491 + } 492 + 493 + static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *img) 494 + { 495 + unsigned char *buf; 496 + int i, retval; 497 + 498 + buf = kzalloc(259, GFP_KERNEL); 499 + if (!buf) 500 + return -ENOMEM; 501 + 502 + /* Send 'start' command */ 503 + buf[0] = WAC_CMD_ICON_START; 504 + buf[1] = 1; 505 + retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_START, 506 + buf, 2, WAC_CMD_RETRIES); 507 + if (retval < 0) 508 + goto out; 509 + 510 + buf[0] = WAC_CMD_ICON_XFER; 511 + buf[1] = button_id & 0x07; 512 + for (i = 0; i < 4; i++) { 513 + buf[2] = i; 514 + memcpy(buf + 3, img + i * 256, 256); 515 + 516 + retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_XFER, 517 + buf, 259, WAC_CMD_RETRIES); 518 + if (retval < 0) 519 + break; 520 + } 521 + 522 + /* Send 'stop' */ 523 + buf[0] = WAC_CMD_ICON_START; 524 + buf[1] = 0; 525 + wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_START, 526 + buf, 2, WAC_CMD_RETRIES); 527 + 528 + out: 529 + kfree(buf); 530 + return retval; 531 + } 532 + 533 + static ssize_t wacom_led_select_store(struct device *dev, 534 + struct device_attribute *attr, 535 + const char *buf, size_t count) 536 + { 537 + struct wacom *wacom = dev_get_drvdata(dev); 538 + unsigned int id; 539 + int err; 540 + 541 + err = kstrtouint(buf, 10, &id); 542 + if (err) 543 + return err; 544 + 545 + mutex_lock(&wacom->lock); 546 + 547 + wacom->led.select = id; 548 + err = wacom_led_control(wacom); 549 + 550 + mutex_unlock(&wacom->lock); 551 + 552 + return err < 0 ? err : count; 553 + } 554 + 555 + static DEVICE_ATTR(status_led_select, S_IWUSR, NULL, wacom_led_select_store); 556 + 557 + static ssize_t wacom_luminance_store(struct wacom *wacom, u8 *dest, 558 + const char *buf, size_t count) 559 + { 560 + unsigned int value; 561 + int err; 562 + 563 + err = kstrtouint(buf, 10, &value); 564 + if (err) 565 + return err; 566 + 567 + mutex_lock(&wacom->lock); 568 + 569 + *dest = value & 0x7f; 570 + err = wacom_led_control(wacom); 571 + 572 + mutex_unlock(&wacom->lock); 573 + 574 + return err < 0 ? err : count; 575 + } 576 + 577 + #define DEVICE_LUMINANCE_ATTR(name, field) \ 578 + static ssize_t wacom_##name##_luminance_store(struct device *dev, \ 579 + struct device_attribute *attr, const char *buf, size_t count) \ 580 + { \ 581 + struct wacom *wacom = dev_get_drvdata(dev); \ 582 + \ 583 + return wacom_luminance_store(wacom, &wacom->led.field, \ 584 + buf, count); \ 585 + } \ 586 + static DEVICE_ATTR(name##_luminance, S_IWUSR, \ 587 + NULL, wacom_##name##_luminance_store) 588 + 589 + DEVICE_LUMINANCE_ATTR(status0, llv); 590 + DEVICE_LUMINANCE_ATTR(status1, hlv); 591 + DEVICE_LUMINANCE_ATTR(buttons, img_lum); 592 + 593 + static ssize_t wacom_button_image_store(struct device *dev, int button_id, 594 + const char *buf, size_t count) 595 + { 596 + struct wacom *wacom = dev_get_drvdata(dev); 597 + int err; 598 + 599 + if (count != 1024) 600 + return -EINVAL; 601 + 602 + mutex_lock(&wacom->lock); 603 + 604 + err = wacom_led_putimage(wacom, button_id, buf); 605 + 606 + mutex_unlock(&wacom->lock); 607 + 608 + return err < 0 ? err : count; 609 + } 610 + 611 + #define DEVICE_BTNIMG_ATTR(BUTTON_ID) \ 612 + static ssize_t wacom_btnimg##BUTTON_ID##_store(struct device *dev, \ 613 + struct device_attribute *attr, const char *buf, size_t count) \ 614 + { \ 615 + return wacom_button_image_store(dev, BUTTON_ID, buf, count); \ 616 + } \ 617 + static DEVICE_ATTR(button##BUTTON_ID##_rawimg, S_IWUSR, \ 618 + NULL, wacom_btnimg##BUTTON_ID##_store) 619 + 620 + DEVICE_BTNIMG_ATTR(0); 621 + DEVICE_BTNIMG_ATTR(1); 622 + DEVICE_BTNIMG_ATTR(2); 623 + DEVICE_BTNIMG_ATTR(3); 624 + DEVICE_BTNIMG_ATTR(4); 625 + DEVICE_BTNIMG_ATTR(5); 626 + DEVICE_BTNIMG_ATTR(6); 627 + DEVICE_BTNIMG_ATTR(7); 628 + 629 + static struct attribute *wacom_led_attrs[] = { 630 + &dev_attr_status0_luminance.attr, 631 + &dev_attr_status1_luminance.attr, 632 + &dev_attr_status_led_select.attr, 633 + &dev_attr_buttons_luminance.attr, 634 + &dev_attr_button0_rawimg.attr, 635 + &dev_attr_button1_rawimg.attr, 636 + &dev_attr_button2_rawimg.attr, 637 + &dev_attr_button3_rawimg.attr, 638 + &dev_attr_button4_rawimg.attr, 639 + &dev_attr_button5_rawimg.attr, 640 + &dev_attr_button6_rawimg.attr, 641 + &dev_attr_button7_rawimg.attr, 642 + NULL 643 + }; 644 + 645 + static struct attribute_group wacom_led_attr_group = { 646 + .name = "wacom_led", 647 + .attrs = wacom_led_attrs, 648 + }; 649 + 650 + static int wacom_initialize_leds(struct wacom *wacom) 651 + { 652 + int error; 653 + 654 + if (wacom->wacom_wac.features.type >= INTUOS4 && 655 + wacom->wacom_wac.features.type <= INTUOS4L) { 656 + 657 + /* Initialize default values */ 658 + wacom->led.select = 0; 659 + wacom->led.llv = 30; 660 + wacom->led.hlv = 20; 661 + wacom->led.img_lum = 10; 662 + wacom_led_control(wacom); 663 + 664 + error = sysfs_create_group(&wacom->intf->dev.kobj, 665 + &wacom_led_attr_group); 666 + if (error) { 667 + dev_err(&wacom->intf->dev, 668 + "cannot create sysfs group err: %d\n", error); 669 + return error; 670 + } 671 + } 672 + 673 + return 0; 674 + } 675 + 676 + static void wacom_destroy_leds(struct wacom *wacom) 677 + { 678 + if (wacom->wacom_wac.features.type >= INTUOS4 && 679 + wacom->wacom_wac.features.type <= INTUOS4L) { 680 + sysfs_remove_group(&wacom->intf->dev.kobj, 681 + &wacom_led_attr_group); 682 + } 683 + } 684 + 493 685 static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) 494 686 { 495 687 struct usb_device *dev = interface_to_usbdev(intf); ··· 792 556 wacom->irq->transfer_dma = wacom->data_dma; 793 557 wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 794 558 795 - error = input_register_device(input_dev); 559 + error = wacom_initialize_leds(wacom); 796 560 if (error) 797 561 goto fail4; 562 + 563 + error = input_register_device(input_dev); 564 + if (error) 565 + goto fail5; 798 566 799 567 /* Note that if query fails it is not a hard failure */ 800 568 wacom_query_tablet_data(intf, features); ··· 806 566 usb_set_intfdata(intf, wacom); 807 567 return 0; 808 568 569 + fail5: wacom_destroy_leds(wacom); 809 570 fail4: wacom_remove_shared_data(wacom_wac); 810 571 fail3: usb_free_urb(wacom->irq); 811 572 fail2: usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma); ··· 823 582 824 583 usb_kill_urb(wacom->irq); 825 584 input_unregister_device(wacom->wacom_wac.input); 585 + wacom_destroy_leds(wacom); 826 586 usb_free_urb(wacom->irq); 827 587 usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX, 828 588 wacom->wacom_wac.data, wacom->data_dma); ··· 846 604 { 847 605 struct wacom *wacom = usb_get_intfdata(intf); 848 606 struct wacom_features *features = &wacom->wacom_wac.features; 849 - int rv; 607 + int rv = 0; 850 608 851 609 mutex_lock(&wacom->lock); 852 610 853 611 /* switch to wacom mode first */ 854 612 wacom_query_tablet_data(intf, features); 613 + wacom_led_control(wacom); 855 614 856 - if (wacom->open) 857 - rv = usb_submit_urb(wacom->irq, GFP_NOIO); 858 - else 859 - rv = 0; 615 + if (wacom->open && usb_submit_urb(wacom->irq, GFP_NOIO) < 0) 616 + rv = -EIO; 860 617 861 618 mutex_unlock(&wacom->lock); 862 619