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>
This commit is contained in:
Родитель
7e66eaf14e
Коммит
5d7e7d4798
|
@ -0,0 +1,64 @@
|
|||
What: /sys/class/hidraw/hidraw*/device/speed
|
||||
Date: April 2010
|
||||
Kernel Version: 2.6.35
|
||||
Contact: linux-bluetooth@vger.kernel.org
|
||||
Description:
|
||||
The /sys/class/hidraw/hidraw*/device/speed file controls
|
||||
reporting speed of Wacom bluetooth tablet. Reading from
|
||||
this file returns 1 if tablet reports in high speed mode
|
||||
or 0 otherwise. Writing to this file one of these values
|
||||
switches reporting speed.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/led
|
||||
Date: August 2011
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description:
|
||||
Attribute group for control of the status LEDs and the OLED
|
||||
displays found on the Wacom Intuos 4 M, L, and XL tablets. This
|
||||
attribute group is not available for other Wacom tablets.
|
||||
Therefore its presence implicitly signifies the presence of
|
||||
said LEDs and OLED displays on the tablet device.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/status0_luminance
|
||||
Date: August 2011
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description:
|
||||
Writing to this file sets the status LED luminance (0..127)
|
||||
when the stylus does not touch the tablet surface, and no
|
||||
button is pressed on the stylus.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/status1_luminance
|
||||
Date: August 2011
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description:
|
||||
Writing to this file sets the status LED luminance (0..127)
|
||||
when the stylus touches the tablet surface, or any button is
|
||||
pressed on the stylus.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/status_led_select
|
||||
Date: August 2011
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description:
|
||||
Writing to this file sets which one of the four status LEDs is
|
||||
active (0..3). The other three LEDs are always inactive. By
|
||||
means of specifying "-1" it is possible to set all status LEDs
|
||||
to inactive.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/buttons_luminance
|
||||
Date: August 2011
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description:
|
||||
Writing to this file sets the overall luminance level (0..15)
|
||||
of all eight button OLED displays.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/button<n>_rawimg
|
||||
Date: August 2011
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description:
|
||||
When writing a 1024 byte raw image in Wacom Intuos 4
|
||||
interleaving format to the file, the image shows up on Button N
|
||||
of the device. The image is a 64x32 pixel 4-bit gray image. The
|
||||
1024 byte binary is split up into 16x 64 byte chunks. Each 64
|
||||
byte chunk encodes the image data for two consecutive lines on
|
||||
the display. The low nibble of each byte contains the first
|
||||
line, and the high nibble contains the second line.
|
|
@ -1,10 +0,0 @@
|
|||
What: /sys/class/hidraw/hidraw*/device/speed
|
||||
Date: April 2010
|
||||
Kernel Version: 2.6.35
|
||||
Contact: linux-bluetooth@vger.kernel.org
|
||||
Description:
|
||||
The /sys/class/hidraw/hidraw*/device/speed file controls
|
||||
reporting speed of wacom bluetooth tablet. Reading from
|
||||
this file returns 1 if tablet reports in high speed mode
|
||||
or 0 otherwise. Writing to this file one of these values
|
||||
switches reporting speed.
|
|
@ -114,6 +114,12 @@ struct wacom {
|
|||
struct mutex lock;
|
||||
bool open;
|
||||
char phys[32];
|
||||
struct wacom_led {
|
||||
u8 select; /* status led selector (0..3, -1=none) */
|
||||
u8 llv; /* status led brightness no button */
|
||||
u8 hlv; /* status led brightness button pressed */
|
||||
u8 img_lum; /* OLED matrix display brightness */
|
||||
} led;
|
||||
};
|
||||
|
||||
extern const struct usb_device_id wacom_ids[];
|
||||
|
|
|
@ -48,27 +48,49 @@ struct hid_descriptor {
|
|||
/* defines to get/set USB message */
|
||||
#define USB_REQ_GET_REPORT 0x01
|
||||
#define USB_REQ_SET_REPORT 0x09
|
||||
|
||||
#define WAC_HID_FEATURE_REPORT 0x03
|
||||
#define WAC_MSG_RETRIES 5
|
||||
|
||||
static int usb_get_report(struct usb_interface *intf, unsigned char type,
|
||||
unsigned char id, void *buf, int size)
|
||||
#define WAC_CMD_LED_CONTROL 0x20
|
||||
#define WAC_CMD_ICON_START 0x21
|
||||
#define WAC_CMD_ICON_XFER 0x23
|
||||
#define WAC_CMD_RETRIES 10
|
||||
|
||||
static int wacom_get_report(struct usb_interface *intf, u8 type, u8 id,
|
||||
void *buf, size_t size, unsigned int retries)
|
||||
{
|
||||
return usb_control_msg(interface_to_usbdev(intf),
|
||||
usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
|
||||
USB_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
(type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
|
||||
buf, size, 100);
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
int retval;
|
||||
|
||||
do {
|
||||
retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
|
||||
USB_REQ_GET_REPORT,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
(type << 8) + id,
|
||||
intf->altsetting[0].desc.bInterfaceNumber,
|
||||
buf, size, 100);
|
||||
} while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int usb_set_report(struct usb_interface *intf, unsigned char type,
|
||||
unsigned char id, void *buf, int size)
|
||||
static int wacom_set_report(struct usb_interface *intf, u8 type, u8 id,
|
||||
void *buf, size_t size, unsigned int retries)
|
||||
{
|
||||
return usb_control_msg(interface_to_usbdev(intf),
|
||||
usb_sndctrlpipe(interface_to_usbdev(intf), 0),
|
||||
USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
(type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
|
||||
buf, size, 1000);
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
int retval;
|
||||
|
||||
do {
|
||||
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
USB_REQ_SET_REPORT,
|
||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
(type << 8) + id,
|
||||
intf->altsetting[0].desc.bInterfaceNumber,
|
||||
buf, size, 1000);
|
||||
} while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
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
|
|||
rep_data[2] = 0;
|
||||
rep_data[3] = 0;
|
||||
report_id = 3;
|
||||
error = usb_set_report(intf, WAC_HID_FEATURE_REPORT,
|
||||
report_id, rep_data, 4);
|
||||
error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT,
|
||||
report_id, rep_data, 4, 1);
|
||||
if (error >= 0)
|
||||
error = usb_get_report(intf,
|
||||
WAC_HID_FEATURE_REPORT, report_id,
|
||||
rep_data, 4);
|
||||
error = wacom_get_report(intf,
|
||||
WAC_HID_FEATURE_REPORT,
|
||||
report_id, rep_data, 4, 1);
|
||||
} while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES);
|
||||
} else if (features->type != TABLETPC) {
|
||||
do {
|
||||
rep_data[0] = 2;
|
||||
rep_data[1] = 2;
|
||||
error = usb_set_report(intf, WAC_HID_FEATURE_REPORT,
|
||||
report_id, rep_data, 2);
|
||||
error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT,
|
||||
report_id, rep_data, 2, 1);
|
||||
if (error >= 0)
|
||||
error = usb_get_report(intf,
|
||||
WAC_HID_FEATURE_REPORT, report_id,
|
||||
rep_data, 2);
|
||||
error = wacom_get_report(intf,
|
||||
WAC_HID_FEATURE_REPORT,
|
||||
report_id, rep_data, 2, 1);
|
||||
} while ((error < 0 || rep_data[1] != 2) && limit++ < WAC_MSG_RETRIES);
|
||||
}
|
||||
|
||||
|
@ -468,6 +490,220 @@ static void wacom_remove_shared_data(struct wacom_wac *wacom)
|
|||
}
|
||||
}
|
||||
|
||||
static int wacom_led_control(struct wacom *wacom)
|
||||
{
|
||||
unsigned char *buf;
|
||||
int retval;
|
||||
|
||||
buf = kzalloc(9, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
buf[0] = WAC_CMD_LED_CONTROL;
|
||||
buf[1] = wacom->led.select >= 0 ? wacom->led.select | 4 : 0;
|
||||
buf[2] = wacom->led.llv;
|
||||
buf[3] = wacom->led.hlv;
|
||||
buf[4] = wacom->led.img_lum;
|
||||
|
||||
retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_LED_CONTROL,
|
||||
buf, 9, WAC_CMD_RETRIES);
|
||||
kfree(buf);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *img)
|
||||
{
|
||||
unsigned char *buf;
|
||||
int i, retval;
|
||||
|
||||
buf = kzalloc(259, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Send 'start' command */
|
||||
buf[0] = WAC_CMD_ICON_START;
|
||||
buf[1] = 1;
|
||||
retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_START,
|
||||
buf, 2, WAC_CMD_RETRIES);
|
||||
if (retval < 0)
|
||||
goto out;
|
||||
|
||||
buf[0] = WAC_CMD_ICON_XFER;
|
||||
buf[1] = button_id & 0x07;
|
||||
for (i = 0; i < 4; i++) {
|
||||
buf[2] = i;
|
||||
memcpy(buf + 3, img + i * 256, 256);
|
||||
|
||||
retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_XFER,
|
||||
buf, 259, WAC_CMD_RETRIES);
|
||||
if (retval < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Send 'stop' */
|
||||
buf[0] = WAC_CMD_ICON_START;
|
||||
buf[1] = 0;
|
||||
wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_START,
|
||||
buf, 2, WAC_CMD_RETRIES);
|
||||
|
||||
out:
|
||||
kfree(buf);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t wacom_led_select_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct wacom *wacom = dev_get_drvdata(dev);
|
||||
unsigned int id;
|
||||
int err;
|
||||
|
||||
err = kstrtouint(buf, 10, &id);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&wacom->lock);
|
||||
|
||||
wacom->led.select = id;
|
||||
err = wacom_led_control(wacom);
|
||||
|
||||
mutex_unlock(&wacom->lock);
|
||||
|
||||
return err < 0 ? err : count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(status_led_select, S_IWUSR, NULL, wacom_led_select_store);
|
||||
|
||||
static ssize_t wacom_luminance_store(struct wacom *wacom, u8 *dest,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
unsigned int value;
|
||||
int err;
|
||||
|
||||
err = kstrtouint(buf, 10, &value);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&wacom->lock);
|
||||
|
||||
*dest = value & 0x7f;
|
||||
err = wacom_led_control(wacom);
|
||||
|
||||
mutex_unlock(&wacom->lock);
|
||||
|
||||
return err < 0 ? err : count;
|
||||
}
|
||||
|
||||
#define DEVICE_LUMINANCE_ATTR(name, field) \
|
||||
static ssize_t wacom_##name##_luminance_store(struct device *dev, \
|
||||
struct device_attribute *attr, const char *buf, size_t count) \
|
||||
{ \
|
||||
struct wacom *wacom = dev_get_drvdata(dev); \
|
||||
\
|
||||
return wacom_luminance_store(wacom, &wacom->led.field, \
|
||||
buf, count); \
|
||||
} \
|
||||
static DEVICE_ATTR(name##_luminance, S_IWUSR, \
|
||||
NULL, wacom_##name##_luminance_store)
|
||||
|
||||
DEVICE_LUMINANCE_ATTR(status0, llv);
|
||||
DEVICE_LUMINANCE_ATTR(status1, hlv);
|
||||
DEVICE_LUMINANCE_ATTR(buttons, img_lum);
|
||||
|
||||
static ssize_t wacom_button_image_store(struct device *dev, int button_id,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct wacom *wacom = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
if (count != 1024)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&wacom->lock);
|
||||
|
||||
err = wacom_led_putimage(wacom, button_id, buf);
|
||||
|
||||
mutex_unlock(&wacom->lock);
|
||||
|
||||
return err < 0 ? err : count;
|
||||
}
|
||||
|
||||
#define DEVICE_BTNIMG_ATTR(BUTTON_ID) \
|
||||
static ssize_t wacom_btnimg##BUTTON_ID##_store(struct device *dev, \
|
||||
struct device_attribute *attr, const char *buf, size_t count) \
|
||||
{ \
|
||||
return wacom_button_image_store(dev, BUTTON_ID, buf, count); \
|
||||
} \
|
||||
static DEVICE_ATTR(button##BUTTON_ID##_rawimg, S_IWUSR, \
|
||||
NULL, wacom_btnimg##BUTTON_ID##_store)
|
||||
|
||||
DEVICE_BTNIMG_ATTR(0);
|
||||
DEVICE_BTNIMG_ATTR(1);
|
||||
DEVICE_BTNIMG_ATTR(2);
|
||||
DEVICE_BTNIMG_ATTR(3);
|
||||
DEVICE_BTNIMG_ATTR(4);
|
||||
DEVICE_BTNIMG_ATTR(5);
|
||||
DEVICE_BTNIMG_ATTR(6);
|
||||
DEVICE_BTNIMG_ATTR(7);
|
||||
|
||||
static struct attribute *wacom_led_attrs[] = {
|
||||
&dev_attr_status0_luminance.attr,
|
||||
&dev_attr_status1_luminance.attr,
|
||||
&dev_attr_status_led_select.attr,
|
||||
&dev_attr_buttons_luminance.attr,
|
||||
&dev_attr_button0_rawimg.attr,
|
||||
&dev_attr_button1_rawimg.attr,
|
||||
&dev_attr_button2_rawimg.attr,
|
||||
&dev_attr_button3_rawimg.attr,
|
||||
&dev_attr_button4_rawimg.attr,
|
||||
&dev_attr_button5_rawimg.attr,
|
||||
&dev_attr_button6_rawimg.attr,
|
||||
&dev_attr_button7_rawimg.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group wacom_led_attr_group = {
|
||||
.name = "wacom_led",
|
||||
.attrs = wacom_led_attrs,
|
||||
};
|
||||
|
||||
static int wacom_initialize_leds(struct wacom *wacom)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (wacom->wacom_wac.features.type >= INTUOS4 &&
|
||||
wacom->wacom_wac.features.type <= INTUOS4L) {
|
||||
|
||||
/* Initialize default values */
|
||||
wacom->led.select = 0;
|
||||
wacom->led.llv = 30;
|
||||
wacom->led.hlv = 20;
|
||||
wacom->led.img_lum = 10;
|
||||
wacom_led_control(wacom);
|
||||
|
||||
error = sysfs_create_group(&wacom->intf->dev.kobj,
|
||||
&wacom_led_attr_group);
|
||||
if (error) {
|
||||
dev_err(&wacom->intf->dev,
|
||||
"cannot create sysfs group err: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wacom_destroy_leds(struct wacom *wacom)
|
||||
{
|
||||
if (wacom->wacom_wac.features.type >= INTUOS4 &&
|
||||
wacom->wacom_wac.features.type <= INTUOS4L) {
|
||||
sysfs_remove_group(&wacom->intf->dev.kobj,
|
||||
&wacom_led_attr_group);
|
||||
}
|
||||
}
|
||||
|
||||
static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
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
|
|||
wacom->irq->transfer_dma = wacom->data_dma;
|
||||
wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
error = input_register_device(input_dev);
|
||||
error = wacom_initialize_leds(wacom);
|
||||
if (error)
|
||||
goto fail4;
|
||||
|
||||
error = input_register_device(input_dev);
|
||||
if (error)
|
||||
goto fail5;
|
||||
|
||||
/* Note that if query fails it is not a hard failure */
|
||||
wacom_query_tablet_data(intf, features);
|
||||
|
||||
usb_set_intfdata(intf, wacom);
|
||||
return 0;
|
||||
|
||||
fail5: wacom_destroy_leds(wacom);
|
||||
fail4: wacom_remove_shared_data(wacom_wac);
|
||||
fail3: usb_free_urb(wacom->irq);
|
||||
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)
|
|||
|
||||
usb_kill_urb(wacom->irq);
|
||||
input_unregister_device(wacom->wacom_wac.input);
|
||||
wacom_destroy_leds(wacom);
|
||||
usb_free_urb(wacom->irq);
|
||||
usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX,
|
||||
wacom->wacom_wac.data, wacom->data_dma);
|
||||
|
@ -604,17 +846,16 @@ static int wacom_resume(struct usb_interface *intf)
|
|||
{
|
||||
struct wacom *wacom = usb_get_intfdata(intf);
|
||||
struct wacom_features *features = &wacom->wacom_wac.features;
|
||||
int rv;
|
||||
int rv = 0;
|
||||
|
||||
mutex_lock(&wacom->lock);
|
||||
|
||||
/* switch to wacom mode first */
|
||||
wacom_query_tablet_data(intf, features);
|
||||
wacom_led_control(wacom);
|
||||
|
||||
if (wacom->open)
|
||||
rv = usb_submit_urb(wacom->irq, GFP_NOIO);
|
||||
else
|
||||
rv = 0;
|
||||
if (wacom->open && usb_submit_urb(wacom->irq, GFP_NOIO) < 0)
|
||||
rv = -EIO;
|
||||
|
||||
mutex_unlock(&wacom->lock);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче