diff options
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Kconfig | 38 | ||||
-rw-r--r-- | drivers/hid/Makefile | 6 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 20 | ||||
-rw-r--r-- | drivers/hid/hid-gt683r.c | 321 | ||||
-rw-r--r-- | drivers/hid/hid-holtek-mouse.c | 4 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 9 | ||||
-rw-r--r-- | drivers/hid/hid-input.c | 6 | ||||
-rw-r--r-- | drivers/hid/hid-logitech-dj.c | 35 | ||||
-rw-r--r-- | drivers/hid/hid-penmount.c | 49 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_core.c | 4 | ||||
-rw-r--r-- | drivers/hid/hid-rmi.c | 44 | ||||
-rw-r--r-- | drivers/hid/hid-sony.c | 100 | ||||
-rw-r--r-- | drivers/hid/hid-thingm.c | 6 | ||||
-rw-r--r-- | drivers/hid/hid-wacom.c | 973 | ||||
-rw-r--r-- | drivers/hid/uhid.c | 394 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 60 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-quirks.c | 5 | ||||
-rw-r--r-- | drivers/hid/wacom.h | 152 | ||||
-rw-r--r-- | drivers/hid/wacom_sys.c | 1587 | ||||
-rw-r--r-- | drivers/hid/wacom_wac.c | 3033 | ||||
-rw-r--r-- | drivers/hid/wacom_wac.h | 196 |
21 files changed, 5776 insertions, 1266 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index e02cf59b048d..f42df4dd58d2 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -261,6 +261,20 @@ config HOLTEK_FF Say Y here if you have a Holtek On Line Grip based game controller and want to have force feedback support for it. +config HID_GT683R + tristate "MSI GT68xR LED support" + depends on LEDS_CLASS && USB_HID + ---help--- + Say Y here if you want to enable support for the three MSI GT68xR LEDs + + This driver support following modes: + - Normal: LEDs are fully on when enabled + - Audio: LEDs brightness depends on sound level + - Breathing: LEDs brightness varies at human breathing rate + + Currently the following devices are know to be supported: + - MSI GT683R + config HID_HUION tristate "Huion tablets" depends on USB_HID @@ -516,6 +530,17 @@ config PANTHERLORD_FF Say Y here if you have a PantherLord/GreenAsia based game controller or adapter and want to enable force feedback support for it. +config HID_PENMOUNT + tristate "Penmount touch device" + depends on USB_HID + ---help--- + This selects a driver for the PenMount 6000 touch controller. + + The driver works around a problem in the report descript allowing + the userspace to touch events instead of mouse events. + + Say Y here if you have a Penmount based touch controller. + config HID_PETALYNX tristate "Petalynx Maxter remote control" depends on HID @@ -750,12 +775,17 @@ config THRUSTMASTER_FF Rumble Force or Force Feedback Wheel. config HID_WACOM - tristate "Wacom Bluetooth devices support" + tristate "Wacom Intuos/Graphire tablet support (USB)" depends on HID - depends on LEDS_CLASS select POWER_SUPPLY - ---help--- - Support for Wacom Graphire Bluetooth and Intuos4 WL tablets. + select NEW_LEDS + select LEDS_CLASS + help + Say Y here if you want to use the USB or BT version of the Wacom Intuos + or Graphire tablet. + + To compile this driver as a module, choose M here: the + module will be called wacom. config HID_WIIMOTE tristate "Nintendo Wii / Wii U peripherals" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 5e96be3ab280..e2850d8af9ca 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o obj-$(CONFIG_HID_ELECOM) += hid-elecom.o obj-$(CONFIG_HID_ELO) += hid-elo.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o +obj-$(CONFIG_HID_GT683R) += hid-gt683r.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o @@ -70,6 +71,7 @@ obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o obj-$(CONFIG_HID_ORTEK) += hid-ortek.o obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o +obj-$(CONFIG_HID_PENMOUNT) += hid-penmount.o obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o hid-picolcd-y += hid-picolcd_core.o @@ -115,7 +117,9 @@ obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o obj-$(CONFIG_HID_XINMO) += hid-xinmo.o obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o -obj-$(CONFIG_HID_WACOM) += hid-wacom.o + +wacom-objs := wacom_wac.o wacom_sys.o +obj-$(CONFIG_HID_WACOM) += wacom.o obj-$(CONFIG_HID_WALTOP) += hid-waltop.o obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o obj-$(CONFIG_HID_SENSOR_HUB) += hid-sensor-hub.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6c813c6092f8..73bd9e2e42bc 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -52,7 +52,7 @@ EXPORT_SYMBOL_GPL(hid_debug); static int hid_ignore_special_drivers = 0; module_param_named(ignore_special_drivers, hid_ignore_special_drivers, int, 0600); -MODULE_PARM_DESC(debug, "Ignore any special drivers and handle all devices by generic driver"); +MODULE_PARM_DESC(ignore_special_drivers, "Ignore any special drivers and handle all devices by generic driver"); /* * Register a new report for a device. @@ -789,6 +789,15 @@ static int hid_scan_report(struct hid_device *hid) /* hid-rmi should take care of them, not hid-generic */ hid->group = HID_GROUP_RMI; + /* + * Vendor specific handlings + */ + switch (hid->vendor) { + case USB_VENDOR_ID_WACOM: + hid->group = HID_GROUP_WACOM; + break; + } + vfree(parser); return 0; } @@ -1582,6 +1591,9 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) if ((connect_mask & HID_CONNECT_HIDRAW) && !hidraw_connect(hdev)) hdev->claimed |= HID_CLAIMED_HIDRAW; + if (connect_mask & HID_CONNECT_DRIVER) + hdev->claimed |= HID_CLAIMED_DRIVER; + /* Drivers with the ->raw_event callback set are not required to connect * to any other listener. */ if (!hdev->claimed && !hdev->driver->raw_event) { @@ -1784,6 +1796,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2) }, { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) }, @@ -1849,6 +1862,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) }, { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) }, @@ -1870,6 +1884,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18) }, { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) }, { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_6000) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, #if IS_ENABLED(CONFIG_HID_ROCCAT) @@ -1937,8 +1952,6 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_Q_PAD) }, @@ -2344,7 +2357,6 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) }, - { HID_USB_DEVICE(USB_VENDOR_ID_WACOM, HID_ANY_ID) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) }, diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c new file mode 100644 index 000000000000..0d6f135e266c --- /dev/null +++ b/drivers/hid/hid-gt683r.c @@ -0,0 +1,321 @@ +/* + * MSI GT683R led driver + * + * Copyright (c) 2014 Janne Kanniainen <janne.kanniainen@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/kernel.h> +#include <linux/leds.h> +#include <linux/module.h> + +#include "hid-ids.h" + +#define GT683R_BUFFER_SIZE 8 + +/* + * GT683R_LED_OFF: all LEDs are off + * GT683R_LED_AUDIO: LEDs brightness depends on sound level + * GT683R_LED_BREATHING: LEDs brightness varies at human breathing rate + * GT683R_LED_NORMAL: LEDs are fully on when enabled + */ +enum gt683r_led_mode { + GT683R_LED_OFF = 0, + GT683R_LED_AUDIO = 2, + GT683R_LED_BREATHING = 3, + GT683R_LED_NORMAL = 5 +}; + +enum gt683r_panels { + GT683R_LED_BACK = 0, + GT683R_LED_SIDE = 1, + GT683R_LED_FRONT = 2, + GT683R_LED_COUNT, +}; + +static const char * const gt683r_panel_names[] = { + "back", + "side", + "front", +}; + +struct gt683r_led { + struct hid_device *hdev; + struct led_classdev led_devs[GT683R_LED_COUNT]; + struct mutex lock; + struct work_struct work; + enum led_brightness brightnesses[GT683R_LED_COUNT]; + enum gt683r_led_mode mode; +}; + +static const struct hid_device_id gt683r_led_id[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) }, + { } +}; + +static void gt683r_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + int i; + struct device *dev = led_cdev->dev->parent; + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct gt683r_led *led = hid_get_drvdata(hdev); + + for (i = 0; i < GT683R_LED_COUNT; i++) { + if (led_cdev == &led->led_devs[i]) + break; + } + + if (i < GT683R_LED_COUNT) { + led->brightnesses[i] = brightness; + schedule_work(&led->work); + } +} + +static ssize_t mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 sysfs_mode; + struct hid_device *hdev = container_of(dev->parent, + struct hid_device, dev); + struct gt683r_led *led = hid_get_drvdata(hdev); + + if (led->mode == GT683R_LED_NORMAL) + sysfs_mode = 0; + else if (led->mode == GT683R_LED_AUDIO) + sysfs_mode = 1; + else + sysfs_mode = 2; + + return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode); +} + +static ssize_t mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u8 sysfs_mode; + struct hid_device *hdev = container_of(dev->parent, + struct hid_device, dev); + struct gt683r_led *led = hid_get_drvdata(hdev); + + + if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2) + return -EINVAL; + + mutex_lock(&led->lock); + + if (sysfs_mode == 0) + led->mode = GT683R_LED_NORMAL; + else if (sysfs_mode == 1) + led->mode = GT683R_LED_AUDIO; + else + led->mode = GT683R_LED_BREATHING; + + mutex_unlock(&led->lock); + schedule_work(&led->work); + + return count; +} + +static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg) +{ + int ret; + + ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + if (ret != GT683R_BUFFER_SIZE) { + hid_err(led->hdev, + "failed to send set report request: %i\n", ret); + if (ret < 0) + return ret; + return -EIO; + } + + return 0; +} + +static int gt683r_leds_set(struct gt683r_led *led, u8 leds) +{ + int ret; + u8 *buffer; + + buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer[0] = 0x01; + buffer[1] = 0x02; + buffer[2] = 0x30; + buffer[3] = leds; + ret = gt683r_led_snd_msg(led, buffer); + + kfree(buffer); + return ret; +} + +static int gt683r_mode_set(struct gt683r_led *led, u8 mode) +{ + int ret; + u8 *buffer; + + buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer[0] = 0x01; + buffer[1] = 0x02; + buffer[2] = 0x20; + buffer[3] = mode; + buffer[4] = 0x01; + ret = gt683r_led_snd_msg(led, buffer); + + kfree(buffer); + return ret; +} + +static void gt683r_led_work(struct work_struct *work) +{ + int i; + u8 leds = 0; + u8 mode; + struct gt683r_led *led = container_of(work, struct gt683r_led, work); + + mutex_lock(&led->lock); + + for (i = 0; i < GT683R_LED_COUNT; i++) { + if (led->brightnesses[i]) + leds |= BIT(i); + } + + if (gt683r_leds_set(led, leds)) + goto fail; + + if (leds) + mode = led->mode; + else + mode = GT683R_LED_OFF; + + gt683r_mode_set(led, mode); +fail: + mutex_unlock(&led->lock); +} + +static DEVICE_ATTR_RW(mode); + +static struct attribute *gt683r_led_attrs[] = { + &dev_attr_mode.attr, + NULL +}; + +static const struct attribute_group gt683r_led_group = { + .name = "gt683r", + .attrs = gt683r_led_attrs, +}; + +static const struct attribute_group *gt683r_led_groups[] = { + >683r_led_group, + NULL +}; + +static int gt683r_led_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int i; + int ret; + int name_sz; + char *name; + struct gt683r_led *led; + + led = devm_kzalloc(&hdev->dev, sizeof(*led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + mutex_init(&led->lock); + INIT_WORK(&led->work, gt683r_led_work); + + led->mode = GT683R_LED_NORMAL; + led->hdev = hdev; + hid_set_drvdata(hdev, led); + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "hid parsing failed\n"); + return ret; + } + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) { + hid_err(hdev, "hw start failed\n"); + return ret; + } + + for (i = 0; i < GT683R_LED_COUNT; i++) { + name_sz = strlen(dev_name(&hdev->dev)) + + strlen(gt683r_panel_names[i]) + 3; + + name = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto fail; + } + + snprintf(name, name_sz, "%s::%s", + dev_name(&hdev->dev), gt683r_panel_names[i]); + led->led_devs[i].name = name; + led->led_devs[i].max_brightness = 1; + led->led_devs[i].brightness_set = gt683r_brightness_set; + led->led_devs[i].groups = gt683r_led_groups; + + ret = led_classdev_register(&hdev->dev, &led->led_devs[i]); + if (ret) { + hid_err(hdev, "could not register led device\n"); + goto fail; + } + } + + return 0; + +fail: + for (i = i - 1; i >= 0; i--) + led_classdev_unregister(&led->led_devs[i]); + hid_hw_stop(hdev); + return ret; +} + +static void gt683r_led_remove(struct hid_device *hdev) +{ + int i; + struct gt683r_led *led = hid_get_drvdata(hdev); + + for (i = 0; i < GT683R_LED_COUNT; i++) + led_classdev_unregister(&led->led_devs[i]); + flush_work(&led->work); + hid_hw_stop(hdev); +} + +static struct hid_driver gt683r_led_driver = { + .probe = gt683r_led_probe, + .remove = gt683r_led_remove, + .name = "gt683r_led", + .id_table = gt683r_led_id, +}; + +module_hid_driver(gt683r_led_driver); + +MODULE_AUTHOR("Janne Kanniainen"); +MODULE_DESCRIPTION("MSI GT683R led driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-holtek-mouse.c b/drivers/hid/hid-holtek-mouse.c index d60fbd0adc0c..78b3a0c76775 100644 --- a/drivers/hid/hid-holtek-mouse.c +++ b/drivers/hid/hid-holtek-mouse.c @@ -29,6 +29,7 @@ * and Zalman ZM-GM1 * - USB ID 04d9:a081, sold as SHARKOON DarkGlider Gaming mouse * - USB ID 04d9:a072, sold as LEETGION Hellion Gaming Mouse + * - USB ID 04d9:a0c2, sold as ETEKCITY Scroll T-140 Gaming Mouse */ static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, @@ -42,6 +43,7 @@ static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, switch (hdev->product) { case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067: case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072: + case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2: if (*rsize >= 122 && rdesc[115] == 0xff && rdesc[116] == 0x7f && rdesc[120] == 0xff && rdesc[121] == 0x7f) { hid_info(hdev, "Fixing up report descriptor\n"); @@ -74,6 +76,8 @@ static const struct hid_device_id holtek_mouse_devices[] = { USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, + USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2) }, { } }; MODULE_DEVICE_TABLE(hid, holtek_mouse_devices); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index d53bdda26207..cd9c9e96cf0e 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -296,6 +296,9 @@ #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7 0x73f7 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 +#define USB_VENDOR_ID_ELAN 0x04f3 +#define USB_DEVICE_ID_ELAN_TOUCHSCREEN 0x0089 + #define USB_VENDOR_ID_ELECOM 0x056e #define USB_DEVICE_ID_ELECOM_BM084 0x0061 @@ -479,6 +482,7 @@ #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070 0xa070 #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072 0xa072 #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081 0xa081 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2 0xa0c2 #define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096 0xa096 #define USB_VENDOR_ID_IMATION 0x0718 @@ -650,7 +654,7 @@ #define USB_DEVICE_ID_GENIUS_KB29E 0x3004 #define USB_VENDOR_ID_MSI 0x1770 -#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL 0xff00 +#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00 #define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400 #define USB_DEVICE_ID_N_S_HARMONY 0xc359 @@ -722,6 +726,7 @@ #define USB_DEVICE_ID_PENMOUNT_PCI 0x3500 #define USB_DEVICE_ID_PENMOUNT_1610 0x1610 #define USB_DEVICE_ID_PENMOUNT_1640 0x1640 +#define USB_DEVICE_ID_PENMOUNT_6000 0x6000 #define USB_VENDOR_ID_PETALYNX 0x18b1 #define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037 @@ -733,6 +738,8 @@ #define USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL 0xff #define USB_VENDOR_ID_PIXART 0x093a +#define USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE_ID2 0x0137 +#define USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE 0x2510 #define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN 0x8001 #define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1 0x8002 #define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2 0x8003 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 2619f7f4517a..2df7fddbd119 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -599,6 +599,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel /* These usage IDs map directly to the usage codes. */ case HID_GD_X: case HID_GD_Y: case HID_GD_Z: case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ: + if (field->flags & HID_MAIN_ITEM_RELATIVE) + map_rel(usage->hid & 0xf); + else + map_abs_clear(usage->hid & 0xf); + break; + case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL: if (field->flags & HID_MAIN_ITEM_RELATIVE) map_rel(usage->hid & 0xf); diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 9bf8637747a5..71f569292cab 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -385,18 +385,6 @@ static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev, djdev = djrcv_dev->paired_dj_devices[dj_report->device_index]; - if (!djdev) { - dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]" - " is NULL, index %d\n", dj_report->device_index); - kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report)); - - if (schedule_work(&djrcv_dev->work) == 0) { - dbg_hid("%s: did not schedule the work item, was already " - "queued\n", __func__); - } - return; - } - memset(reportbuffer, 0, sizeof(reportbuffer)); for (i = 0; i < NUMBER_OF_HID_REPORTS; i++) { @@ -421,18 +409,6 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev, dj_device = djrcv_dev->paired_dj_devices[dj_report->device_index]; - if (dj_device == NULL) { - dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]" - " is NULL, index %d\n", dj_report->device_index); - kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report)); - - if (schedule_work(&djrcv_dev->work) == 0) { - dbg_hid("%s: did not schedule the work item, was already " - "queued\n", __func__); - } - return; - } - if ((dj_report->report_type > ARRAY_SIZE(hid_reportid_size_map) - 1) || (hid_reportid_size_map[dj_report->report_type] == 0)) { dbg_hid("invalid report type:%x\n", dj_report->report_type); @@ -701,8 +677,17 @@ static int logi_dj_raw_event(struct hid_device *hdev, } spin_lock_irqsave(&djrcv_dev->lock, flags); + + if (!djrcv_dev->paired_dj_devices[dj_report->device_index]) { + /* received an event for an unknown device, bail out */ + logi_dj_recv_queue_notification(djrcv_dev, dj_report); + goto out; + } + switch (dj_report->report_type) { case REPORT_TYPE_NOTIF_DEVICE_PAIRED: + /* pairing notifications are handled above the switch */ + break; case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED: logi_dj_recv_queue_notification(djrcv_dev, dj_report); break; @@ -715,6 +700,8 @@ static int logi_dj_raw_event(struct hid_device *hdev, default: logi_dj_recv_forward_report(djrcv_dev, dj_report); } + +out: spin_unlock_irqrestore(&djrcv_dev->lock, flags); return true; diff --git a/drivers/hid/hid-penmount.c b/drivers/hid/hid-penmount.c new file mode 100644 index 000000000000..c11dce85cd18 --- /dev/null +++ b/drivers/hid/hid-penmount.c @@ -0,0 +1,49 @@ +/* + * HID driver for PenMount touchscreens + * + * Copyright (c) 2014 Christian Gmeiner <christian.gmeiner <at> gmail.com> + * + * based on hid-penmount copyrighted by + * PenMount Touch Solutions <penmount <at> seed.net.tw> + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/module.h> +#include <linux/hid.h> +#include "hid-ids.h" + +static int penmount_input_mapping(struct hid_device *hdev, + struct hid_input *hi, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) { + hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); + return 1; + } + + return 0; +} + +static const struct hid_device_id penmount_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_6000) }, + { } +}; +MODULE_DEVICE_TABLE(hid, penmount_devices); + +static struct hid_driver penmount_driver = { + .name = "hid-penmount", + .id_table = penmount_devices, + .input_mapping = penmount_input_mapping, +}; + +module_hid_driver(penmount_driver); + +MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>"); +MODULE_DESCRIPTION("PenMount HID TouchScreen driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c index 020df3c2e8b4..c1b29a9eb41a 100644 --- a/drivers/hid/hid-picolcd_core.c +++ b/drivers/hid/hid-picolcd_core.c @@ -351,8 +351,8 @@ static int picolcd_raw_event(struct hid_device *hdev, return 1; if (size > 64) { - hid_warn(hdev, "invalid size value (%d) for picolcd raw event\n", - size); + hid_warn(hdev, "invalid size value (%d) for picolcd raw event (%d)\n", + size, report->id); return 0; } diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 8389e8109218..3cccff73b9b9 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -320,10 +320,7 @@ static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data, int offset; int i; - if (size < hdata->f11.report_size) - return 0; - - if (!(irq & hdata->f11.irq_mask)) + if (!(irq & hdata->f11.irq_mask) || size <= 0) return 0; offset = (hdata->max_fingers >> 2) + 1; @@ -332,9 +329,19 @@ static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data, int fs_bit_position = (i & 0x3) << 1; int finger_state = (data[fs_byte_position] >> fs_bit_position) & 0x03; + int position = offset + 5 * i; + + if (position + 5 > size) { + /* partial report, go on with what we received */ + printk_once(KERN_WARNING + "%s %s: Detected incomplete finger report. Finger reports may occasionally get dropped on this platform.\n", + dev_driver_string(&hdev->dev), + dev_name(&hdev->dev)); + hid_dbg(hdev, "Incomplete finger report\n"); + break; + } - rmi_f11_process_touch(hdata, i, finger_state, - &data[offset + 5 * i]); + rmi_f11_process_touch(hdata, i, finger_state, &data[position]); } input_mt_sync_frame(hdata->input); input_sync(hdata->input); @@ -352,6 +359,11 @@ static int rmi_f30_input_event(struct hid_device *hdev, u8 irq, u8 *data, if (!(irq & hdata->f30.irq_mask)) return 0; + if (size < (int)hdata->f30.report_size) { + hid_warn(hdev, "Click Button pressed, but the click data is missing\n"); + return 0; + } + for (i = 0; i < hdata->gpio_led_count; i++) { if (test_bit(i, &hdata->button_mask)) { value = (data[i / 8] >> (i & 0x07)) & BIT(0); @@ -412,9 +424,29 @@ static int rmi_read_data_event(struct hid_device *hdev, u8 *data, int size) return 1; } +static int rmi_check_sanity(struct hid_device *hdev, u8 *data, int size) +{ + int valid_size = size; + /* + * On the Dell XPS 13 9333, the bus sometimes get confused and fills + * the report with a sentinel value "ff". Synaptics told us that such + * behavior does not comes from the touchpad itself, so we filter out + * such reports here. + */ + + while ((data[valid_size - 1] == 0xff) && valid_size > 0) + valid_size--; + + return valid_size; +} + static int rmi_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { + size = rmi_check_sanity(hdev, data, size); + if (size < 2) + return 0; + switch (data[0]) { case RMI_READ_DATA_REPORT_ID: return rmi_read_data_event(hdev, data, size); diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index c372368e438c..bc4269e559f1 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1,5 +1,5 @@ /* - * HID driver for Sony / PS2 / PS3 BD devices. + * HID driver for Sony / PS2 / PS3 / PS4 BD devices. * * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> @@ -8,6 +8,7 @@ * Copyright (c) 2012 David Dillow <dave@thedillows.org> * Copyright (c) 2006-2013 Jiri Kosina * Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com> + * Copyright (c) 2014 Frank Praznik <frank.praznik@gmail.com> */ /* @@ -176,7 +177,7 @@ static u8 dualshock4_usb_rdesc[] = { 0x75, 0x06, /* Report Size (6), */ 0x95, 0x01, /* Report Count (1), */ 0x15, 0x00, /* Logical Minimum (0), */ - 0x25, 0x7F, /* Logical Maximum (127), */ + 0x25, 0x3F, /* Logical Maximum (63), */ 0x81, 0x02, /* Input (Variable), */ 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x33, /* Usage (Rx), */ @@ -200,14 +201,14 @@ static u8 dualshock4_usb_rdesc[] = { 0x81, 0x02, /* Input (Variable), */ 0x19, 0x43, /* Usage Minimum (43h), */ 0x29, 0x45, /* Usage Maximum (45h), */ - 0x16, 0xFF, 0xBF, /* Logical Minimum (-16385), */ - 0x26, 0x00, 0x40, /* Logical Maximum (16384), */ + 0x16, 0x00, 0xE0, /* Logical Minimum (-8192), */ + 0x26, 0xFF, 0x1F, /* Logical Maximum (8191), */ 0x95, 0x03, /* Report Count (3), */ 0x81, 0x02, /* Input (Variable), */ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ 0x09, 0x21, /* Usage (21h), */ 0x15, 0x00, /* Logical Minimum (0), */ - 0x25, 0xFF, /* Logical Maximum (255), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ 0x75, 0x08, /* Report Size (8), */ 0x95, 0x27, /* Report Count (39), */ 0x81, 0x02, /* Input (Variable), */ @@ -395,11 +396,11 @@ static u8 dualshock4_usb_rdesc[] = { /* * The default behavior of the Dualshock 4 is to send reports using report - * type 1 when running over Bluetooth. However, as soon as it receives a - * report of type 17 to set the LEDs or rumble it starts returning it's state - * in report 17 instead of 1. Since report 17 is undefined in the default HID + * type 1 when running over Bluetooth. However, when feature report 2 is + * requested during the controller initialization it starts sending input + * reports in report 17. Since report 17 is undefined in the default HID * descriptor the button and axis definitions must be moved to report 17 or - * the HID layer won't process the received input once a report is sent. + * the HID layer won't process the received input. */ static u8 dualshock4_bt_rdesc[] = { 0x05, 0x01, /* Usage Page (Desktop), */ @@ -509,8 +510,8 @@ static u8 dualshock4_bt_rdesc[] = { 0x81, 0x02, /* Input (Variable), */ 0x19, 0x43, /* Usage Minimum (43h), */ 0x29, 0x45, /* Usage Maximum (45h), */ - 0x16, 0xFF, 0xBF, /* Logical Minimum (-16385), */ - 0x26, 0x00, 0x40, /* Logical Maximum (16384), */ + 0x16, 0x00, 0xE0, /* Logical Minimum (-8192), */ + 0x26, 0xFF, 0x1F, /* Logical Maximum (8191), */ 0x95, 0x03, /* Report Count (3), */ 0x81, 0x02, /* Input (Variable), */ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ @@ -935,12 +936,13 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size) if (rd[30] >= 0xee) { battery_capacity = 100; battery_charging = !(rd[30] & 0x01); + cable_state = 1; } else { __u8 index = rd[30] <= 5 ? rd[30] : 5; battery_capacity = sixaxis_battery_capacity[index]; battery_charging = 0; + cable_state = 0; } - cable_state = !(rd[31] & 0x04); spin_lock_irqsave(&sc->lock, flags); sc->cable_state = cable_state; @@ -1082,6 +1084,38 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; } +static int sony_register_touchpad(struct hid_input *hi, int touch_count, + int w, int h) +{ + struct input_dev *input_dev = hi->input; + int ret; + + ret = input_mt_init_slots(input_dev, touch_count, 0); + if (ret < 0) + return ret; + + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, w, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, h, 0, 0); + + return 0; +} + +static void sony_input_configured(struct hid_device *hdev, + struct hid_input *hidinput) +{ + struct sony_sc *sc = hid_get_drvdata(hdev); + + /* + * The Dualshock 4 touchpad supports 2 touches and has a + * resolution of 1920x942 (44.86 dots/mm). + */ + if (sc->quirks & DUALSHOCK4_CONTROLLER) { + if (sony_register_touchpad(hidinput, 2, 1920, 942) != 0) + hid_err(sc->hdev, + "Unable to initialize multi-touch slots\n"); + } +} + /* * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller * to "operational". Without this, the ps3 controller will not report any @@ -1654,26 +1688,6 @@ static void sony_battery_remove(struct sony_sc *sc) sc->battery.name = NULL; } -static int sony_register_touchpad(struct sony_sc *sc, int touch_count, - int w, int h) -{ - struct hid_input *hidinput = list_entry(sc->hdev->inputs.next, - struct hid_input, list); - struct input_dev *input_dev = hidinput->input; - int ret; - - ret = input_mt_init_slots(input_dev, touch_count, 0); - if (ret < 0) { - hid_err(sc->hdev, "Unable to initialize multi-touch slots\n"); - return ret; - } - - input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, w, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, h, 0, 0); - - return 0; -} - /* * If a controller is plugged in via USB while already connected via Bluetooth * it will show up as two devices. A global list of connected controllers and @@ -1923,13 +1937,6 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_stop; } } - /* - * The Dualshock 4 touchpad supports 2 touches and has a - * resolution of 1920x940. - */ - ret = sony_register_touchpad(sc, 2, 1920, 940); - if (ret < 0) - goto err_stop; sony_init_work(sc, dualshock4_state_worker); } else { @@ -2037,13 +2044,14 @@ static const struct hid_device_id sony_devices[] = { MODULE_DEVICE_TABLE(hid, sony_devices); static struct hid_driver sony_driver = { - .name = "sony", - .id_table = sony_devices, - .input_mapping = sony_mapping, - .probe = sony_probe, - .remove = sony_remove, - .report_fixup = sony_report_fixup, - .raw_event = sony_raw_event + .name = "sony", + .id_table = sony_devices, + .input_mapping = sony_mapping, + .input_configured = sony_input_configured, + .probe = sony_probe, + .remove = sony_remove, + .report_fixup = sony_report_fixup, + .raw_event = sony_raw_event }; static int __init sony_init(void) diff --git a/drivers/hid/hid-thingm.c b/drivers/hid/hid-thingm.c index f91f97144ace..b95d3978c272 100644 --- a/drivers/hid/hid-thingm.c +++ b/drivers/hid/hid-thingm.c @@ -208,10 +208,10 @@ unregister_red: static void thingm_remove_rgb(struct thingm_rgb *rgb) { - flush_work(&rgb->work); led_classdev_unregister(&rgb->red.ldev); led_classdev_unregister(&rgb->green.ldev); led_classdev_unregister(&rgb->blue.ldev); + flush_work(&rgb->work); } static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id) @@ -287,10 +287,10 @@ static void thingm_remove(struct hid_device *hdev) struct thingm_device *tdev = hid_get_drvdata(hdev); int i; + hid_hw_stop(hdev); + for (i = 0; i < tdev->fwinfo->numrgb; ++i) thingm_remove_rgb(tdev->rgb + i); - - hid_hw_stop(hdev); } static const struct hid_device_id thingm_table[] = { diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c deleted file mode 100644 index 902013ec041b..000000000000 --- a/drivers/hid/hid-wacom.c +++ /dev/null @@ -1,973 +0,0 @@ -/* - * Bluetooth Wacom Tablet support - * - * Copyright (c) 1999 Andreas Gal - * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> - * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc - * Copyright (c) 2006-2007 Jiri Kosina - * Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com> - * Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru> - * Copyright (c) 2009 Bastien Nocera <hadess@hadess.net> - * Copyright (c) 2011 Przemysław Firszt <przemo@firszt.eu> - */ - -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/device.h> -#include <linux/hid.h> -#include <linux/module.h> -#include <linux/leds.h> -#include <linux/slab.h> -#include <linux/power_supply.h> - -#include "hid-ids.h" - -#define PAD_DEVICE_ID 0x0F - -#define WAC_CMD_LED_CONTROL 0x20 -#define WAC_CMD_ICON_START_STOP 0x21 -#define WAC_CMD_ICON_TRANSFER 0x26 - -struct wacom_data { - __u16 tool; - __u16 butstate; - __u8 whlstate; - __u8 features; - __u32 id; - __u32 serial; - unsigned char high_speed; - __u8 battery_capacity; - __u8 power_raw; - __u8 ps_connected; - __u8 bat_charging; - struct power_supply battery; - struct power_supply ac; - __u8 led_selector; - struct led_classdev *leds[4]; -}; - -/*percent of battery capacity for Graphire - 8th value means AC online and show 100% capacity */ -static unsigned short batcap_gr[8] = { 1, 15, 25, 35, 50, 70, 100, 100 }; -/*percent of battery capacity for Intuos4 WL, AC has a separate bit*/ -static unsigned short batcap_i4[8] = { 1, 15, 30, 45, 60, 70, 85, 100 }; - -static enum power_supply_property wacom_battery_props[] = { - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_SCOPE, - POWER_SUPPLY_PROP_STATUS, -}; - -static enum power_supply_property wacom_ac_props[] = { - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_SCOPE, -}; - -static void wacom_scramble(__u8 *image) -{ - __u16 mask; - __u16 s1; - __u16 s2; - __u16 r1 ; - __u16 r2 ; - __u16 r; - __u8 buf[256]; - int i, w, x, y, z; - - for (x = 0; x < 32; x++) { - for (y = 0; y < 8; y++) - buf[(8 * x) + (7 - y)] = image[(8 * x) + y]; - } - - /* Change 76543210 into GECA6420 as required by Intuos4 WL - * HGFEDCBA HFDB7531 - */ - for (x = 0; x < 4; x++) { - for (y = 0; y < 4; y++) { - for (z = 0; z < 8; z++) { - mask = 0x0001; - r1 = 0; - r2 = 0; - i = (x << 6) + (y << 4) + z; - s1 = buf[i]; - s2 = buf[i+8]; - for (w = 0; w < 8; w++) { - r1 |= (s1 & mask); - r2 |= (s2 & mask); - s1 <<= 1; - s2 <<= 1; - mask <<= 2; - } - r = r1 | (r2 << 1); - i = (x << 6) + (y << 4) + (z << 1); - image[i] = 0xFF & r; - image[i+1] = (0xFF00 & r) >> 8; - } - } - } -} - -static void wacom_set_image(struct hid_device *hdev, const char *image, - __u8 icon_no) -{ - __u8 rep_data[68]; - __u8 p[256]; - int ret, i, j; - - for (i = 0; i < 256; i++) - p[i] = image[i]; - - rep_data[0] = WAC_CMD_ICON_START_STOP; - rep_data[1] = 0; - ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2, - HID_FEATURE_REPORT, HID_REQ_SET_REPORT); - if (ret < 0) - goto err; - - rep_data[0] = WAC_CMD_ICON_TRANSFER; - rep_data[1] = icon_no & 0x07; - - wacom_scramble(p); - - for (i = 0; i < 4; i++) { - for (j = 0; j < 64; j++) - rep_data[j + 3] = p[(i << 6) + j]; - - rep_data[2] = i; - ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 67, - HID_FEATURE_REPORT, HID_REQ_SET_REPORT); - } - - rep_data[0] = WAC_CMD_ICON_START_STOP; - rep_data[1] = 0; - - ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2, - HID_FEATURE_REPORT, HID_REQ_SET_REPORT); - -err: - return; -} - -static void wacom_leds_set_brightness(struct led_classdev *led_dev, - enum led_brightness value) -{ - struct device *dev = led_dev->dev->parent; - struct hid_device *hdev; - struct wacom_data *wdata; - unsigned char *buf; - __u8 led = 0; - int i; - - hdev = container_of(dev, struct hid_device, dev); - wdata = hid_get_drvdata(hdev); - for (i = 0; i < 4; ++i) { - if (wdata->leds[i] == led_dev) - wdata->led_selector = i; - } - - led = wdata->led_selector | 0x04; - buf = kzalloc(9, GFP_KERNEL); - if (buf) { - buf[0] = WAC_CMD_LED_CONTROL; - buf[1] = led; - buf[2] = value >> 2; - buf[3] = value; - /* use fixed brightness for OLEDs */ - buf[4] = 0x08; - hid_hw_raw_request(hdev, buf[0], buf, 9, HID_FEATURE_REPORT, - HID_REQ_SET_REPORT); - kfree(buf); - } - - return; -} - -static enum led_brightness wacom_leds_get_brightness(struct led_classdev *led_dev) -{ - struct wacom_data *wdata; - struct device *dev = led_dev->dev->parent; - int value = 0; - int i; - - wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev)); - - for (i = 0; i < 4; ++i) { - if (wdata->leds[i] == led_dev) { - value = wdata->leds[i]->brightness; - break; - } - } - - return value; -} - - -static int wacom_initialize_leds(struct hid_device *hdev) -{ - struct wacom_data *wdata = hid_get_drvdata(hdev); - struct led_classdev *led; - struct device *dev = &hdev->dev; - size_t namesz = strlen(dev_name(dev)) + 12; - char *name; - int i, ret; - - wdata->led_selector = 0; - - for (i = 0; i < 4; i++) { - led = kzalloc(sizeof(struct led_classdev) + namesz, GFP_KERNEL); - if (!led) { - hid_warn(hdev, - "can't allocate memory for LED selector\n"); - ret = -ENOMEM; - goto err; - } - - name = (void *)&led[1]; - snprintf(name, namesz, "%s:selector:%d", dev_name(dev), i); - led->name = name; - led->brightness = 0; - led->max_brightness = 127; - led->brightness_get = wacom_leds_get_brightness; - led->brightness_set = wacom_leds_set_brightness; - - wdata->leds[i] = led; - - ret = led_classdev_register(dev, wdata->leds[i]); - - if (ret) { - wdata->leds[i] = NULL; - kfree(led); - hid_warn(hdev, "can't register LED\n"); - goto err; - } - } - -err: - return ret; -} - -static void wacom_destroy_leds(struct hid_device *hdev) -{ - struct wacom_data *wdata = hid_get_drvdata(hdev); - struct led_classdev *led; - int i; - - for (i = 0; i < 4; ++i) { - if (wdata->leds[i]) { - led = wdata->leds[i]; - wdata->leds[i] = NULL; - led_classdev_unregister(led); - kfree(led); - } - } - -} - -static int wacom_battery_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct wacom_data *wdata = container_of(psy, - struct wacom_data, battery); - int ret = 0; - - switch (psp) { - case POWER_SUPPLY_PROP_PRESENT: - val->intval = 1; - break; - case POWER_SUPPLY_PROP_SCOPE: - val->intval = POWER_SUPPLY_SCOPE_DEVICE; - break; - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = wdata->battery_capacity; - break; - case POWER_SUPPLY_PROP_STATUS: - if (wdata->bat_charging) - val->intval = POWER_SUPPLY_STATUS_CHARGING; - else - if (wdata->battery_capacity == 100 && wdata->ps_connected) - val->intval = POWER_SUPPLY_STATUS_FULL; - else - val->intval = POWER_SUPPLY_STATUS_DISCHARGING; - break; - default: - ret = -EINVAL; - break; - } - return ret; -} - -static int wacom_ac_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct wacom_data *wdata = container_of(psy, struct wacom_data, ac); - int ret = 0; - - switch (psp) { - case POWER_SUPPLY_PROP_PRESENT: - /* fall through */ - case POWER_SUPPLY_PROP_ONLINE: - val->intval = wdata->ps_connected; - break; - case POWER_SUPPLY_PROP_SCOPE: - val->intval = POWER_SUPPLY_SCOPE_DEVICE; - break; - default: - ret = -EINVAL; - break; - } - return ret; -} - -static void wacom_set_features(struct hid_device *hdev, u8 speed) -{ - struct wacom_data *wdata = hid_get_drvdata(hdev); - int limit, ret; - __u8 rep_data[2]; - - switch (hdev->product) { - case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH: - rep_data[0] = 0x03 ; rep_data[1] = 0x00; - limit = 3; - do { - ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2, - HID_FEATURE_REPORT, HID_REQ_SET_REPORT); - } while (ret < 0 && limit-- > 0); - - if (ret >= 0) { - if (speed == 0) - rep_data[0] = 0x05; - else - rep_data[0] = 0x06; - - rep_data[1] = 0x00; - limit = 3; - do { - ret = hid_hw_raw_request(hdev, rep_data[0], - rep_data, 2, HID_FEATURE_REPORT, - HID_REQ_SET_REPORT); - } while (ret < 0 && limit-- > 0); - - if (ret >= 0) { - wdata->high_speed = speed; - return; - } - } - - /* - * Note that if the raw queries fail, it's not a hard failure - * and it is safe to continue - */ - hid_warn(hdev, "failed to poke device, command %d, err %d\n", - rep_data[0], ret); - break; - case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH: - if (speed == 1) - wdata->features &= ~0x20; - else - wdata->features |= 0x20; - - rep_data[0] = 0x03; - rep_data[1] = wdata->features; - - ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2, - HID_FEATURE_REPORT, HID_REQ_SET_REPORT); - if (ret >= 0) - wdata->high_speed = speed; - break; - } - - return; -} - -static ssize_t wacom_show_speed(struct device *dev, - struct device_attribute - *attr, char *buf) -{ - struct wacom_data *wdata = dev_get_drvdata(dev); - - return snprintf(buf, PAGE_SIZE, "%i\n", wdata->high_speed); -} - -static ssize_t wacom_store_speed(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - int new_speed; - - if (sscanf(buf, "%1d", &new_speed ) != 1) - return -EINVAL; - - if (new_speed == 0 || new_speed == 1) { - wacom_set_features(hdev, new_speed); - return strnlen(buf, PAGE_SIZE); - } else - return -EINVAL; -} - -static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP, - wacom_show_speed, wacom_store_speed); - -#define WACOM_STORE(OLED_ID) \ -static ssize_t wacom_oled##OLED_ID##_store(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - struct hid_device *hdev = container_of(dev, struct hid_device, \ - dev); \ - \ - if (count != 256) \ - return -EINVAL; \ - \ - wacom_set_image(hdev, buf, OLED_ID); \ - \ - return count; \ -} \ - \ -static DEVICE_ATTR(oled##OLED_ID##_img, S_IWUSR | S_IWGRP, NULL, \ - wacom_oled##OLED_ID##_store) - -WACOM_STORE(0); -WACOM_STORE(1); -WACOM_STORE(2); -WACOM_STORE(3); -WACOM_STORE(4); -WACOM_STORE(5); -WACOM_STORE(6); -WACOM_STORE(7); - -static int wacom_gr_parse_report(struct hid_device *hdev, - struct wacom_data *wdata, - struct input_dev *input, unsigned char *data) -{ - int tool, x, y, rw; - - tool = 0; - /* Get X & Y positions */ - x = le16_to_cpu(*(__le16 *) &data[2]); - y = le16_to_cpu(*(__le16 *) &data[4]); - - /* Get current tool identifier */ - if (data[1] & 0x90) { /* If pen is in the in/active area */ - switch ((data[1] >> 5) & 3) { - case 0: /* Pen */ - tool = BTN_TOOL_PEN; - break; - - case 1: /* Rubber */ - tool = BTN_TOOL_RUBBER; - break; - - case 2: /* Mouse with wheel */ - case 3: /* Mouse without wheel */ - tool = BTN_TOOL_MOUSE; - break; - } - - /* Reset tool if out of active tablet area */ - if (!(data[1] & 0x10)) - tool = 0; - } - - /* If tool changed, notify input subsystem */ - if (wdata->tool != tool) { - if (wdata->tool) { - /* Completely reset old tool state */ - if (wdata->tool == BTN_TOOL_MOUSE) { - input_report_key(input, BTN_LEFT, 0); - input_report_key(input, BTN_RIGHT, 0); - input_report_key(input, BTN_MIDDLE, 0); - input_report_abs(input, ABS_DISTANCE, - input_abs_get_max(input, ABS_DISTANCE)); - } else { - input_report_key(input, BTN_TOUCH, 0); - input_report_key(input, BTN_STYLUS, 0); - input_report_key(input, BTN_STYLUS2, 0); - input_report_abs(input, ABS_PRESSURE, 0); - } - input_report_key(input, wdata->tool, 0); - input_sync(input); - } - wdata->tool = tool; - if (tool) - input_report_key(input, tool, 1); - } - - if (tool) { - input_report_abs(input, ABS_X, x); - input_report_abs(input, ABS_Y, y); - - switch ((data[1] >> 5) & 3) { - case 2: /* Mouse with wheel */ - input_report_key(input, BTN_MIDDLE, data[1] & 0x04); - rw = (data[6] & 0x01) ? -1 : - (data[6] & 0x02) ? 1 : 0; - input_report_rel(input, REL_WHEEL, rw); - /* fall through */ - - case 3: /* Mouse without wheel */ - input_report_key(input, BTN_LEFT, data[1] & 0x01); - input_report_key(input, BTN_RIGHT, data[1] & 0x02); - /* Compute distance between mouse and tablet */ - rw = 44 - (data[6] >> 2); - if (rw < 0) - rw = 0; - else if (rw > 31) - rw = 31; - input_report_abs(input, ABS_DISTANCE, rw); - break; - - default: - input_report_abs(input, ABS_PRESSURE, - data[6] | (((__u16) (data[1] & 0x08)) << 5)); - input_report_key(input, BTN_TOUCH, data[1] & 0x01); - input_report_key(input, BTN_STYLUS, data[1] & 0x02); - input_report_key(input, BTN_STYLUS2, (tool == BTN_TOOL_PEN) && data[1] & 0x04); - break; - } - - input_sync(input); - } - - /* Report the state of the two buttons at the top of the tablet - * as two extra fingerpad keys (buttons 4 & 5). */ - rw = data[7] & 0x03; - if (rw != wdata->butstate) { - wdata->butstate = rw; - input_report_key(input, BTN_0, rw & 0x02); - input_report_key(input, BTN_1, rw & 0x01); - input_report_key(input, BTN_TOOL_FINGER, 0xf0); - input_event(input, EV_MSC, MSC_SERIAL, 0xf0); - input_sync(input); - } - - /* Store current battery capacity and power supply state*/ - rw = (data[7] >> 2 & 0x07); - if (rw != wdata->power_raw) { - wdata->power_raw = rw; - wdata->battery_capacity = batcap_gr[rw]; - if (rw == 7) - wdata->ps_connected = 1; - else - wdata->ps_connected = 0; - } - return 1; -} - -static void wacom_i4_parse_button_report(struct wacom_data *wdata, - struct input_dev *input, unsigned char *data) -{ - __u16 new_butstate; - __u8 new_whlstate; - __u8 sync = 0; - - new_whlstate = data[1]; - if (new_whlstate != wdata->whlstate) { - wdata->whlstate = new_whlstate; - if (new_whlstate & 0x80) { - input_report_key(input, BTN_TOUCH, 1); - input_report_abs(input, ABS_WHEEL, (new_whlstate & 0x7f)); - input_report_key(input, BTN_TOOL_FINGER, 1); - } else { - input_report_key(input, BTN_TOUCH, 0); - input_report_abs(input, ABS_WHEEL, 0); - input_report_key(input, BTN_TOOL_FINGER, 0); - } - sync = 1; - } - - new_butstate = (data[3] << 1) | (data[2] & 0x01); - if (new_butstate != wdata->butstate) { - wdata->butstate = new_butstate; - input_report_key(input, BTN_0, new_butstate & 0x001); - input_report_key(input, BTN_1, new_butstate & 0x002); - input_report_key(input, BTN_2, new_butstate & 0x004); - input_report_key(input, BTN_3, new_butstate & 0x008); - input_report_key(input, BTN_4, new_butstate & 0x010); - input_report_key(input, BTN_5, new_butstate & 0x020); - input_report_key(input, BTN_6, new_butstate & 0x040); - input_report_key(input, BTN_7, new_butstate & 0x080); - input_report_key(input, BTN_8, new_butstate & 0x100); - input_report_key(input, BTN_TOOL_FINGER, 1); - sync = 1; - } - - if (sync) { - input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); - input_event(input, EV_MSC, MSC_SERIAL, 0xffffffff); - input_sync(input); - } -} - -static void wacom_i4_parse_pen_report(struct wacom_data *wdata, - struct input_dev *input, unsigned char *data) -{ - __u16 x, y, pressure; - __u8 distance; - __u8 tilt_x, tilt_y; - - switch (data[1]) { - case 0x80: /* Out of proximity report */ - input_report_key(input, BTN_TOUCH, 0); - input_report_abs(input, ABS_PRESSURE, 0); - input_report_key(input, BTN_STYLUS, 0); - input_report_key(input, BTN_STYLUS2, 0); - input_report_key(input, wdata->tool, 0); - input_report_abs(input, ABS_MISC, 0); - input_event(input, EV_MSC, MSC_SERIAL, wdata->serial); - wdata->tool = 0; - input_sync(input); - break; - case 0xC2: /* Tool report */ - wdata->id = ((data[2] << 4) | (data[3] >> 4) | - ((data[7] & 0x0f) << 20) | - ((data[8] & 0xf0) << 12)); - wdata->serial = ((data[3] & 0x0f) << 28) + - (data[4] << 20) + (data[5] << 12) + - (data[6] << 4) + (data[7] >> 4); - - switch (wdata->id) { - case 0x100802: - wdata->tool = BTN_TOOL_PEN; - break; - case 0x10080A: - wdata->tool = BTN_TOOL_RUBBER; - break; - } - break; - default: /* Position/pressure report */ - x = data[2] << 9 | data[3] << 1 | ((data[9] & 0x02) >> 1); - y = data[4] << 9 | data[5] << 1 | (data[9] & 0x01); - pressure = (data[6] << 3) | ((data[7] & 0xC0) >> 5) - | (data[1] & 0x01); - distance = (data[9] >> 2) & 0x3f; - tilt_x = ((data[7] << 1) & 0x7e) | (data[8] >> 7); - tilt_y = data[8] & 0x7f; - - input_report_key(input, BTN_TOUCH, pressure > 1); - - input_report_key(input, BTN_STYLUS, data[1] & 0x02); - input_report_key(input, BTN_STYLUS2, data[1] & 0x04); - input_report_key(input, wdata->tool, 1); - input_report_abs(input, ABS_X, x); - input_report_abs(input, ABS_Y, y); - input_report_abs(input, ABS_PRESSURE, pressure); - input_report_abs(input, ABS_DISTANCE, distance); - input_report_abs(input, ABS_TILT_X, tilt_x); - input_report_abs(input, ABS_TILT_Y, tilt_y); - input_report_abs(input, ABS_MISC, wdata->id); - input_event(input, EV_MSC, MSC_SERIAL, wdata->serial); - input_report_key(input, wdata->tool, 1); - input_sync(input); - break; - } - - return; -} - -static void wacom_i4_parse_report(struct hid_device *hdev, - struct wacom_data *wdata, - struct input_dev *input, unsigned char *data) -{ - switch (data[0]) { - case 0x00: /* Empty report */ - break; - case 0x02: /* Pen report */ - wacom_i4_parse_pen_report(wdata, input, data); - break; - case 0x03: /* Features Report */ - wdata->features = data[2]; - break; - case 0x0C: /* Button report */ - wacom_i4_parse_button_report(wdata, input, data); - break; - default: - hid_err(hdev, "Unknown report: %d,%d\n", data[0], data[1]); - break; - } -} - -static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, - u8 *raw_data, int size) -{ - struct wacom_data *wdata = hid_get_drvdata(hdev); - struct hid_input *hidinput; - struct input_dev *input; - unsigned char *data = (unsigned char *) raw_data; - int i; - __u8 power_raw; - - if (!(hdev->claimed & HID_CLAIMED_INPUT)) - return 0; - - hidinput = list_entry(hdev->inputs.next, struct hid_input, list); - input = hidinput->input; - - switch (hdev->product) { - case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH: - if (data[0] == 0x03) { - return wacom_gr_parse_report(hdev, wdata, input, data); - } else { - hid_err(hdev, "Unknown report: %d,%d size:%d\n", - data[0], data[1], size); - return 0; - } - break; - case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH: - i = 1; - - switch (data[0]) { - case 0x04: - wacom_i4_parse_report(hdev, wdata, input, data + i); - i += 10; - /* fall through */ - case 0x03: - wacom_i4_parse_report(hdev, wdata, input, data + i); - i += 10; - wacom_i4_parse_report(hdev, wdata, input, data + i); - power_raw = data[i+10]; - if (power_raw != wdata->power_raw) { - wdata->power_raw = power_raw; - wdata->battery_capacity = batcap_i4[power_raw & 0x07]; - wdata->bat_charging = (power_raw & 0x08) ? 1 : 0; - wdata->ps_connected = (power_raw & 0x10) ? 1 : 0; - } - - break; - default: - hid_err(hdev, "Unknown report: %d,%d size:%d\n", - data[0], data[1], size); - return 0; - } - } - return 1; -} - -static int wacom_input_mapped(struct hid_device *hdev, struct hid_input *hi, - struct hid_field *field, struct hid_usage *usage, unsigned long **bit, - int *max) -{ - struct input_dev *input = hi->input; - - __set_bit(INPUT_PROP_POINTER, input->propbit); - - /* Basics */ - input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL); - - __set_bit(REL_WHEEL, input->relbit); - - __set_bit(BTN_TOOL_PEN, input->keybit); - __set_bit(BTN_TOUCH, input->keybit); - __set_bit(BTN_STYLUS, input->keybit); - __set_bit(BTN_STYLUS2, input->keybit); - __set_bit(BTN_LEFT, input->keybit); - __set_bit(BTN_RIGHT, input->keybit); - __set_bit(BTN_MIDDLE, input->keybit); - - /* Pad */ - input_set_capability(input, EV_MSC, MSC_SERIAL); - - __set_bit(BTN_0, input->keybit); - __set_bit(BTN_1, input->keybit); - __set_bit(BTN_TOOL_FINGER, input->keybit); - - /* Distance, rubber and mouse */ - __set_bit(BTN_TOOL_RUBBER, input->keybit); - __set_bit(BTN_TOOL_MOUSE, input->keybit); - - switch (hdev->product) { - case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH: - input_set_abs_params(input, ABS_X, 0, 16704, 4, 0); - input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0); - input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0); - input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0); - break; - case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH: - __set_bit(ABS_WHEEL, input->absbit); - __set_bit(ABS_MISC, input->absbit); - __set_bit(BTN_2, input->keybit); - __set_bit(BTN_3, input->keybit); - __set_bit(BTN_4, input->keybit); - __set_bit(BTN_5, input->keybit); - __set_bit(BTN_6, input->keybit); - __set_bit(BTN_7, input->keybit); - __set_bit(BTN_8, input->keybit); - input_set_abs_params(input, ABS_WHEEL, 0, 71, 0, 0); - input_set_abs_params(input, ABS_X, 0, 40640, 4, 0); - input_set_abs_params(input, ABS_Y, 0, 25400, 4, 0); - input_set_abs_params(input, ABS_PRESSURE, 0, 2047, 0, 0); - input_set_abs_params(input, ABS_DISTANCE, 0, 63, 0, 0); - input_set_abs_params(input, ABS_TILT_X, 0, 127, 0, 0); - input_set_abs_params(input, ABS_TILT_Y, 0, 127, 0, 0); - break; - } - - return 0; -} - -static int wacom_probe(struct hid_device *hdev, - const struct hid_device_id *id) -{ - struct wacom_data *wdata; - int ret; - - wdata = kzalloc(sizeof(*wdata), GFP_KERNEL); - if (wdata == NULL) { - hid_err(hdev, "can't alloc wacom descriptor\n"); - return -ENOMEM; - } - - hid_set_drvdata(hdev, wdata); - - /* Parse the HID report now */ - ret = hid_parse(hdev); - if (ret) { - hid_err(hdev, "parse failed\n"); - goto err_free; - } - - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (ret) { - hid_err(hdev, "hw start failed\n"); - goto err_free; - } - - ret = device_create_file(&hdev->dev, &dev_attr_speed); - if (ret) - hid_warn(hdev, - "can't create sysfs speed attribute err: %d\n", ret); - -#define OLED_INIT(OLED_ID) \ - do { \ - ret = device_create_file(&hdev->dev, \ - &dev_attr_oled##OLED_ID##_img); \ - if (ret) \ - hid_warn(hdev, \ - "can't create sysfs oled attribute, err: %d\n", ret);\ - } while (0) - -OLED_INIT(0); -OLED_INIT(1); -OLED_INIT(2); -OLED_INIT(3); -OLED_INIT(4); -OLED_INIT(5); -OLED_INIT(6); -OLED_INIT(7); - - wdata->features = 0; - wacom_set_features(hdev, 1); - - if (hdev->product == USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) { - sprintf(hdev->name, "%s", "Wacom Intuos4 WL"); - ret = wacom_initialize_leds(hdev); - if (ret) - hid_warn(hdev, - "can't create led attribute, err: %d\n", ret); - } - - wdata->battery.properties = wacom_battery_props; - wdata->battery.num_properties = ARRAY_SIZE(wacom_battery_props); - wdata->battery.get_property = wacom_battery_get_property; - wdata->battery.name = "wacom_battery"; - wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY; - wdata->battery.use_for_apm = 0; - - - ret = power_supply_register(&hdev->dev, &wdata->battery); - if (ret) { - hid_err(hdev, "can't create sysfs battery attribute, err: %d\n", - ret); - goto err_battery; - } - - power_supply_powers(&wdata->battery, &hdev->dev); - - wdata->ac.properties = wacom_ac_props; - wdata->ac.num_properties = ARRAY_SIZE(wacom_ac_props); - wdata->ac.get_property = wacom_ac_get_property; - wdata->ac.name = "wacom_ac"; - wdata->ac.type = POWER_SUPPLY_TYPE_MAINS; - wdata->ac.use_for_apm = 0; - - ret = power_supply_register(&hdev->dev, &wdata->ac); - if (ret) { - hid_err(hdev, - "can't create ac battery attribute, err: %d\n", ret); - goto err_ac; - } - - power_supply_powers(&wdata->ac, &hdev->dev); - return 0; - -err_ac: - power_supply_unregister(&wdata->battery); -err_battery: - wacom_destroy_leds(hdev); - device_remove_file(&hdev->dev, &dev_attr_oled0_img); - device_remove_file(&hdev->dev, &dev_attr_oled1_img); - device_remove_file(&hdev->dev, &dev_attr_oled2_img); - device_remove_file(&hdev->dev, &dev_attr_oled3_img); - device_remove_file(&hdev->dev, &dev_attr_oled4_img); - device_remove_file(&hdev->dev, &dev_attr_oled5_img); - device_remove_file(&hdev->dev, &dev_attr_oled6_img); - device_remove_file(&hdev->dev, &dev_attr_oled7_img); - device_remove_file(&hdev->dev, &dev_attr_speed); - hid_hw_stop(hdev); -err_free: - kfree(wdata); - return ret; -} - -static void wacom_remove(struct hid_device *hdev) -{ - struct wacom_data *wdata = hid_get_drvdata(hdev); - - wacom_destroy_leds(hdev); - device_remove_file(&hdev->dev, &dev_attr_oled0_img); - device_remove_file(&hdev->dev, &dev_attr_oled1_img); - device_remove_file(&hdev->dev, &dev_attr_oled2_img); - device_remove_file(&hdev->dev, &dev_attr_oled3_img); - device_remove_file(&hdev->dev, &dev_attr_oled4_img); - device_remove_file(&hdev->dev, &dev_attr_oled5_img); - device_remove_file(&hdev->dev, &dev_attr_oled6_img); - device_remove_file(&hdev->dev, &dev_attr_oled7_img); - device_remove_file(&hdev->dev, &dev_attr_speed); - hid_hw_stop(hdev); - - power_supply_unregister(&wdata->battery); - power_supply_unregister(&wdata->ac); - kfree(hid_get_drvdata(hdev)); -} - -static const struct hid_device_id wacom_devices[] = { - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) }, - - { } -}; -MODULE_DEVICE_TABLE(hid, wacom_devices); - -static struct hid_driver wacom_driver = { - .name = "wacom", - .id_table = wacom_devices, - .probe = wacom_probe, - .remove = wacom_remove, - .raw_event = wacom_raw_event, - .input_mapped = wacom_input_mapped, -}; -module_hid_driver(wacom_driver); - -MODULE_DESCRIPTION("Driver for Wacom Graphire Bluetooth and Wacom Intuos4 WL"); -MODULE_LICENSE("GPL"); diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 0cb92e347258..e094c572b86e 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -44,10 +44,12 @@ struct uhid_device { __u8 tail; struct uhid_event *outq[UHID_BUFSIZE]; + /* blocking GET_REPORT support; state changes protected by qlock */ struct mutex report_lock; wait_queue_head_t report_wait; - atomic_t report_done; - atomic_t report_id; + bool report_running; + u32 report_id; + u32 report_type; struct uhid_event report_buf; }; @@ -90,8 +92,27 @@ static int uhid_queue_event(struct uhid_device *uhid, __u32 event) static int uhid_hid_start(struct hid_device *hid) { struct uhid_device *uhid = hid->driver_data; + struct uhid_event *ev; + unsigned long flags; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = UHID_START; - return uhid_queue_event(uhid, UHID_START); + if (hid->report_enum[HID_FEATURE_REPORT].numbered) + ev->u.start.dev_flags |= UHID_DEV_NUMBERED_FEATURE_REPORTS; + if (hid->report_enum[HID_OUTPUT_REPORT].numbered) + ev->u.start.dev_flags |= UHID_DEV_NUMBERED_OUTPUT_REPORTS; + if (hid->report_enum[HID_INPUT_REPORT].numbered) + ev->u.start.dev_flags |= UHID_DEV_NUMBERED_INPUT_REPORTS; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return 0; } static void uhid_hid_stop(struct hid_device *hid) @@ -123,87 +144,169 @@ static int uhid_hid_parse(struct hid_device *hid) return hid_parse_report(hid, uhid->rd_data, uhid->rd_size); } -static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, - __u8 *buf, size_t count, unsigned char rtype) +/* must be called with report_lock held */ +static int __uhid_report_queue_and_wait(struct uhid_device *uhid, + struct uhid_event *ev, + __u32 *report_id) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&uhid->qlock, flags); + *report_id = ++uhid->report_id; + uhid->report_type = ev->type + 1; + uhid->report_running = true; + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + ret = wait_event_interruptible_timeout(uhid->report_wait, + !uhid->report_running || !uhid->running, + 5 * HZ); + if (!ret || !uhid->running || uhid->report_running) + ret = -EIO; + else if (ret < 0) + ret = -ERESTARTSYS; + else + ret = 0; + + uhid->report_running = false; + + return ret; +} + +static void uhid_report_wake_up(struct uhid_device *uhid, u32 id, + const struct uhid_event *ev) +{ + unsigned long flags; + + spin_lock_irqsave(&uhid->qlock, flags); + + /* id for old report; drop it silently */ + if (uhid->report_type != ev->type || uhid->report_id != id) + goto unlock; + if (!uhid->report_running) + goto unlock; + + memcpy(&uhid->report_buf, ev, sizeof(*ev)); + uhid->report_running = false; + wake_up_interruptible(&uhid->report_wait); + +unlock: + spin_unlock_irqrestore(&uhid->qlock, flags); +} + +static int uhid_hid_get_report(struct hid_device *hid, unsigned char rnum, + u8 *buf, size_t count, u8 rtype) { struct uhid_device *uhid = hid->driver_data; - __u8 report_type; + struct uhid_get_report_reply_req *req; struct uhid_event *ev; - unsigned long flags; int ret; - size_t uninitialized_var(len); - struct uhid_feature_answer_req *req; if (!uhid->running) return -EIO; - switch (rtype) { - case HID_FEATURE_REPORT: - report_type = UHID_FEATURE_REPORT; - break; - case HID_OUTPUT_REPORT: - report_type = UHID_OUTPUT_REPORT; - break; - case HID_INPUT_REPORT: - report_type = UHID_INPUT_REPORT; - break; - default: - return -EINVAL; - } + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = UHID_GET_REPORT; + ev->u.get_report.rnum = rnum; + ev->u.get_report.rtype = rtype; ret = mutex_lock_interruptible(&uhid->report_lock); - if (ret) + if (ret) { + kfree(ev); return ret; + } - ev = kzalloc(sizeof(*ev), GFP_KERNEL); - if (!ev) { - ret = -ENOMEM; + /* this _always_ takes ownership of @ev */ + ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.get_report.id); + if (ret) goto unlock; + + req = &uhid->report_buf.u.get_report_reply; + if (req->err) { + ret = -EIO; + } else { + ret = min3(count, (size_t)req->size, (size_t)UHID_DATA_MAX); + memcpy(buf, req->data, ret); } - spin_lock_irqsave(&uhid->qlock, flags); - ev->type = UHID_FEATURE; - ev->u.feature.id = atomic_inc_return(&uhid->report_id); - ev->u.feature.rnum = rnum; - ev->u.feature.rtype = report_type; +unlock: + mutex_unlock(&uhid->report_lock); + return ret; +} - atomic_set(&uhid->report_done, 0); - uhid_queue(uhid, ev); - spin_unlock_irqrestore(&uhid->qlock, flags); +static int uhid_hid_set_report(struct hid_device *hid, unsigned char rnum, + const u8 *buf, size_t count, u8 rtype) +{ + struct uhid_device *uhid = hid->driver_data; + struct uhid_event *ev; + int ret; - ret = wait_event_interruptible_timeout(uhid->report_wait, - atomic_read(&uhid->report_done), 5 * HZ); - - /* - * Make sure "uhid->running" is cleared on shutdown before - * "uhid->report_done" is set. - */ - smp_rmb(); - if (!ret || !uhid->running) { - ret = -EIO; - } else if (ret < 0) { - ret = -ERESTARTSYS; - } else { - spin_lock_irqsave(&uhid->qlock, flags); - req = &uhid->report_buf.u.feature_answer; + if (!uhid->running || count > UHID_DATA_MAX) + return -EIO; - if (req->err) { - ret = -EIO; - } else { - ret = 0; - len = min(count, - min_t(size_t, req->size, UHID_DATA_MAX)); - memcpy(buf, req->data, len); - } + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = UHID_SET_REPORT; + ev->u.set_report.rnum = rnum; + ev->u.set_report.rtype = rtype; + ev->u.set_report.size = count; + memcpy(ev->u.set_report.data, buf, count); - spin_unlock_irqrestore(&uhid->qlock, flags); + ret = mutex_lock_interruptible(&uhid->report_lock); + if (ret) { + kfree(ev); + return ret; } - atomic_set(&uhid->report_done, 1); + /* this _always_ takes ownership of @ev */ + ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.set_report.id); + if (ret) + goto unlock; + + if (uhid->report_buf.u.set_report_reply.err) + ret = -EIO; + else + ret = count; unlock: mutex_unlock(&uhid->report_lock); - return ret ? ret : len; + return ret; +} + +static int uhid_hid_raw_request(struct hid_device *hid, unsigned char reportnum, + __u8 *buf, size_t len, unsigned char rtype, + int reqtype) +{ + u8 u_rtype; + + switch (rtype) { + case HID_FEATURE_REPORT: + u_rtype = UHID_FEATURE_REPORT; + break; + case HID_OUTPUT_REPORT: + u_rtype = UHID_OUTPUT_REPORT; + break; + case HID_INPUT_REPORT: + u_rtype = UHID_INPUT_REPORT; + break; + default: + return -EINVAL; + } + + switch (reqtype) { + case HID_REQ_GET_REPORT: + return uhid_hid_get_report(hid, reportnum, buf, len, u_rtype); + case HID_REQ_SET_REPORT: + return uhid_hid_set_report(hid, reportnum, buf, len, u_rtype); + default: + return -EIO; + } } static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, @@ -250,29 +353,14 @@ static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf, return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT); } -static int uhid_raw_request(struct hid_device *hid, unsigned char reportnum, - __u8 *buf, size_t len, unsigned char rtype, - int reqtype) -{ - switch (reqtype) { - case HID_REQ_GET_REPORT: - return uhid_hid_get_raw(hid, reportnum, buf, len, rtype); - case HID_REQ_SET_REPORT: - /* TODO: implement proper SET_REPORT functionality */ - return -ENOSYS; - default: - return -EIO; - } -} - static struct hid_ll_driver uhid_hid_driver = { .start = uhid_hid_start, .stop = uhid_hid_stop, .open = uhid_hid_open, .close = uhid_hid_close, .parse = uhid_hid_parse, + .raw_request = uhid_hid_raw_request, .output_report = uhid_hid_output_report, - .raw_request = uhid_raw_request, }; #ifdef CONFIG_COMPAT @@ -363,28 +451,27 @@ static int uhid_event_from_user(const char __user *buffer, size_t len, } #endif -static int uhid_dev_create(struct uhid_device *uhid, - const struct uhid_event *ev) +static int uhid_dev_create2(struct uhid_device *uhid, + const struct uhid_event *ev) { struct hid_device *hid; + size_t rd_size, len; + void *rd_data; int ret; if (uhid->running) return -EALREADY; - uhid->rd_size = ev->u.create.rd_size; - if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE) + rd_size = ev->u.create2.rd_size; + if (rd_size <= 0 || rd_size > HID_MAX_DESCRIPTOR_SIZE) return -EINVAL; - uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL); - if (!uhid->rd_data) + rd_data = kmemdup(ev->u.create2.rd_data, rd_size, GFP_KERNEL); + if (!rd_data) return -ENOMEM; - if (copy_from_user(uhid->rd_data, ev->u.create.rd_data, - uhid->rd_size)) { - ret = -EFAULT; - goto err_free; - } + uhid->rd_size = rd_size; + uhid->rd_data = rd_data; hid = hid_allocate_device(); if (IS_ERR(hid)) { @@ -392,19 +479,19 @@ static int uhid_dev_create(struct uhid_device *uhid, goto err_free; } - strncpy(hid->name, ev->u.create.name, 127); - hid->name[127] = 0; - strncpy(hid->phys, ev->u.create.phys, 63); - hid->phys[63] = 0; - strncpy(hid->uniq, ev->u.create.uniq, 63); - hid->uniq[63] = 0; + len = min(sizeof(hid->name), sizeof(ev->u.create2.name)) - 1; + strncpy(hid->name, ev->u.create2.name, len); + len = min(sizeof(hid->phys), sizeof(ev->u.create2.phys)) - 1; + strncpy(hid->phys, ev->u.create2.phys, len); + len = min(sizeof(hid->uniq), sizeof(ev->u.create2.uniq)) - 1; + strncpy(hid->uniq, ev->u.create2.uniq, len); hid->ll_driver = &uhid_hid_driver; - hid->bus = ev->u.create.bus; - hid->vendor = ev->u.create.vendor; - hid->product = ev->u.create.product; - hid->version = ev->u.create.version; - hid->country = ev->u.create.country; + hid->bus = ev->u.create2.bus; + hid->vendor = ev->u.create2.vendor; + hid->product = ev->u.create2.product; + hid->version = ev->u.create2.version; + hid->country = ev->u.create2.country; hid->driver_data = uhid; hid->dev.parent = uhid_misc.this_device; @@ -425,67 +512,34 @@ err_hid: uhid->running = false; err_free: kfree(uhid->rd_data); + uhid->rd_data = NULL; + uhid->rd_size = 0; return ret; } -static int uhid_dev_create2(struct uhid_device *uhid, - const struct uhid_event *ev) +static int uhid_dev_create(struct uhid_device *uhid, + struct uhid_event *ev) { - struct hid_device *hid; - int ret; + struct uhid_create_req orig; - if (uhid->running) - return -EALREADY; + orig = ev->u.create; - uhid->rd_size = ev->u.create2.rd_size; - if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE) + if (orig.rd_size <= 0 || orig.rd_size > HID_MAX_DESCRIPTOR_SIZE) return -EINVAL; + if (copy_from_user(&ev->u.create2.rd_data, orig.rd_data, orig.rd_size)) + return -EFAULT; - uhid->rd_data = kmemdup(ev->u.create2.rd_data, uhid->rd_size, - GFP_KERNEL); - if (!uhid->rd_data) - return -ENOMEM; - - hid = hid_allocate_device(); - if (IS_ERR(hid)) { - ret = PTR_ERR(hid); - goto err_free; - } - - strncpy(hid->name, ev->u.create2.name, 127); - hid->name[127] = 0; - strncpy(hid->phys, ev->u.create2.phys, 63); - hid->phys[63] = 0; - strncpy(hid->uniq, ev->u.create2.uniq, 63); - hid->uniq[63] = 0; - - hid->ll_driver = &uhid_hid_driver; - hid->bus = ev->u.create2.bus; - hid->vendor = ev->u.create2.vendor; - hid->product = ev->u.create2.product; - hid->version = ev->u.create2.version; - hid->country = ev->u.create2.country; - hid->driver_data = uhid; - hid->dev.parent = uhid_misc.this_device; - - uhid->hid = hid; - uhid->running = true; - - ret = hid_add_device(hid); - if (ret) { - hid_err(hid, "Cannot register HID device\n"); - goto err_hid; - } - - return 0; - -err_hid: - hid_destroy_device(hid); - uhid->hid = NULL; - uhid->running = false; -err_free: - kfree(uhid->rd_data); - return ret; + memcpy(ev->u.create2.name, orig.name, sizeof(orig.name)); + memcpy(ev->u.create2.phys, orig.phys, sizeof(orig.phys)); + memcpy(ev->u.create2.uniq, orig.uniq, sizeof(orig.uniq)); + ev->u.create2.rd_size = orig.rd_size; + ev->u.create2.bus = orig.bus; + ev->u.create2.vendor = orig.vendor; + ev->u.create2.product = orig.product; + ev->u.create2.version = orig.version; + ev->u.create2.country = orig.country; + + return uhid_dev_create2(uhid, ev); } static int uhid_dev_destroy(struct uhid_device *uhid) @@ -493,10 +547,7 @@ static int uhid_dev_destroy(struct uhid_device *uhid) if (!uhid->running) return -EINVAL; - /* clear "running" before setting "report_done" */ uhid->running = false; - smp_wmb(); - atomic_set(&uhid->report_done, 1); wake_up_interruptible(&uhid->report_wait); hid_destroy_device(uhid->hid); @@ -527,28 +578,23 @@ static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev) return 0; } -static int uhid_dev_feature_answer(struct uhid_device *uhid, - struct uhid_event *ev) +static int uhid_dev_get_report_reply(struct uhid_device *uhid, + struct uhid_event *ev) { - unsigned long flags; - if (!uhid->running) return -EINVAL; - spin_lock_irqsave(&uhid->qlock, flags); - - /* id for old report; drop it silently */ - if (atomic_read(&uhid->report_id) != ev->u.feature_answer.id) - goto unlock; - if (atomic_read(&uhid->report_done)) - goto unlock; + uhid_report_wake_up(uhid, ev->u.get_report_reply.id, ev); + return 0; +} - memcpy(&uhid->report_buf, ev, sizeof(*ev)); - atomic_set(&uhid->report_done, 1); - wake_up_interruptible(&uhid->report_wait); +static int uhid_dev_set_report_reply(struct uhid_device *uhid, + struct uhid_event *ev) +{ + if (!uhid->running) + return -EINVAL; -unlock: - spin_unlock_irqrestore(&uhid->qlock, flags); + uhid_report_wake_up(uhid, ev->u.set_report_reply.id, ev); return 0; } @@ -566,7 +612,6 @@ static int uhid_char_open(struct inode *inode, struct file *file) init_waitqueue_head(&uhid->waitq); init_waitqueue_head(&uhid->report_wait); uhid->running = false; - atomic_set(&uhid->report_done, 1); file->private_data = uhid; nonseekable_open(inode, file); @@ -675,8 +720,11 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, case UHID_INPUT2: ret = uhid_dev_input2(uhid, &uhid->input_buf); break; - case UHID_FEATURE_ANSWER: - ret = uhid_dev_feature_answer(uhid, &uhid->input_buf); + case UHID_GET_REPORT_REPLY: + ret = uhid_dev_get_report_reply(uhid, &uhid->input_buf); + break; + case UHID_SET_REPORT_REPLY: + ret = uhid_dev_set_report_reply(uhid, &uhid->input_buf); break; default: ret = -EOPNOTSUPP; diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 79cf503e37bf..ca6849a0121e 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -82,7 +82,7 @@ static int hid_start_in(struct hid_device *hid) struct usbhid_device *usbhid = hid->driver_data; spin_lock_irqsave(&usbhid->lock, flags); - if (hid->open > 0 && + if ((hid->open > 0 || hid->quirks & HID_QUIRK_ALWAYS_POLL) && !test_bit(HID_DISCONNECTED, &usbhid->iofl) && !test_bit(HID_SUSPENDED, &usbhid->iofl) && !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) { @@ -116,40 +116,24 @@ static void hid_reset(struct work_struct *work) struct usbhid_device *usbhid = container_of(work, struct usbhid_device, reset_work); struct hid_device *hid = usbhid->hid; - int rc = 0; + int rc; if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) { dev_dbg(&usbhid->intf->dev, "clear halt\n"); rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->urbin->pipe); clear_bit(HID_CLEAR_HALT, &usbhid->iofl); - hid_start_in(hid); - } - - else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) { - dev_dbg(&usbhid->intf->dev, "resetting device\n"); - rc = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf); if (rc == 0) { - rc = usb_reset_device(hid_to_usb_dev(hid)); - usb_unlock_device(hid_to_usb_dev(hid)); + hid_start_in(hid); + } else { + dev_dbg(&usbhid->intf->dev, + "clear-halt failed: %d\n", rc); + set_bit(HID_RESET_PENDING, &usbhid->iofl); } - clear_bit(HID_RESET_PENDING, &usbhid->iofl); } - switch (rc) { - case 0: - if (!test_bit(HID_IN_RUNNING, &usbhid->iofl)) - hid_io_error(hid); - break; - default: - hid_err(hid, "can't reset device, %s-%s/input%d, status %d\n", - hid_to_usb_dev(hid)->bus->bus_name, - hid_to_usb_dev(hid)->devpath, - usbhid->ifnum, rc); - /* FALLTHROUGH */ - case -EHOSTUNREACH: - case -ENODEV: - case -EINTR: - break; + if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) { + dev_dbg(&usbhid->intf->dev, "resetting device\n"); + usb_queue_reset_device(usbhid->intf); } } @@ -292,6 +276,8 @@ static void hid_irq_in(struct urb *urb) case 0: /* success */ usbhid_mark_busy(usbhid); usbhid->retry_delay = 0; + if ((hid->quirks & HID_QUIRK_ALWAYS_POLL) && !hid->open) + break; hid_input_report(urb->context, HID_INPUT_REPORT, urb->transfer_buffer, urb->actual_length, 1); @@ -735,8 +721,10 @@ void usbhid_close(struct hid_device *hid) if (!--hid->open) { spin_unlock_irq(&usbhid->lock); hid_cancel_delayed_stuff(usbhid); - usb_kill_urb(usbhid->urbin); - usbhid->intf->needs_remote_wakeup = 0; + if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL)) { + usb_kill_urb(usbhid->urbin); + usbhid->intf->needs_remote_wakeup = 0; + } } else { spin_unlock_irq(&usbhid->lock); } @@ -1134,6 +1122,19 @@ static int usbhid_start(struct hid_device *hid) set_bit(HID_STARTED, &usbhid->iofl); + if (hid->quirks & HID_QUIRK_ALWAYS_POLL) { + ret = usb_autopm_get_interface(usbhid->intf); + if (ret) + goto fail; + usbhid->intf->needs_remote_wakeup = 1; + ret = hid_start_in(hid); + if (ret) { + dev_err(&hid->dev, + "failed to start in urb: %d\n", ret); + } + usb_autopm_put_interface(usbhid->intf); + } + /* Some keyboards don't work until their LEDs have been set. * Since BIOSes do set the LEDs, it must be safe for any device * that supports the keyboard boot protocol. @@ -1166,6 +1167,9 @@ static void usbhid_stop(struct hid_device *hid) if (WARN_ON(!usbhid)) return; + if (hid->quirks & HID_QUIRK_ALWAYS_POLL) + usbhid->intf->needs_remote_wakeup = 0; + clear_bit(HID_STARTED, &usbhid->iofl); spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */ set_bit(HID_DISCONNECTED, &usbhid->iofl); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 0dd568170d6e..f3cb5b0a4345 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -70,15 +70,18 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET }, { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_1610, HID_QUIRK_NOGET }, { USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_1640, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL }, + { USB_VENDOR_ID_KYE, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE_ID2, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS }, diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h new file mode 100644 index 000000000000..0cc53440543a --- /dev/null +++ b/drivers/hid/wacom.h @@ -0,0 +1,152 @@ +/* + * drivers/input/tablet/wacom.h + * + * USB Wacom tablet support + * + * Copyright (c) 2000-2004 Vojtech Pavlik <vojtech@ucw.cz> + * Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk> + * Copyright (c) 2000 Clifford Wolf <clifford@clifford.at> + * Copyright (c) 2000 Sam Mosel <sam.mosel@computer.org> + * Copyright (c) 2000 James E. Blair <corvus@gnu.org> + * Copyright (c) 2000 Daniel Egger <egger@suse.de> + * Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com> + * Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be> + * Copyright (c) 2002-2011 Ping Cheng <pingc@wacom.com> + * Copyright (c) 2014 Benjamin Tissoires <benjamin.tissoires@redhat.com> + * + * ChangeLog: + * v0.1 (vp) - Initial release + * v0.2 (aba) - Support for all buttons / combinations + * v0.3 (vp) - Support for Intuos added + * v0.4 (sm) - Support for more Intuos models, menustrip + * relative mode, proximity. + * v0.5 (vp) - Big cleanup, nifty features removed, + * they belong in userspace + * v1.8 (vp) - Submit URB only when operating, moved to CVS, + * use input_report_key instead of report_btn and + * other cleanups + * v1.11 (vp) - Add URB ->dev setting for new kernels + * v1.11 (jb) - Add support for the 4D Mouse & Lens + * v1.12 (de) - Add support for two more inking pen IDs + * v1.14 (vp) - Use new USB device id probing scheme. + * Fix Wacom Graphire mouse wheel + * v1.18 (vp) - Fix mouse wheel direction + * Make mouse relative + * v1.20 (fl) - Report tool id for Intuos devices + * - Multi tools support + * - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...) + * - Add PL models support + * - Fix Wacom Graphire mouse wheel again + * v1.21 (vp) - Removed protocol descriptions + * - Added MISC_SERIAL for tool serial numbers + * (gb) - Identify version on module load. + * v1.21.1 (fl) - added Graphire2 support + * v1.21.2 (fl) - added Intuos2 support + * - added all the PL ids + * v1.21.3 (fl) - added another eraser id from Neil Okamoto + * - added smooth filter for Graphire from Peri Hankey + * - added PenPartner support from Olaf van Es + * - new tool ids from Ole Martin Bjoerndalen + * v1.29 (pc) - Add support for more tablets + * - Fix pressure reporting + * v1.30 (vp) - Merge 2.4 and 2.5 drivers + * - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse + * - Cleanups here and there + * v1.30.1 (pi) - Added Graphire3 support + * v1.40 (pc) - Add support for several new devices, fix eraser reporting, ... + * v1.43 (pc) - Added support for Cintiq 21UX + * - Fixed a Graphire bug + * - Merged wacom_intuos3_irq into wacom_intuos_irq + * v1.44 (pc) - Added support for Graphire4, Cintiq 710, Intuos3 6x11, etc. + * - Report Device IDs + * v1.45 (pc) - Added support for DTF 521, Intuos3 12x12 and 12x19 + * - Minor data report fix + * v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c, + * - where wacom_sys.c deals with system specific code, + * - and wacom_wac.c deals with Wacom specific code + * - Support Intuos3 4x6 + * v1.47 (pc) - Added support for Bamboo + * v1.48 (pc) - Added support for Bamboo1, BambooFun, and Cintiq 12WX + * v1.49 (pc) - Added support for USB Tablet PC (0x90, 0x93, and 0x9A) + * v1.50 (pc) - Fixed a TabletPC touch bug in 2.6.28 + * v1.51 (pc) - Added support for Intuos4 + * v1.52 (pc) - Query Wacom data upon system resume + * - add defines for features->type + * - add new devices (0x9F, 0xE2, and 0XE3) + * v2.00 (bt) - conversion to a HID driver + * - integration of the Bluetooth devices + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef WACOM_H +#define WACOM_H +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/hid.h> +#include <linux/usb/input.h> +#include <linux/power_supply.h> +#include <asm/unaligned.h> + +/* + * Version Information + */ +#define DRIVER_VERSION "v2.00" +#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>" +#define DRIVER_DESC "USB Wacom tablet driver" +#define DRIVER_LICENSE "GPL" + +#define USB_VENDOR_ID_WACOM 0x056a +#define USB_VENDOR_ID_LENOVO 0x17ef + +struct wacom { + struct usb_device *usbdev; + struct usb_interface *intf; + struct wacom_wac wacom_wac; + struct hid_device *hdev; + struct mutex lock; + struct work_struct work; + struct wacom_led { + u8 select[2]; /* status led selector (0..3) */ + u8 llv; /* status led brightness no button (1..127) */ + u8 hlv; /* status led brightness button pressed (1..127) */ + u8 img_lum; /* OLED matrix display brightness */ + } led; + bool led_initialized; + struct power_supply battery; + struct power_supply ac; +}; + +static inline void wacom_schedule_work(struct wacom_wac *wacom_wac) +{ + struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); + schedule_work(&wacom->work); +} + +static inline void wacom_notify_battery(struct wacom_wac *wacom_wac) +{ + struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); + + power_supply_changed(&wacom->battery); +} + +extern const struct hid_device_id wacom_ids[]; + +void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len); +void wacom_setup_device_quirks(struct wacom_features *features); +int wacom_setup_input_capabilities(struct input_dev *input_dev, + struct wacom_wac *wacom_wac); +int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, + struct wacom_wac *wacom_wac); +void wacom_wac_usage_mapping(struct hid_device *hdev, + struct hid_field *field, struct hid_usage *usage); +int wacom_wac_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value); +void wacom_wac_report(struct hid_device *hdev, struct hid_report *report); +#endif diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c new file mode 100644 index 000000000000..8593047bb726 --- /dev/null +++ b/drivers/hid/wacom_sys.c @@ -0,0 +1,1587 @@ +/* + * drivers/input/tablet/wacom_sys.c + * + * USB Wacom tablet support - system specific code + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "wacom_wac.h" +#include "wacom.h" + +#define WAC_MSG_RETRIES 5 + +#define WAC_CMD_WL_LED_CONTROL 0x03 +#define WAC_CMD_LED_CONTROL 0x20 +#define WAC_CMD_ICON_START 0x21 +#define WAC_CMD_ICON_XFER 0x23 +#define WAC_CMD_ICON_BT_XFER 0x26 +#define WAC_CMD_RETRIES 10 + +#define DEV_ATTR_RW_PERM (S_IRUGO | S_IWUSR | S_IWGRP) +#define DEV_ATTR_WO_PERM (S_IWUSR | S_IWGRP) + +static int wacom_get_report(struct hid_device *hdev, u8 type, u8 *buf, + size_t size, unsigned int retries) +{ + int retval; + + do { + retval = hid_hw_raw_request(hdev, buf[0], buf, size, type, + HID_REQ_GET_REPORT); + } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries); + + return retval; +} + +static int wacom_set_report(struct hid_device *hdev, u8 type, u8 *buf, + size_t size, unsigned int retries) +{ + int retval; + + do { + retval = hid_hw_raw_request(hdev, buf[0], buf, size, type, + HID_REQ_SET_REPORT); + } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries); + + return retval; +} + +static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *raw_data, int size) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + + if (size > WACOM_PKGLEN_MAX) + return 1; + + memcpy(wacom->wacom_wac.data, raw_data, size); + + wacom_wac_irq(&wacom->wacom_wac, size); + + return 0; +} + +static int wacom_open(struct input_dev *dev) +{ + struct wacom *wacom = input_get_drvdata(dev); + int retval; + + mutex_lock(&wacom->lock); + retval = hid_hw_open(wacom->hdev); + mutex_unlock(&wacom->lock); + + return retval; +} + +static void wacom_close(struct input_dev *dev) +{ + struct wacom *wacom = input_get_drvdata(dev); + + mutex_lock(&wacom->lock); + hid_hw_close(wacom->hdev); + mutex_unlock(&wacom->lock); +} + +/* + * Calculate the resolution of the X or Y axis using hidinput_calc_abs_res. + */ +static int wacom_calc_hid_res(int logical_extents, int physical_extents, + unsigned unit, int exponent) +{ + struct hid_field field = { + .logical_maximum = logical_extents, + .physical_maximum = physical_extents, + .unit = unit, + .unit_exponent = exponent, + }; + + return hidinput_calc_abs_res(&field, ABS_X); +} + +static void wacom_feature_mapping(struct hid_device *hdev, + struct hid_field *field, struct hid_usage *usage) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_features *features = &wacom->wacom_wac.features; + struct hid_data *hid_data = &wacom->wacom_wac.hid_data; + u8 *data; + int ret; + + switch (usage->hid) { + case HID_DG_CONTACTMAX: + /* leave touch_max as is if predefined */ + if (!features->touch_max) { + /* read manually */ + data = kzalloc(2, GFP_KERNEL); + if (!data) + break; + data[0] = field->report->id; + ret = wacom_get_report(hdev, HID_FEATURE_REPORT, + data, 2, 0); + if (ret == 2) + features->touch_max = data[1]; + kfree(data); + } + break; + case HID_DG_INPUTMODE: + /* Ignore if value index is out of bounds. */ + if (usage->usage_index >= field->report_count) { + dev_err(&hdev->dev, "HID_DG_INPUTMODE out of range\n"); + break; + } + + hid_data->inputmode = field->report->id; + hid_data->inputmode_index = usage->usage_index; + break; + } +} + +/* + * Interface Descriptor of wacom devices can be incomplete and + * inconsistent so wacom_features table is used to store stylus + * device's packet lengths, various maximum values, and tablet + * resolution based on product ID's. + * + * For devices that contain 2 interfaces, wacom_features table is + * inaccurate for the touch interface. Since the Interface Descriptor + * for touch interfaces has pretty complete data, this function exists + * to query tablet for this missing information instead of hard coding in + * an additional table. + * + * A typical Interface Descriptor for a stylus will contain a + * boot mouse application collection that is not of interest and this + * function will ignore it. + * + * It also contains a digitizer application collection that also is not + * of interest since any information it contains would be duplicate + * of what is in wacom_features. Usually it defines a report of an array + * of bytes that could be used as max length of the stylus packet returned. + * If it happens to define a Digitizer-Stylus Physical Collection then + * the X and Y logical values contain valid data but it is ignored. + * + * A typical Interface Descriptor for a touch interface will contain a + * Digitizer-Finger Physical Collection which will define both logical + * X/Y maximum as well as the physical size of tablet. Since touch + * interfaces haven't supported pressure or distance, this is enough + * information to override invalid values in the wacom_features table. + * + * Intuos5 touch interface and 3rd gen Bamboo Touch do not contain useful + * data. We deal with them after returning from this function. + */ +static void wacom_usage_mapping(struct hid_device *hdev, + struct hid_field *field, struct hid_usage *usage) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_features *features = &wacom->wacom_wac.features; + bool finger = (field->logical == HID_DG_FINGER) || + (field->physical == HID_DG_FINGER); + bool pen = (field->logical == HID_DG_STYLUS) || + (field->physical == HID_DG_STYLUS); + + /* + * Requiring Stylus Usage will ignore boot mouse + * X/Y values and some cases of invalid Digitizer X/Y + * values commonly reported. + */ + if (!pen && !finger) + return; + + if (finger && !features->touch_max) + /* touch device at least supports one touch point */ + features->touch_max = 1; + + switch (usage->hid) { + case HID_GD_X: + features->x_max = field->logical_maximum; + if (finger) { + features->device_type = BTN_TOOL_FINGER; + features->x_phy = field->physical_maximum; + if (features->type != BAMBOO_PT) { + features->unit = field->unit; + features->unitExpo = field->unit_exponent; + } + } else { + features->device_type = BTN_TOOL_PEN; + } + break; + case HID_GD_Y: + features->y_max = field->logical_maximum; + if (finger) { + features->y_phy = field->physical_maximum; + if (features->type != BAMBOO_PT) { + features->unit = field->unit; + features->unitExpo = field->unit_exponent; + } + } + break; + case HID_DG_TIPPRESSURE: + if (pen) + features->pressure_max = field->logical_maximum; + break; + } + + if (features->type == HID_GENERIC) + wacom_wac_usage_mapping(hdev, field, usage); +} + +static void wacom_parse_hid(struct hid_device *hdev, + struct wacom_features *features) +{ + struct hid_report_enum *rep_enum; + struct hid_report *hreport; + int i, j; + + /* check features first */ + rep_enum = &hdev->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(hreport, &rep_enum->report_list, list) { + for (i = 0; i < hreport->maxfield; i++) { + /* Ignore if report count is out of bounds. */ + if (hreport->field[i]->report_count < 1) + continue; + + for (j = 0; j < hreport->field[i]->maxusage; j++) { + wacom_feature_mapping(hdev, hreport->field[i], + hreport->field[i]->usage + j); + } + } + } + + /* now check the input usages */ + rep_enum = &hdev->report_enum[HID_INPUT_REPORT]; + list_for_each_entry(hreport, &rep_enum->report_list, list) { + + if (!hreport->maxfield) + continue; + + for (i = 0; i < hreport->maxfield; i++) + for (j = 0; j < hreport->field[i]->maxusage; j++) + wacom_usage_mapping(hdev, hreport->field[i], + hreport->field[i]->usage + j); + } +} + +static int wacom_hid_set_device_mode(struct hid_device *hdev) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct hid_data *hid_data = &wacom->wacom_wac.hid_data; + struct hid_report *r; + struct hid_report_enum *re; + + if (hid_data->inputmode < 0) + return 0; + + re = &(hdev->report_enum[HID_FEATURE_REPORT]); + r = re->report_id_hash[hid_data->inputmode]; + if (r) { + r->field[0]->value[hid_data->inputmode_index] = 2; + hid_hw_request(hdev, r, HID_REQ_SET_REPORT); + } + return 0; +} + +static int wacom_set_device_mode(struct hid_device *hdev, int report_id, + int length, int mode) +{ + unsigned char *rep_data; + int error = -ENOMEM, limit = 0; + + rep_data = kzalloc(length, GFP_KERNEL); + if (!rep_data) + return error; + + do { + rep_data[0] = report_id; + rep_data[1] = mode; + + error = wacom_set_report(hdev, HID_FEATURE_REPORT, rep_data, + length, 1); + if (error >= 0) + error = wacom_get_report(hdev, HID_FEATURE_REPORT, + rep_data, length, 1); + } while ((error < 0 || rep_data[1] != mode) && limit++ < WAC_MSG_RETRIES); + + kfree(rep_data); + + return error < 0 ? error : 0; +} + +static int wacom_bt_query_tablet_data(struct hid_device *hdev, u8 speed, + struct wacom_features *features) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + int ret; + u8 rep_data[2]; + + switch (features->type) { + case GRAPHIRE_BT: + rep_data[0] = 0x03; + rep_data[1] = 0x00; + ret = wacom_set_report(hdev, HID_FEATURE_REPORT, rep_data, 2, + 3); + + if (ret >= 0) { + rep_data[0] = speed == 0 ? 0x05 : 0x06; + rep_data[1] = 0x00; + + ret = wacom_set_report(hdev, HID_FEATURE_REPORT, + rep_data, 2, 3); + + if (ret >= 0) { + wacom->wacom_wac.bt_high_speed = speed; + return 0; + } + } + + /* + * Note that if the raw queries fail, it's not a hard failure + * and it is safe to continue + */ + hid_warn(hdev, "failed to poke device, command %d, err %d\n", + rep_data[0], ret); + break; + case INTUOS4WL: + if (speed == 1) + wacom->wacom_wac.bt_features &= ~0x20; + else + wacom->wacom_wac.bt_features |= 0x20; + + rep_data[0] = 0x03; + rep_data[1] = wacom->wacom_wac.bt_features; + + ret = wacom_set_report(hdev, HID_FEATURE_REPORT, rep_data, 2, + 1); + if (ret >= 0) + wacom->wacom_wac.bt_high_speed = speed; + break; + } + + return 0; +} + +/* + * Switch the tablet into its most-capable mode. Wacom tablets are + * typically configured to power-up in a mode which sends mouse-like + * reports to the OS. To get absolute position, pressure data, etc. + * from the tablet, it is necessary to switch the tablet out of this + * mode and into one which sends the full range of tablet data. + */ +static int wacom_query_tablet_data(struct hid_device *hdev, + struct wacom_features *features) +{ + if (hdev->bus == BUS_BLUETOOTH) + return wacom_bt_query_tablet_data(hdev, 1, features); + + if (features->type == HID_GENERIC) + return wacom_hid_set_device_mode(hdev); + + if (features->device_type == BTN_TOOL_FINGER) { + if (features->type > TABLETPC) { + /* MT Tablet PC touch */ + return wacom_set_device_mode(hdev, 3, 4, 4); + } + else if (features->type == WACOM_24HDT || features->type == CINTIQ_HYBRID) { + return wacom_set_device_mode(hdev, 18, 3, 2); + } + } else if (features->device_type == BTN_TOOL_PEN) { + if (features->type <= BAMBOO_PT && features->type != WIRELESS) { + return wacom_set_device_mode(hdev, 2, 2, 2); + } + } + + return 0; +} + +static void wacom_retrieve_hid_descriptor(struct hid_device *hdev, + struct wacom_features *features) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct usb_interface *intf = wacom->intf; + + /* default features */ + features->device_type = BTN_TOOL_PEN; + features->x_fuzz = 4; + features->y_fuzz = 4; + features->pressure_fuzz = 0; + features->distance_fuzz = 0; + + /* + * The wireless device HID is basic and layout conflicts with + * other tablets (monitor and touch interface can look like pen). + * Skip the query for this type and modify defaults based on + * interface number. + */ + if (features->type == WIRELESS) { + if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { + features->device_type = 0; + } else if (intf->cur_altsetting->desc.bInterfaceNumber == 2) { + features->device_type = BTN_TOOL_FINGER; + features->pktlen = WACOM_PKGLEN_BBTOUCH3; + } + } + + /* only devices that support touch need to retrieve the info */ + if (features->type < BAMBOO_PT) + return; + + wacom_parse_hid(hdev, features); +} + +struct wacom_hdev_data { + struct list_head list; + struct kref kref; + struct hid_device *dev; + struct wacom_shared shared; +}; + +static LIST_HEAD(wacom_udev_list); +static DEFINE_MUTEX(wacom_udev_list_lock); + +static bool wacom_are_sibling(struct hid_device *hdev, + struct hid_device *sibling) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_features *features = &wacom->wacom_wac.features; + int vid = features->oVid; + int pid = features->oPid; + int n1,n2; + + if (vid == 0 && pid == 0) { + vid = hdev->vendor; + pid = hdev->product; + } + + if (vid != sibling->vendor || pid != sibling->product) + return false; + + /* Compare the physical path. */ + n1 = strrchr(hdev->phys, '.') - hdev->phys; + n2 = strrchr(sibling->phys, '.') - sibling->phys; + if (n1 != n2 || n1 <= 0 || n2 <= 0) + return false; + + return !strncmp(hdev->phys, sibling->phys, n1); +} + +static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev) +{ + struct wacom_hdev_data *data; + + list_for_each_entry(data, &wacom_udev_list, list) { + if (wacom_are_sibling(hdev, data->dev)) { + kref_get(&data->kref); + return data; + } + } + + return NULL; +} + +static int wacom_add_shared_data(struct hid_device *hdev) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct wacom_hdev_data *data; + int retval = 0; + + mutex_lock(&wacom_udev_list_lock); + + data = wacom_get_hdev_data(hdev); + if (!data) { + data = kzalloc(sizeof(struct wacom_hdev_data), GFP_KERNEL); + if (!data) { + retval = -ENOMEM; + goto out; + } + + kref_init(&data->kref); + data->dev = hdev; + list_add_tail(&data->list, &wacom_udev_list); + } + + wacom_wac->shared = &data->shared; + +out: + mutex_unlock(&wacom_udev_list_lock); + return retval; +} + +static void wacom_release_shared_data(struct kref *kref) +{ + struct wacom_hdev_data *data = + container_of(kref, struct wacom_hdev_data, kref); + + mutex_lock(&wacom_udev_list_lock); + list_del(&data->list); + mutex_unlock(&wacom_udev_list_lock); + + kfree(data); +} + +static void wacom_remove_shared_data(struct wacom_wac *wacom) +{ + struct wacom_hdev_data *data; + + if (wacom->shared) { + data = container_of(wacom->shared, struct wacom_hdev_data, shared); + kref_put(&data->kref, wacom_release_shared_data); + wacom->shared = NULL; + } +} + +static int wacom_led_control(struct wacom *wacom) +{ + unsigned char *buf; + int retval; + unsigned char report_id = WAC_CMD_LED_CONTROL; + int buf_size = 9; + + if (wacom->wacom_wac.pid) { /* wireless connected */ + report_id = WAC_CMD_WL_LED_CONTROL; + buf_size = 13; + } + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (wacom->wacom_wac.features.type >= INTUOS5S && + wacom->wacom_wac.features.type <= INTUOSPL) { + /* + * Touch Ring and crop mark LED luminance may take on + * one of four values: + * 0 = Low; 1 = Medium; 2 = High; 3 = Off + */ + int ring_led = wacom->led.select[0] & 0x03; + int ring_lum = (((wacom->led.llv & 0x60) >> 5) - 1) & 0x03; + int crop_lum = 0; + unsigned char led_bits = (crop_lum << 4) | (ring_lum << 2) | (ring_led); + + buf[0] = report_id; + if (wacom->wacom_wac.pid) { + wacom_get_report(wacom->hdev, HID_FEATURE_REPORT, + buf, buf_size, WAC_CMD_RETRIES); + buf[0] = report_id; + buf[4] = led_bits; + } else + buf[1] = led_bits; + } + else { + int led = wacom->led.select[0] | 0x4; + + if (wacom->wacom_wac.features.type == WACOM_21UX2 || + wacom->wacom_wac.features.type == WACOM_24HD) + led |= (wacom->led.select[1] << 4) | 0x40; + + buf[0] = report_id; + buf[1] = led; + buf[2] = wacom->led.llv; + buf[3] = wacom->led.hlv; + buf[4] = wacom->led.img_lum; + } + + retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, buf_size, + WAC_CMD_RETRIES); + kfree(buf); + + return retval; +} + +static int wacom_led_putimage(struct wacom *wacom, int button_id, u8 xfer_id, + const unsigned len, const void *img) +{ + unsigned char *buf; + int i, retval; + const unsigned chunk_len = len / 4; /* 4 chunks are needed to be sent */ + + buf = kzalloc(chunk_len + 3 , GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* Send 'start' command */ + buf[0] = WAC_CMD_ICON_START; + buf[1] = 1; + retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, 2, + WAC_CMD_RETRIES); + if (retval < 0) + goto out; + + buf[0] = xfer_id; + buf[1] = button_id & 0x07; + for (i = 0; i < 4; i++) { + buf[2] = i; + memcpy(buf + 3, img + i * chunk_len, chunk_len); + + retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, + buf, chunk_len + 3, WAC_CMD_RETRIES); + if (retval < 0) + break; + } + + /* Send 'stop' */ + buf[0] = WAC_CMD_ICON_START; + buf[1] = 0; + wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, 2, + WAC_CMD_RETRIES); + +out: + kfree(buf); + return retval; +} + +static ssize_t wacom_led_select_store(struct device *dev, int set_id, + const char *buf, size_t count) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct wacom *wacom = hid_get_drvdata(hdev); + unsigned int id; + int err; + + err = kstrtouint(buf, 10, &id); + if (err) + return err; + + mutex_lock(&wacom->lock); + + wacom->led.select[set_id] = id & 0x3; + err = wacom_led_control(wacom); + + mutex_unlock(&wacom->lock); + + return err < 0 ? err : count; +} + +#define DEVICE_LED_SELECT_ATTR(SET_ID) \ +static ssize_t wacom_led##SET_ID##_select_store(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + return wacom_led_select_store(dev, SET_ID, buf, count); \ +} \ +static ssize_t wacom_led##SET_ID##_select_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct hid_device *hdev = container_of(dev, struct hid_device, dev);\ + struct wacom *wacom = hid_get_drvdata(hdev); \ + return scnprintf(buf, PAGE_SIZE, "%d\n", \ + wacom->led.select[SET_ID]); \ +} \ +static DEVICE_ATTR(status_led##SET_ID##_select, DEV_ATTR_RW_PERM, \ + wacom_led##SET_ID##_select_show, \ + wacom_led##SET_ID##_select_store) + +DEVICE_LED_SELECT_ATTR(0); +DEVICE_LED_SELECT_ATTR(1); + +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 hid_device *hdev = container_of(dev, struct hid_device, dev);\ + struct wacom *wacom = hid_get_drvdata(hdev); \ + \ + return wacom_luminance_store(wacom, &wacom->led.field, \ + buf, count); \ +} \ +static ssize_t wacom_##name##_luminance_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct wacom *wacom = dev_get_drvdata(dev); \ + return scnprintf(buf, PAGE_SIZE, "%d\n", wacom->led.field); \ +} \ +static DEVICE_ATTR(name##_luminance, DEV_ATTR_RW_PERM, \ + wacom_##name##_luminance_show, \ + 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 hid_device *hdev = container_of(dev, struct hid_device, dev); + struct wacom *wacom = hid_get_drvdata(hdev); + int err; + unsigned len; + u8 xfer_id; + + if (hdev->bus == BUS_BLUETOOTH) { + len = 256; + xfer_id = WAC_CMD_ICON_BT_XFER; + } else { + len = 1024; + xfer_id = WAC_CMD_ICON_XFER; + } + + if (count != len) + return -EINVAL; + + mutex_lock(&wacom->lock); + + err = wacom_led_putimage(wacom, button_id, xfer_id, len, 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, DEV_ATTR_WO_PERM, \ + 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 *cintiq_led_attrs[] = { + &dev_attr_status_led0_select.attr, + &dev_attr_status_led1_select.attr, + NULL +}; + +static struct attribute_group cintiq_led_attr_group = { + .name = "wacom_led", + .attrs = cintiq_led_attrs, +}; + +static struct attribute *intuos4_led_attrs[] = { + &dev_attr_status0_luminance.attr, + &dev_attr_status1_luminance.attr, + &dev_attr_status_led0_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 intuos4_led_attr_group = { + .name = "wacom_led", + .attrs = intuos4_led_attrs, +}; + +static struct attribute *intuos5_led_attrs[] = { + &dev_attr_status0_luminance.attr, + &dev_attr_status_led0_select.attr, + NULL +}; + +static struct attribute_group intuos5_led_attr_group = { + .name = "wacom_led", + .attrs = intuos5_led_attrs, +}; + +static int wacom_initialize_leds(struct wacom *wacom) +{ + int error; + + /* Initialize default values */ + switch (wacom->wacom_wac.features.type) { + case INTUOS4S: + case INTUOS4: + case INTUOS4WL: + case INTUOS4L: + wacom->led.select[0] = 0; + wacom->led.select[1] = 0; + wacom->led.llv = 10; + wacom->led.hlv = 20; + wacom->led.img_lum = 10; + error = sysfs_create_group(&wacom->hdev->dev.kobj, + &intuos4_led_attr_group); + break; + + case WACOM_24HD: + case WACOM_21UX2: + wacom->led.select[0] = 0; + wacom->led.select[1] = 0; + wacom->led.llv = 0; + wacom->led.hlv = 0; + wacom->led.img_lum = 0; + + error = sysfs_create_group(&wacom->hdev->dev.kobj, + &cintiq_led_attr_group); + break; + + case INTUOS5S: + case INTUOS5: + case INTUOS5L: + case INTUOSPS: + case INTUOSPM: + case INTUOSPL: + if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN) { + wacom->led.select[0] = 0; + wacom->led.select[1] = 0; + wacom->led.llv = 32; + wacom->led.hlv = 0; + wacom->led.img_lum = 0; + + error = sysfs_create_group(&wacom->hdev->dev.kobj, + &intuos5_led_attr_group); + } else + return 0; + break; + + default: + return 0; + } + + if (error) { + hid_err(wacom->hdev, + "cannot create sysfs group err: %d\n", error); + return error; + } + wacom_led_control(wacom); + wacom->led_initialized = true; + + return 0; +} + +static void wacom_destroy_leds(struct wacom *wacom) +{ + if (!wacom->led_initialized) + return; + + wacom->led_initialized = false; + + switch (wacom->wacom_wac.features.type) { + case INTUOS4S: + case INTUOS4: + case INTUOS4WL: + case INTUOS4L: + sysfs_remove_group(&wacom->hdev->dev.kobj, + &intuos4_led_attr_group); + break; + + case WACOM_24HD: + case WACOM_21UX2: + sysfs_remove_group(&wacom->hdev->dev.kobj, + &cintiq_led_attr_group); + break; + + case INTUOS5S: + case INTUOS5: + case INTUOS5L: + case INTUOSPS: + case INTUOSPM: + case INTUOSPL: + if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN) + sysfs_remove_group(&wacom->hdev->dev.kobj, + &intuos5_led_attr_group); + break; + } +} + +static enum power_supply_property wacom_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_CAPACITY +}; + +static enum power_supply_property wacom_ac_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_SCOPE, +}; + +static int wacom_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct wacom *wacom = container_of(psy, struct wacom, battery); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = + wacom->wacom_wac.battery_capacity; + break; + case POWER_SUPPLY_PROP_STATUS: + if (wacom->wacom_wac.bat_charging) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (wacom->wacom_wac.battery_capacity == 100 && + wacom->wacom_wac.ps_connected) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int wacom_ac_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct wacom *wacom = container_of(psy, struct wacom, ac); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + /* fall through */ + case POWER_SUPPLY_PROP_ONLINE: + val->intval = wacom->wacom_wac.ps_connected; + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int wacom_initialize_battery(struct wacom *wacom) +{ + static atomic_t battery_no = ATOMIC_INIT(0); + int error; + unsigned long n; + + if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) { + n = atomic_inc_return(&battery_no) - 1; + + wacom->battery.properties = wacom_battery_props; + wacom->battery.num_properties = ARRAY_SIZE(wacom_battery_props); + wacom->battery.get_property = wacom_battery_get_property; + sprintf(wacom->wacom_wac.bat_name, "wacom_battery_%ld", n); + wacom->battery.name = wacom->wacom_wac.bat_name; + wacom->battery.type = POWER_SUPPLY_TYPE_BATTERY; + wacom->battery.use_for_apm = 0; + + wacom->ac.properties = wacom_ac_props; + wacom->ac.num_properties = ARRAY_SIZE(wacom_ac_props); + wacom->ac.get_property = wacom_ac_get_property; + sprintf(wacom->wacom_wac.ac_name, "wacom_ac_%ld", n); + wacom->ac.name = wacom->wacom_wac.ac_name; + wacom->ac.type = POWER_SUPPLY_TYPE_MAINS; + wacom->ac.use_for_apm = 0; + + error = power_supply_register(&wacom->hdev->dev, + &wacom->battery); + if (error) + return error; + + power_supply_powers(&wacom->battery, &wacom->hdev->dev); + + error = power_supply_register(&wacom->hdev->dev, &wacom->ac); + if (error) { + power_supply_unregister(&wacom->battery); + return error; + } + + power_supply_powers(&wacom->ac, &wacom->hdev->dev); + } + + return 0; +} + +static void wacom_destroy_battery(struct wacom *wacom) +{ + if ((wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) && + wacom->battery.dev) { + power_supply_unregister(&wacom->battery); + wacom->battery.dev = NULL; + power_supply_unregister(&wacom->ac); + wacom->ac.dev = NULL; + } +} + +static ssize_t wacom_show_speed(struct device *dev, + struct device_attribute + *attr, char *buf) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct wacom *wacom = hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%i\n", wacom->wacom_wac.bt_high_speed); +} + +static ssize_t wacom_store_speed(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct wacom *wacom = hid_get_drvdata(hdev); + u8 new_speed; + + if (kstrtou8(buf, 0, &new_speed)) + return -EINVAL; + + if (new_speed != 0 && new_speed != 1) + return -EINVAL; + + wacom_bt_query_tablet_data(hdev, new_speed, &wacom->wacom_wac.features); + + return count; +} + +static DEVICE_ATTR(speed, DEV_ATTR_RW_PERM, + wacom_show_speed, wacom_store_speed); + +static struct input_dev *wacom_allocate_input(struct wacom *wacom) +{ + struct input_dev *input_dev; + struct hid_device *hdev = wacom->hdev; + struct wacom_wac *wacom_wac = &(wacom->wacom_wac); + + input_dev = input_allocate_device(); + if (!input_dev) + return NULL; + + input_dev->name = wacom_wac->name; + input_dev->phys = hdev->phys; + input_dev->dev.parent = &hdev->dev; + input_dev->open = wacom_open; + input_dev->close = wacom_close; + input_dev->uniq = hdev->uniq; + input_dev->id.bustype = hdev->bus; + input_dev->id.vendor = hdev->vendor; + input_dev->id.product = wacom_wac->pid ? wacom_wac->pid : hdev->product; + input_dev->id.version = hdev->version; + input_set_drvdata(input_dev, wacom); + + return input_dev; +} + +static void wacom_free_inputs(struct wacom *wacom) +{ + struct wacom_wac *wacom_wac = &(wacom->wacom_wac); + + if (wacom_wac->input) + input_free_device(wacom_wac->input); + if (wacom_wac->pad_input) + input_free_device(wacom_wac->pad_input); + wacom_wac->input = NULL; + wacom_wac->pad_input = NULL; +} + +static int wacom_allocate_inputs(struct wacom *wacom) +{ + struct input_dev *input_dev, *pad_input_dev; + struct wacom_wac *wacom_wac = &(wacom->wacom_wac); + + input_dev = wacom_allocate_input(wacom); + pad_input_dev = wacom_allocate_input(wacom); + if (!input_dev || !pad_input_dev) { + wacom_free_inputs(wacom); + return -ENOMEM; + } + + wacom_wac->input = input_dev; + wacom_wac->pad_input = pad_input_dev; + wacom_wac->pad_input->name = wacom_wac->pad_name; + + return 0; +} + +static void wacom_clean_inputs(struct wacom *wacom) +{ + if (wacom->wacom_wac.input) { + if (wacom->wacom_wac.input_registered) + input_unregister_device(wacom->wacom_wac.input); + else + input_free_device(wacom->wacom_wac.input); + } + if (wacom->wacom_wac.pad_input) { + if (wacom->wacom_wac.input_registered) + input_unregister_device(wacom->wacom_wac.pad_input); + else + input_free_device(wacom->wacom_wac.pad_input); + } + wacom->wacom_wac.input = NULL; + wacom->wacom_wac.pad_input = NULL; + wacom_destroy_leds(wacom); +} + +static int wacom_register_inputs(struct wacom *wacom) +{ + struct input_dev *input_dev, *pad_input_dev; + struct wacom_wac *wacom_wac = &(wacom->wacom_wac); + int error; + + input_dev = wacom_wac->input; + pad_input_dev = wacom_wac->pad_input; + + if (!input_dev || !pad_input_dev) + return -EINVAL; + + error = wacom_setup_input_capabilities(input_dev, wacom_wac); + if (error) + return error; + + error = input_register_device(input_dev); + if (error) + return error; + + error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac); + if (error) { + /* no pad in use on this interface */ + input_free_device(pad_input_dev); + wacom_wac->pad_input = NULL; + pad_input_dev = NULL; + } else { + error = input_register_device(pad_input_dev); + if (error) + goto fail_register_pad_input; + + error = wacom_initialize_leds(wacom); + if (error) + goto fail_leds; + } + + wacom_wac->input_registered = true; + + return 0; + +fail_leds: + input_unregister_device(pad_input_dev); + pad_input_dev = NULL; +fail_register_pad_input: + input_unregister_device(input_dev); + wacom_wac->input = NULL; + return error; +} + +static void wacom_wireless_work(struct work_struct *work) +{ + struct wacom *wacom = container_of(work, struct wacom, work); + struct usb_device *usbdev = wacom->usbdev; + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct hid_device *hdev1, *hdev2; + struct wacom *wacom1, *wacom2; + struct wacom_wac *wacom_wac1, *wacom_wac2; + int error; + + /* + * Regardless if this is a disconnect or a new tablet, + * remove any existing input and battery devices. + */ + + wacom_destroy_battery(wacom); + + /* Stylus interface */ + hdev1 = usb_get_intfdata(usbdev->config->interface[1]); + wacom1 = hid_get_drvdata(hdev1); + wacom_wac1 = &(wacom1->wacom_wac); + wacom_clean_inputs(wacom1); + + /* Touch interface */ + hdev2 = usb_get_intfdata(usbdev->config->interface[2]); + wacom2 = hid_get_drvdata(hdev2); + wacom_wac2 = &(wacom2->wacom_wac); + wacom_clean_inputs(wacom2); + + if (wacom_wac->pid == 0) { + hid_info(wacom->hdev, "wireless tablet disconnected\n"); + wacom_wac1->shared->type = 0; + } else { + const struct hid_device_id *id = wacom_ids; + + hid_info(wacom->hdev, "wireless tablet connected with PID %x\n", + wacom_wac->pid); + + while (id->bus) { + if (id->vendor == USB_VENDOR_ID_WACOM && + id->product == wacom_wac->pid) + break; + id++; + } + + if (!id->bus) { + hid_info(wacom->hdev, "ignoring unknown PID.\n"); + return; + } + + /* Stylus interface */ + wacom_wac1->features = + *((struct wacom_features *)id->driver_data); + wacom_wac1->features.device_type = BTN_TOOL_PEN; + snprintf(wacom_wac1->name, WACOM_NAME_MAX, "%s (WL) Pen", + wacom_wac1->features.name); + snprintf(wacom_wac1->pad_name, WACOM_NAME_MAX, "%s (WL) Pad", + wacom_wac1->features.name); + wacom_wac1->shared->touch_max = wacom_wac1->features.touch_max; + wacom_wac1->shared->type = wacom_wac1->features.type; + wacom_wac1->pid = wacom_wac->pid; + error = wacom_allocate_inputs(wacom1) || + wacom_register_inputs(wacom1); + if (error) + goto fail; + + /* Touch interface */ + if (wacom_wac1->features.touch_max || + wacom_wac1->features.type == INTUOSHT) { + wacom_wac2->features = + *((struct wacom_features *)id->driver_data); + wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3; + wacom_wac2->features.device_type = BTN_TOOL_FINGER; + wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096; + if (wacom_wac2->features.touch_max) + snprintf(wacom_wac2->name, WACOM_NAME_MAX, + "%s (WL) Finger",wacom_wac2->features.name); + else + snprintf(wacom_wac2->name, WACOM_NAME_MAX, + "%s (WL) Pad",wacom_wac2->features.name); + snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX, + "%s (WL) Pad", wacom_wac2->features.name); + wacom_wac2->pid = wacom_wac->pid; + error = wacom_allocate_inputs(wacom2) || + wacom_register_inputs(wacom2); + if (error) + goto fail; + + if (wacom_wac1->features.type == INTUOSHT && + wacom_wac1->features.touch_max) + wacom_wac->shared->touch_input = wacom_wac2->input; + } + + error = wacom_initialize_battery(wacom); + if (error) + goto fail; + } + + return; + +fail: + wacom_clean_inputs(wacom1); + wacom_clean_inputs(wacom2); + return; +} + +/* + * Not all devices report physical dimensions from HID. + * Compute the default from hardcoded logical dimension + * and resolution before driver overwrites them. + */ +static void wacom_set_default_phy(struct wacom_features *features) +{ + if (features->x_resolution) { + features->x_phy = (features->x_max * 100) / + features->x_resolution; + features->y_phy = (features->y_max * 100) / + features->y_resolution; + } +} + +static void wacom_calculate_res(struct wacom_features *features) +{ + features->x_resolution = wacom_calc_hid_res(features->x_max, + features->x_phy, + features->unit, + features->unitExpo); + features->y_resolution = wacom_calc_hid_res(features->y_max, + features->y_phy, + features->unit, + features->unitExpo); +} + +static int wacom_hid_report_len(struct hid_report *report) +{ + /* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */ + return ((report->size - 1) >> 3) + 1 + (report->id > 0); +} + +static size_t wacom_compute_pktlen(struct hid_device *hdev) +{ + struct hid_report_enum *report_enum; + struct hid_report *report; + size_t size = 0; + + report_enum = hdev->report_enum + HID_INPUT_REPORT; + + list_for_each_entry(report, &report_enum->report_list, list) { + size_t report_size = wacom_hid_report_len(report); + if (report_size > size) + size = report_size; + } + + return size; +} + +static int wacom_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_device *dev = interface_to_usbdev(intf); + struct wacom *wacom; + struct wacom_wac *wacom_wac; + struct wacom_features *features; + int error; + unsigned int connect_mask = HID_CONNECT_HIDRAW; + + if (!id->driver_data) + return -EINVAL; + + hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS; + + wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); + if (!wacom) + return -ENOMEM; + + hid_set_drvdata(hdev, wacom); + wacom->hdev = hdev; + + /* ask for the report descriptor to be loaded by HID */ + error = hid_parse(hdev); + if (error) { + hid_err(hdev, "parse failed\n"); + goto fail_parse; + } + + wacom_wac = &wacom->wacom_wac; + wacom_wac->features = *((struct wacom_features *)id->driver_data); + features = &wacom_wac->features; + features->pktlen = wacom_compute_pktlen(hdev); + if (features->pktlen > WACOM_PKGLEN_MAX) { + error = -EINVAL; + goto fail_pktlen; + } + + if (features->check_for_hid_type && features->hid_type != hdev->type) { + error = -ENODEV; + goto fail_type; + } + + wacom->usbdev = dev; + wacom->intf = intf; + mutex_init(&wacom->lock); + INIT_WORK(&wacom->work, wacom_wireless_work); + + if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) { + error = wacom_allocate_inputs(wacom); + if (error) + goto fail_allocate_inputs; + } + + /* set the default size in case we do not get them from hid */ + wacom_set_default_phy(features); + + /* Retrieve the physical and logical size for touch devices */ + wacom_retrieve_hid_descriptor(hdev, features); + + /* + * Intuos5 has no useful data about its touch interface in its + * HID descriptor. If this is the touch interface (PacketSize + * of WACOM_PKGLEN_BBTOUCH3), override the table values. + */ + if (features->type >= INTUOS5S && features->type <= INTUOSHT) { + if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) { + features->device_type = BTN_TOOL_FINGER; + + features->x_max = 4096; + features->y_max = 4096; + } else { + features->device_type = BTN_TOOL_PEN; + } + } + + /* + * Same thing for Bamboo 3rd gen. + */ + if ((features->type == BAMBOO_PT) && + (features->pktlen == WACOM_PKGLEN_BBTOUCH3) && + (features->device_type == BTN_TOOL_PEN)) { + features->device_type = BTN_TOOL_FINGER; + + features->x_max = 4096; + features->y_max = 4096; + } + + if (hdev->bus == BUS_BLUETOOTH) + features->quirks |= WACOM_QUIRK_BATTERY; + + wacom_setup_device_quirks(features); + + /* set unit to "100th of a mm" for devices not reported by HID */ + if (!features->unit) { + features->unit = 0x11; + features->unitExpo = -3; + } + wacom_calculate_res(features); + + strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name)); + snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name), + "%s Pad", features->name); + + if (features->quirks & WACOM_QUIRK_MULTI_INPUT) { + /* Append the device type to the name */ + if (features->device_type != BTN_TOOL_FINGER) + strlcat(wacom_wac->name, " Pen", WACOM_NAME_MAX); + else if (features->touch_max) + strlcat(wacom_wac->name, " Finger", WACOM_NAME_MAX); + else + strlcat(wacom_wac->name, " Pad", WACOM_NAME_MAX); + + error = wacom_add_shared_data(hdev); + if (error) + goto fail_shared_data; + } + + if (!(features->quirks & WACOM_QUIRK_MONITOR) && + (features->quirks & WACOM_QUIRK_BATTERY)) { + error = wacom_initialize_battery(wacom); + if (error) + goto fail_battery; + } + + if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) { + error = wacom_register_inputs(wacom); + if (error) + goto fail_register_inputs; + } + + if (hdev->bus == BUS_BLUETOOTH) { + error = device_create_file(&hdev->dev, &dev_attr_speed); + if (error) + hid_warn(hdev, + "can't create sysfs speed attribute err: %d\n", + error); + } + + if (features->type == HID_GENERIC) + connect_mask |= HID_CONNECT_DRIVER; + + /* Regular HID work starts now */ + error = hid_hw_start(hdev, connect_mask); + if (error) { + hid_err(hdev, "hw start failed\n"); + goto fail_hw_start; + } + + /* Note that if query fails it is not a hard failure */ + wacom_query_tablet_data(hdev, features); + + if (features->quirks & WACOM_QUIRK_MONITOR) + error = hid_hw_open(hdev); + + if (wacom_wac->features.type == INTUOSHT && wacom_wac->features.touch_max) { + if (wacom_wac->features.device_type == BTN_TOOL_FINGER) + wacom_wac->shared->touch_input = wacom_wac->input; + } + + return 0; + +fail_hw_start: + if (hdev->bus == BUS_BLUETOOTH) + device_remove_file(&hdev->dev, &dev_attr_speed); +fail_register_inputs: + wacom_clean_inputs(wacom); + wacom_destroy_battery(wacom); +fail_battery: + wacom_remove_shared_data(wacom_wac); +fail_shared_data: + wacom_clean_inputs(wacom); +fail_allocate_inputs: +fail_type: +fail_pktlen: +fail_parse: + kfree(wacom); + hid_set_drvdata(hdev, NULL); + return error; +} + +static void wacom_remove(struct hid_device *hdev) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + + hid_hw_stop(hdev); + + cancel_work_sync(&wacom->work); + wacom_clean_inputs(wacom); + if (hdev->bus == BUS_BLUETOOTH) + device_remove_file(&hdev->dev, &dev_attr_speed); + wacom_destroy_battery(wacom); + wacom_remove_shared_data(&wacom->wacom_wac); + + hid_set_drvdata(hdev, NULL); + kfree(wacom); +} + +#ifdef CONFIG_PM +static int wacom_resume(struct hid_device *hdev) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_features *features = &wacom->wacom_wac.features; + + mutex_lock(&wacom->lock); + + /* switch to wacom mode first */ + wacom_query_tablet_data(hdev, features); + wacom_led_control(wacom); + + mutex_unlock(&wacom->lock); + + return 0; +} + +static int wacom_reset_resume(struct hid_device *hdev) +{ + return wacom_resume(hdev); +} +#endif /* CONFIG_PM */ + +static struct hid_driver wacom_driver = { + .name = "wacom", + .id_table = wacom_ids, + .probe = wacom_probe, + .remove = wacom_remove, + .event = wacom_wac_event, + .report = wacom_wac_report, +#ifdef CONFIG_PM + .resume = wacom_resume, + .reset_resume = wacom_reset_resume, +#endif + .raw_event = wacom_raw_event, +}; +module_hid_driver(wacom_driver); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c new file mode 100644 index 000000000000..586b2405b0d4 --- /dev/null +++ b/drivers/hid/wacom_wac.c @@ -0,0 +1,3033 @@ +/* + * drivers/input/tablet/wacom_wac.c + * + * USB Wacom tablet support - Wacom specific code + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "wacom_wac.h" +#include "wacom.h" +#include <linux/input/mt.h> +#include <linux/hid.h> + +/* resolution for penabled devices */ +#define WACOM_PL_RES 20 +#define WACOM_PENPRTN_RES 40 +#define WACOM_VOLITO_RES 50 +#define WACOM_GRAPHIRE_RES 80 +#define WACOM_INTUOS_RES 100 +#define WACOM_INTUOS3_RES 200 + +/* + * Scale factor relating reported contact size to logical contact area. + * 2^14/pi is a good approximation on Intuos5 and 3rd-gen Bamboo + */ +#define WACOM_CONTACT_AREA_SCALE 2607 + +/* + * Percent of battery capacity for Graphire. + * 8th value means AC online and show 100% capacity. + */ +static unsigned short batcap_gr[8] = { 1, 15, 25, 35, 50, 70, 100, 100 }; + +/* + * Percent of battery capacity for Intuos4 WL, AC has a separate bit. + */ +static unsigned short batcap_i4[8] = { 1, 15, 30, 45, 60, 70, 85, 100 }; + +static int wacom_penpartner_irq(struct wacom_wac *wacom) +{ + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + + switch (data[0]) { + case 1: + if (data[5] & 0x80) { + wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + wacom->id[0] = (data[5] & 0x20) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID; + input_report_key(input, wacom->tool[0], 1); + input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */ + input_report_abs(input, ABS_X, get_unaligned_le16(&data[1])); + input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3])); + input_report_abs(input, ABS_PRESSURE, (signed char)data[6] + 127); + input_report_key(input, BTN_TOUCH, ((signed char)data[6] > -127)); + input_report_key(input, BTN_STYLUS, (data[5] & 0x40)); + } else { + input_report_key(input, wacom->tool[0], 0); + input_report_abs(input, ABS_MISC, 0); /* report tool id */ + input_report_abs(input, ABS_PRESSURE, -1); + input_report_key(input, BTN_TOUCH, 0); + } + break; + + case 2: + input_report_key(input, BTN_TOOL_PEN, 1); + input_report_abs(input, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */ + input_report_abs(input, ABS_X, get_unaligned_le16(&data[1])); + input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3])); + input_report_abs(input, ABS_PRESSURE, (signed char)data[6] + 127); + input_report_key(input, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20)); + input_report_key(input, BTN_STYLUS, (data[5] & 0x40)); + break; + + default: + dev_dbg(input->dev.parent, + "%s: received unknown report #%d\n", __func__, data[0]); + return 0; + } + + return 1; +} + +static int wacom_pl_irq(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + int prox, pressure; + + if (data[0] != WACOM_REPORT_PENABLED) { + dev_dbg(input->dev.parent, + "%s: received unknown report #%d\n", __func__, data[0]); + return 0; + } + + prox = data[1] & 0x40; + + if (prox) { + wacom->id[0] = ERASER_DEVICE_ID; + pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1)); + if (features->pressure_max > 255) + pressure = (pressure << 1) | ((data[4] >> 6) & 1); + pressure += (features->pressure_max + 1) / 2; + + /* + * if going from out of proximity into proximity select between the eraser + * and the pen based on the state of the stylus2 button, choose eraser if + * pressed else choose pen. if not a proximity change from out to in, send + * an out of proximity for previous tool then a in for new tool. + */ + if (!wacom->tool[0]) { + /* Eraser bit set for DTF */ + if (data[1] & 0x10) + wacom->tool[1] = BTN_TOOL_RUBBER; + else + /* Going into proximity select tool */ + wacom->tool[1] = (data[4] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + } else { + /* was entered with stylus2 pressed */ + if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) { + /* report out proximity for previous tool */ + input_report_key(input, wacom->tool[1], 0); + input_sync(input); + wacom->tool[1] = BTN_TOOL_PEN; + return 0; + } + } + if (wacom->tool[1] != BTN_TOOL_RUBBER) { + /* Unknown tool selected default to pen tool */ + wacom->tool[1] = BTN_TOOL_PEN; + wacom->id[0] = STYLUS_DEVICE_ID; + } + input_report_key(input, wacom->tool[1], prox); /* report in proximity for tool */ + input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */ + input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14)); + input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14)); + input_report_abs(input, ABS_PRESSURE, pressure); + + input_report_key(input, BTN_TOUCH, data[4] & 0x08); + input_report_key(input, BTN_STYLUS, data[4] & 0x10); + /* Only allow the stylus2 button to be reported for the pen tool. */ + input_report_key(input, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20)); + } else { + /* report proximity-out of a (valid) tool */ + if (wacom->tool[1] != BTN_TOOL_RUBBER) { + /* Unknown tool selected default to pen tool */ + wacom->tool[1] = BTN_TOOL_PEN; + } + input_report_key(input, wacom->tool[1], prox); + } + + wacom->tool[0] = prox; /* Save proximity state */ + return 1; +} + +static int wacom_ptu_irq(struct wacom_wac *wacom) +{ + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + + if (data[0] != WACOM_REPORT_PENABLED) { + dev_dbg(input->dev.parent, + "%s: received unknown report #%d\n", __func__, data[0]); + return 0; + } + + if (data[1] & 0x04) { + input_report_key(input, BTN_TOOL_RUBBER, data[1] & 0x20); + input_report_key(input, BTN_TOUCH, data[1] & 0x08); + wacom->id[0] = ERASER_DEVICE_ID; + } else { + input_report_key(input, BTN_TOOL_PEN, data[1] & 0x20); + input_report_key(input, BTN_TOUCH, data[1] & 0x01); + wacom->id[0] = STYLUS_DEVICE_ID; + } + input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */ + input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); + input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); + input_report_abs(input, ABS_PRESSURE, le16_to_cpup((__le16 *)&data[6])); + input_report_key(input, BTN_STYLUS, data[1] & 0x02); + input_report_key(input, BTN_STYLUS2, data[1] & 0x10); + return 1; +} + +static int wacom_dtu_irq(struct wacom_wac *wacom) +{ + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + int prox = data[1] & 0x20; + + dev_dbg(input->dev.parent, + "%s: received report #%d", __func__, data[0]); + + if (prox) { + /* Going into proximity select tool */ + wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + if (wacom->tool[0] == BTN_TOOL_PEN) + wacom->id[0] = STYLUS_DEVICE_ID; + else + wacom->id[0] = ERASER_DEVICE_ID; + } + input_report_key(input, BTN_STYLUS, data[1] & 0x02); + input_report_key(input, BTN_STYLUS2, data[1] & 0x10); + input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); + input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); + input_report_abs(input, ABS_PRESSURE, ((data[7] & 0x01) << 8) | data[6]); + input_report_key(input, BTN_TOUCH, data[1] & 0x05); + if (!prox) /* out-prox */ + wacom->id[0] = 0; + input_report_key(input, wacom->tool[0], prox); + input_report_abs(input, ABS_MISC, wacom->id[0]); + return 1; +} + +static int wacom_dtus_irq(struct wacom_wac *wacom) +{ + char *data = wacom->data; + struct input_dev *input = wacom->input; + unsigned short prox, pressure = 0; + + if (data[0] != WACOM_REPORT_DTUS && data[0] != WACOM_REPORT_DTUSPAD) { + dev_dbg(input->dev.parent, + "%s: received unknown report #%d", __func__, data[0]); + return 0; + } else if (data[0] == WACOM_REPORT_DTUSPAD) { + input = wacom->pad_input; + input_report_key(input, BTN_0, (data[1] & 0x01)); + input_report_key(input, BTN_1, (data[1] & 0x02)); + input_report_key(input, BTN_2, (data[1] & 0x04)); + input_report_key(input, BTN_3, (data[1] & 0x08)); + input_report_abs(input, ABS_MISC, + data[1] & 0x0f ? PAD_DEVICE_ID : 0); + return 1; + } else { + prox = data[1] & 0x80; + if (prox) { + switch ((data[1] >> 3) & 3) { + case 1: /* Rubber */ + wacom->tool[0] = BTN_TOOL_RUBBER; + wacom->id[0] = ERASER_DEVICE_ID; + break; + + case 2: /* Pen */ + wacom->tool[0] = BTN_TOOL_PEN; + wacom->id[0] = STYLUS_DEVICE_ID; + break; + } + } + + input_report_key(input, BTN_STYLUS, data[1] & 0x20); + input_report_key(input, BTN_STYLUS2, data[1] & 0x40); + input_report_abs(input, ABS_X, get_unaligned_be16(&data[3])); + input_report_abs(input, ABS_Y, get_unaligned_be16(&data[5])); + pressure = ((data[1] & 0x03) << 8) | (data[2] & 0xff); + input_report_abs(input, ABS_PRESSURE, pressure); + input_report_key(input, BTN_TOUCH, pressure > 10); + + if (!prox) /* out-prox */ + wacom->id[0] = 0; + input_report_key(input, wacom->tool[0], prox); + input_report_abs(input, ABS_MISC, wacom->id[0]); + return 1; + } +} + +static int wacom_graphire_irq(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + struct input_dev *pad_input = wacom->pad_input; + int battery_capacity, ps_connected; + int prox; + int rw = 0; + int retval = 0; + + if (features->type == GRAPHIRE_BT) { + if (data[0] != WACOM_REPORT_PENABLED_BT) { + dev_dbg(input->dev.parent, + "%s: received unknown report #%d\n", __func__, + data[0]); + goto exit; + } + } else if (data[0] != WACOM_REPORT_PENABLED) { + dev_dbg(input->dev.parent, + "%s: received unknown report #%d\n", __func__, data[0]); + goto exit; + } + + prox = data[1] & 0x80; + if (prox || wacom->id[0]) { + if (prox) { + switch ((data[1] >> 5) & 3) { + + case 0: /* Pen */ + wacom->tool[0] = BTN_TOOL_PEN; + wacom->id[0] = STYLUS_DEVICE_ID; + break; + + case 1: /* Rubber */ + wacom->tool[0] = BTN_TOOL_RUBBER; + wacom->id[0] = ERASER_DEVICE_ID; + break; + + case 2: /* Mouse with wheel */ + input_report_key(input, BTN_MIDDLE, data[1] & 0x04); + /* fall through */ + + case 3: /* Mouse without wheel */ + wacom->tool[0] = BTN_TOOL_MOUSE; + wacom->id[0] = CURSOR_DEVICE_ID; + break; + } + } + input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); + input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); + if (wacom->tool[0] != BTN_TOOL_MOUSE) { + if (features->type == GRAPHIRE_BT) + input_report_abs(input, ABS_PRESSURE, data[6] | + (((__u16) (data[1] & 0x08)) << 5)); + else + input_report_abs(input, ABS_PRESSURE, data[6] | + ((data[7] & 0x03) << 8)); + input_report_key(input, BTN_TOUCH, data[1] & 0x01); + input_report_key(input, BTN_STYLUS, data[1] & 0x02); + input_report_key(input, BTN_STYLUS2, data[1] & 0x04); + } else { + input_report_key(input, BTN_LEFT, data[1] & 0x01); + input_report_key(input, BTN_RIGHT, data[1] & 0x02); + if (features->type == WACOM_G4 || + features->type == WACOM_MO) { + input_report_abs(input, ABS_DISTANCE, data[6] & 0x3f); + rw = (data[7] & 0x04) - (data[7] & 0x03); + } else if (features->type == GRAPHIRE_BT) { + /* Compute distance between mouse and tablet */ + rw = 44 - (data[6] >> 2); + rw = clamp_val(rw, 0, 31); + input_report_abs(input, ABS_DISTANCE, rw); + if (((data[1] >> 5) & 3) == 2) { + /* Mouse with wheel */ + input_report_key(input, BTN_MIDDLE, + data[1] & 0x04); + rw = (data[6] & 0x01) ? -1 : + (data[6] & 0x02) ? 1 : 0; + } else { + rw = 0; + } + } else { + input_report_abs(input, ABS_DISTANCE, data[7] & 0x3f); + rw = -(signed char)data[6]; + } + input_report_rel(input, REL_WHEEL, rw); + } + + if (!prox) + wacom->id[0] = 0; + input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */ + input_report_key(input, wacom->tool[0], prox); + input_sync(input); /* sync last event */ + } + + /* send pad data */ + switch (features->type) { + case WACOM_G4: + prox = data[7] & 0xf8; + if (prox || wacom->id[1]) { + wacom->id[1] = PAD_DEVICE_ID; + input_report_key(pad_input, BTN_BACK, (data[7] & 0x40)); + input_report_key(pad_input, BTN_FORWARD, (data[7] & 0x80)); + rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3); + input_report_rel(pad_input, REL_WHEEL, rw); + if (!prox) + wacom->id[1] = 0; + input_report_abs(pad_input, ABS_MISC, wacom->id[1]); + retval = 1; + } + break; + + case WACOM_MO: + prox = (data[7] & 0xf8) || data[8]; + if (prox || wacom->id[1]) { + wacom->id[1] = PAD_DEVICE_ID; + input_report_key(pad_input, BTN_BACK, (data[7] & 0x08)); + input_report_key(pad_input, BTN_LEFT, (data[7] & 0x20)); + input_report_key(pad_input, BTN_FORWARD, (data[7] & 0x10)); + input_report_key(pad_input, BTN_RIGHT, (data[7] & 0x40)); + input_report_abs(pad_input, ABS_WHEEL, (data[8] & 0x7f)); + if (!prox) + wacom->id[1] = 0; + input_report_abs(pad_input, ABS_MISC, wacom->id[1]); + retval = 1; + } + break; + case GRAPHIRE_BT: + prox = data[7] & 0x03; + if (prox || wacom->id[1]) { + wacom->id[1] = PAD_DEVICE_ID; + input_report_key(pad_input, BTN_0, (data[7] & 0x02)); + input_report_key(pad_input, BTN_1, (data[7] & 0x01)); + if (!prox) + wacom->id[1] = 0; + input_report_abs(pad_input, ABS_MISC, wacom->id[1]); + retval = 1; + } + break; + } + + /* Store current battery capacity and power supply state */ + if (features->type == GRAPHIRE_BT) { + rw = (data[7] >> 2 & 0x07); + battery_capacity = batcap_gr[rw]; + ps_connected = rw == 7; + if ((wacom->battery_capacity != battery_capacity) || + (wacom->ps_connected != ps_connected)) { + wacom->battery_capacity = battery_capacity; + wacom->ps_connected = ps_connected; + wacom_notify_battery(wacom); + } + } +exit: + return retval; +} + +static int wacom_intuos_inout(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + int idx = 0; + + /* tool number */ + if (features->type == INTUOS) + idx = data[1] & 0x01; + + /* Enter report */ + if ((data[1] & 0xfc) == 0xc0) { + if (features->quirks & WACOM_QUIRK_MULTI_INPUT) + wacom->shared->stylus_in_proximity = true; + + /* serial number of the tool */ + wacom->serial[idx] = ((data[3] & 0x0f) << 28) + + (data[4] << 20) + (data[5] << 12) + + (data[6] << 4) + (data[7] >> 4); + + wacom->id[idx] = (data[2] << 4) | (data[3] >> 4) | + ((data[7] & 0x0f) << 20) | ((data[8] & 0xf0) << 12); + + switch (wacom->id[idx]) { + case 0x812: /* Inking pen */ + case 0x801: /* Intuos3 Inking pen */ + case 0x120802: /* Intuos4/5 Inking Pen */ + case 0x012: + wacom->tool[idx] = BTN_TOOL_PENCIL; + break; + + case 0x822: /* Pen */ + case 0x842: + case 0x852: + case 0x823: /* Intuos3 Grip Pen */ + case 0x813: /* Intuos3 Classic Pen */ + case 0x885: /* Intuos3 Marker Pen */ + case 0x802: /* Intuos4/5 13HD/24HD General Pen */ + case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */ + case 0x022: + case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */ + case 0x140802: /* Intuos4/5 13HD/24HD Classic Pen */ + case 0x160802: /* Cintiq 13HD Pro Pen */ + case 0x180802: /* DTH2242 Pen */ + case 0x100802: /* Intuos4/5 13HD/24HD General Pen */ + wacom->tool[idx] = BTN_TOOL_PEN; + break; + + case 0x832: /* Stroke pen */ + case 0x032: + wacom->tool[idx] = BTN_TOOL_BRUSH; + break; + + case 0x007: /* Mouse 4D and 2D */ + case 0x09c: + case 0x094: + case 0x017: /* Intuos3 2D Mouse */ + case 0x806: /* Intuos4 Mouse */ + wacom->tool[idx] = BTN_TOOL_MOUSE; + break; + + case 0x096: /* Lens cursor */ + case 0x097: /* Intuos3 Lens cursor */ + case 0x006: /* Intuos4 Lens cursor */ + wacom->tool[idx] = BTN_TOOL_LENS; + break; + + case 0x82a: /* Eraser */ + case 0x85a: + case 0x91a: + case 0xd1a: + case 0x0fa: + case 0x82b: /* Intuos3 Grip Pen Eraser */ + case 0x81b: /* Intuos3 Classic Pen Eraser */ + case 0x91b: /* Intuos3 Airbrush Eraser */ + case 0x80c: /* Intuos4/5 13HD/24HD Marker Pen Eraser */ + case 0x80a: /* Intuos4/5 13HD/24HD General Pen Eraser */ + case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */ + case 0x14080a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */ + case 0x10090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */ + case 0x10080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */ + case 0x16080a: /* Cintiq 13HD Pro Pen Eraser */ + case 0x18080a: /* DTH2242 Eraser */ + case 0x10080a: /* Intuos4/5 13HD/24HD General Pen Eraser */ + wacom->tool[idx] = BTN_TOOL_RUBBER; + break; + + case 0xd12: + case 0x912: + case 0x112: + case 0x913: /* Intuos3 Airbrush */ + case 0x902: /* Intuos4/5 13HD/24HD Airbrush */ + case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */ + wacom->tool[idx] = BTN_TOOL_AIRBRUSH; + break; + + default: /* Unknown tool */ + wacom->tool[idx] = BTN_TOOL_PEN; + break; + } + return 1; + } + + /* older I4 styli don't work with new Cintiqs */ + if (!((wacom->id[idx] >> 20) & 0x01) && + (features->type == WACOM_21UX2)) + return 1; + + /* Range Report */ + if ((data[1] & 0xfe) == 0x20) { + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_report_abs(input, ABS_DISTANCE, wacom->features.distance_max); + if (features->quirks & WACOM_QUIRK_MULTI_INPUT) + wacom->shared->stylus_in_proximity = true; + } + + /* Exit report */ + if ((data[1] & 0xfe) == 0x80) { + if (features->quirks & WACOM_QUIRK_MULTI_INPUT) + wacom->shared->stylus_in_proximity = false; + + /* + * Reset all states otherwise we lose the initial states + * when in-prox next time + */ + input_report_abs(input, ABS_X, 0); + input_report_abs(input, ABS_Y, 0); + input_report_abs(input, ABS_DISTANCE, 0); + input_report_abs(input, ABS_TILT_X, 0); + input_report_abs(input, ABS_TILT_Y, 0); + if (wacom->tool[idx] >= BTN_TOOL_MOUSE) { + input_report_key(input, BTN_LEFT, 0); + input_report_key(input, BTN_MIDDLE, 0); + input_report_key(input, BTN_RIGHT, 0); + input_report_key(input, BTN_SIDE, 0); + input_report_key(input, BTN_EXTRA, 0); + input_report_abs(input, ABS_THROTTLE, 0); + input_report_abs(input, ABS_RZ, 0); + } else { + input_report_abs(input, ABS_PRESSURE, 0); + input_report_key(input, BTN_STYLUS, 0); + input_report_key(input, BTN_STYLUS2, 0); + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_WHEEL, 0); + if (features->type >= INTUOS3S) + input_report_abs(input, ABS_Z, 0); + } + input_report_key(input, wacom->tool[idx], 0); + input_report_abs(input, ABS_MISC, 0); /* reset tool id */ + input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]); + wacom->id[idx] = 0; + return 2; + } + return 0; +} + +static void wacom_intuos_general(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + unsigned int t; + + /* general pen packet */ + if ((data[1] & 0xb8) == 0xa0) { + t = (data[6] << 2) | ((data[7] >> 6) & 3); + if (features->type >= INTUOS4S && features->type <= CINTIQ_HYBRID) { + t = (t << 1) | (data[1] & 1); + } + input_report_abs(input, ABS_PRESSURE, t); + input_report_abs(input, ABS_TILT_X, + ((data[7] << 1) & 0x7e) | (data[8] >> 7)); + input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f); + input_report_key(input, BTN_STYLUS, data[1] & 2); + input_report_key(input, BTN_STYLUS2, data[1] & 4); + input_report_key(input, BTN_TOUCH, t > 10); + } + + /* airbrush second packet */ + if ((data[1] & 0xbc) == 0xb4) { + input_report_abs(input, ABS_WHEEL, + (data[6] << 2) | ((data[7] >> 6) & 3)); + input_report_abs(input, ABS_TILT_X, + ((data[7] << 1) & 0x7e) | (data[8] >> 7)); + input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f); + } +} + +static int wacom_intuos_irq(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + unsigned int t; + int idx = 0, result; + + if (data[0] != WACOM_REPORT_PENABLED && + data[0] != WACOM_REPORT_INTUOSREAD && + data[0] != WACOM_REPORT_INTUOSWRITE && + data[0] != WACOM_REPORT_INTUOSPAD && + data[0] != WACOM_REPORT_INTUOS5PAD) { + dev_dbg(input->dev.parent, + "%s: received unknown report #%d\n", __func__, data[0]); + return 0; + } + + /* tool number */ + if (features->type == INTUOS) + idx = data[1] & 0x01; + + /* pad packets. Works as a second tool and is always in prox */ + if (data[0] == WACOM_REPORT_INTUOSPAD || data[0] == WACOM_REPORT_INTUOS5PAD) { + input = wacom->pad_input; + if (features->type >= INTUOS4S && features->type <= INTUOS4L) { + input_report_key(input, BTN_0, (data[2] & 0x01)); + input_report_key(input, BTN_1, (data[3] & 0x01)); + input_report_key(input, BTN_2, (data[3] & 0x02)); + input_report_key(input, BTN_3, (data[3] & 0x04)); + input_report_key(input, BTN_4, (data[3] & 0x08)); + input_report_key(input, BTN_5, (data[3] & 0x10)); + input_report_key(input, BTN_6, (data[3] & 0x20)); + if (data[1] & 0x80) { + input_report_abs(input, ABS_WHEEL, (data[1] & 0x7f)); + } else { + /* Out of proximity, clear wheel value. */ + input_report_abs(input, ABS_WHEEL, 0); + } + if (features->type != INTUOS4S) { + input_report_key(input, BTN_7, (data[3] & 0x40)); + input_report_key(input, BTN_8, (data[3] & 0x80)); + } + if (data[1] | (data[2] & 0x01) | data[3]) { + input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); + } else { + input_report_abs(input, ABS_MISC, 0); + } + } else if (features->type == DTK) { + input_report_key(input, BTN_0, (data[6] & 0x01)); + input_report_key(input, BTN_1, (data[6] & 0x02)); + input_report_key(input, BTN_2, (data[6] & 0x04)); + input_report_key(input, BTN_3, (data[6] & 0x08)); + input_report_key(input, BTN_4, (data[6] & 0x10)); + input_report_key(input, BTN_5, (data[6] & 0x20)); + if (data[6] & 0x3f) { + input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); + } else { + input_report_abs(input, ABS_MISC, 0); + } + } else if (features->type == WACOM_13HD) { + input_report_key(input, BTN_0, (data[3] & 0x01)); + input_report_key(input, BTN_1, (data[4] & 0x01)); + input_report_key(input, BTN_2, (data[4] & 0x02)); + input_report_key(input, BTN_3, (data[4] & 0x04)); + input_report_key(input, BTN_4, (data[4] & 0x08)); + input_report_key(input, BTN_5, (data[4] & 0x10)); + input_report_key(input, BTN_6, (data[4] & 0x20)); + input_report_key(input, BTN_7, (data[4] & 0x40)); + input_report_key(input, BTN_8, (data[4] & 0x80)); + if ((data[3] & 0x01) | data[4]) { + input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); + } else { + input_report_abs(input, ABS_MISC, 0); + } + } else if (features->type == WACOM_24HD) { + input_report_key(input, BTN_0, (data[6] & 0x01)); + input_report_key(input, BTN_1, (data[6] & 0x02)); + input_report_key(input, BTN_2, (data[6] & 0x04)); + input_report_key(input, BTN_3, (data[6] & 0x08)); + input_report_key(input, BTN_4, (data[6] & 0x10)); + input_report_key(input, BTN_5, (data[6] & 0x20)); + input_report_key(input, BTN_6, (data[6] & 0x40)); + input_report_key(input, BTN_7, (data[6] & 0x80)); + input_report_key(input, BTN_8, (data[8] & 0x01)); + input_report_key(input, BTN_9, (data[8] & 0x02)); + input_report_key(input, BTN_A, (data[8] & 0x04)); + input_report_key(input, BTN_B, (data[8] & 0x08)); + input_report_key(input, BTN_C, (data[8] & 0x10)); + input_report_key(input, BTN_X, (data[8] & 0x20)); + input_report_key(input, BTN_Y, (data[8] & 0x40)); + input_report_key(input, BTN_Z, (data[8] & 0x80)); + + /* + * Three "buttons" are available on the 24HD which are + * physically implemented as a touchstrip. Each button + * is approximately 3 bits wide with a 2 bit spacing. + * The raw touchstrip bits are stored at: + * ((data[3] & 0x1f) << 8) | data[4]) + */ + input_report_key(input, KEY_PROG1, data[4] & 0x07); + input_report_key(input, KEY_PROG2, data[4] & 0xE0); + input_report_key(input, KEY_PROG3, data[3] & 0x1C); + + if (data[1] & 0x80) { + input_report_abs(input, ABS_WHEEL, (data[1] & 0x7f)); + } else { + /* Out of proximity, clear wheel value. */ + input_report_abs(input, ABS_WHEEL, 0); + } + + if (data[2] & 0x80) { + input_report_abs(input, ABS_THROTTLE, (data[2] & 0x7f)); + } else { + /* Out of proximity, clear second wheel value. */ + input_report_abs(input, ABS_THROTTLE, 0); + } + + if (data[1] | data[2] | (data[3] & 0x1f) | data[4] | data[6] | data[8]) { + input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); + } else { + input_report_abs(input, ABS_MISC, 0); + } + } else if (features->type == CINTIQ_HYBRID) { + /* + * Do not send hardware buttons under Android. They + * are already sent to the system through GPIO (and + * have different meaning). + */ + input_report_key(input, BTN_1, (data[4] & 0x01)); + input_report_key(input, BTN_2, (data[4] & 0x02)); + input_report_key(input, BTN_3, (data[4] & 0x04)); + input_report_key(input, BTN_4, (data[4] & 0x08)); + + input_report_key(input, BTN_5, (data[4] & 0x10)); /* Right */ + input_report_key(input, BTN_6, (data[4] & 0x20)); /* Up */ + input_report_key(input, BTN_7, (data[4] & 0x40)); /* Left */ + input_report_key(input, BTN_8, (data[4] & 0x80)); /* Down */ + input_report_key(input, BTN_0, (data[3] & 0x01)); /* Center */ + } else if (features->type >= INTUOS5S && features->type <= INTUOSPL) { + int i; + + /* Touch ring mode switch has no capacitive sensor */ + input_report_key(input, BTN_0, (data[3] & 0x01)); + + /* + * ExpressKeys on Intuos5/Intuos Pro have a capacitive sensor in + * addition to the mechanical switch. Switch data is + * stored in data[4], capacitive data in data[5]. + */ + for (i = 0; i < 8; i++) + input_report_key(input, BTN_1 + i, data[4] & (1 << i)); + + if (data[2] & 0x80) { + input_report_abs(input, ABS_WHEEL, (data[2] & 0x7f)); + } else { + /* Out of proximity, clear wheel value. */ + input_report_abs(input, ABS_WHEEL, 0); + } + + if (data[2] | (data[3] & 0x01) | data[4] | data[5]) { + input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); + } else { + input_report_abs(input, ABS_MISC, 0); + } + } else { + if (features->type == WACOM_21UX2 || features->type == WACOM_22HD) { + input_report_key(input, BTN_0, (data[5] & 0x01)); + input_report_key(input, BTN_1, (data[6] & 0x01)); + input_report_key(input, BTN_2, (data[6] & 0x02)); + input_report_key(input, BTN_3, (data[6] & 0x04)); + input_report_key(input, BTN_4, (data[6] & 0x08)); + input_report_key(input, BTN_5, (data[6] & 0x10)); + input_report_key(input, BTN_6, (data[6] & 0x20)); + input_report_key(input, BTN_7, (data[6] & 0x40)); + input_report_key(input, BTN_8, (data[6] & 0x80)); + input_report_key(input, BTN_9, (data[7] & 0x01)); + input_report_key(input, BTN_A, (data[8] & 0x01)); + input_report_key(input, BTN_B, (data[8] & 0x02)); + input_report_key(input, BTN_C, (data[8] & 0x04)); + input_report_key(input, BTN_X, (data[8] & 0x08)); + input_report_key(input, BTN_Y, (data[8] & 0x10)); + input_report_key(input, BTN_Z, (data[8] & 0x20)); + input_report_key(input, BTN_BASE, (data[8] & 0x40)); + input_report_key(input, BTN_BASE2, (data[8] & 0x80)); + + if (features->type == WACOM_22HD) { + input_report_key(input, KEY_PROG1, data[9] & 0x01); + input_report_key(input, KEY_PROG2, data[9] & 0x02); + input_report_key(input, KEY_PROG3, data[9] & 0x04); + } + } else { + input_report_key(input, BTN_0, (data[5] & 0x01)); + input_report_key(input, BTN_1, (data[5] & 0x02)); + input_report_key(input, BTN_2, (data[5] & 0x04)); + input_report_key(input, BTN_3, (data[5] & 0x08)); + input_report_key(input, BTN_4, (data[6] & 0x01)); + input_report_key(input, BTN_5, (data[6] & 0x02)); + input_report_key(input, BTN_6, (data[6] & 0x04)); + input_report_key(input, BTN_7, (data[6] & 0x08)); + input_report_key(input, BTN_8, (data[5] & 0x10)); + input_report_key(input, BTN_9, (data[6] & 0x10)); + } + input_report_abs(input, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]); + input_report_abs(input, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]); + + if ((data[5] & 0x1f) | data[6] | (data[1] & 0x1f) | + data[2] | (data[3] & 0x1f) | data[4] | data[8] | + (data[7] & 0x01)) { + input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); + } else { + input_report_abs(input, ABS_MISC, 0); + } + } + return 1; + } + + /* process in/out prox events */ + result = wacom_intuos_inout(wacom); + if (result) + return result - 1; + + /* don't proceed if we don't know the ID */ + if (!wacom->id[idx]) + return 0; + + /* Only large Intuos support Lense Cursor */ + if (wacom->tool[idx] == BTN_TOOL_LENS && + (features->type == INTUOS3 || + features->type == INTUOS3S || + features->type == INTUOS4 || + features->type == INTUOS4S || + features->type == INTUOS5 || + features->type == INTUOS5S || + features->type == INTUOSPM || + features->type == INTUOSPS)) { + + return 0; + } + + /* Cintiq doesn't send data when RDY bit isn't set */ + if (features->type == CINTIQ && !(data[1] & 0x40)) + return 0; + + if (features->type >= INTUOS3S) { + input_report_abs(input, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1)); + input_report_abs(input, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1)); + input_report_abs(input, ABS_DISTANCE, ((data[9] >> 2) & 0x3f)); + } else { + input_report_abs(input, ABS_X, be16_to_cpup((__be16 *)&data[2])); + input_report_abs(input, ABS_Y, be16_to_cpup((__be16 *)&data[4])); + input_report_abs(input, ABS_DISTANCE, ((data[9] >> 3) & 0x1f)); + } + + /* process general packets */ + wacom_intuos_general(wacom); + + /* 4D mouse, 2D mouse, marker pen rotation, tilt mouse, or Lens cursor packets */ + if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0 || (data[1] & 0xbc) == 0xac) { + + if (data[1] & 0x02) { + /* Rotation packet */ + if (features->type >= INTUOS3S) { + /* I3 marker pen rotation */ + t = (data[6] << 3) | ((data[7] >> 5) & 7); + t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) : + ((t-1) / 2 + 450)) : (450 - t / 2) ; + input_report_abs(input, ABS_Z, t); + } else { + /* 4D mouse rotation packet */ + t = (data[6] << 3) | ((data[7] >> 5) & 7); + input_report_abs(input, ABS_RZ, (data[7] & 0x20) ? + ((t - 1) / 2) : -t / 2); + } + + } else if (!(data[1] & 0x10) && features->type < INTUOS3S) { + /* 4D mouse packet */ + input_report_key(input, BTN_LEFT, data[8] & 0x01); + input_report_key(input, BTN_MIDDLE, data[8] & 0x02); + input_report_key(input, BTN_RIGHT, data[8] & 0x04); + + input_report_key(input, BTN_SIDE, data[8] & 0x20); + input_report_key(input, BTN_EXTRA, data[8] & 0x10); + t = (data[6] << 2) | ((data[7] >> 6) & 3); + input_report_abs(input, ABS_THROTTLE, (data[8] & 0x08) ? -t : t); + + } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) { + /* I4 mouse */ + if (features->type >= INTUOS4S && features->type <= INTUOSPL) { + input_report_key(input, BTN_LEFT, data[6] & 0x01); + input_report_key(input, BTN_MIDDLE, data[6] & 0x02); + input_report_key(input, BTN_RIGHT, data[6] & 0x04); + input_report_rel(input, REL_WHEEL, ((data[7] & 0x80) >> 7) + - ((data[7] & 0x40) >> 6)); + input_report_key(input, BTN_SIDE, data[6] & 0x08); + input_report_key(input, BTN_EXTRA, data[6] & 0x10); + + input_report_abs(input, ABS_TILT_X, + ((data[7] << 1) & 0x7e) | (data[8] >> 7)); + input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f); + } else { + /* 2D mouse packet */ + input_report_key(input, BTN_LEFT, data[8] & 0x04); + input_report_key(input, BTN_MIDDLE, data[8] & 0x08); + input_report_key(input, BTN_RIGHT, data[8] & 0x10); + input_report_rel(input, REL_WHEEL, (data[8] & 0x01) + - ((data[8] & 0x02) >> 1)); + + /* I3 2D mouse side buttons */ + if (features->type >= INTUOS3S && features->type <= INTUOS3L) { + input_report_key(input, BTN_SIDE, data[8] & 0x40); + input_report_key(input, BTN_EXTRA, data[8] & 0x20); + } + } + } else if ((features->type < INTUOS3S || features->type == INTUOS3L || + features->type == INTUOS4L || features->type == INTUOS5L || + features->type == INTUOSPL) && + wacom->tool[idx] == BTN_TOOL_LENS) { + /* Lens cursor packets */ + input_report_key(input, BTN_LEFT, data[8] & 0x01); + input_report_key(input, BTN_MIDDLE, data[8] & 0x02); + input_report_key(input, BTN_RIGHT, data[8] & 0x04); + input_report_key(input, BTN_SIDE, data[8] & 0x10); + input_report_key(input, BTN_EXTRA, data[8] & 0x08); + } + } + + input_report_abs(input, ABS_MISC, wacom->id[idx]); /* report tool id */ + input_report_key(input, wacom->tool[idx], 1); + input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]); + return 1; +} + +static int int_dist(int x1, int y1, int x2, int y2) +{ + int x = x2 - x1; + int y = y2 - y1; + + return int_sqrt(x*x + y*y); +} + +static void wacom_intuos_bt_process_data(struct wacom_wac *wacom, + unsigned char *data) +{ + memcpy(wacom->data, data, 10); + wacom_intuos_irq(wacom); + + input_sync(wacom->input); + if (wacom->pad_input) + input_sync(wacom->pad_input); +} + +static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len) +{ + unsigned char data[WACOM_PKGLEN_MAX]; + int i = 1; + unsigned power_raw, battery_capacity, bat_charging, ps_connected; + + memcpy(data, wacom->data, len); + + switch (data[0]) { + case 0x04: + wacom_intuos_bt_process_data(wacom, data + i); + i += 10; + /* fall through */ + case 0x03: + wacom_intuos_bt_process_data(wacom, data + i); + i += 10; + wacom_intuos_bt_process_data(wacom, data + i); + i += 10; + power_raw = data[i]; + bat_charging = (power_raw & 0x08) ? 1 : 0; + ps_connected = (power_raw & 0x10) ? 1 : 0; + battery_capacity = batcap_i4[power_raw & 0x07]; + if ((wacom->battery_capacity != battery_capacity) || + (wacom->bat_charging != bat_charging) || + (wacom->ps_connected != ps_connected)) { + wacom->battery_capacity = battery_capacity; + wacom->bat_charging = bat_charging; + wacom->ps_connected = ps_connected; + wacom_notify_battery(wacom); + } + + break; + default: + dev_dbg(wacom->input->dev.parent, + "Unknown report: %d,%d size:%zu\n", + data[0], data[1], len); + return 0; + } + return 0; +} + +static int wacom_24hdt_irq(struct wacom_wac *wacom) +{ + struct input_dev *input = wacom->input; + unsigned char *data = wacom->data; + int i; + int current_num_contacts = data[61]; + int contacts_to_send = 0; + + /* + * First packet resets the counter since only the first + * packet in series will have non-zero current_num_contacts. + */ + if (current_num_contacts) + wacom->num_contacts_left = current_num_contacts; + + /* There are at most 4 contacts per packet */ + contacts_to_send = min(4, wacom->num_contacts_left); + + for (i = 0; i < contacts_to_send; i++) { + int offset = (WACOM_BYTES_PER_24HDT_PACKET * i) + 1; + bool touch = data[offset] & 0x1 && !wacom->shared->stylus_in_proximity; + int slot = input_mt_get_slot_by_key(input, data[offset + 1]); + + if (slot < 0) + continue; + input_mt_slot(input, slot); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); + + if (touch) { + int t_x = get_unaligned_le16(&data[offset + 2]); + int c_x = get_unaligned_le16(&data[offset + 4]); + int t_y = get_unaligned_le16(&data[offset + 6]); + int c_y = get_unaligned_le16(&data[offset + 8]); + int w = get_unaligned_le16(&data[offset + 10]); + int h = get_unaligned_le16(&data[offset + 12]); + + input_report_abs(input, ABS_MT_POSITION_X, t_x); + input_report_abs(input, ABS_MT_POSITION_Y, t_y); + input_report_abs(input, ABS_MT_TOUCH_MAJOR, min(w,h)); + input_report_abs(input, ABS_MT_WIDTH_MAJOR, min(w, h) + int_dist(t_x, t_y, c_x, c_y)); + input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h)); + input_report_abs(input, ABS_MT_ORIENTATION, w > h); + } + } + input_mt_report_pointer_emulation(input, true); + + wacom->num_contacts_left -= contacts_to_send; + if (wacom->num_contacts_left <= 0) + wacom->num_contacts_left = 0; + + return 1; +} + +static int wacom_mt_touch(struct wacom_wac *wacom) +{ + struct input_dev *input = wacom->input; + unsigned char *data = wacom->data; + int i; + int current_num_contacts = data[2]; + int contacts_to_send = 0; + int x_offset = 0; + + /* MTTPC does not support Height and Width */ + if (wacom->features.type == MTTPC || wacom->features.type == MTTPC_B) + x_offset = -4; + + /* + * First packet resets the counter since only the first + * packet in series will have non-zero current_num_contacts. + */ + if (current_num_contacts) + wacom->num_contacts_left = current_num_contacts; + + /* There are at most 5 contacts per packet */ + contacts_to_send = min(5, wacom->num_contacts_left); + + for (i = 0; i < contacts_to_send; i++) { + int offset = (WACOM_BYTES_PER_MT_PACKET + x_offset) * i + 3; + bool touch = data[offset] & 0x1; + int id = get_unaligned_le16(&data[offset + 1]); + int slot = input_mt_get_slot_by_key(input, id); + + if (slot < 0) + continue; + + input_mt_slot(input, slot); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); + if (touch) { + int x = get_unaligned_le16(&data[offset + x_offset + 7]); + int y = get_unaligned_le16(&data[offset + x_offset + 9]); + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + } + } + input_mt_report_pointer_emulation(input, true); + + wacom->num_contacts_left -= contacts_to_send; + if (wacom->num_contacts_left < 0) + wacom->num_contacts_left = 0; + + return 1; +} + +static int wacom_tpc_mt_touch(struct wacom_wac *wacom) +{ + struct input_dev *input = wacom->input; + unsigned char *data = wacom->data; + int contact_with_no_pen_down_count = 0; + int i; + + for (i = 0; i < 2; i++) { + int p = data[1] & (1 << i); + bool touch = p && !wacom->shared->stylus_in_proximity; + + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); + if (touch) { + int x = le16_to_cpup((__le16 *)&data[i * 2 + 2]) & 0x7fff; + int y = le16_to_cpup((__le16 *)&data[i * 2 + 6]) & 0x7fff; + + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + contact_with_no_pen_down_count++; + } + } + input_mt_report_pointer_emulation(input, true); + + /* keep touch state for pen event */ + wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); + + return 1; +} + +static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len) +{ + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + bool prox; + int x = 0, y = 0; + + if (wacom->features.touch_max > 1 || len > WACOM_PKGLEN_TPC2FG) + return 0; + + if (!wacom->shared->stylus_in_proximity) { + if (len == WACOM_PKGLEN_TPC1FG) { + prox = data[0] & 0x01; + x = get_unaligned_le16(&data[1]); + y = get_unaligned_le16(&data[3]); + } else if (len == WACOM_PKGLEN_TPC1FG_B) { + prox = data[2] & 0x01; + x = get_unaligned_le16(&data[3]); + y = get_unaligned_le16(&data[5]); + } else { + prox = data[1] & 0x01; + x = le16_to_cpup((__le16 *)&data[2]); + y = le16_to_cpup((__le16 *)&data[4]); + } + } else + /* force touch out when pen is in prox */ + prox = 0; + + if (prox) { + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + } + input_report_key(input, BTN_TOUCH, prox); + + /* keep touch state for pen events */ + wacom->shared->touch_down = prox; + + return 1; +} + +static int wacom_tpc_pen(struct wacom_wac *wacom) +{ + unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; + bool prox = data[1] & 0x20; + + if (!wacom->shared->stylus_in_proximity) /* first in prox */ + /* Going into proximity select tool */ + wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + + /* keep pen state for touch events */ + wacom->shared->stylus_in_proximity = prox; + + /* send pen events only when touch is up or forced out */ + if (!wacom->shared->touch_down) { + input_report_key(input, BTN_STYLUS, data[1] & 0x02); + input_report_key(input, BTN_STYLUS2, data[1] & 0x10); + input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); + input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); + input_report_abs(input, ABS_PRESSURE, ((data[7] & 0x07) << 8) | data[6]); + input_report_key(input, BTN_TOUCH, data[1] & 0x05); + input_report_key(input, wacom->tool[0], prox); + return 1; + } + + return 0; +} + +static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) +{ + unsigned char *data = wacom->data; + + dev_dbg(wacom->input->dev.parent, + "%s: received report #%d\n", __func__, data[0]); + + switch (len) { + case WACOM_PKGLEN_TPC1FG: + return wacom_tpc_single_touch(wacom, len); + + case WACOM_PKGLEN_TPC2FG: + return wacom_tpc_mt_touch(wacom); + + case WACOM_PKGLEN_PENABLED: + return wacom_tpc_pen(wacom); + + default: + switch (data[0]) { + case WACOM_REPORT_TPC1FG: + case WACOM_REPORT_TPCHID: + case WACOM_REPORT_TPCST: + case WACOM_REPORT_TPC1FGE: + return wacom_tpc_single_touch(wacom, len); + + case WACOM_REPORT_TPCMT: + case WACOM_REPORT_TPCMT2: + return wacom_mt_touch(wacom); + + case WACOM_REPORT_PENABLED: + return wacom_tpc_pen(wacom); + } + } + + return 0; +} + +static void wacom_map_usage(struct wacom *wacom, struct hid_usage *usage, + struct hid_field *field, __u8 type, __u16 code, int fuzz) +{ + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct input_dev *input = wacom_wac->input; + int fmin = field->logical_minimum; + int fmax = field->logical_maximum; + + usage->type = type; + usage->code = code; + + set_bit(type, input->evbit); + + switch (type) { + case EV_ABS: + input_set_abs_params(input, code, fmin, fmax, fuzz, 0); + input_abs_set_res(input, code, + hidinput_calc_abs_res(field, code)); + break; + case EV_KEY: + input_set_capability(input, EV_KEY, code); + break; + case EV_MSC: + input_set_capability(input, EV_MSC, code); + break; + } +} + +static void wacom_wac_pen_usage_mapping(struct hid_device *hdev, + struct hid_field *field, struct hid_usage *usage) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + + switch (usage->hid) { + case HID_GD_X: + wacom_map_usage(wacom, usage, field, EV_ABS, ABS_X, 4); + break; + case HID_GD_Y: + wacom_map_usage(wacom, usage, field, EV_ABS, ABS_Y, 4); + break; + case HID_DG_TIPPRESSURE: + wacom_map_usage(wacom, usage, field, EV_ABS, ABS_PRESSURE, 0); + break; + case HID_DG_INRANGE: + wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOOL_PEN, 0); + break; + case HID_DG_INVERT: + wacom_map_usage(wacom, usage, field, EV_KEY, + BTN_TOOL_RUBBER, 0); + break; + case HID_DG_ERASER: + case HID_DG_TIPSWITCH: + wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOUCH, 0); + break; + case HID_DG_BARRELSWITCH: + wacom_map_usage(wacom, usage, field, EV_KEY, BTN_STYLUS, 0); + break; + case HID_DG_BARRELSWITCH2: + wacom_map_usage(wacom, usage, field, EV_KEY, BTN_STYLUS2, 0); + break; + case HID_DG_TOOLSERIALNUMBER: + wacom_map_usage(wacom, usage, field, EV_MSC, MSC_SERIAL, 0); + break; + } +} + +static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct input_dev *input = wacom_wac->input; + + /* checking which Tool / tip switch to send */ + switch (usage->hid) { + case HID_DG_INRANGE: + wacom_wac->hid_data.inrange_state = value; + return 0; + case HID_DG_INVERT: + wacom_wac->hid_data.invert_state = value; + return 0; + case HID_DG_ERASER: + case HID_DG_TIPSWITCH: + wacom_wac->hid_data.tipswitch |= value; + return 0; + } + + /* send pen events only when touch is up or forced out */ + if (!usage->type || wacom_wac->shared->touch_down) + return 0; + + input_event(input, usage->type, usage->code, value); + + return 0; +} + +static void wacom_wac_pen_report(struct hid_device *hdev, + struct hid_report *report) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct input_dev *input = wacom_wac->input; + bool prox = wacom_wac->hid_data.inrange_state; + + if (!wacom_wac->shared->stylus_in_proximity) /* first in prox */ + /* Going into proximity select tool */ + wacom_wac->tool[0] = wacom_wac->hid_data.invert_state ? + BTN_TOOL_RUBBER : BTN_TOOL_PEN; + + /* keep pen state for touch events */ + wacom_wac->shared->stylus_in_proximity = prox; + + /* send pen events only when touch is up or forced out */ + if (!wacom_wac->shared->touch_down) { + input_report_key(input, BTN_TOUCH, + wacom_wac->hid_data.tipswitch); + input_report_key(input, wacom_wac->tool[0], prox); + + wacom_wac->hid_data.tipswitch = false; + + input_sync(input); + } +} + +static void wacom_wac_finger_usage_mapping(struct hid_device *hdev, + struct hid_field *field, struct hid_usage *usage) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct input_dev *input = wacom_wac->input; + unsigned touch_max = wacom_wac->features.touch_max; + + switch (usage->hid) { + case HID_GD_X: + if (touch_max == 1) + wacom_map_usage(wacom, usage, field, EV_ABS, ABS_X, 4); + else + wacom_map_usage(wacom, usage, field, EV_ABS, + ABS_MT_POSITION_X, 4); + break; + case HID_GD_Y: + if (touch_max == 1) + wacom_map_usage(wacom, usage, field, EV_ABS, ABS_Y, 4); + else + wacom_map_usage(wacom, usage, field, EV_ABS, + ABS_MT_POSITION_Y, 4); + break; + case HID_DG_CONTACTID: + input_mt_init_slots(input, wacom_wac->features.touch_max, + INPUT_MT_DIRECT); + break; + case HID_DG_INRANGE: + break; + case HID_DG_INVERT: + break; + case HID_DG_TIPSWITCH: + wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOUCH, 0); + break; + } +} + +static int wacom_wac_finger_event(struct hid_device *hdev, + struct hid_field *field, struct hid_usage *usage, __s32 value) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + + switch (usage->hid) { + case HID_GD_X: + wacom_wac->hid_data.x = value; + break; + case HID_GD_Y: + wacom_wac->hid_data.y = value; + break; + case HID_DG_CONTACTID: + wacom_wac->hid_data.id = value; + break; + case HID_DG_TIPSWITCH: + wacom_wac->hid_data.tipswitch = value; + break; + } + + + return 0; +} + +static void wacom_wac_finger_mt_report(struct wacom_wac *wacom_wac, + struct input_dev *input, bool touch) +{ + int slot; + struct hid_data *hid_data = &wacom_wac->hid_data; + + slot = input_mt_get_slot_by_key(input, hid_data->id); + + input_mt_slot(input, slot); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); + if (touch) { + input_report_abs(input, ABS_MT_POSITION_X, hid_data->x); + input_report_abs(input, ABS_MT_POSITION_Y, hid_data->y); + } + input_mt_sync_frame(input); +} + +static void wacom_wac_finger_single_touch_report(struct wacom_wac *wacom_wac, + struct input_dev *input, bool touch) +{ + struct hid_data *hid_data = &wacom_wac->hid_data; + + if (touch) { + input_report_abs(input, ABS_X, hid_data->x); + input_report_abs(input, ABS_Y, hid_data->y); + } + input_report_key(input, BTN_TOUCH, touch); +} + +static void wacom_wac_finger_report(struct hid_device *hdev, + struct hid_report *report) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct input_dev *input = wacom_wac->input; + bool touch = wacom_wac->hid_data.tipswitch && + !wacom_wac->shared->stylus_in_proximity; + unsigned touch_max = wacom_wac->features.touch_max; + + if (touch_max > 1) + wacom_wac_finger_mt_report(wacom_wac, input, touch); + else + wacom_wac_finger_single_touch_report(wacom_wac, input, touch); + input_sync(input); + + /* keep touch state for pen event */ + wacom_wac->shared->touch_down = touch; +} + +#define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \ + ((f)->physical == HID_DG_STYLUS)) +#define WACOM_FINGER_FIELD(f) (((f)->logical == HID_DG_FINGER) || \ + ((f)->physical == HID_DG_FINGER)) + +void wacom_wac_usage_mapping(struct hid_device *hdev, + struct hid_field *field, struct hid_usage *usage) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct input_dev *input = wacom_wac->input; + + /* currently, only direct devices have proper hid report descriptors */ + __set_bit(INPUT_PROP_DIRECT, input->propbit); + + if (WACOM_PEN_FIELD(field)) + return wacom_wac_pen_usage_mapping(hdev, field, usage); + + if (WACOM_FINGER_FIELD(field)) + return wacom_wac_finger_usage_mapping(hdev, field, usage); +} + +int wacom_wac_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + + if (wacom->wacom_wac.features.type != HID_GENERIC) + return 0; + + if (WACOM_PEN_FIELD(field)) + return wacom_wac_pen_event(hdev, field, usage, value); + + if (WACOM_FINGER_FIELD(field)) + return wacom_wac_finger_event(hdev, field, usage, value); + + return 0; +} + +void wacom_wac_report(struct hid_device *hdev, struct hid_report *report) +{ + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct hid_field *field = report->field[0]; + + if (wacom_wac->features.type != HID_GENERIC) + return; + + if (WACOM_PEN_FIELD(field)) + return wacom_wac_pen_report(hdev, report); + + if (WACOM_FINGER_FIELD(field)) + return wacom_wac_finger_report(hdev, report); +} + +static int wacom_bpt_touch(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + struct input_dev *input = wacom->input; + struct input_dev *pad_input = wacom->pad_input; + unsigned char *data = wacom->data; + int i; + + if (data[0] != 0x02) + return 0; + + for (i = 0; i < 2; i++) { + int offset = (data[1] & 0x80) ? (8 * i) : (9 * i); + bool touch = data[offset + 3] & 0x80; + + /* + * Touch events need to be disabled while stylus is + * in proximity because user's hand is resting on touchpad + * and sending unwanted events. User expects tablet buttons + * to continue working though. + */ + touch = touch && !wacom->shared->stylus_in_proximity; + + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); + if (touch) { + int x = get_unaligned_be16(&data[offset + 3]) & 0x7ff; + int y = get_unaligned_be16(&data[offset + 5]) & 0x7ff; + if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) { + x <<= 5; + y <<= 5; + } + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + } + } + + input_mt_report_pointer_emulation(input, true); + + input_report_key(pad_input, BTN_LEFT, (data[1] & 0x08) != 0); + input_report_key(pad_input, BTN_FORWARD, (data[1] & 0x04) != 0); + input_report_key(pad_input, BTN_BACK, (data[1] & 0x02) != 0); + input_report_key(pad_input, BTN_RIGHT, (data[1] & 0x01) != 0); + + return 1; +} + +static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) +{ + struct wacom_features *features = &wacom->features; + struct input_dev *input = wacom->input; + bool touch = data[1] & 0x80; + int slot = input_mt_get_slot_by_key(input, data[0]); + + if (slot < 0) + return; + + touch = touch && !wacom->shared->stylus_in_proximity; + + input_mt_slot(input, slot); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); + + if (touch) { + int x = (data[2] << 4) | (data[4] >> 4); + int y = (data[3] << 4) | (data[4] & 0x0f); + int width, height; + + if (features->type >= INTUOSPS && features->type <= INTUOSPL) { + width = data[5] * 100; + height = data[6] * 100; + } else { + /* + * "a" is a scaled-down area which we assume is + * roughly circular and which can be described as: + * a=(pi*r^2)/C. + */ + int a = data[5]; + int x_res = input_abs_get_res(input, ABS_MT_POSITION_X); + int y_res = input_abs_get_res(input, ABS_MT_POSITION_Y); + width = 2 * int_sqrt(a * WACOM_CONTACT_AREA_SCALE); + height = width * y_res / x_res; + } + + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + input_report_abs(input, ABS_MT_TOUCH_MAJOR, width); + input_report_abs(input, ABS_MT_TOUCH_MINOR, height); + } +} + +static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data) +{ + struct input_dev *input = wacom->pad_input; + struct wacom_features *features = &wacom->features; + + if (features->type == INTUOSHT) { + input_report_key(input, BTN_LEFT, (data[1] & 0x02) != 0); + input_report_key(input, BTN_BACK, (data[1] & 0x08) != 0); + } else { + input_report_key(input, BTN_BACK, (data[1] & 0x02) != 0); + input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0); + } + input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0); + input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0); +} + +static int wacom_bpt3_touch(struct wacom_wac *wacom) +{ + struct input_dev *input = wacom->input; + unsigned char *data = wacom->data; + int count = data[1] & 0x07; + int i; + + if (data[0] != 0x02) + return 0; + + /* data has up to 7 fixed sized 8-byte messages starting at data[2] */ + for (i = 0; i < count; i++) { + int offset = (8 * i) + 2; + int msg_id = data[offset]; + + if (msg_id >= 2 && msg_id <= 17) + wacom_bpt3_touch_msg(wacom, data + offset); + else if (msg_id == 128) + wacom_bpt3_button_msg(wacom, data + offset); + + } + input_mt_report_pointer_emulation(input, true); + + return 1; +} + +static int wacom_bpt_pen(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + struct input_dev *input = wacom->input; + unsigned char *data = wacom->data; + int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0; + + if (data[0] != WACOM_REPORT_PENABLED && data[0] != WACOM_REPORT_USB) + return 0; + + if (data[0] == WACOM_REPORT_USB) { + if (features->type == INTUOSHT && features->touch_max) { + input_report_switch(wacom->shared->touch_input, + SW_MUTE_DEVICE, data[8] & 0x40); + input_sync(wacom->shared->touch_input); + } + return 0; + } + + prox = (data[1] & 0x20) == 0x20; + + /* + * All reports shared between PEN and RUBBER tool must be + * forced to a known starting value (zero) when transitioning to + * out-of-prox. + * + * If not reset then, to userspace, it will look like lost events + * if new tool comes in-prox with same values as previous tool sent. + * + * Hardware does report zero in most out-of-prox cases but not all. + */ + if (prox) { + if (!wacom->shared->stylus_in_proximity) { + if (data[1] & 0x08) { + wacom->tool[0] = BTN_TOOL_RUBBER; + wacom->id[0] = ERASER_DEVICE_ID; + } else { + wacom->tool[0] = BTN_TOOL_PEN; + wacom->id[0] = STYLUS_DEVICE_ID; + } + wacom->shared->stylus_in_proximity = true; + } + x = le16_to_cpup((__le16 *)&data[2]); + y = le16_to_cpup((__le16 *)&data[4]); + p = le16_to_cpup((__le16 *)&data[6]); + /* + * Convert distance from out prox to distance from tablet. + * distance will be greater than distance_max once + * touching and applying pressure; do not report negative + * distance. + */ + if (data[8] <= features->distance_max) + d = features->distance_max - data[8]; + + pen = data[1] & 0x01; + btn1 = data[1] & 0x02; + btn2 = data[1] & 0x04; + } + + input_report_key(input, BTN_TOUCH, pen); + input_report_key(input, BTN_STYLUS, btn1); + input_report_key(input, BTN_STYLUS2, btn2); + + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_PRESSURE, p); + input_report_abs(input, ABS_DISTANCE, d); + + if (!prox) { + wacom->id[0] = 0; + wacom->shared->stylus_in_proximity = false; + } + + input_report_key(input, wacom->tool[0], prox); /* PEN or RUBBER */ + input_report_abs(input, ABS_MISC, wacom->id[0]); /* TOOL ID */ + + return 1; +} + +static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) +{ + if (len == WACOM_PKGLEN_BBTOUCH) + return wacom_bpt_touch(wacom); + else if (len == WACOM_PKGLEN_BBTOUCH3) + return wacom_bpt3_touch(wacom); + else if (len == WACOM_PKGLEN_BBFUN || len == WACOM_PKGLEN_BBPEN) + return wacom_bpt_pen(wacom); + + return 0; +} + +static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) +{ + unsigned char *data = wacom->data; + int connected; + + if (len != WACOM_PKGLEN_WIRELESS || data[0] != WACOM_REPORT_WL) + return 0; + + connected = data[1] & 0x01; + if (connected) { + int pid, battery, ps_connected; + + if ((wacom->shared->type == INTUOSHT) && + wacom->shared->touch_max) { + input_report_switch(wacom->shared->touch_input, + SW_MUTE_DEVICE, data[5] & 0x40); + input_sync(wacom->shared->touch_input); + } + + pid = get_unaligned_be16(&data[6]); + battery = (data[5] & 0x3f) * 100 / 31; + ps_connected = !!(data[5] & 0x80); + if (wacom->pid != pid) { + wacom->pid = pid; + wacom_schedule_work(wacom); + } + + if (wacom->shared->type && + (battery != wacom->battery_capacity || + ps_connected != wacom->ps_connected)) { + wacom->battery_capacity = battery; + wacom->ps_connected = ps_connected; + wacom->bat_charging = ps_connected && + wacom->battery_capacity < 100; + wacom_notify_battery(wacom); + } + } else if (wacom->pid != 0) { + /* disconnected while previously connected */ + wacom->pid = 0; + wacom_schedule_work(wacom); + wacom->battery_capacity = 0; + wacom->bat_charging = 0; + wacom->ps_connected = 0; + } + + return 0; +} + +void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) +{ + bool sync; + + switch (wacom_wac->features.type) { + case PENPARTNER: + sync = wacom_penpartner_irq(wacom_wac); + break; + + case PL: + sync = wacom_pl_irq(wacom_wac); + break; + + case WACOM_G4: + case GRAPHIRE: + case GRAPHIRE_BT: + case WACOM_MO: + sync = wacom_graphire_irq(wacom_wac); + break; + + case PTU: + sync = wacom_ptu_irq(wacom_wac); + break; + + case DTU: + sync = wacom_dtu_irq(wacom_wac); + break; + + case DTUS: + sync = wacom_dtus_irq(wacom_wac); + break; + + case INTUOS: + case INTUOS3S: + case INTUOS3: + case INTUOS3L: + case INTUOS4S: + case INTUOS4: + case INTUOS4L: + case CINTIQ: + case WACOM_BEE: + case WACOM_13HD: + case WACOM_21UX2: + case WACOM_22HD: + case WACOM_24HD: + case DTK: + case CINTIQ_HYBRID: + sync = wacom_intuos_irq(wacom_wac); + break; + + case INTUOS4WL: + sync = wacom_intuos_bt_irq(wacom_wac, len); + break; + + case WACOM_24HDT: + sync = wacom_24hdt_irq(wacom_wac); + break; + + case INTUOS5S: + case INTUOS5: + case INTUOS5L: + case INTUOSPS: + case INTUOSPM: + case INTUOSPL: + if (len == WACOM_PKGLEN_BBTOUCH3) + sync = wacom_bpt3_touch(wacom_wac); + else + sync = wacom_intuos_irq(wacom_wac); + break; + + case TABLETPC: + case TABLETPCE: + case TABLETPC2FG: + case MTSCREEN: + case MTTPC: + case MTTPC_B: + sync = wacom_tpc_irq(wacom_wac, len); + break; + + case BAMBOO_PT: + case INTUOSHT: + sync = wacom_bpt_irq(wacom_wac, len); + break; + + case WIRELESS: + sync = wacom_wireless_irq(wacom_wac, len); + break; + + default: + sync = false; + break; + } + + if (sync) { + input_sync(wacom_wac->input); + if (wacom_wac->pad_input) + input_sync(wacom_wac->pad_input); + } +} + +static void wacom_setup_cintiq(struct wacom_wac *wacom_wac) +{ + struct input_dev *input_dev = wacom_wac->input; + + input_set_capability(input_dev, EV_MSC, MSC_SERIAL); + + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_TOOL_BRUSH, input_dev->keybit); + __set_bit(BTN_TOOL_PENCIL, input_dev->keybit); + __set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + __set_bit(BTN_STYLUS2, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_DISTANCE, + 0, wacom_wac->features.distance_max, 0, 0); + input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0); + input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0); + input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0); +} + +static void wacom_setup_intuos(struct wacom_wac *wacom_wac) +{ + struct input_dev *input_dev = wacom_wac->input; + + input_set_capability(input_dev, EV_REL, REL_WHEEL); + + wacom_setup_cintiq(wacom_wac); + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(BTN_MIDDLE, input_dev->keybit); + __set_bit(BTN_SIDE, input_dev->keybit); + __set_bit(BTN_EXTRA, input_dev->keybit); + __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); + __set_bit(BTN_TOOL_LENS, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0); + input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0); +} + +void wacom_setup_device_quirks(struct wacom_features *features) +{ + + /* touch device found but size is not defined. use default */ + if (features->device_type == BTN_TOOL_FINGER && !features->x_max) { + features->x_max = 1023; + features->y_max = 1023; + } + + /* these device have multiple inputs */ + if (features->type >= WIRELESS || + (features->type >= INTUOS5S && features->type <= INTUOSHT) || + (features->oVid && features->oPid)) + features->quirks |= WACOM_QUIRK_MULTI_INPUT; + + /* quirk for bamboo touch with 2 low res touches */ + if (features->type == BAMBOO_PT && + features->pktlen == WACOM_PKGLEN_BBTOUCH) { + features->x_max <<= 5; + features->y_max <<= 5; + features->x_fuzz <<= 5; + features->y_fuzz <<= 5; + features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES; + } + + if (features->type == WIRELESS) { + + /* monitor never has input and pen/touch have delayed create */ + features->quirks |= WACOM_QUIRK_NO_INPUT; + + /* must be monitor interface if no device_type set */ + if (!features->device_type) { + features->quirks |= WACOM_QUIRK_MONITOR; + features->quirks |= WACOM_QUIRK_BATTERY; + } + } +} + +static void wacom_abs_set_axis(struct input_dev *input_dev, + struct wacom_wac *wacom_wac) +{ + struct wacom_features *features = &wacom_wac->features; + + if (features->device_type == BTN_TOOL_PEN) { + input_set_abs_params(input_dev, ABS_X, features->x_min, + features->x_max, features->x_fuzz, 0); + input_set_abs_params(input_dev, ABS_Y, features->y_min, + features->y_max, features->y_fuzz, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, + features->pressure_max, features->pressure_fuzz, 0); + + /* penabled devices have fixed resolution for each model */ + input_abs_set_res(input_dev, ABS_X, features->x_resolution); + input_abs_set_res(input_dev, ABS_Y, features->y_resolution); + } else { + if (features->touch_max == 1) { + input_set_abs_params(input_dev, ABS_X, 0, + features->x_max, features->x_fuzz, 0); + input_set_abs_params(input_dev, ABS_Y, 0, + features->y_max, features->y_fuzz, 0); + input_abs_set_res(input_dev, ABS_X, + features->x_resolution); + input_abs_set_res(input_dev, ABS_Y, + features->y_resolution); + } + + if (features->touch_max > 1) { + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, + features->x_max, features->x_fuzz, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, + features->y_max, features->y_fuzz, 0); + input_abs_set_res(input_dev, ABS_MT_POSITION_X, + features->x_resolution); + input_abs_set_res(input_dev, ABS_MT_POSITION_Y, + features->y_resolution); + } + } +} + +int wacom_setup_input_capabilities(struct input_dev *input_dev, + struct wacom_wac *wacom_wac) +{ + struct wacom_features *features = &wacom_wac->features; + + input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + if (features->type == HID_GENERIC) + /* setup has already been done */ + return 0; + + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(ABS_MISC, input_dev->absbit); + + wacom_abs_set_axis(input_dev, wacom_wac); + + switch (features->type) { + case WACOM_MO: + input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); + /* fall through */ + + case WACOM_G4: + /* fall through */ + + case GRAPHIRE: + input_set_capability(input_dev, EV_REL, REL_WHEEL); + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(BTN_MIDDLE, input_dev->keybit); + + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + __set_bit(BTN_STYLUS2, input_dev->keybit); + + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + break; + + case GRAPHIRE_BT: + __clear_bit(ABS_MISC, input_dev->absbit); + input_set_abs_params(input_dev, ABS_DISTANCE, 0, + features->distance_max, + 0, 0); + + input_set_capability(input_dev, EV_REL, REL_WHEEL); + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(BTN_MIDDLE, input_dev->keybit); + + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + __set_bit(BTN_STYLUS2, input_dev->keybit); + + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + break; + + case WACOM_24HD: + input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0); + /* fall through */ + + case DTK: + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + wacom_setup_cintiq(wacom_wac); + break; + + case WACOM_22HD: + case WACOM_21UX2: + case WACOM_BEE: + case CINTIQ: + input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + wacom_setup_cintiq(wacom_wac); + break; + + case WACOM_13HD: + input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + wacom_setup_cintiq(wacom_wac); + break; + + case INTUOS3: + case INTUOS3L: + case INTUOS3S: + input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + /* fall through */ + + case INTUOS: + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + + wacom_setup_intuos(wacom_wac); + break; + + case INTUOS5: + case INTUOS5L: + case INTUOSPM: + case INTUOSPL: + case INTUOS5S: + case INTUOSPS: + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + + if (features->device_type == BTN_TOOL_PEN) { + input_set_abs_params(input_dev, ABS_DISTANCE, 0, + features->distance_max, + 0, 0); + + input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + + wacom_setup_intuos(wacom_wac); + } else if (features->device_type == BTN_TOOL_FINGER) { + __clear_bit(ABS_MISC, input_dev->absbit); + + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, features->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, + 0, features->y_max, 0, 0); + input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER); + } + break; + + case INTUOS4: + case INTUOS4WL: + case INTUOS4L: + case INTUOS4S: + input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + wacom_setup_intuos(wacom_wac); + + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + break; + + case WACOM_24HDT: + if (features->device_type == BTN_TOOL_FINGER) { + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, features->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR, 0, features->y_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0); + } + /* fall through */ + + case MTSCREEN: + case MTTPC: + case MTTPC_B: + case TABLETPC2FG: + if (features->device_type == BTN_TOOL_FINGER && features->touch_max > 1) + input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_DIRECT); + /* fall through */ + + case TABLETPC: + case TABLETPCE: + __clear_bit(ABS_MISC, input_dev->absbit); + + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + if (features->device_type != BTN_TOOL_PEN) + break; /* no need to process stylus stuff */ + + /* fall through */ + + case DTUS: + case PL: + case DTU: + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + __set_bit(BTN_STYLUS2, input_dev->keybit); + + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + break; + + case PTU: + __set_bit(BTN_STYLUS2, input_dev->keybit); + /* fall through */ + + case PENPARTNER: + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + break; + + case INTUOSHT: + if (features->touch_max && + features->device_type == BTN_TOOL_FINGER) { + input_dev->evbit[0] |= BIT_MASK(EV_SW); + __set_bit(SW_MUTE_DEVICE, input_dev->swbit); + } + /* fall through */ + + case BAMBOO_PT: + __clear_bit(ABS_MISC, input_dev->absbit); + + if (features->device_type == BTN_TOOL_FINGER) { + + if (features->touch_max) { + if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) { + input_set_abs_params(input_dev, + ABS_MT_TOUCH_MAJOR, + 0, features->x_max, 0, 0); + input_set_abs_params(input_dev, + ABS_MT_TOUCH_MINOR, + 0, features->y_max, 0, 0); + } + input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER); + } else { + /* buttons/keys only interface */ + __clear_bit(ABS_X, input_dev->absbit); + __clear_bit(ABS_Y, input_dev->absbit); + __clear_bit(BTN_TOUCH, input_dev->keybit); + } + } else if (features->device_type == BTN_TOOL_PEN) { + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + __set_bit(BTN_STYLUS2, input_dev->keybit); + input_set_abs_params(input_dev, ABS_DISTANCE, 0, + features->distance_max, + 0, 0); + } + break; + + case CINTIQ_HYBRID: + input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + wacom_setup_cintiq(wacom_wac); + break; + } + return 0; +} + +int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, + struct wacom_wac *wacom_wac) +{ + struct wacom_features *features = &wacom_wac->features; + int i; + + input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + /* kept for making legacy xf86-input-wacom working with the wheels */ + __set_bit(ABS_MISC, input_dev->absbit); + + /* kept for making legacy xf86-input-wacom accepting the pad */ + input_set_abs_params(input_dev, ABS_X, 0, 1, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 1, 0, 0); + + /* kept for making udev and libwacom accepting the pad */ + __set_bit(BTN_STYLUS, input_dev->keybit); + + switch (features->type) { + case GRAPHIRE_BT: + __set_bit(BTN_0, input_dev->keybit); + __set_bit(BTN_1, input_dev->keybit); + break; + + case WACOM_MO: + __set_bit(BTN_BACK, input_dev->keybit); + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_FORWARD, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); + break; + + case WACOM_G4: + __set_bit(BTN_BACK, input_dev->keybit); + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_FORWARD, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + input_set_capability(input_dev, EV_REL, REL_WHEEL); + break; + + case WACOM_24HD: + __set_bit(BTN_A, input_dev->keybit); + __set_bit(BTN_B, input_dev->keybit); + __set_bit(BTN_C, input_dev->keybit); + __set_bit(BTN_X, input_dev->keybit); + __set_bit(BTN_Y, input_dev->keybit); + __set_bit(BTN_Z, input_dev->keybit); + + for (i = 0; i < 10; i++) + __set_bit(BTN_0 + i, input_dev->keybit); + + __set_bit(KEY_PROG1, input_dev->keybit); + __set_bit(KEY_PROG2, input_dev->keybit); + __set_bit(KEY_PROG3, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); + input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0); + break; + + case DTK: + for (i = 0; i < 6; i++) + __set_bit(BTN_0 + i, input_dev->keybit); + + break; + + case WACOM_22HD: + __set_bit(KEY_PROG1, input_dev->keybit); + __set_bit(KEY_PROG2, input_dev->keybit); + __set_bit(KEY_PROG3, input_dev->keybit); + /* fall through */ + + case WACOM_21UX2: + __set_bit(BTN_A, input_dev->keybit); + __set_bit(BTN_B, input_dev->keybit); + __set_bit(BTN_C, input_dev->keybit); + __set_bit(BTN_X, input_dev->keybit); + __set_bit(BTN_Y, input_dev->keybit); + __set_bit(BTN_Z, input_dev->keybit); + __set_bit(BTN_BASE, input_dev->keybit); + __set_bit(BTN_BASE2, input_dev->keybit); + /* fall through */ + + case WACOM_BEE: + __set_bit(BTN_8, input_dev->keybit); + __set_bit(BTN_9, input_dev->keybit); + /* fall through */ + + case CINTIQ: + for (i = 0; i < 8; i++) + __set_bit(BTN_0 + i, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0); + input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0); + break; + + case WACOM_13HD: + for (i = 0; i < 9; i++) + __set_bit(BTN_0 + i, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); + break; + + case INTUOS3: + case INTUOS3L: + __set_bit(BTN_4, input_dev->keybit); + __set_bit(BTN_5, input_dev->keybit); + __set_bit(BTN_6, input_dev->keybit); + __set_bit(BTN_7, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0); + /* fall through */ + + case INTUOS3S: + __set_bit(BTN_0, input_dev->keybit); + __set_bit(BTN_1, input_dev->keybit); + __set_bit(BTN_2, input_dev->keybit); + __set_bit(BTN_3, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0); + break; + + case INTUOS5: + case INTUOS5L: + case INTUOSPM: + case INTUOSPL: + __set_bit(BTN_7, input_dev->keybit); + __set_bit(BTN_8, input_dev->keybit); + /* fall through */ + + case INTUOS5S: + case INTUOSPS: + /* touch interface does not have the pad device */ + if (features->device_type != BTN_TOOL_PEN) + return 1; + + for (i = 0; i < 7; i++) + __set_bit(BTN_0 + i, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); + break; + + case INTUOS4WL: + /* + * For Bluetooth devices, the udev rule does not work correctly + * for pads unless we add a stylus capability, which forces + * ID_INPUT_TABLET to be set. + */ + __set_bit(BTN_STYLUS, input_dev->keybit); + /* fall through */ + + case INTUOS4: + case INTUOS4L: + __set_bit(BTN_7, input_dev->keybit); + __set_bit(BTN_8, input_dev->keybit); + /* fall through */ + + case INTUOS4S: + for (i = 0; i < 7; i++) + __set_bit(BTN_0 + i, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); + break; + + case CINTIQ_HYBRID: + for (i = 0; i < 9; i++) + __set_bit(BTN_0 + i, input_dev->keybit); + + break; + + case DTUS: + for (i = 0; i < 4; i++) + __set_bit(BTN_0 + i, input_dev->keybit); + break; + + case INTUOSHT: + case BAMBOO_PT: + /* pad device is on the touch interface */ + if (features->device_type != BTN_TOOL_FINGER) + return 1; + + __clear_bit(ABS_MISC, input_dev->absbit); + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_FORWARD, input_dev->keybit); + __set_bit(BTN_BACK, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + + break; + + default: + /* no pad supported */ + return 1; + } + return 0; +} + +static const struct wacom_features wacom_features_0x00 = + { "Wacom Penpartner", 5040, 3780, 255, 0, + PENPARTNER, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES }; +static const struct wacom_features wacom_features_0x10 = + { "Wacom Graphire", 10206, 7422, 511, 63, + GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x81 = + { "Wacom Graphire BT", 16704, 12064, 511, 32, + GRAPHIRE_BT, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x11 = + { "Wacom Graphire2 4x5", 10206, 7422, 511, 63, + GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x12 = + { "Wacom Graphire2 5x7", 13918, 10206, 511, 63, + GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x13 = + { "Wacom Graphire3", 10208, 7424, 511, 63, + GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x14 = + { "Wacom Graphire3 6x8", 16704, 12064, 511, 63, + GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x15 = + { "Wacom Graphire4 4x5", 10208, 7424, 511, 63, + WACOM_G4, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x16 = + { "Wacom Graphire4 6x8", 16704, 12064, 511, 63, + WACOM_G4, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x17 = + { "Wacom BambooFun 4x5", 14760, 9225, 511, 63, + WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x18 = + { "Wacom BambooFun 6x8", 21648, 13530, 511, 63, + WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x19 = + { "Wacom Bamboo1 Medium", 16704, 12064, 511, 63, + GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; +static const struct wacom_features wacom_features_0x60 = + { "Wacom Volito", 5104, 3712, 511, 63, + GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; +static const struct wacom_features wacom_features_0x61 = + { "Wacom PenStation2", 3250, 2320, 255, 63, + GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; +static const struct wacom_features wacom_features_0x62 = + { "Wacom Volito2 4x5", 5104, 3712, 511, 63, + GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; +static const struct wacom_features wacom_features_0x63 = + { "Wacom Volito2 2x3", 3248, 2320, 511, 63, + GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; +static const struct wacom_features wacom_features_0x64 = + { "Wacom PenPartner2", 3250, 2320, 511, 63, + GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; +static const struct wacom_features wacom_features_0x65 = + { "Wacom Bamboo", 14760, 9225, 511, 63, + WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x69 = + { "Wacom Bamboo1", 5104, 3712, 511, 63, + GRAPHIRE, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES }; +static const struct wacom_features wacom_features_0x6A = + { "Wacom Bamboo1 4x6", 14760, 9225, 1023, 63, + GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x6B = + { "Wacom Bamboo1 5x8", 21648, 13530, 1023, 63, + GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x20 = + { "Wacom Intuos 4x5", 12700, 10600, 1023, 31, + INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x21 = + { "Wacom Intuos 6x8", 20320, 16240, 1023, 31, + INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x22 = + { "Wacom Intuos 9x12", 30480, 24060, 1023, 31, + INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x23 = + { "Wacom Intuos 12x12", 30480, 31680, 1023, 31, + INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x24 = + { "Wacom Intuos 12x18", 45720, 31680, 1023, 31, + INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x30 = + { "Wacom PL400", 5408, 4056, 255, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x31 = + { "Wacom PL500", 6144, 4608, 255, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x32 = + { "Wacom PL600", 6126, 4604, 255, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x33 = + { "Wacom PL600SX", 6260, 5016, 255, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x34 = + { "Wacom PL550", 6144, 4608, 511, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x35 = + { "Wacom PL800", 7220, 5780, 511, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x37 = + { "Wacom PL700", 6758, 5406, 511, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x38 = + { "Wacom PL510", 6282, 4762, 511, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x39 = + { "Wacom DTU710", 34080, 27660, 511, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0xC4 = + { "Wacom DTF521", 6282, 4762, 511, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0xC0 = + { "Wacom DTF720", 6858, 5506, 511, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0xC2 = + { "Wacom DTF720a", 6858, 5506, 511, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x03 = + { "Wacom Cintiq Partner", 20480, 15360, 511, 0, + PTU, WACOM_PL_RES, WACOM_PL_RES }; +static const struct wacom_features wacom_features_0x41 = + { "Wacom Intuos2 4x5", 12700, 10600, 1023, 31, + INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x42 = + { "Wacom Intuos2 6x8", 20320, 16240, 1023, 31, + INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x43 = + { "Wacom Intuos2 9x12", 30480, 24060, 1023, 31, + INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x44 = + { "Wacom Intuos2 12x12", 30480, 31680, 1023, 31, + INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x45 = + { "Wacom Intuos2 12x18", 45720, 31680, 1023, 31, + INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xB0 = + { "Wacom Intuos3 4x5", 25400, 20320, 1023, 63, + INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xB1 = + { "Wacom Intuos3 6x8", 40640, 30480, 1023, 63, + INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xB2 = + { "Wacom Intuos3 9x12", 60960, 45720, 1023, 63, + INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xB3 = + { "Wacom Intuos3 12x12", 60960, 60960, 1023, 63, + INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xB4 = + { "Wacom Intuos3 12x19", 97536, 60960, 1023, 63, + INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xB5 = + { "Wacom Intuos3 6x11", 54204, 31750, 1023, 63, + INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xB7 = + { "Wacom Intuos3 4x6", 31496, 19685, 1023, 63, + INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xB8 = + { "Wacom Intuos4 4x6", 31496, 19685, 2047, 63, + INTUOS4S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xB9 = + { "Wacom Intuos4 6x9", 44704, 27940, 2047, 63, + INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xBA = + { "Wacom Intuos4 8x13", 65024, 40640, 2047, 63, + INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xBB = + { "Wacom Intuos4 12x19", 97536, 60960, 2047, 63, + INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xBC = + { "Wacom Intuos4 WL", 40640, 25400, 2047, 63, + INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xBD = + { "Wacom Intuos4 WL", 40640, 25400, 2047, 63, + INTUOS4WL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0x26 = + { "Wacom Intuos5 touch S", 31496, 19685, 2047, 63, + INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16 }; +static const struct wacom_features wacom_features_0x27 = + { "Wacom Intuos5 touch M", 44704, 27940, 2047, 63, + INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16 }; +static const struct wacom_features wacom_features_0x28 = + { "Wacom Intuos5 touch L", 65024, 40640, 2047, 63, + INTUOS5L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16 }; +static const struct wacom_features wacom_features_0x29 = + { "Wacom Intuos5 S", 31496, 19685, 2047, 63, + INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0x2A = + { "Wacom Intuos5 M", 44704, 27940, 2047, 63, + INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0x314 = + { "Wacom Intuos Pro S", 31496, 19685, 2047, 63, + INTUOSPS, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0x315 = + { "Wacom Intuos Pro M", 44704, 27940, 2047, 63, + INTUOSPM, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0x317 = + { "Wacom Intuos Pro L", 65024, 40640, 2047, 63, + INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0xF4 = + { "Wacom Cintiq 24HD", 104280, 65400, 2047, 63, + WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; +static const struct wacom_features wacom_features_0xF8 = + { "Wacom Cintiq 24HD touch", 104280, 65400, 2047, 63, /* Pen */ + WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf6 }; +static const struct wacom_features wacom_features_0xF6 = + { "Wacom Cintiq 24HD touch", .type = WACOM_24HDT, /* Touch */ + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf8, .touch_max = 10, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0x3F = + { "Wacom Cintiq 21UX", 87200, 65600, 1023, 63, + CINTIQ, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xC5 = + { "Wacom Cintiq 20WSX", 86680, 54180, 1023, 63, + WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0xC6 = + { "Wacom Cintiq 12WX", 53020, 33440, 1023, 63, + WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0x304 = + { "Wacom Cintiq 13HD", 59352, 33648, 1023, 63, + WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; +static const struct wacom_features wacom_features_0xC7 = + { "Wacom DTU1931", 37832, 30305, 511, 0, + PL, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xCE = + { "Wacom DTU2231", 47864, 27011, 511, 0, + DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBMOUSE }; +static const struct wacom_features wacom_features_0xF0 = + { "Wacom DTU1631", 34623, 19553, 511, 0, + DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xFB = + { "Wacom DTU1031", 22096, 13960, 511, 0, + DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x57 = + { "Wacom DTK2241", 95640, 54060, 2047, 63, + DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; +static const struct wacom_features wacom_features_0x59 = /* Pen */ + { "Wacom DTH2242", 95640, 54060, 2047, 63, + DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5D }; +static const struct wacom_features wacom_features_0x5D = /* Touch */ + { "Wacom DTH2242", .type = WACOM_24HDT, + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x59, .touch_max = 10, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0xCC = + { "Wacom Cintiq 21UX2", 87000, 65400, 2047, 63, + WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; +static const struct wacom_features wacom_features_0xFA = + { "Wacom Cintiq 22HD", 95640, 54060, 2047, 63, + WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; +static const struct wacom_features wacom_features_0x5B = + { "Wacom Cintiq 22HDT", 95640, 54060, 2047, 63, + WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5e }; +static const struct wacom_features wacom_features_0x5E = + { "Wacom Cintiq 22HDT", .type = WACOM_24HDT, + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5b, .touch_max = 10, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0x90 = + { "Wacom ISDv4 90", 26202, 16325, 255, 0, + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x93 = + { "Wacom ISDv4 93", 26202, 16325, 255, 0, + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x97 = + { "Wacom ISDv4 97", 26202, 16325, 511, 0, + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x9A = + { "Wacom ISDv4 9A", 26202, 16325, 255, 0, + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x9F = + { "Wacom ISDv4 9F", 26202, 16325, 255, 0, + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xE2 = + { "Wacom ISDv4 E2", 26202, 16325, 255, 0, + TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; +static const struct wacom_features wacom_features_0xE3 = + { "Wacom ISDv4 E3", 26202, 16325, 255, 0, + TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; +static const struct wacom_features wacom_features_0xE5 = + { "Wacom ISDv4 E5", 26202, 16325, 255, 0, + MTSCREEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xE6 = + { "Wacom ISDv4 E6", 27760, 15694, 255, 0, + TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; +static const struct wacom_features wacom_features_0xEC = + { "Wacom ISDv4 EC", 25710, 14500, 255, 0, + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xED = + { "Wacom ISDv4 ED", 26202, 16325, 255, 0, + TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xEF = + { "Wacom ISDv4 EF", 26202, 16325, 255, 0, + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x100 = + { "Wacom ISDv4 100", 26202, 16325, 255, 0, + MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x101 = + { "Wacom ISDv4 101", 26202, 16325, 255, 0, + MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x10D = + { "Wacom ISDv4 10D", 26202, 16325, 255, 0, + MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x10E = + { "Wacom ISDv4 10E", 27760, 15694, 255, 0, + MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x10F = + { "Wacom ISDv4 10F", 27760, 15694, 255, 0, + MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x116 = + { "Wacom ISDv4 116", 26202, 16325, 255, 0, + TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x12C = + { "Wacom ISDv4 12C", 27848, 15752, 2047, 0, + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x4001 = + { "Wacom ISDv4 4001", 26202, 16325, 255, 0, + MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x4004 = + { "Wacom ISDv4 4004", 11060, 6220, 255, 0, + MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x5000 = + { "Wacom ISDv4 5000", 27848, 15752, 1023, 0, + MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x5002 = + { "Wacom ISDv4 5002", 29576, 16724, 1023, 0, + MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x47 = + { "Wacom Intuos2 6x8", 20320, 16240, 1023, 31, + INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x84 = + { "Wacom Wireless Receiver", 0, 0, 0, 0, + WIRELESS, 0, 0, .touch_max = 16 }; +static const struct wacom_features wacom_features_0xD0 = + { "Wacom Bamboo 2FG", 14720, 9200, 1023, 31, + BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; +static const struct wacom_features wacom_features_0xD1 = + { "Wacom Bamboo 2FG 4x5", 14720, 9200, 1023, 31, + BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; +static const struct wacom_features wacom_features_0xD2 = + { "Wacom Bamboo Craft", 14720, 9200, 1023, 31, + BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; +static const struct wacom_features wacom_features_0xD3 = + { "Wacom Bamboo 2FG 6x8", 21648, 13700, 1023, 31, + BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; +static const struct wacom_features wacom_features_0xD4 = + { "Wacom Bamboo Pen", 14720, 9200, 1023, 31, + BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD5 = + { "Wacom Bamboo Pen 6x8", 21648, 13700, 1023, 31, + BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD6 = + { "Wacom BambooPT 2FG 4x5", 14720, 9200, 1023, 31, + BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; +static const struct wacom_features wacom_features_0xD7 = + { "Wacom BambooPT 2FG Small", 14720, 9200, 1023, 31, + BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; +static const struct wacom_features wacom_features_0xD8 = + { "Wacom Bamboo Comic 2FG", 21648, 13700, 1023, 31, + BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; +static const struct wacom_features wacom_features_0xDA = + { "Wacom Bamboo 2FG 4x5 SE", 14720, 9200, 1023, 31, + BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; +static const struct wacom_features wacom_features_0xDB = + { "Wacom Bamboo 2FG 6x8 SE", 21648, 13700, 1023, 31, + BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 }; +static const struct wacom_features wacom_features_0xDD = + { "Wacom Bamboo Connect", 14720, 9200, 1023, 31, + BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xDE = + { "Wacom Bamboo 16FG 4x5", 14720, 9200, 1023, 31, + BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16 }; +static const struct wacom_features wacom_features_0xDF = + { "Wacom Bamboo 16FG 6x8", 21648, 13700, 1023, 31, + BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16 }; +static const struct wacom_features wacom_features_0x300 = + { "Wacom Bamboo One S", 14720, 9225, 1023, 31, + BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x301 = + { "Wacom Bamboo One M", 21648, 13530, 1023, 31, + BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x302 = + { "Wacom Intuos PT S", 15200, 9500, 1023, 31, + INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0x303 = + { "Wacom Intuos PT M", 21600, 13500, 1023, 31, + INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0x30E = + { "Wacom Intuos S", 15200, 9500, 1023, 31, + INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0x6004 = + { "ISD-V4", 12800, 8000, 255, 0, + TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x307 = + { "Wacom ISDv5 307", 59352, 33648, 2047, 63, + CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x309 }; +static const struct wacom_features wacom_features_0x309 = + { "Wacom ISDv5 309", .type = WACOM_24HDT, /* Touch */ + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x0307, .touch_max = 10, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0x30A = + { "Wacom ISDv5 30A", 59352, 33648, 2047, 63, + CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30C }; +static const struct wacom_features wacom_features_0x30C = + { "Wacom ISDv5 30C", .type = WACOM_24HDT, /* Touch */ + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30A, .touch_max = 10, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; + +static const struct wacom_features wacom_features_HID_ANY_ID = + { "Wacom HID", .type = HID_GENERIC }; + +#define USB_DEVICE_WACOM(prod) \ + HID_DEVICE(BUS_USB, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\ + .driver_data = (kernel_ulong_t)&wacom_features_##prod + +#define BT_DEVICE_WACOM(prod) \ + HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\ + .driver_data = (kernel_ulong_t)&wacom_features_##prod + +#define USB_DEVICE_LENOVO(prod) \ + HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, prod), \ + .driver_data = (kernel_ulong_t)&wacom_features_##prod + +const struct hid_device_id wacom_ids[] = { + { USB_DEVICE_WACOM(0x00) }, + { USB_DEVICE_WACOM(0x03) }, + { USB_DEVICE_WACOM(0x10) }, + { USB_DEVICE_WACOM(0x11) }, + { USB_DEVICE_WACOM(0x12) }, + { USB_DEVICE_WACOM(0x13) }, + { USB_DEVICE_WACOM(0x14) }, + { USB_DEVICE_WACOM(0x15) }, + { USB_DEVICE_WACOM(0x16) }, + { USB_DEVICE_WACOM(0x17) }, + { USB_DEVICE_WACOM(0x18) }, + { USB_DEVICE_WACOM(0x19) }, + { USB_DEVICE_WACOM(0x20) }, + { USB_DEVICE_WACOM(0x21) }, + { USB_DEVICE_WACOM(0x22) }, + { USB_DEVICE_WACOM(0x23) }, + { USB_DEVICE_WACOM(0x24) }, + { USB_DEVICE_WACOM(0x26) }, + { USB_DEVICE_WACOM(0x27) }, + { USB_DEVICE_WACOM(0x28) }, + { USB_DEVICE_WACOM(0x29) }, + { USB_DEVICE_WACOM(0x2A) }, + { USB_DEVICE_WACOM(0x30) }, + { USB_DEVICE_WACOM(0x31) }, + { USB_DEVICE_WACOM(0x32) }, + { USB_DEVICE_WACOM(0x33) }, + { USB_DEVICE_WACOM(0x34) }, + { USB_DEVICE_WACOM(0x35) }, + { USB_DEVICE_WACOM(0x37) }, + { USB_DEVICE_WACOM(0x38) }, + { USB_DEVICE_WACOM(0x39) }, + { USB_DEVICE_WACOM(0x3F) }, + { USB_DEVICE_WACOM(0x41) }, + { USB_DEVICE_WACOM(0x42) }, + { USB_DEVICE_WACOM(0x43) }, + { USB_DEVICE_WACOM(0x44) }, + { USB_DEVICE_WACOM(0x45) }, + { USB_DEVICE_WACOM(0x47) }, + { USB_DEVICE_WACOM(0x57) }, + { USB_DEVICE_WACOM(0x59) }, + { USB_DEVICE_WACOM(0x5B) }, + { USB_DEVICE_WACOM(0x5D) }, + { USB_DEVICE_WACOM(0x5E) }, + { USB_DEVICE_WACOM(0x60) }, + { USB_DEVICE_WACOM(0x61) }, + { USB_DEVICE_WACOM(0x62) }, + { USB_DEVICE_WACOM(0x63) }, + { USB_DEVICE_WACOM(0x64) }, + { USB_DEVICE_WACOM(0x65) }, + { USB_DEVICE_WACOM(0x69) }, + { USB_DEVICE_WACOM(0x6A) }, + { USB_DEVICE_WACOM(0x6B) }, + { BT_DEVICE_WACOM(0x81) }, + { USB_DEVICE_WACOM(0x84) }, + { USB_DEVICE_WACOM(0x90) }, + { USB_DEVICE_WACOM(0x93) }, + { USB_DEVICE_WACOM(0x97) }, + { USB_DEVICE_WACOM(0x9A) }, + { USB_DEVICE_WACOM(0x9F) }, + { USB_DEVICE_WACOM(0xB0) }, + { USB_DEVICE_WACOM(0xB1) }, + { USB_DEVICE_WACOM(0xB2) }, + { USB_DEVICE_WACOM(0xB3) }, + { USB_DEVICE_WACOM(0xB4) }, + { USB_DEVICE_WACOM(0xB5) }, + { USB_DEVICE_WACOM(0xB7) }, + { USB_DEVICE_WACOM(0xB8) }, + { USB_DEVICE_WACOM(0xB9) }, + { USB_DEVICE_WACOM(0xBA) }, + { USB_DEVICE_WACOM(0xBB) }, + { USB_DEVICE_WACOM(0xBC) }, + { BT_DEVICE_WACOM(0xBD) }, + { USB_DEVICE_WACOM(0xC0) }, + { USB_DEVICE_WACOM(0xC2) }, + { USB_DEVICE_WACOM(0xC4) }, + { USB_DEVICE_WACOM(0xC5) }, + { USB_DEVICE_WACOM(0xC6) }, + { USB_DEVICE_WACOM(0xC7) }, + { USB_DEVICE_WACOM(0xCC) }, + { USB_DEVICE_WACOM(0xCE) }, + { USB_DEVICE_WACOM(0xD0) }, + { USB_DEVICE_WACOM(0xD1) }, + { USB_DEVICE_WACOM(0xD2) }, + { USB_DEVICE_WACOM(0xD3) }, + { USB_DEVICE_WACOM(0xD4) }, + { USB_DEVICE_WACOM(0xD5) }, + { USB_DEVICE_WACOM(0xD6) }, + { USB_DEVICE_WACOM(0xD7) }, + { USB_DEVICE_WACOM(0xD8) }, + { USB_DEVICE_WACOM(0xDA) }, + { USB_DEVICE_WACOM(0xDB) }, + { USB_DEVICE_WACOM(0xDD) }, + { USB_DEVICE_WACOM(0xDE) }, + { USB_DEVICE_WACOM(0xDF) }, + { USB_DEVICE_WACOM(0xE2) }, + { USB_DEVICE_WACOM(0xE3) }, + { USB_DEVICE_WACOM(0xE5) }, + { USB_DEVICE_WACOM(0xE6) }, + { USB_DEVICE_WACOM(0xEC) }, + { USB_DEVICE_WACOM(0xED) }, + { USB_DEVICE_WACOM(0xEF) }, + { USB_DEVICE_WACOM(0xF0) }, + { USB_DEVICE_WACOM(0xF4) }, + { USB_DEVICE_WACOM(0xF6) }, + { USB_DEVICE_WACOM(0xF8) }, + { USB_DEVICE_WACOM(0xFA) }, + { USB_DEVICE_WACOM(0xFB) }, + { USB_DEVICE_WACOM(0x100) }, + { USB_DEVICE_WACOM(0x101) }, + { USB_DEVICE_WACOM(0x10D) }, + { USB_DEVICE_WACOM(0x10E) }, + { USB_DEVICE_WACOM(0x10F) }, + { USB_DEVICE_WACOM(0x116) }, + { USB_DEVICE_WACOM(0x12C) }, + { USB_DEVICE_WACOM(0x300) }, + { USB_DEVICE_WACOM(0x301) }, + { USB_DEVICE_WACOM(0x302) }, + { USB_DEVICE_WACOM(0x303) }, + { USB_DEVICE_WACOM(0x304) }, + { USB_DEVICE_WACOM(0x307) }, + { USB_DEVICE_WACOM(0x309) }, + { USB_DEVICE_WACOM(0x30A) }, + { USB_DEVICE_WACOM(0x30C) }, + { USB_DEVICE_WACOM(0x30E) }, + { USB_DEVICE_WACOM(0x314) }, + { USB_DEVICE_WACOM(0x315) }, + { USB_DEVICE_WACOM(0x317) }, + { USB_DEVICE_WACOM(0x4001) }, + { USB_DEVICE_WACOM(0x4004) }, + { USB_DEVICE_WACOM(0x5000) }, + { USB_DEVICE_WACOM(0x5002) }, + + { USB_DEVICE_WACOM(HID_ANY_ID) }, + { } +}; +MODULE_DEVICE_TABLE(hid, wacom_ids); diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h new file mode 100644 index 000000000000..0f0b85ec1322 --- /dev/null +++ b/drivers/hid/wacom_wac.h @@ -0,0 +1,196 @@ +/* + * drivers/input/tablet/wacom_wac.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef WACOM_WAC_H +#define WACOM_WAC_H + +#include <linux/types.h> + +/* maximum packet length for USB devices */ +#define WACOM_PKGLEN_MAX 68 + +#define WACOM_NAME_MAX 64 + +/* packet length for individual models */ +#define WACOM_PKGLEN_PENPRTN 7 +#define WACOM_PKGLEN_GRAPHIRE 8 +#define WACOM_PKGLEN_BBFUN 9 +#define WACOM_PKGLEN_INTUOS 10 +#define WACOM_PKGLEN_TPC1FG 5 +#define WACOM_PKGLEN_TPC1FG_B 10 +#define WACOM_PKGLEN_TPC2FG 14 +#define WACOM_PKGLEN_BBTOUCH 20 +#define WACOM_PKGLEN_BBTOUCH3 64 +#define WACOM_PKGLEN_BBPEN 10 +#define WACOM_PKGLEN_WIRELESS 32 +#define WACOM_PKGLEN_MTOUCH 62 +#define WACOM_PKGLEN_MTTPC 40 +#define WACOM_PKGLEN_DTUS 68 +#define WACOM_PKGLEN_PENABLED 8 + +/* wacom data size per MT contact */ +#define WACOM_BYTES_PER_MT_PACKET 11 +#define WACOM_BYTES_PER_24HDT_PACKET 14 + +/* device IDs */ +#define STYLUS_DEVICE_ID 0x02 +#define TOUCH_DEVICE_ID 0x03 +#define CURSOR_DEVICE_ID 0x06 +#define ERASER_DEVICE_ID 0x0A +#define PAD_DEVICE_ID 0x0F + +/* wacom data packet report IDs */ +#define WACOM_REPORT_PENABLED 2 +#define WACOM_REPORT_PENABLED_BT 3 +#define WACOM_REPORT_INTUOSREAD 5 +#define WACOM_REPORT_INTUOSWRITE 6 +#define WACOM_REPORT_INTUOSPAD 12 +#define WACOM_REPORT_INTUOS5PAD 3 +#define WACOM_REPORT_DTUSPAD 21 +#define WACOM_REPORT_TPC1FG 6 +#define WACOM_REPORT_TPC2FG 13 +#define WACOM_REPORT_TPCMT 13 +#define WACOM_REPORT_TPCMT2 3 +#define WACOM_REPORT_TPCHID 15 +#define WACOM_REPORT_TPCST 16 +#define WACOM_REPORT_DTUS 17 +#define WACOM_REPORT_TPC1FGE 18 +#define WACOM_REPORT_24HDT 1 +#define WACOM_REPORT_WL 128 +#define WACOM_REPORT_USB 192 + +/* device quirks */ +#define WACOM_QUIRK_MULTI_INPUT 0x0001 +#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002 +#define WACOM_QUIRK_NO_INPUT 0x0004 +#define WACOM_QUIRK_MONITOR 0x0008 +#define WACOM_QUIRK_BATTERY 0x0010 + +enum { + PENPARTNER = 0, + GRAPHIRE, + GRAPHIRE_BT, + WACOM_G4, + PTU, + PL, + DTU, + DTUS, + INTUOS, + INTUOS3S, + INTUOS3, + INTUOS3L, + INTUOS4S, + INTUOS4, + INTUOS4WL, + INTUOS4L, + INTUOS5S, + INTUOS5, + INTUOS5L, + INTUOSPS, + INTUOSPM, + INTUOSPL, + INTUOSHT, + WACOM_21UX2, + WACOM_22HD, + DTK, + WACOM_24HD, + CINTIQ_HYBRID, + CINTIQ, + WACOM_BEE, + WACOM_13HD, + WACOM_MO, + WIRELESS, + BAMBOO_PT, + WACOM_24HDT, + TABLETPC, /* add new TPC below */ + TABLETPCE, + TABLETPC2FG, + MTSCREEN, + MTTPC, + MTTPC_B, + HID_GENERIC, + MAX_TYPE +}; + +struct wacom_features { + const char *name; + int x_max; + int y_max; + int pressure_max; + int distance_max; + int type; + int x_resolution; + int y_resolution; + int x_min; + int y_min; + int device_type; + int x_phy; + int y_phy; + unsigned unit; + int unitExpo; + int x_fuzz; + int y_fuzz; + int pressure_fuzz; + int distance_fuzz; + unsigned quirks; + unsigned touch_max; + int oVid; + int oPid; + int pktlen; + bool check_for_hid_type; + int hid_type; +}; + +struct wacom_shared { + bool stylus_in_proximity; + bool touch_down; + /* for wireless device to access USB interfaces */ + unsigned touch_max; + int type; + struct input_dev *touch_input; +}; + +struct hid_data { + __s16 inputmode; /* InputMode HID feature, -1 if non-existent */ + __s16 inputmode_index; /* InputMode HID feature index in the report */ + bool inrange_state; + bool invert_state; + bool tipswitch; + int x; + int y; + int pressure; + int width; + int height; + int id; +}; + +struct wacom_wac { + char name[WACOM_NAME_MAX]; + char pad_name[WACOM_NAME_MAX]; + char bat_name[WACOM_NAME_MAX]; + char ac_name[WACOM_NAME_MAX]; + unsigned char data[WACOM_PKGLEN_MAX]; + int tool[2]; + int id[2]; + __u32 serial[2]; + struct wacom_features features; + struct wacom_shared *shared; + struct input_dev *input; + struct input_dev *pad_input; + bool input_registered; + int pid; + int battery_capacity; + int num_contacts_left; + int bat_charging; + int ps_connected; + u8 bt_features; + u8 bt_high_speed; + struct hid_data hid_data; +}; + +#endif |