diff options
author | Eduard Hasenleithner <eduard@hasenleithner.at> | 2011-09-07 17:08:54 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2011-09-07 17:19:01 -0400 |
commit | 5d7e7d479856f23eebc272128905a7ecada367fb (patch) | |
tree | de9a5eab7bee6007b7a576b6f96a9a32ce06dc57 /drivers/input/tablet/wacom_sys.c | |
parent | 7e66eaf14e19c032433be7c4df3c892fa2a5282f (diff) |
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>
Diffstat (limited to 'drivers/input/tablet/wacom_sys.c')
-rw-r--r-- | drivers/input/tablet/wacom_sys.c | 301 |
1 files changed, 271 insertions, 30 deletions
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index d27c9d91630b..0eccf57df5cd 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c | |||
@@ -48,27 +48,49 @@ struct hid_descriptor { | |||
48 | /* defines to get/set USB message */ | 48 | /* defines to get/set USB message */ |
49 | #define USB_REQ_GET_REPORT 0x01 | 49 | #define USB_REQ_GET_REPORT 0x01 |
50 | #define USB_REQ_SET_REPORT 0x09 | 50 | #define USB_REQ_SET_REPORT 0x09 |
51 | |||
51 | #define WAC_HID_FEATURE_REPORT 0x03 | 52 | #define WAC_HID_FEATURE_REPORT 0x03 |
52 | #define WAC_MSG_RETRIES 5 | 53 | #define WAC_MSG_RETRIES 5 |
53 | 54 | ||
54 | static int usb_get_report(struct usb_interface *intf, unsigned char type, | 55 | #define WAC_CMD_LED_CONTROL 0x20 |
55 | unsigned char id, void *buf, int size) | 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), | 63 | struct usb_device *dev = interface_to_usbdev(intf); |
58 | usb_rcvctrlpipe(interface_to_usbdev(intf), 0), | 64 | int retval; |
59 | USB_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, | 65 | |
60 | (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber, | 66 | do { |
61 | buf, size, 100); | 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, | 78 | static int wacom_set_report(struct usb_interface *intf, u8 type, u8 id, |
65 | unsigned char id, void *buf, int size) | 79 | void *buf, size_t size, unsigned int retries) |
66 | { | 80 | { |
67 | return usb_control_msg(interface_to_usbdev(intf), | 81 | struct usb_device *dev = interface_to_usbdev(intf); |
68 | usb_sndctrlpipe(interface_to_usbdev(intf), 0), | 82 | int retval; |
69 | USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, | 83 | |
70 | (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber, | 84 | do { |
71 | buf, size, 1000); | 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 | static void wacom_sys_irq(struct urb *urb) | 96 | static void wacom_sys_irq(struct urb *urb) |
@@ -333,23 +355,23 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat | |||
333 | rep_data[2] = 0; | 355 | rep_data[2] = 0; |
334 | rep_data[3] = 0; | 356 | rep_data[3] = 0; |
335 | report_id = 3; | 357 | report_id = 3; |
336 | error = usb_set_report(intf, WAC_HID_FEATURE_REPORT, | 358 | error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT, |
337 | report_id, rep_data, 4); | 359 | report_id, rep_data, 4, 1); |
338 | if (error >= 0) | 360 | if (error >= 0) |
339 | error = usb_get_report(intf, | 361 | error = wacom_get_report(intf, |
340 | WAC_HID_FEATURE_REPORT, report_id, | 362 | WAC_HID_FEATURE_REPORT, |
341 | rep_data, 4); | 363 | report_id, rep_data, 4, 1); |
342 | } while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES); | 364 | } while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES); |
343 | } else if (features->type != TABLETPC) { | 365 | } else if (features->type != TABLETPC) { |
344 | do { | 366 | do { |
345 | rep_data[0] = 2; | 367 | rep_data[0] = 2; |
346 | rep_data[1] = 2; | 368 | rep_data[1] = 2; |
347 | error = usb_set_report(intf, WAC_HID_FEATURE_REPORT, | 369 | error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT, |
348 | report_id, rep_data, 2); | 370 | report_id, rep_data, 2, 1); |
349 | if (error >= 0) | 371 | if (error >= 0) |
350 | error = usb_get_report(intf, | 372 | error = wacom_get_report(intf, |
351 | WAC_HID_FEATURE_REPORT, report_id, | 373 | WAC_HID_FEATURE_REPORT, |
352 | rep_data, 2); | 374 | report_id, rep_data, 2, 1); |
353 | } while ((error < 0 || rep_data[1] != 2) && limit++ < WAC_MSG_RETRIES); | 375 | } while ((error < 0 || rep_data[1] != 2) && limit++ < WAC_MSG_RETRIES); |
354 | } | 376 | } |
355 | 377 | ||
@@ -468,6 +490,220 @@ static void wacom_remove_shared_data(struct wacom_wac *wacom) | |||
468 | } | 490 | } |
469 | } | 491 | } |
470 | 492 | ||
493 | static int wacom_led_control(struct wacom *wacom) | ||
494 | { | ||
495 | unsigned char *buf; | ||
496 | int retval; | ||
497 | |||
498 | buf = kzalloc(9, GFP_KERNEL); | ||
499 | if (!buf) | ||
500 | return -ENOMEM; | ||
501 | |||
502 | buf[0] = WAC_CMD_LED_CONTROL; | ||
503 | buf[1] = wacom->led.select >= 0 ? wacom->led.select | 4 : 0; | ||
504 | buf[2] = wacom->led.llv; | ||
505 | buf[3] = wacom->led.hlv; | ||
506 | buf[4] = wacom->led.img_lum; | ||
507 | |||
508 | retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_LED_CONTROL, | ||
509 | buf, 9, WAC_CMD_RETRIES); | ||
510 | kfree(buf); | ||
511 | |||
512 | return retval; | ||
513 | } | ||
514 | |||
515 | static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *img) | ||
516 | { | ||
517 | unsigned char *buf; | ||
518 | int i, retval; | ||
519 | |||
520 | buf = kzalloc(259, GFP_KERNEL); | ||
521 | if (!buf) | ||
522 | return -ENOMEM; | ||
523 | |||
524 | /* Send 'start' command */ | ||
525 | buf[0] = WAC_CMD_ICON_START; | ||
526 | buf[1] = 1; | ||
527 | retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_START, | ||
528 | buf, 2, WAC_CMD_RETRIES); | ||
529 | if (retval < 0) | ||
530 | goto out; | ||
531 | |||
532 | buf[0] = WAC_CMD_ICON_XFER; | ||
533 | buf[1] = button_id & 0x07; | ||
534 | for (i = 0; i < 4; i++) { | ||
535 | buf[2] = i; | ||
536 | memcpy(buf + 3, img + i * 256, 256); | ||
537 | |||
538 | retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_XFER, | ||
539 | buf, 259, WAC_CMD_RETRIES); | ||
540 | if (retval < 0) | ||
541 | break; | ||
542 | } | ||
543 | |||
544 | /* Send 'stop' */ | ||
545 | buf[0] = WAC_CMD_ICON_START; | ||
546 | buf[1] = 0; | ||
547 | wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_START, | ||
548 | buf, 2, WAC_CMD_RETRIES); | ||
549 | |||
550 | out: | ||
551 | kfree(buf); | ||
552 | return retval; | ||
553 | } | ||
554 | |||
555 | static ssize_t wacom_led_select_store(struct device *dev, | ||
556 | struct device_attribute *attr, | ||
557 | const char *buf, size_t count) | ||
558 | { | ||
559 | struct wacom *wacom = dev_get_drvdata(dev); | ||
560 | unsigned int id; | ||
561 | int err; | ||
562 | |||
563 | err = kstrtouint(buf, 10, &id); | ||
564 | if (err) | ||
565 | return err; | ||
566 | |||
567 | mutex_lock(&wacom->lock); | ||
568 | |||
569 | wacom->led.select = id; | ||
570 | err = wacom_led_control(wacom); | ||
571 | |||
572 | mutex_unlock(&wacom->lock); | ||
573 | |||
574 | return err < 0 ? err : count; | ||
575 | } | ||
576 | |||
577 | static DEVICE_ATTR(status_led_select, S_IWUSR, NULL, wacom_led_select_store); | ||
578 | |||
579 | static ssize_t wacom_luminance_store(struct wacom *wacom, u8 *dest, | ||
580 | const char *buf, size_t count) | ||
581 | { | ||
582 | unsigned int value; | ||
583 | int err; | ||
584 | |||
585 | err = kstrtouint(buf, 10, &value); | ||
586 | if (err) | ||
587 | return err; | ||
588 | |||
589 | mutex_lock(&wacom->lock); | ||
590 | |||
591 | *dest = value & 0x7f; | ||
592 | err = wacom_led_control(wacom); | ||
593 | |||
594 | mutex_unlock(&wacom->lock); | ||
595 | |||
596 | return err < 0 ? err : count; | ||
597 | } | ||
598 | |||
599 | #define DEVICE_LUMINANCE_ATTR(name, field) \ | ||
600 | static ssize_t wacom_##name##_luminance_store(struct device *dev, \ | ||
601 | struct device_attribute *attr, const char *buf, size_t count) \ | ||
602 | { \ | ||
603 | struct wacom *wacom = dev_get_drvdata(dev); \ | ||
604 | \ | ||
605 | return wacom_luminance_store(wacom, &wacom->led.field, \ | ||
606 | buf, count); \ | ||
607 | } \ | ||
608 | static DEVICE_ATTR(name##_luminance, S_IWUSR, \ | ||
609 | NULL, wacom_##name##_luminance_store) | ||
610 | |||
611 | DEVICE_LUMINANCE_ATTR(status0, llv); | ||
612 | DEVICE_LUMINANCE_ATTR(status1, hlv); | ||
613 | DEVICE_LUMINANCE_ATTR(buttons, img_lum); | ||
614 | |||
615 | static ssize_t wacom_button_image_store(struct device *dev, int button_id, | ||
616 | const char *buf, size_t count) | ||
617 | { | ||
618 | struct wacom *wacom = dev_get_drvdata(dev); | ||
619 | int err; | ||
620 | |||
621 | if (count != 1024) | ||
622 | return -EINVAL; | ||
623 | |||
624 | mutex_lock(&wacom->lock); | ||
625 | |||
626 | err = wacom_led_putimage(wacom, button_id, buf); | ||
627 | |||
628 | mutex_unlock(&wacom->lock); | ||
629 | |||
630 | return err < 0 ? err : count; | ||
631 | } | ||
632 | |||
633 | #define DEVICE_BTNIMG_ATTR(BUTTON_ID) \ | ||
634 | static ssize_t wacom_btnimg##BUTTON_ID##_store(struct device *dev, \ | ||
635 | struct device_attribute *attr, const char *buf, size_t count) \ | ||
636 | { \ | ||
637 | return wacom_button_image_store(dev, BUTTON_ID, buf, count); \ | ||
638 | } \ | ||
639 | static DEVICE_ATTR(button##BUTTON_ID##_rawimg, S_IWUSR, \ | ||
640 | NULL, wacom_btnimg##BUTTON_ID##_store) | ||
641 | |||
642 | DEVICE_BTNIMG_ATTR(0); | ||
643 | DEVICE_BTNIMG_ATTR(1); | ||
644 | DEVICE_BTNIMG_ATTR(2); | ||
645 | DEVICE_BTNIMG_ATTR(3); | ||
646 | DEVICE_BTNIMG_ATTR(4); | ||
647 | DEVICE_BTNIMG_ATTR(5); | ||
648 | DEVICE_BTNIMG_ATTR(6); | ||
649 | DEVICE_BTNIMG_ATTR(7); | ||
650 | |||
651 | static struct attribute *wacom_led_attrs[] = { | ||
652 | &dev_attr_status0_luminance.attr, | ||
653 | &dev_attr_status1_luminance.attr, | ||
654 | &dev_attr_status_led_select.attr, | ||
655 | &dev_attr_buttons_luminance.attr, | ||
656 | &dev_attr_button0_rawimg.attr, | ||
657 | &dev_attr_button1_rawimg.attr, | ||
658 | &dev_attr_button2_rawimg.attr, | ||
659 | &dev_attr_button3_rawimg.attr, | ||
660 | &dev_attr_button4_rawimg.attr, | ||
661 | &dev_attr_button5_rawimg.attr, | ||
662 | &dev_attr_button6_rawimg.attr, | ||
663 | &dev_attr_button7_rawimg.attr, | ||
664 | NULL | ||
665 | }; | ||
666 | |||
667 | static struct attribute_group wacom_led_attr_group = { | ||
668 | .name = "wacom_led", | ||
669 | .attrs = wacom_led_attrs, | ||
670 | }; | ||
671 | |||
672 | static int wacom_initialize_leds(struct wacom *wacom) | ||
673 | { | ||
674 | int error; | ||
675 | |||
676 | if (wacom->wacom_wac.features.type >= INTUOS4 && | ||
677 | wacom->wacom_wac.features.type <= INTUOS4L) { | ||
678 | |||
679 | /* Initialize default values */ | ||
680 | wacom->led.select = 0; | ||
681 | wacom->led.llv = 30; | ||
682 | wacom->led.hlv = 20; | ||
683 | wacom->led.img_lum = 10; | ||
684 | wacom_led_control(wacom); | ||
685 | |||
686 | error = sysfs_create_group(&wacom->intf->dev.kobj, | ||
687 | &wacom_led_attr_group); | ||
688 | if (error) { | ||
689 | dev_err(&wacom->intf->dev, | ||
690 | "cannot create sysfs group err: %d\n", error); | ||
691 | return error; | ||
692 | } | ||
693 | } | ||
694 | |||
695 | return 0; | ||
696 | } | ||
697 | |||
698 | static void wacom_destroy_leds(struct wacom *wacom) | ||
699 | { | ||
700 | if (wacom->wacom_wac.features.type >= INTUOS4 && | ||
701 | wacom->wacom_wac.features.type <= INTUOS4L) { | ||
702 | sysfs_remove_group(&wacom->intf->dev.kobj, | ||
703 | &wacom_led_attr_group); | ||
704 | } | ||
705 | } | ||
706 | |||
471 | static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) | 707 | static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) |
472 | { | 708 | { |
473 | struct usb_device *dev = interface_to_usbdev(intf); | 709 | struct usb_device *dev = interface_to_usbdev(intf); |
@@ -556,16 +792,21 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i | |||
556 | wacom->irq->transfer_dma = wacom->data_dma; | 792 | wacom->irq->transfer_dma = wacom->data_dma; |
557 | wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | 793 | wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
558 | 794 | ||
559 | error = input_register_device(input_dev); | 795 | error = wacom_initialize_leds(wacom); |
560 | if (error) | 796 | if (error) |
561 | goto fail4; | 797 | goto fail4; |
562 | 798 | ||
799 | error = input_register_device(input_dev); | ||
800 | if (error) | ||
801 | goto fail5; | ||
802 | |||
563 | /* Note that if query fails it is not a hard failure */ | 803 | /* Note that if query fails it is not a hard failure */ |
564 | wacom_query_tablet_data(intf, features); | 804 | wacom_query_tablet_data(intf, features); |
565 | 805 | ||
566 | usb_set_intfdata(intf, wacom); | 806 | usb_set_intfdata(intf, wacom); |
567 | return 0; | 807 | return 0; |
568 | 808 | ||
809 | fail5: wacom_destroy_leds(wacom); | ||
569 | fail4: wacom_remove_shared_data(wacom_wac); | 810 | fail4: wacom_remove_shared_data(wacom_wac); |
570 | fail3: usb_free_urb(wacom->irq); | 811 | fail3: usb_free_urb(wacom->irq); |
571 | fail2: usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma); | 812 | fail2: usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma); |
@@ -582,6 +823,7 @@ static void wacom_disconnect(struct usb_interface *intf) | |||
582 | 823 | ||
583 | usb_kill_urb(wacom->irq); | 824 | usb_kill_urb(wacom->irq); |
584 | input_unregister_device(wacom->wacom_wac.input); | 825 | input_unregister_device(wacom->wacom_wac.input); |
826 | wacom_destroy_leds(wacom); | ||
585 | usb_free_urb(wacom->irq); | 827 | usb_free_urb(wacom->irq); |
586 | usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX, | 828 | usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX, |
587 | wacom->wacom_wac.data, wacom->data_dma); | 829 | wacom->wacom_wac.data, wacom->data_dma); |
@@ -604,17 +846,16 @@ static int wacom_resume(struct usb_interface *intf) | |||
604 | { | 846 | { |
605 | struct wacom *wacom = usb_get_intfdata(intf); | 847 | struct wacom *wacom = usb_get_intfdata(intf); |
606 | struct wacom_features *features = &wacom->wacom_wac.features; | 848 | struct wacom_features *features = &wacom->wacom_wac.features; |
607 | int rv; | 849 | int rv = 0; |
608 | 850 | ||
609 | mutex_lock(&wacom->lock); | 851 | mutex_lock(&wacom->lock); |
610 | 852 | ||
611 | /* switch to wacom mode first */ | 853 | /* switch to wacom mode first */ |
612 | wacom_query_tablet_data(intf, features); | 854 | wacom_query_tablet_data(intf, features); |
855 | wacom_led_control(wacom); | ||
613 | 856 | ||
614 | if (wacom->open) | 857 | if (wacom->open && usb_submit_urb(wacom->irq, GFP_NOIO) < 0) |
615 | rv = usb_submit_urb(wacom->irq, GFP_NOIO); | 858 | rv = -EIO; |
616 | else | ||
617 | rv = 0; | ||
618 | 859 | ||
619 | mutex_unlock(&wacom->lock); | 860 | mutex_unlock(&wacom->lock); |
620 | 861 | ||