diff options
48 files changed, 4515 insertions, 2885 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-wacom b/Documentation/ABI/testing/sysfs-driver-wacom index 7fc7810..c4f0fed 100644 --- a/Documentation/ABI/testing/sysfs-driver-wacom +++ b/Documentation/ABI/testing/sysfs-driver-wacom @@ -1,48 +1,27 @@ -WWhat: /sys/class/hidraw/hidraw*/device/oled*_img -Date: June 2012 -Contact: linux-bluetooth@vger.kernel.org -Description: - The /sys/class/hidraw/hidraw*/device/oled*_img files control - OLED mocro displays on Intuos4 Wireless tablet. Accepted image - has to contain 256 bytes (64x32 px 1 bit colour). The format - is the same as PBM image 62x32px without header (64 bits per - horizontal line, 32 lines). An example of setting OLED No. 0: - dd bs=256 count=1 if=img_file of=[path to oled0_img]/oled0_img - The attribute is read only and no local copy of the image is - stored. - -What: /sys/class/hidraw/hidraw*/device/speed +What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/speed Date: April 2010 Kernel Version: 2.6.35 Contact: linux-bluetooth@vger.kernel.org Description: - The /sys/class/hidraw/hidraw*/device/speed file controls - reporting speed of Wacom bluetooth tablet. Reading from - this file returns 1 if tablet reports in high speed mode + The /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/speed file + controls reporting speed of Wacom bluetooth tablet. Reading + from this file returns 1 if tablet reports in high speed mode or 0 otherwise. Writing to this file one of these values switches reporting speed. -What: /sys/class/leds/0005\:056A\:00BD.0001\:selector\:*/ -Date: May 2012 -Kernel Version: 3.5 -Contact: linux-bluetooth@vger.kernel.org -Description: - LED selector for Intuos4 WL. There are 4 leds, but only one LED - can be lit at a time. Max brightness is 127. - -What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/led -Date: August 2011 +What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/led +Date: August 2014 Contact: linux-input@vger.kernel.org Description: Attribute group for control of the status LEDs and the OLEDs. This attribute group is only available for Intuos 4 M, L, - and XL (with LEDs and OLEDs), Intuos 5 (LEDs only), and Cintiq - 21UX2 and Cintiq 24HD (LEDs only). Therefore its presence - implicitly signifies the presence of said LEDs and OLEDs on the - tablet device. + and XL (with LEDs and OLEDs), Intuos 4 WL, Intuos 5 (LEDs only), + Intuos Pro (LEDs only) and Cintiq 21UX2 and Cintiq 24HD + (LEDs only). Therefore its presence implicitly signifies the + presence of said LEDs and OLEDs on the tablet device. -What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/status0_luminance -Date: August 2011 +What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status0_luminance +Date: August 2014 Contact: linux-input@vger.kernel.org Description: Writing to this file sets the status LED luminance (1..127) @@ -50,16 +29,16 @@ Description: button is pressed on the stylus. This luminance level is normally lower than the level when a button is pressed. -What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/status1_luminance -Date: August 2011 +What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status1_luminance +Date: August 2014 Contact: linux-input@vger.kernel.org Description: Writing to this file sets the status LED luminance (1..127) when the stylus touches the tablet surface, or any button is pressed on the stylus. -What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/status_led0_select -Date: August 2011 +What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status_led0_select +Date: August 2014 Contact: linux-input@vger.kernel.org Description: Writing to this file sets which one of the four (for Intuos 4 @@ -67,23 +46,23 @@ Description: 24HD) status LEDs is active (0..3). The other three LEDs on the same side are always inactive. -What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/status_led1_select -Date: September 2011 +What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status_led1_select +Date: August 2014 Contact: linux-input@vger.kernel.org Description: Writing to this file sets which one of the left four (for Cintiq 21UX2 and Cintiq 24HD) status LEDs is active (0..3). The other three LEDs on the left are always inactive. -What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/buttons_luminance -Date: August 2011 +What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/buttons_luminance +Date: August 2014 Contact: linux-input@vger.kernel.org Description: Writing to this file sets the overall luminance level (0..15) of all eight button OLED displays. -What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/button<n>_rawimg -Date: August 2011 +What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/button<n>_rawimg +Date: August 2014 Contact: linux-input@vger.kernel.org Description: When writing a 1024 byte raw image in Wacom Intuos 4 @@ -93,3 +72,8 @@ Description: byte chunk encodes the image data for two consecutive lines on the display. The low nibble of each byte contains the first line, and the high nibble contains the second line. + When the Wacom Intuos 4 is connected over Bluetooth, the + image has to contain 256 bytes (64x32 px 1 bit colour). + The format is also scrambled, like in the USB mode, and it can + be summarized by converting 76543210 into GECA6420. + HGFEDCBA HFDB7531 diff --git a/Documentation/devicetree/bindings/input/atmel,maxtouch.txt b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt new file mode 100644 index 0000000..baef432 --- /dev/null +++ b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt @@ -0,0 +1,25 @@ +Atmel maXTouch touchscreen/touchpad + +Required properties: +- compatible: + atmel,maxtouch + +- reg: The I2C address of the device + +- interrupts: The sink for the touchpad's IRQ output + See ../interrupt-controller/interrupts.txt + +Optional properties for main touchpad device: + +- linux,gpio-keymap: An array of up to 4 entries indicating the Linux + keycode generated by each GPIO. Linux keycodes are defined in + <dt-bindings/input/input.h>. + +Example: + + touch@4b { + compatible = "atmel,maxtouch"; + reg = <0x4b>; + interrupt-parent = <&gpio>; + interrupts = <TEGRA_GPIO(W, 3) IRQ_TYPE_LEVEL_LOW>; + }; diff --git a/Documentation/devicetree/bindings/input/cap1106.txt b/Documentation/devicetree/bindings/input/cap1106.txt new file mode 100644 index 0000000..4b46390 --- /dev/null +++ b/Documentation/devicetree/bindings/input/cap1106.txt @@ -0,0 +1,53 @@ +Device tree bindings for Microchip CAP1106, 6 channel capacitive touch sensor + +The node for this driver must be a child of a I2C controller node, as the +device communication via I2C only. + +Required properties: + + compatible: Must be "microchip,cap1106" + + reg: The I2C slave address of the device. + Only 0x28 is valid. + + interrupts: Property describing the interrupt line the + device's ALERT#/CM_IRQ# pin is connected to. + The device only has one interrupt source. + +Optional properties: + + autorepeat: Enables the Linux input system's autorepeat + feature on the input device. + + microchip,sensor-gain: Defines the gain of the sensor circuitry. This + effectively controls the sensitivity, as a + smaller delta capacitance is required to + generate the same delta count values. + Valid values are 1, 2, 4, and 8. + By default, a gain of 1 is set. + + linux,keycodes: Specifies an array of numeric keycode values to + be used for the channels. If this property is + omitted, KEY_A, KEY_B, etc are used as + defaults. The array must have exactly six + entries. + +Example: + +i2c_controller { + cap1106@28 { + compatible = "microchip,cap1106"; + interrupt-parent = <&gpio1>; + interrupts = <0 0>; + reg = <0x28>; + autorepeat; + microchip,sensor-gain = <2>; + + linux,keycodes = <103 /* KEY_UP */ + 106 /* KEY_RIGHT */ + 108 /* KEY_DOWN */ + 105 /* KEY_LEFT */ + 109 /* KEY_PAGEDOWN */ + 104>; /* KEY_PAGEUP */ + }; +} diff --git a/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt b/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt new file mode 100644 index 0000000..6e55109 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt @@ -0,0 +1,26 @@ +* Pixcir I2C touchscreen controllers + +Required properties: +- compatible: must be "pixcir,pixcir_ts" or "pixcir,pixcir_tangoc" +- reg: I2C address of the chip +- interrupts: interrupt to which the chip is connected +- attb-gpio: GPIO connected to the ATTB line of the chip +- touchscreen-size-x: horizontal resolution of touchscreen (in pixels) +- touchscreen-size-y: vertical resolution of touchscreen (in pixels) + +Example: + + i2c@00000000 { + /* ... */ + + pixcir_ts@5c { + compatible = "pixcir,pixcir_ts"; + reg = <0x5c>; + interrupts = <2 0>; + attb-gpio = <&gpf 2 0 2>; + touchscreen-size-x = <800>; + touchscreen-size-y = <600>; + }; + + /* ... */ + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/zforce_ts.txt b/Documentation/devicetree/bindings/input/touchscreen/zforce_ts.txt index 2faf1f1..80c37df 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/zforce_ts.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/zforce_ts.txt @@ -9,6 +9,9 @@ Required properties: - x-size: horizontal resolution of touchscreen - y-size: vertical resolution of touchscreen +Optional properties: +- vdd-supply: Regulator controlling the controller supply + Example: i2c@00000000 { @@ -18,6 +21,7 @@ Example: compatible = "neonode,zforce"; reg = <0x50>; interrupts = <2 0>; + vdd-supply = <®_zforce_vdd>; gpios = <&gpio5 6 0>, /* INT */ <&gpio5 9 0>; /* RST */ diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 3dc9188..ac7269f 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -103,6 +103,7 @@ panasonic Panasonic Corporation phytec PHYTEC Messtechnik GmbH picochip Picochip Ltd plathome Plat'Home Co., Ltd. +pixcir PIXCIR MICROELECTRONICS Co., Ltd powervr PowerVR (deprecated, use img) qca Qualcomm Atheros, Inc. qcom Qualcomm Technologies, Inc diff --git a/MAINTAINERS b/MAINTAINERS index 30873e7..3bb6df5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9877,6 +9877,13 @@ M: Pierre Ossman <pierre@ossman.eu> S: Maintained F: drivers/mmc/host/wbsd.* +WACOM PROTOCOL 4 SERIAL TABLETS +M: Julian Squires <julian@cipht.net> +M: Hans de Goede <hdegoede@redhat.com> +L: linux-input@vger.kernel.org +S: Maintained +F: drivers/input/tablet/wacom_serial4.c + WATCHDOG DEVICE DRIVERS M: Wim Van Sebroeck <wim@iguana.be> L: linux-watchdog@vger.kernel.org diff --git a/arch/arm/boot/dts/am437x-gp-evm.dts b/arch/arm/boot/dts/am437x-gp-evm.dts index f0422c2..646a6ea 100644 --- a/arch/arm/boot/dts/am437x-gp-evm.dts +++ b/arch/arm/boot/dts/am437x-gp-evm.dts @@ -334,8 +334,8 @@ attb-gpio = <&gpio3 22 GPIO_ACTIVE_HIGH>; - x-size = <1024>; - y-size = <600>; + touchscreen-size-x = <1024>; + touchscreen-size-y = <600>; }; }; diff --git a/arch/arm/boot/dts/am43x-epos-evm.dts b/arch/arm/boot/dts/am43x-epos-evm.dts index f1ee7495..ed7dd239 100644 --- a/arch/arm/boot/dts/am43x-epos-evm.dts +++ b/arch/arm/boot/dts/am43x-epos-evm.dts @@ -403,8 +403,8 @@ attb-gpio = <&gpio1 17 GPIO_ACTIVE_HIGH>; - x-size = <1024>; - y-size = <600>; + touchscreen-size-x = <1024>; + touchscreen-size-y = <600>; }; }; diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index ec48c82..c18d5d7 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -764,12 +764,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 a69f0ad..4dbac7f 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -116,7 +116,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 b52baa1..12b6e67 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -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; } @@ -1938,8 +1947,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) }, @@ -2345,7 +2352,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-wacom.c b/drivers/hid/hid-wacom.c deleted file mode 100644 index 902013e..0000000 --- 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/input/tablet/wacom.h b/drivers/hid/wacom.h index 9ebf0ed..64bc1b2 100644 --- a/drivers/input/tablet/wacom.h +++ b/drivers/hid/wacom.h @@ -12,6 +12,7 @@ * 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 @@ -72,6 +73,8 @@ * 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 */ /* @@ -93,35 +96,30 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.53" +#define DRIVER_VERSION "v2.00" #define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>" #define DRIVER_DESC "USB Wacom tablet driver" #define DRIVER_LICENSE "GPL" -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE(DRIVER_LICENSE); - #define USB_VENDOR_ID_WACOM 0x056a #define USB_VENDOR_ID_LENOVO 0x17ef struct wacom { - dma_addr_t data_dma; struct usb_device *usbdev; struct usb_interface *intf; - struct urb *irq; struct wacom_wac wacom_wac; + struct hid_device *hdev; struct mutex lock; struct work_struct work; - bool open; - char phys[32]; 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) @@ -130,10 +128,19 @@ static inline void wacom_schedule_work(struct wacom_wac *wacom_wac) schedule_work(&wacom->work); } -extern const struct usb_device_id wacom_ids[]; +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); #endif diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/hid/wacom_sys.c index 2c613cd..3e388ec 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -13,246 +13,106 @@ #include "wacom_wac.h" #include "wacom.h" +#include <linux/hid.h> -/* defines to get HID report descriptor */ -#define HID_DEVICET_HID (USB_TYPE_CLASS | 0x01) -#define HID_DEVICET_REPORT (USB_TYPE_CLASS | 0x02) -#define HID_USAGE_UNDEFINED 0x00 -#define HID_USAGE_PAGE 0x05 -#define HID_USAGE_PAGE_DIGITIZER 0x0d -#define HID_USAGE_PAGE_DESKTOP 0x01 -#define HID_USAGE 0x09 -#define HID_USAGE_X ((HID_USAGE_PAGE_DESKTOP << 16) | 0x30) -#define HID_USAGE_Y ((HID_USAGE_PAGE_DESKTOP << 16) | 0x31) -#define HID_USAGE_PRESSURE ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x30) -#define HID_USAGE_X_TILT ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x3d) -#define HID_USAGE_Y_TILT ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x3e) -#define HID_USAGE_FINGER ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x22) -#define HID_USAGE_STYLUS ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x20) -#define HID_USAGE_CONTACTMAX ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x55) -#define HID_COLLECTION 0xa1 -#define HID_COLLECTION_LOGICAL 0x02 -#define HID_COLLECTION_END 0xc0 - -struct hid_descriptor { - struct usb_descriptor_header header; - __le16 bcdHID; - u8 bCountryCode; - u8 bNumDescriptors; - u8 bDescriptorType; - __le16 wDescriptorLength; -} __attribute__ ((packed)); - -/* defines to get/set USB message */ -#define USB_REQ_GET_REPORT 0x01 -#define USB_REQ_SET_REPORT 0x09 - -#define WAC_HID_FEATURE_REPORT 0x03 #define WAC_MSG_RETRIES 5 #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 -static int wacom_get_report(struct usb_interface *intf, u8 type, u8 id, +static int wacom_get_report(struct hid_device *hdev, u8 type, u8 id, void *buf, size_t size, unsigned int retries) { - struct usb_device *dev = interface_to_usbdev(intf); int retval; do { - retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_REPORT, - USB_DIR_IN | USB_TYPE_CLASS | - USB_RECIP_INTERFACE, - (type << 8) + id, - intf->altsetting[0].desc.bInterfaceNumber, - buf, size, 100); + retval = hid_hw_raw_request(hdev, id, buf, size, type, + HID_REQ_GET_REPORT); } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries); return retval; } -static int wacom_set_report(struct usb_interface *intf, u8 type, u8 id, - void *buf, size_t size, unsigned int retries) +static int wacom_set_report(struct hid_device *hdev, u8 type, u8 *buf, + size_t size, unsigned int retries) { - struct usb_device *dev = interface_to_usbdev(intf); int retval; do { - retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_SET_REPORT, - USB_TYPE_CLASS | USB_RECIP_INTERFACE, - (type << 8) + id, - intf->altsetting[0].desc.bInterfaceNumber, - buf, size, 1000); + retval = hid_hw_raw_request(hdev, buf[0], buf, size, type, + HID_REQ_SET_REPORT); } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries); return retval; } -static void wacom_sys_irq(struct urb *urb) +static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *raw_data, int size) { - struct wacom *wacom = urb->context; - struct device *dev = &wacom->intf->dev; - int retval; + struct wacom *wacom = hid_get_drvdata(hdev); - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dev_dbg(dev, "%s - urb shutting down with status: %d\n", - __func__, urb->status); - return; - default: - dev_dbg(dev, "%s - nonzero urb status received: %d\n", - __func__, urb->status); - goto exit; - } + if (size > WACOM_PKGLEN_MAX) + return 1; + + memcpy(wacom->wacom_wac.data, raw_data, size); - wacom_wac_irq(&wacom->wacom_wac, urb->actual_length); + wacom_wac_irq(&wacom->wacom_wac, size); - exit: - usb_mark_last_busy(wacom->usbdev); - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(dev, "%s - usb_submit_urb failed with result %d\n", - __func__, retval); + return 0; } static int wacom_open(struct input_dev *dev) { struct wacom *wacom = input_get_drvdata(dev); - int retval = 0; - - if (usb_autopm_get_interface(wacom->intf) < 0) - return -EIO; + int retval; mutex_lock(&wacom->lock); - - if (usb_submit_urb(wacom->irq, GFP_KERNEL)) { - retval = -EIO; - goto out; - } - - wacom->open = true; - wacom->intf->needs_remote_wakeup = 1; - -out: + retval = hid_hw_open(wacom->hdev); mutex_unlock(&wacom->lock); - usb_autopm_put_interface(wacom->intf); + return retval; } static void wacom_close(struct input_dev *dev) { struct wacom *wacom = input_get_drvdata(dev); - int autopm_error; - - autopm_error = usb_autopm_get_interface(wacom->intf); mutex_lock(&wacom->lock); - usb_kill_urb(wacom->irq); - wacom->open = false; - wacom->intf->needs_remote_wakeup = 0; + hid_hw_close(wacom->hdev); mutex_unlock(&wacom->lock); - - if (!autopm_error) - usb_autopm_put_interface(wacom->intf); } /* - * Calculate the resolution of the X or Y axis, given appropriate HID data. - * This function is little more than hidinput_calc_abs_res stripped down. + * 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 char unit, unsigned char exponent) -{ - int prev, unit_exponent; - - /* Check if the extents are sane */ - if (logical_extents <= 0 || physical_extents <= 0) - return 0; - - /* Get signed value of nybble-sized twos-compliment exponent */ - unit_exponent = exponent; - if (unit_exponent > 7) - unit_exponent -= 16; - - /* Convert physical_extents to millimeters */ - if (unit == 0x11) { /* If centimeters */ - unit_exponent += 1; - } else if (unit == 0x13) { /* If inches */ - prev = physical_extents; - physical_extents *= 254; - if (physical_extents < prev) - return 0; - unit_exponent -= 1; - } else { - return 0; - } - - /* Apply negative unit exponent */ - for (; unit_exponent < 0; unit_exponent++) { - prev = logical_extents; - logical_extents *= 10; - if (logical_extents < prev) - return 0; - } - /* Apply positive unit exponent */ - for (; unit_exponent > 0; unit_exponent--) { - prev = physical_extents; - physical_extents *= 10; - if (physical_extents < prev) - return 0; - } - - /* Calculate resolution */ - return logical_extents / physical_extents; -} - -static int wacom_parse_logical_collection(unsigned char *report, - struct wacom_features *features) + unsigned unit, int exponent) { - int length = 0; - - if (features->type == BAMBOO_PT) { - - /* Logical collection is only used by 3rd gen Bamboo Touch */ - features->pktlen = WACOM_PKGLEN_BBTOUCH3; - features->device_type = BTN_TOOL_FINGER; - - features->x_max = features->y_max = - get_unaligned_le16(&report[10]); - - length = 11; - } - return length; + 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_retrieve_report_data(struct usb_interface *intf, - struct wacom_features *features) +static void wacom_feature_mapping(struct hid_device *hdev, + struct hid_field *field, struct hid_usage *usage) { - int result = 0; - unsigned char *rep_data; - - rep_data = kmalloc(2, GFP_KERNEL); - if (rep_data) { - - rep_data[0] = 12; - result = wacom_get_report(intf, WAC_HID_FEATURE_REPORT, - rep_data[0], rep_data, 2, - WAC_MSG_RETRIES); - - if (result >= 0 && rep_data[1] > 2) - features->touch_max = rep_data[1]; + struct wacom *wacom = hid_get_drvdata(hdev); + struct wacom_features *features = &wacom->wacom_wac.features; - kfree(rep_data); + switch (usage->hid) { + case HID_DG_CONTACTMAX: + /* leave touch_max as is if predefined */ + if (!features->touch_max) + features->touch_max = field->value[0]; + break; } } @@ -285,243 +145,100 @@ static void wacom_retrieve_report_data(struct usb_interface *intf, * interfaces haven't supported pressure or distance, this is enough * information to override invalid values in the wacom_features table. * - * 3rd gen Bamboo Touch no longer define a Digitizer-Finger Pysical - * Collection. Instead they define a Logical Collection with a single - * Logical Maximum for both X and Y. - * - * Intuos5 touch interface does not contain useful data. We deal with - * this after returning from this function. + * Intuos5 touch interface and 3rd gen Bamboo Touch do not contain useful + * data. We deal with them after returning from this function. */ -static int wacom_parse_hid(struct usb_interface *intf, - struct hid_descriptor *hid_desc, - struct wacom_features *features) +static void wacom_usage_mapping(struct hid_device *hdev, + struct hid_field *field, struct hid_usage *usage) { - struct usb_device *dev = interface_to_usbdev(intf); - char limit = 0; - /* result has to be defined as int for some devices */ - int result = 0, touch_max = 0; - int i = 0, page = 0, finger = 0, pen = 0; - unsigned char *report; - - report = kzalloc(hid_desc->wDescriptorLength, GFP_KERNEL); - if (!report) - return -ENOMEM; - - /* retrive report descriptors */ - do { - result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_DESCRIPTOR, - USB_RECIP_INTERFACE | USB_DIR_IN, - HID_DEVICET_REPORT << 8, - intf->altsetting[0].desc.bInterfaceNumber, /* interface */ - report, - hid_desc->wDescriptorLength, - 5000); /* 5 secs */ - } while (result < 0 && limit++ < WAC_MSG_RETRIES); - - /* No need to parse the Descriptor. It isn't an error though */ - if (result < 0) - goto out; - - for (i = 0; i < hid_desc->wDescriptorLength; i++) { - - switch (report[i]) { - case HID_USAGE_PAGE: - page = report[i + 1]; - i++; - break; - - case HID_USAGE: - switch (page << 16 | report[i + 1]) { - case HID_USAGE_X: - if (finger) { - features->device_type = BTN_TOOL_FINGER; - /* touch device at least supports one touch point */ - touch_max = 1; - switch (features->type) { - case TABLETPC2FG: - features->pktlen = WACOM_PKGLEN_TPC2FG; - break; - - case MTSCREEN: - case WACOM_24HDT: - features->pktlen = WACOM_PKGLEN_MTOUCH; - break; - - case MTTPC: - case MTTPC_B: - features->pktlen = WACOM_PKGLEN_MTTPC; - break; - - case BAMBOO_PT: - features->pktlen = WACOM_PKGLEN_BBTOUCH; - break; - - default: - features->pktlen = WACOM_PKGLEN_GRAPHIRE; - break; - } - - switch (features->type) { - case BAMBOO_PT: - features->x_phy = - get_unaligned_le16(&report[i + 5]); - features->x_max = - get_unaligned_le16(&report[i + 8]); - i += 15; - break; - - case WACOM_24HDT: - features->x_max = - get_unaligned_le16(&report[i + 3]); - features->x_phy = - get_unaligned_le16(&report[i + 8]); - features->unit = report[i - 1]; - features->unitExpo = report[i - 3]; - i += 12; - break; - - case MTTPC_B: - features->x_max = - get_unaligned_le16(&report[i + 3]); - features->x_phy = - get_unaligned_le16(&report[i + 6]); - features->unit = report[i - 5]; - features->unitExpo = report[i - 3]; - i += 9; - break; - - default: - features->x_max = - get_unaligned_le16(&report[i + 3]); - features->x_phy = - get_unaligned_le16(&report[i + 6]); - features->unit = report[i + 9]; - features->unitExpo = report[i + 11]; - i += 12; - break; - } - } else if (pen) { - /* penabled only accepts exact bytes of data */ - if (features->type >= TABLETPC) - features->pktlen = WACOM_PKGLEN_GRAPHIRE; - features->device_type = BTN_TOOL_PEN; - features->x_max = - get_unaligned_le16(&report[i + 3]); - i += 4; - } - break; - - case HID_USAGE_Y: - if (finger) { - switch (features->type) { - case TABLETPC2FG: - case MTSCREEN: - case MTTPC: - features->y_max = - get_unaligned_le16(&report[i + 3]); - features->y_phy = - get_unaligned_le16(&report[i + 6]); - i += 7; - break; - - case WACOM_24HDT: - features->y_max = - get_unaligned_le16(&report[i + 3]); - features->y_phy = - get_unaligned_le16(&report[i - 2]); - i += 7; - break; - - case BAMBOO_PT: - features->y_phy = - get_unaligned_le16(&report[i + 3]); - features->y_max = - get_unaligned_le16(&report[i + 6]); - i += 12; - break; - - case MTTPC_B: - features->y_max = - get_unaligned_le16(&report[i + 3]); - features->y_phy = - get_unaligned_le16(&report[i + 6]); - i += 9; - break; - - default: - features->y_max = - features->x_max; - features->y_phy = - get_unaligned_le16(&report[i + 3]); - i += 4; - break; - } - } else if (pen) { - features->y_max = - get_unaligned_le16(&report[i + 3]); - i += 4; - } - break; - - case HID_USAGE_FINGER: - finger = 1; - i++; - break; + 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. - */ - case HID_USAGE_STYLUS: - pen = 1; - i++; - break; + /* + * 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; - case HID_USAGE_CONTACTMAX: - /* leave touch_max as is if predefined */ - if (!features->touch_max) - wacom_retrieve_report_data(intf, features); - i++; - break; + if (finger && !features->touch_max) + /* touch device at least supports one touch point */ + features->touch_max = 1; - case HID_USAGE_PRESSURE: - if (pen) { - features->pressure_max = - get_unaligned_le16(&report[i + 3]); - i += 4; - } - break; + 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; } - break; - - case HID_COLLECTION_END: - /* reset UsagePage and Finger */ - finger = page = 0; - break; + } 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; + } +} - case HID_COLLECTION: - i++; - switch (report[i]) { - case HID_COLLECTION_LOGICAL: - i += wacom_parse_logical_collection(&report[i], - features); - break; +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); } - break; } } - out: - if (!features->touch_max && touch_max) - features->touch_max = touch_max; - result = 0; - kfree(report); - return result; + /* 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_set_device_mode(struct usb_interface *intf, int report_id, int length, int mode) +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; @@ -534,8 +251,11 @@ static int wacom_set_device_mode(struct usb_interface *intf, int report_id, int rep_data[0] = report_id; rep_data[1] = mode; - error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT, - report_id, rep_data, length, 1); + error = wacom_set_report(hdev, HID_FEATURE_REPORT, rep_data, + length, 1); + if (error >= 0) + error = wacom_get_report(hdev, HID_FEATURE_REPORT, + report_id, rep_data, length, 1); } while ((error < 0 || rep_data[1] != mode) && limit++ < WAC_MSG_RETRIES); kfree(rep_data); @@ -543,6 +263,59 @@ static int wacom_set_device_mode(struct usb_interface *intf, int report_id, int 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 @@ -550,31 +323,34 @@ static int wacom_set_device_mode(struct usb_interface *intf, int report_id, int * 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 usb_interface *intf, struct wacom_features *features) +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->device_type == BTN_TOOL_FINGER) { if (features->type > TABLETPC) { /* MT Tablet PC touch */ - return wacom_set_device_mode(intf, 3, 4, 4); + return wacom_set_device_mode(hdev, 3, 4, 4); } else if (features->type == WACOM_24HDT || features->type == CINTIQ_HYBRID) { - return wacom_set_device_mode(intf, 18, 3, 2); + 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(intf, 2, 2, 2); + return wacom_set_device_mode(hdev, 2, 2, 2); } } return 0; } -static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, +static void wacom_retrieve_hid_descriptor(struct hid_device *hdev, struct wacom_features *features) { - int error = 0; - struct usb_host_interface *interface = intf->cur_altsetting; - struct hid_descriptor *hid_desc; + struct wacom *wacom = hid_get_drvdata(hdev); + struct usb_interface *intf = wacom->intf; /* default features */ features->device_type = BTN_TOOL_PEN; @@ -599,66 +375,54 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, } /* only devices that support touch need to retrieve the info */ - if (features->type < BAMBOO_PT) { - goto out; - } - - error = usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc); - if (error) { - error = usb_get_extra_descriptor(&interface->endpoint[0], - HID_DEVICET_REPORT, &hid_desc); - if (error) { - dev_err(&intf->dev, - "can not retrieve extra class descriptor\n"); - goto out; - } - } - error = wacom_parse_hid(intf, hid_desc, features); + if (features->type < BAMBOO_PT) + return; - out: - return error; + wacom_parse_hid(hdev, features); } -struct wacom_usbdev_data { +struct wacom_hdev_data { struct list_head list; struct kref kref; - struct usb_device *dev; + struct hid_device *dev; struct wacom_shared shared; }; static LIST_HEAD(wacom_udev_list); static DEFINE_MUTEX(wacom_udev_list_lock); -static struct usb_device *wacom_get_sibling(struct usb_device *dev, int vendor, int product) +static bool wacom_are_sibling(struct hid_device *hdev, + struct hid_device *sibling) { - int port1; - struct usb_device *sibling; - - if (vendor == 0 && product == 0) - return dev; + 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 (dev->parent == NULL) - return NULL; + if (vid == 0 && pid == 0) { + vid = hdev->vendor; + pid = hdev->product; + } - usb_hub_for_each_child(dev->parent, port1, sibling) { - struct usb_device_descriptor *d; - if (sibling == NULL) - continue; + if (vid != sibling->vendor || pid != sibling->product) + return false; - d = &sibling->descriptor; - if (d->idVendor == vendor && d->idProduct == product) - return sibling; - } + /* 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 NULL; + return !strncmp(hdev->phys, sibling->phys, n1); } -static struct wacom_usbdev_data *wacom_get_usbdev_data(struct usb_device *dev) +static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev) { - struct wacom_usbdev_data *data; + struct wacom_hdev_data *data; list_for_each_entry(data, &wacom_udev_list, list) { - if (data->dev == dev) { + if (wacom_are_sibling(hdev, data->dev)) { kref_get(&data->kref); return data; } @@ -667,28 +431,29 @@ static struct wacom_usbdev_data *wacom_get_usbdev_data(struct usb_device *dev) return NULL; } -static int wacom_add_shared_data(struct wacom_wac *wacom, - struct usb_device *dev) +static int wacom_add_shared_data(struct hid_device *hdev) { - struct wacom_usbdev_data *data; + 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_usbdev_data(dev); + data = wacom_get_hdev_data(hdev); if (!data) { - data = kzalloc(sizeof(struct wacom_usbdev_data), GFP_KERNEL); + data = kzalloc(sizeof(struct wacom_hdev_data), GFP_KERNEL); if (!data) { retval = -ENOMEM; goto out; } kref_init(&data->kref); - data->dev = dev; + data->dev = hdev; list_add_tail(&data->list, &wacom_udev_list); } - wacom->shared = &data->shared; + wacom_wac->shared = &data->shared; out: mutex_unlock(&wacom_udev_list_lock); @@ -697,8 +462,8 @@ out: static void wacom_release_shared_data(struct kref *kref) { - struct wacom_usbdev_data *data = - container_of(kref, struct wacom_usbdev_data, kref); + struct wacom_hdev_data *data = + container_of(kref, struct wacom_hdev_data, kref); mutex_lock(&wacom_udev_list_lock); list_del(&data->list); @@ -709,10 +474,10 @@ static void wacom_release_shared_data(struct kref *kref) static void wacom_remove_shared_data(struct wacom_wac *wacom) { - struct wacom_usbdev_data *data; + struct wacom_hdev_data *data; if (wacom->shared) { - data = container_of(wacom->shared, struct wacom_usbdev_data, shared); + data = container_of(wacom->shared, struct wacom_hdev_data, shared); kref_put(&data->kref, wacom_release_shared_data); wacom->shared = NULL; } @@ -755,38 +520,40 @@ static int wacom_led_control(struct wacom *wacom) buf[4] = wacom->led.img_lum; } - retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_LED_CONTROL, - buf, 9, WAC_CMD_RETRIES); + retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, 9, + WAC_CMD_RETRIES); kfree(buf); return retval; } -static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *img) +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(259, GFP_KERNEL); + 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->intf, 0x03, WAC_CMD_ICON_START, - buf, 2, WAC_CMD_RETRIES); + retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, 2, + WAC_CMD_RETRIES); if (retval < 0) goto out; - buf[0] = WAC_CMD_ICON_XFER; + buf[0] = xfer_id; buf[1] = button_id & 0x07; for (i = 0; i < 4; i++) { buf[2] = i; - memcpy(buf + 3, img + i * 256, 256); + memcpy(buf + 3, img + i * chunk_len, chunk_len); - retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_XFER, - buf, 259, WAC_CMD_RETRIES); + retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, + buf, chunk_len + 3, WAC_CMD_RETRIES); if (retval < 0) break; } @@ -794,8 +561,8 @@ static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *im /* Send 'stop' */ buf[0] = WAC_CMD_ICON_START; buf[1] = 0; - wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_START, - buf, 2, WAC_CMD_RETRIES); + wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, 2, + WAC_CMD_RETRIES); out: kfree(buf); @@ -805,7 +572,8 @@ out: static ssize_t wacom_led_select_store(struct device *dev, int set_id, const char *buf, size_t count) { - struct wacom *wacom = dev_get_drvdata(dev); + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct wacom *wacom = hid_get_drvdata(hdev); unsigned int id; int err; @@ -832,7 +600,8 @@ static ssize_t wacom_led##SET_ID##_select_store(struct device *dev, \ static ssize_t wacom_led##SET_ID##_select_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ - struct wacom *wacom = dev_get_drvdata(dev); \ + struct hid_device *hdev = container_of(dev, struct hid_device, dev);\ + struct wacom *wacom = hid_get_drvdata(hdev); \ return snprintf(buf, 2, "%d\n", wacom->led.select[SET_ID]); \ } \ static DEVICE_ATTR(status_led##SET_ID##_select, S_IWUSR | S_IRUSR, \ @@ -866,7 +635,8 @@ static ssize_t wacom_luminance_store(struct wacom *wacom, u8 *dest, static ssize_t wacom_##name##_luminance_store(struct device *dev, \ struct device_attribute *attr, const char *buf, size_t count) \ { \ - struct wacom *wacom = dev_get_drvdata(dev); \ + 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); \ @@ -881,15 +651,26 @@ DEVICE_LUMINANCE_ATTR(buttons, img_lum); static ssize_t wacom_button_image_store(struct device *dev, int button_id, const char *buf, size_t count) { - struct wacom *wacom = dev_get_drvdata(dev); + 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 != 1024) + if (count != len) return -EINVAL; mutex_lock(&wacom->lock); - err = wacom_led_putimage(wacom, button_id, buf); + err = wacom_led_putimage(wacom, button_id, xfer_id, len, buf); mutex_unlock(&wacom->lock); @@ -965,13 +746,14 @@ static int wacom_initialize_leds(struct wacom *wacom) 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->intf->dev.kobj, + error = sysfs_create_group(&wacom->hdev->dev.kobj, &intuos4_led_attr_group); break; @@ -983,7 +765,7 @@ static int wacom_initialize_leds(struct wacom *wacom) wacom->led.hlv = 0; wacom->led.img_lum = 0; - error = sysfs_create_group(&wacom->intf->dev.kobj, + error = sysfs_create_group(&wacom->hdev->dev.kobj, &cintiq_led_attr_group); break; @@ -1000,7 +782,7 @@ static int wacom_initialize_leds(struct wacom *wacom) wacom->led.hlv = 0; wacom->led.img_lum = 0; - error = sysfs_create_group(&wacom->intf->dev.kobj, + error = sysfs_create_group(&wacom->hdev->dev.kobj, &intuos5_led_attr_group); } else return 0; @@ -1011,28 +793,35 @@ static int wacom_initialize_leds(struct wacom *wacom) } if (error) { - dev_err(&wacom->intf->dev, + 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->intf->dev.kobj, + sysfs_remove_group(&wacom->hdev->dev.kobj, &intuos4_led_attr_group); break; case WACOM_24HD: case WACOM_21UX2: - sysfs_remove_group(&wacom->intf->dev.kobj, + sysfs_remove_group(&wacom->hdev->dev.kobj, &cintiq_led_attr_group); break; @@ -1043,17 +832,24 @@ static void wacom_destroy_leds(struct wacom *wacom) case INTUOSPM: case INTUOSPL: if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN) - sysfs_remove_group(&wacom->intf->dev.kobj, + 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) @@ -1067,7 +863,16 @@ static int wacom_battery_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CAPACITY: val->intval = - wacom->wacom_wac.battery_capacity * 100 / 31; + 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; @@ -1077,74 +882,201 @@ static int wacom_battery_get_property(struct power_supply *psy, 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) { - int error = 0; + 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; - if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR) { wacom->battery.properties = wacom_battery_props; wacom->battery.num_properties = ARRAY_SIZE(wacom_battery_props); wacom->battery.get_property = wacom_battery_get_property; - wacom->battery.name = "wacom_battery"; + 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; - error = power_supply_register(&wacom->usbdev->dev, + 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); - if (!error) - power_supply_powers(&wacom->battery, - &wacom->usbdev->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 error; + return 0; } static void wacom_destroy_battery(struct wacom *wacom) { - if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR && - wacom->battery.dev) { + 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 int wacom_register_input(struct wacom *wacom) +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, S_IRUGO | S_IWUSR | S_IWGRP, + wacom_show_speed, wacom_store_speed); + +static struct input_dev *wacom_allocate_input(struct wacom *wacom) { struct input_dev *input_dev; - struct usb_interface *intf = wacom->intf; - struct usb_device *dev = interface_to_usbdev(intf); + struct hid_device *hdev = wacom->hdev; struct wacom_wac *wacom_wac = &(wacom->wacom_wac); - int error; input_dev = input_allocate_device(); - if (!input_dev) { - error = -ENOMEM; - goto fail1; - } + if (!input_dev) + return NULL; input_dev->name = wacom_wac->name; - input_dev->dev.parent = &intf->dev; + input_dev->phys = hdev->phys; + input_dev->dev.parent = &hdev->dev; input_dev->open = wacom_open; input_dev->close = wacom_close; - usb_to_input_id(dev, &input_dev->id); + input_dev->uniq = hdev->uniq; + input_dev->id.bustype = hdev->bus; + input_dev->id.vendor = hdev->vendor; + input_dev->id.product = hdev->product; + input_dev->id.version = hdev->version; input_set_drvdata(input_dev, wacom); + return input_dev; +} + +static void wacom_unregister_inputs(struct wacom *wacom) +{ + if (wacom->wacom_wac.input) + input_unregister_device(wacom->wacom_wac.input); + if (wacom->wacom_wac.pad_input) + input_unregister_device(wacom->wacom_wac.pad_input); + wacom->wacom_wac.input = NULL; + wacom->wacom_wac.pad_input = NULL; +} + +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_allocate_input(wacom); + pad_input_dev = wacom_allocate_input(wacom); + if (!input_dev || !pad_input_dev) { + error = -ENOMEM; + goto fail1; + } + wacom_wac->input = input_dev; + wacom_wac->pad_input = pad_input_dev; + wacom_wac->pad_input->name = wacom_wac->pad_name; + error = wacom_setup_input_capabilities(input_dev, wacom_wac); if (error) - goto fail1; + goto fail2; error = input_register_device(input_dev); if (error) goto fail2; + 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 fail3; + } + return 0; +fail3: + input_unregister_device(input_dev); + input_dev = NULL; fail2: - input_free_device(input_dev); wacom_wac->input = NULL; + wacom_wac->pad_input = NULL; fail1: + if (input_dev) + input_free_device(input_dev); + if (pad_input_dev) + input_free_device(pad_input_dev); return error; } @@ -1153,6 +1085,7 @@ 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; @@ -1165,50 +1098,49 @@ static void wacom_wireless_work(struct work_struct *work) wacom_destroy_battery(wacom); /* Stylus interface */ - wacom1 = usb_get_intfdata(usbdev->config->interface[1]); + hdev1 = usb_get_intfdata(usbdev->config->interface[1]); + wacom1 = hid_get_drvdata(hdev1); wacom_wac1 = &(wacom1->wacom_wac); - if (wacom_wac1->input) - input_unregister_device(wacom_wac1->input); - wacom_wac1->input = NULL; + wacom_unregister_inputs(wacom1); /* Touch interface */ - wacom2 = usb_get_intfdata(usbdev->config->interface[2]); + hdev2 = usb_get_intfdata(usbdev->config->interface[2]); + wacom2 = hid_get_drvdata(hdev2); wacom_wac2 = &(wacom2->wacom_wac); - if (wacom_wac2->input) - input_unregister_device(wacom_wac2->input); - wacom_wac2->input = NULL; + wacom_unregister_inputs(wacom2); if (wacom_wac->pid == 0) { - dev_info(&wacom->intf->dev, "wireless tablet disconnected\n"); + hid_info(wacom->hdev, "wireless tablet disconnected\n"); + wacom_wac1->shared->type = 0; } else { - const struct usb_device_id *id = wacom_ids; + const struct hid_device_id *id = wacom_ids; - dev_info(&wacom->intf->dev, - "wireless tablet connected with PID %x\n", + hid_info(wacom->hdev, "wireless tablet connected with PID %x\n", wacom_wac->pid); - while (id->match_flags) { - if (id->idVendor == USB_VENDOR_ID_WACOM && - id->idProduct == wacom_wac->pid) + while (id->bus) { + if (id->vendor == USB_VENDOR_ID_WACOM && + id->product == wacom_wac->pid) break; id++; } - if (!id->match_flags) { - dev_info(&wacom->intf->dev, - "ignoring unknown PID.\n"); + if (!id->bus) { + hid_info(wacom->hdev, "ignoring unknown PID.\n"); return; } /* Stylus interface */ wacom_wac1->features = - *((struct wacom_features *)id->driver_info); + *((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; - error = wacom_register_input(wacom1); + error = wacom_register_inputs(wacom1); if (error) goto fail; @@ -1216,7 +1148,7 @@ static void wacom_wireless_work(struct work_struct *work) if (wacom_wac1->features.touch_max || wacom_wac1->features.type == INTUOSHT) { wacom_wac2->features = - *((struct wacom_features *)id->driver_info); + *((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; @@ -1226,7 +1158,9 @@ static void wacom_wireless_work(struct work_struct *work) else snprintf(wacom_wac2->name, WACOM_NAME_MAX, "%s (WL) Pad",wacom_wac2->features.name); - error = wacom_register_input(wacom2); + snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX, + "%s (WL) Pad", wacom_wac2->features.name); + error = wacom_register_inputs(wacom2); if (error) goto fail; @@ -1243,15 +1177,8 @@ static void wacom_wireless_work(struct work_struct *work) return; fail: - if (wacom_wac2->input) { - input_unregister_device(wacom_wac2->input); - wacom_wac2->input = NULL; - } - - if (wacom_wac1->input) { - input_unregister_device(wacom_wac1->input); - wacom_wac1->input = NULL; - } + wacom_unregister_inputs(wacom1); + wacom_unregister_inputs(wacom2); return; } @@ -1282,69 +1209,89 @@ static void wacom_calculate_res(struct wacom_features *features) features->unitExpo); } -static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) +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 usb_endpoint_descriptor *endpoint; struct wacom *wacom; struct wacom_wac *wacom_wac; struct wacom_features *features; int error; - if (!id->driver_info) + if (!id->driver_data) return -EINVAL; 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 fail1; + } + wacom_wac = &wacom->wacom_wac; - wacom_wac->features = *((struct wacom_features *)id->driver_info); + 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 fail1; } - wacom_wac->data = usb_alloc_coherent(dev, WACOM_PKGLEN_MAX, - GFP_KERNEL, &wacom->data_dma); - if (!wacom_wac->data) { - error = -ENOMEM; + if (features->check_for_hid_type && features->hid_type != hdev->type) { + error = -ENODEV; goto fail1; } - wacom->irq = usb_alloc_urb(0, GFP_KERNEL); - if (!wacom->irq) { - error = -ENOMEM; - goto fail2; - } - wacom->usbdev = dev; wacom->intf = intf; mutex_init(&wacom->lock); INIT_WORK(&wacom->work, wacom_wireless_work); - usb_make_path(dev, wacom->phys, sizeof(wacom->phys)); - strlcat(wacom->phys, "/input0", sizeof(wacom->phys)); - - endpoint = &intf->cur_altsetting->endpoint[0].desc; /* 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 */ - error = wacom_retrieve_hid_descriptor(intf, features); - if (error) - goto fail3; + 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 (wMaxPacketSize + * 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 (endpoint->wMaxPacketSize == WACOM_PKGLEN_BBTOUCH3) { + if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) { features->device_type = BTN_TOOL_FINGER; - features->pktlen = WACOM_PKGLEN_BBTOUCH3; features->x_max = 4096; features->y_max = 4096; @@ -1353,20 +1300,35 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i } } + /* + * 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 = 16 - 3; + 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) { - struct usb_device *other_dev; - /* Append the device type to the name */ if (features->device_type != BTN_TOOL_FINGER) strlcat(wacom_wac->name, " Pen", WACOM_NAME_MAX); @@ -1375,43 +1337,49 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i else strlcat(wacom_wac->name, " Pad", WACOM_NAME_MAX); - other_dev = wacom_get_sibling(dev, features->oVid, features->oPid); - if (other_dev == NULL || wacom_get_usbdev_data(other_dev) == NULL) - other_dev = dev; - error = wacom_add_shared_data(wacom_wac, other_dev); + error = wacom_add_shared_data(hdev); if (error) - goto fail3; + goto fail1; } - usb_fill_int_urb(wacom->irq, dev, - usb_rcvintpipe(dev, endpoint->bEndpointAddress), - wacom_wac->data, features->pktlen, - wacom_sys_irq, wacom, endpoint->bInterval); - wacom->irq->transfer_dma = wacom->data_dma; - wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - error = wacom_initialize_leds(wacom); if (error) - goto fail4; + goto fail2; + + if (!(features->quirks & WACOM_QUIRK_MONITOR) && + (features->quirks & WACOM_QUIRK_BATTERY)) { + error = wacom_initialize_battery(wacom); + if (error) + goto fail3; + } if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) { - error = wacom_register_input(wacom); + error = wacom_register_inputs(wacom); if (error) - goto fail5; + goto fail4; } - /* Note that if query fails it is not a hard failure */ - wacom_query_tablet_data(intf, features); + 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); + } - usb_set_intfdata(intf, wacom); + /* Note that if query fails it is not a hard failure */ + wacom_query_tablet_data(hdev, features); - if (features->quirks & WACOM_QUIRK_MONITOR) { - if (usb_submit_urb(wacom->irq, GFP_KERNEL)) { - error = -EIO; - goto fail5; - } + /* Regular HID work starts now */ + error = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (error) { + hid_err(hdev, "hw start failed\n"); + goto fail5; } + 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; @@ -1419,79 +1387,70 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i return 0; - fail5: wacom_destroy_leds(wacom); - fail4: wacom_remove_shared_data(wacom_wac); - fail3: usb_free_urb(wacom->irq); - fail2: usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma); + fail5: if (hdev->bus == BUS_BLUETOOTH) + device_remove_file(&hdev->dev, &dev_attr_speed); + wacom_unregister_inputs(wacom); + fail4: wacom_destroy_battery(wacom); + fail3: wacom_destroy_leds(wacom); + fail2: wacom_remove_shared_data(wacom_wac); fail1: kfree(wacom); + hid_set_drvdata(hdev, NULL); return error; } -static void wacom_disconnect(struct usb_interface *intf) +static void wacom_remove(struct hid_device *hdev) { - struct wacom *wacom = usb_get_intfdata(intf); + struct wacom *wacom = hid_get_drvdata(hdev); - usb_set_intfdata(intf, NULL); + hid_hw_stop(hdev); - usb_kill_urb(wacom->irq); cancel_work_sync(&wacom->work); - if (wacom->wacom_wac.input) - input_unregister_device(wacom->wacom_wac.input); + wacom_unregister_inputs(wacom); + if (hdev->bus == BUS_BLUETOOTH) + device_remove_file(&hdev->dev, &dev_attr_speed); wacom_destroy_battery(wacom); wacom_destroy_leds(wacom); - usb_free_urb(wacom->irq); - usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX, - wacom->wacom_wac.data, wacom->data_dma); wacom_remove_shared_data(&wacom->wacom_wac); - kfree(wacom); -} - -static int wacom_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct wacom *wacom = usb_get_intfdata(intf); - - mutex_lock(&wacom->lock); - usb_kill_urb(wacom->irq); - mutex_unlock(&wacom->lock); - return 0; + hid_set_drvdata(hdev, NULL); + kfree(wacom); } -static int wacom_resume(struct usb_interface *intf) +static int wacom_resume(struct hid_device *hdev) { - struct wacom *wacom = usb_get_intfdata(intf); + struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_features *features = &wacom->wacom_wac.features; - int rv = 0; mutex_lock(&wacom->lock); /* switch to wacom mode first */ - wacom_query_tablet_data(intf, features); + wacom_query_tablet_data(hdev, features); wacom_led_control(wacom); - if ((wacom->open || (features->quirks & WACOM_QUIRK_MONITOR)) && - usb_submit_urb(wacom->irq, GFP_NOIO) < 0) - rv = -EIO; - mutex_unlock(&wacom->lock); - return rv; + return 0; } -static int wacom_reset_resume(struct usb_interface *intf) +static int wacom_reset_resume(struct hid_device *hdev) { - return wacom_resume(intf); + return wacom_resume(hdev); } -static struct usb_driver wacom_driver = { +static struct hid_driver wacom_driver = { .name = "wacom", .id_table = wacom_ids, .probe = wacom_probe, - .disconnect = wacom_disconnect, - .suspend = wacom_suspend, + .remove = wacom_remove, +#ifdef CONFIG_PM .resume = wacom_resume, .reset_resume = wacom_reset_resume, - .supports_autosuspend = 1, +#endif + .raw_event = wacom_raw_event, }; +module_hid_driver(wacom_driver); -module_usb_driver(wacom_driver); +MODULE_VERSION(DRIVER_VERSION); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/hid/wacom_wac.c index e73cf2c..aa6a08e 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -25,11 +25,23 @@ #define WACOM_INTUOS_RES 100 #define WACOM_INTUOS3_RES 200 -/* Scale factor relating reported contact size to logical contact area. +/* + * 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; @@ -217,17 +229,13 @@ static int wacom_dtus_irq(struct wacom_wac *wacom) "%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); - /* - * Serial number is required when expresskeys are - * reported through pen interface. - */ - input_event(input, EV_MSC, MSC_SERIAL, 0xf0); return 1; } else { prox = data[1] & 0x80; @@ -257,7 +265,6 @@ static int wacom_dtus_irq(struct wacom_wac *wacom) wacom->id[0] = 0; input_report_key(input, wacom->tool[0], prox); input_report_abs(input, ABS_MISC, wacom->id[0]); - input_event(input, EV_MSC, MSC_SERIAL, 1); return 1; } } @@ -267,11 +274,20 @@ 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 (data[0] != WACOM_REPORT_PENABLED) { + 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; @@ -305,7 +321,12 @@ static int wacom_graphire_irq(struct wacom_wac *wacom) 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) { - input_report_abs(input, ABS_PRESSURE, data[6] | ((data[7] & 0x03) << 8)); + 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); @@ -316,6 +337,20 @@ static int wacom_graphire_irq(struct wacom_wac *wacom) 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]; @@ -327,7 +362,6 @@ static int wacom_graphire_irq(struct wacom_wac *wacom) 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_event(input, EV_MSC, MSC_SERIAL, 1); input_sync(input); /* sync last event */ } @@ -337,14 +371,13 @@ static int wacom_graphire_irq(struct wacom_wac *wacom) prox = data[7] & 0xf8; if (prox || wacom->id[1]) { wacom->id[1] = PAD_DEVICE_ID; - input_report_key(input, BTN_BACK, (data[7] & 0x40)); - input_report_key(input, BTN_FORWARD, (data[7] & 0x80)); + 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(input, REL_WHEEL, rw); + input_report_rel(pad_input, REL_WHEEL, rw); if (!prox) wacom->id[1] = 0; - input_report_abs(input, ABS_MISC, wacom->id[1]); - input_event(input, EV_MSC, MSC_SERIAL, 0xf0); + input_report_abs(pad_input, ABS_MISC, wacom->id[1]); retval = 1; } break; @@ -353,19 +386,43 @@ static int wacom_graphire_irq(struct wacom_wac *wacom) prox = (data[7] & 0xf8) || data[8]; if (prox || wacom->id[1]) { wacom->id[1] = PAD_DEVICE_ID; - input_report_key(input, BTN_BACK, (data[7] & 0x08)); - input_report_key(input, BTN_LEFT, (data[7] & 0x20)); - input_report_key(input, BTN_FORWARD, (data[7] & 0x10)); - input_report_key(input, BTN_RIGHT, (data[7] & 0x40)); - input_report_abs(input, ABS_WHEEL, (data[8] & 0x7f)); + 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(input, ABS_MISC, wacom->id[1]); - input_event(input, EV_MSC, MSC_SERIAL, 0xf0); + 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; } @@ -584,6 +641,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) /* 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)); @@ -773,7 +831,6 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) input_report_abs(input, ABS_MISC, 0); } } - input_event(input, EV_MSC, MSC_SERIAL, 0xffffffff); return 1; } @@ -901,6 +958,58 @@ static int int_dist(int x1, int y1, int x2, int y2) 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; @@ -1093,7 +1202,7 @@ static int wacom_tpc_pen(struct wacom_wac *wacom) 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] & 0x03) << 8) | data[6]); + 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; @@ -1143,6 +1252,7 @@ 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; @@ -1177,14 +1287,12 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) input_mt_report_pointer_emulation(input, true); - 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_BACK, (data[1] & 0x02) != 0); - input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0); - - input_sync(input); + 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 0; + return 1; } static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) @@ -1232,7 +1340,7 @@ static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data) { - struct input_dev *input = wacom->input; + struct input_dev *input = wacom->pad_input; struct wacom_features *features = &wacom->features; if (features->type == INTUOSHT) { @@ -1269,9 +1377,7 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom) } input_mt_report_pointer_emulation(input, true); - input_sync(input); - - return 0; + return 1; } static int wacom_bpt_pen(struct wacom_wac *wacom) @@ -1375,7 +1481,7 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) connected = data[1] & 0x01; if (connected) { - int pid, battery; + int pid, battery, ps_connected; if ((wacom->shared->type == INTUOSHT) && wacom->shared->touch_max) { @@ -1385,17 +1491,29 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) } pid = get_unaligned_be16(&data[6]); - battery = data[5] & 0x3f; + battery = (data[5] & 0x3f) * 100 / 31; + ps_connected = !!(data[5] & 0x80); if (wacom->pid != pid) { wacom->pid = pid; wacom_schedule_work(wacom); } - wacom->battery_capacity = battery; + + 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; @@ -1416,6 +1534,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) case WACOM_G4: case GRAPHIRE: + case GRAPHIRE_BT: case WACOM_MO: sync = wacom_graphire_irq(wacom_wac); break; @@ -1450,6 +1569,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) 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; @@ -1489,8 +1612,11 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) break; } - if (sync) + 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) @@ -1565,8 +1691,10 @@ void wacom_setup_device_quirks(struct wacom_features *features) features->quirks |= WACOM_QUIRK_NO_INPUT; /* must be monitor interface if no device_type set */ - if (!features->device_type) + if (!features->device_type) { features->quirks |= WACOM_QUIRK_MONITOR; + features->quirks |= WACOM_QUIRK_BATTERY; + } } } @@ -1615,7 +1743,6 @@ int wacom_setup_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); @@ -1630,10 +1757,6 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, /* fall through */ case WACOM_G4: - input_set_capability(input_dev, EV_MSC, MSC_SERIAL); - - __set_bit(BTN_BACK, input_dev->keybit); - __set_bit(BTN_FORWARD, input_dev->keybit); /* fall through */ case GRAPHIRE: @@ -1652,62 +1775,42 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, __set_bit(INPUT_PROP_POINTER, input_dev->propbit); 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); + case GRAPHIRE_BT: + __clear_bit(ABS_MISC, input_dev->absbit); + input_set_abs_params(input_dev, ABS_DISTANCE, 0, + features->distance_max, + 0, 0); - for (i = 6; i < 10; i++) - __set_bit(BTN_0 + i, input_dev->keybit); + input_set_capability(input_dev, EV_REL, REL_WHEEL); - __set_bit(KEY_PROG1, input_dev->keybit); - __set_bit(KEY_PROG2, input_dev->keybit); - __set_bit(KEY_PROG3, input_dev->keybit); + __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: - for (i = 0; i < 6; i++) - __set_bit(BTN_0 + i, input_dev->keybit); - __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); wacom_setup_cintiq(wacom_wac); 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); input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); @@ -1716,9 +1819,6 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, 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_Z, -900, 899, 0, 0); __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); wacom_setup_cintiq(wacom_wac); @@ -1726,21 +1826,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, 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); input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); /* fall through */ @@ -1754,20 +1840,11 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, case INTUOS5L: case INTUOSPM: case INTUOSPL: - if (features->device_type == BTN_TOOL_PEN) { - __set_bit(BTN_7, input_dev->keybit); - __set_bit(BTN_8, input_dev->keybit); - } - /* fall through */ - case INTUOS5S: case INTUOSPS: __set_bit(INPUT_PROP_POINTER, input_dev->propbit); if (features->device_type == BTN_TOOL_PEN) { - for (i = 0; i < 7; i++) - __set_bit(BTN_0 + i, input_dev->keybit); - input_set_abs_params(input_dev, ABS_DISTANCE, 0, features->distance_max, 0, 0); @@ -1787,15 +1864,9 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, break; case INTUOS4: + case INTUOS4WL: 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_Z, -900, 899, 0, 0); wacom_setup_intuos(wacom_wac); @@ -1833,11 +1904,6 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, case DTUS: case PL: case DTU: - if (features->type == DTUS) { - input_set_capability(input_dev, EV_MSC, MSC_SERIAL); - for (i = 0; i < 4; i++) - __set_bit(BTN_0 + i, input_dev->keybit); - } __set_bit(BTN_TOOL_PEN, input_dev->keybit); __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); __set_bit(BTN_STYLUS, input_dev->keybit); @@ -1871,11 +1937,6 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, if (features->device_type == BTN_TOOL_FINGER) { - __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); - if (features->touch_max) { if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) { input_set_abs_params(input_dev, @@ -1905,449 +1966,629 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, 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); + + switch (features->type) { + case GRAPHIRE_BT: + __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); - __set_bit(BTN_4, 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); - __set_bit(BTN_8, 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_Z, -900, 899, 0, 0); - __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0); + break; - wacom_setup_cintiq(wacom_wac); + 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", WACOM_PKGLEN_PENPRTN, 5040, 3780, 255, - 0, PENPARTNER, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES }; + { "Wacom Penpartner", 5040, 3780, 255, 0, + PENPARTNER, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES }; static const struct wacom_features wacom_features_0x10 = - { "Wacom Graphire", WACOM_PKGLEN_GRAPHIRE, 10206, 7422, 511, - 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; + { "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", WACOM_PKGLEN_GRAPHIRE, 10206, 7422, 511, - 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; + { "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", WACOM_PKGLEN_GRAPHIRE, 13918, 10206, 511, - 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; + { "Wacom Graphire2 5x7", 13918, 10206, 511, 63, + GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; static const struct wacom_features wacom_features_0x13 = - { "Wacom Graphire3", WACOM_PKGLEN_GRAPHIRE, 10208, 7424, 511, - 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; + { "Wacom Graphire3", 10208, 7424, 511, 63, + GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; static const struct wacom_features wacom_features_0x14 = - { "Wacom Graphire3 6x8", WACOM_PKGLEN_GRAPHIRE, 16704, 12064, 511, - 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; + { "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", WACOM_PKGLEN_GRAPHIRE, 10208, 7424, 511, - 63, WACOM_G4, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; + { "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", WACOM_PKGLEN_GRAPHIRE, 16704, 12064, 511, - 63, WACOM_G4, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; + { "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", WACOM_PKGLEN_BBFUN, 14760, 9225, 511, - 63, WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_BBFUN, 21648, 13530, 511, - 63, WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_GRAPHIRE, 16704, 12064, 511, - 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; + { "Wacom Bamboo1 Medium", 16704, 12064, 511, 63, + GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; static const struct wacom_features wacom_features_0x60 = - { "Wacom Volito", WACOM_PKGLEN_GRAPHIRE, 5104, 3712, 511, - 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; + { "Wacom Volito", 5104, 3712, 511, 63, + GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; static const struct wacom_features wacom_features_0x61 = - { "Wacom PenStation2", WACOM_PKGLEN_GRAPHIRE, 3250, 2320, 255, - 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; + { "Wacom PenStation2", 3250, 2320, 255, 63, + GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; static const struct wacom_features wacom_features_0x62 = - { "Wacom Volito2 4x5", WACOM_PKGLEN_GRAPHIRE, 5104, 3712, 511, - 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; + { "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", WACOM_PKGLEN_GRAPHIRE, 3248, 2320, 511, - 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; + { "Wacom Volito2 2x3", 3248, 2320, 511, 63, + GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; static const struct wacom_features wacom_features_0x64 = - { "Wacom PenPartner2", WACOM_PKGLEN_GRAPHIRE, 3250, 2320, 511, - 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; + { "Wacom PenPartner2", 3250, 2320, 511, 63, + GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; static const struct wacom_features wacom_features_0x65 = - { "Wacom Bamboo", WACOM_PKGLEN_BBFUN, 14760, 9225, 511, - 63, WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "Wacom Bamboo", 14760, 9225, 511, 63, + WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x69 = - { "Wacom Bamboo1", WACOM_PKGLEN_GRAPHIRE, 5104, 3712, 511, - 63, GRAPHIRE, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES }; + { "Wacom Bamboo1", 5104, 3712, 511, 63, + GRAPHIRE, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES }; static const struct wacom_features wacom_features_0x6A = - { "Wacom Bamboo1 4x6", WACOM_PKGLEN_GRAPHIRE, 14760, 9225, 1023, - 63, GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_GRAPHIRE, 21648, 13530, 1023, - 63, GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_INTUOS, 12700, 10600, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_INTUOS, 30480, 24060, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_INTUOS, 30480, 31680, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_INTUOS, 45720, 31680, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "Wacom Intuos 12x18", 45720, 31680, 1023, 31, + INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x30 = - { "Wacom PL400", WACOM_PKGLEN_GRAPHIRE, 5408, 4056, 255, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; + { "Wacom PL400", 5408, 4056, 255, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x31 = - { "Wacom PL500", WACOM_PKGLEN_GRAPHIRE, 6144, 4608, 255, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; + { "Wacom PL500", 6144, 4608, 255, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x32 = - { "Wacom PL600", WACOM_PKGLEN_GRAPHIRE, 6126, 4604, 255, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; + { "Wacom PL600", 6126, 4604, 255, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x33 = - { "Wacom PL600SX", WACOM_PKGLEN_GRAPHIRE, 6260, 5016, 255, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; + { "Wacom PL600SX", 6260, 5016, 255, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x34 = - { "Wacom PL550", WACOM_PKGLEN_GRAPHIRE, 6144, 4608, 511, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; + { "Wacom PL550", 6144, 4608, 511, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x35 = - { "Wacom PL800", WACOM_PKGLEN_GRAPHIRE, 7220, 5780, 511, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; + { "Wacom PL800", 7220, 5780, 511, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x37 = - { "Wacom PL700", WACOM_PKGLEN_GRAPHIRE, 6758, 5406, 511, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; + { "Wacom PL700", 6758, 5406, 511, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x38 = - { "Wacom PL510", WACOM_PKGLEN_GRAPHIRE, 6282, 4762, 511, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; + { "Wacom PL510", 6282, 4762, 511, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x39 = - { "Wacom DTU710", WACOM_PKGLEN_GRAPHIRE, 34080, 27660, 511, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; + { "Wacom DTU710", 34080, 27660, 511, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0xC4 = - { "Wacom DTF521", WACOM_PKGLEN_GRAPHIRE, 6282, 4762, 511, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; + { "Wacom DTF521", 6282, 4762, 511, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0xC0 = - { "Wacom DTF720", WACOM_PKGLEN_GRAPHIRE, 6858, 5506, 511, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; + { "Wacom DTF720", 6858, 5506, 511, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0xC2 = - { "Wacom DTF720a", WACOM_PKGLEN_GRAPHIRE, 6858, 5506, 511, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; + { "Wacom DTF720a", 6858, 5506, 511, 0, + PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x03 = - { "Wacom Cintiq Partner", WACOM_PKGLEN_GRAPHIRE, 20480, 15360, 511, - 0, PTU, WACOM_PL_RES, WACOM_PL_RES }; + { "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", WACOM_PKGLEN_INTUOS, 12700, 10600, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_INTUOS, 30480, 24060, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_INTUOS, 30480, 31680, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_INTUOS, 45720, 31680, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_INTUOS, 25400, 20320, 1023, - 63, INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 40640, 30480, 1023, - 63, INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 60960, 45720, 1023, - 63, INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 60960, 60960, 1023, - 63, INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 97536, 60960, 1023, - 63, INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 54204, 31750, 1023, - 63, INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 31496, 19685, 1023, - 63, INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047, - 63, INTUOS4S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047, - 63, INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 65024, 40640, 2047, - 63, INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 97536, 60960, 2047, - 63, INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 40640, 25400, 2047, - 63, INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047, - 63, INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, - .touch_max = 16 }; + { "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", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047, - 63, INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, - .touch_max = 16 }; + { "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", WACOM_PKGLEN_INTUOS, 65024, 40640, 2047, - 63, INTUOS5L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, - .touch_max = 16 }; + { "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", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047, - 63, INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047, - 63, INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047, - 63, INTUOSPS, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, - .touch_max = 16 }; + { "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", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047, - 63, INTUOSPM, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, - .touch_max = 16 }; + { "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", WACOM_PKGLEN_INTUOS, 65024, 40640, 2047, - 63, INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, - .touch_max = 16 }; + { "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", WACOM_PKGLEN_INTUOS, 104280, 65400, 2047, - 63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; + { "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", WACOM_PKGLEN_INTUOS, 104280, 65400, 2047, /* Pen */ - 63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, + { "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 }; + .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", WACOM_PKGLEN_INTUOS, 87200, 65600, 1023, - 63, CINTIQ, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 86680, 54180, 1023, - 63, WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 53020, 33440, 1023, - 63, WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; + { "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", WACOM_PKGLEN_INTUOS, 59352, 33648, 1023, - 63, WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; + { "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", WACOM_PKGLEN_GRAPHIRE, 37832, 30305, 511, - 0, PL, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "Wacom DTU1931", 37832, 30305, 511, 0, + PL, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0xCE = - { "Wacom DTU2231", WACOM_PKGLEN_GRAPHIRE, 47864, 27011, 511, - 0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_GRAPHIRE, 34623, 19553, 511, - 0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "Wacom DTU1631", 34623, 19553, 511, 0, + DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0xFB = - { "Wacom DTU1031", WACOM_PKGLEN_DTUS, 22096, 13960, 511, - 0, DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "Wacom DTU1031", 22096, 13960, 511, 0, + DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x57 = - { "Wacom DTK2241", WACOM_PKGLEN_INTUOS, 95640, 54060, 2047, - 63, DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; + { "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", WACOM_PKGLEN_INTUOS, 95640, 54060, 2047, - 63, DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, + { "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 }; + .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", WACOM_PKGLEN_INTUOS, 87000, 65400, 2047, - 63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; + { "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", WACOM_PKGLEN_INTUOS, 95640, 54060, 2047, - 63, WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; + { "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", WACOM_PKGLEN_INTUOS, 95640, 54060, 2047, - 63, WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, + { "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 }; + .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", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, - 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, - 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 511, - 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, - 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, - 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255, - 0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; + { "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", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255, - 0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; + { "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", WACOM_PKGLEN_MTOUCH, 26202, 16325, 255, - 0, MTSCREEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_TPC2FG, 27760, 15694, 255, - 0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; + { "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", WACOM_PKGLEN_GRAPHIRE, 25710, 14500, 255, - 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, - 0, TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, - 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_MTTPC, 26202, 16325, 255, - 0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_MTTPC, 26202, 16325, 255, - 0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_MTTPC, 26202, 16325, 255, - 0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_MTTPC, 27760, 15694, 255, - 0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_MTTPC, 27760, 15694, 255, - 0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, - 0, TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_MTTPC, 26202, 16325, 255, - 0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_MTTPC, 11060, 6220, 255, - 0, MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_MTTPC, 27848, 15752, 1023, - 0, MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_MTTPC, 29576, 16724, 1023, - 0, MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_WIRELESS, 0, 0, 0, - 0, WIRELESS, 0, 0, .touch_max = 16 }; + { "Wacom Wireless Receiver", 0, 0, 0, 0, + WIRELESS, 0, 0, .touch_max = 16 }; static const struct wacom_features wacom_features_0xD0 = - { "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; + { "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", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; + { "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", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; + { "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", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; + { "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", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; + { "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", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; + { "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", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; + { "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", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; + { "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", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; + { "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", WACOM_PKGLEN_BBPEN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_BBPEN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 16 }; + { "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", WACOM_PKGLEN_BBPEN, 21648, 13700, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 16 }; + { "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", WACOM_PKGLEN_BBPEN, 14720, 9225, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_BBPEN, 21648, 13530, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_BBPEN, 15200, 9500, 1023, - 31, INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 16 }; + { "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", WACOM_PKGLEN_BBPEN, 21600, 13500, 1023, - 31, INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 16 }; + { "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", WACOM_PKGLEN_BBPEN, 15200, 9500, 1023, - 31, INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + { "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", WACOM_PKGLEN_GRAPHIRE, 12800, 8000, 255, - 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x0307 = - { "Wacom ISDv5 307", WACOM_PKGLEN_INTUOS, 59352, 33648, 2047, - 63, CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, + { "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_0x0309 = +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 }; + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x0307, .touch_max = 10, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; -#define USB_DEVICE_WACOM(prod) \ - USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \ - .driver_info = (kernel_ulong_t)&wacom_features_##prod +#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 USB_DEVICE_DETAILED(prod, class, sub, proto) \ - USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_WACOM, prod, class, \ - sub, proto), \ - .driver_info = (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) \ - USB_DEVICE(USB_VENDOR_ID_LENOVO, prod), \ - .driver_info = (kernel_ulong_t)&wacom_features_##prod + HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, prod), \ + .driver_data = (kernel_ulong_t)&wacom_features_##prod -const struct usb_device_id wacom_ids[] = { +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) }, @@ -2358,20 +2599,16 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x17) }, { USB_DEVICE_WACOM(0x18) }, { USB_DEVICE_WACOM(0x19) }, - { 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) }, { 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) }, @@ -2381,20 +2618,34 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x37) }, { USB_DEVICE_WACOM(0x38) }, { USB_DEVICE_WACOM(0x39) }, - { USB_DEVICE_WACOM(0xC4) }, - { USB_DEVICE_WACOM(0xC0) }, - { USB_DEVICE_WACOM(0xC2) }, - { USB_DEVICE_WACOM(0x03) }, + { 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_DETAILED(0x5D, USB_CLASS_HID, 0, 0) }, { USB_DEVICE_WACOM(0x5B) }, - { USB_DEVICE_DETAILED(0x5E, USB_CLASS_HID, 0, 0) }, + { 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) }, @@ -2407,23 +2658,15 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0xBA) }, { USB_DEVICE_WACOM(0xBB) }, { USB_DEVICE_WACOM(0xBC) }, - { USB_DEVICE_WACOM(0x26) }, - { USB_DEVICE_WACOM(0x27) }, - { USB_DEVICE_WACOM(0x28) }, - { USB_DEVICE_WACOM(0x29) }, - { USB_DEVICE_WACOM(0x2A) }, - { USB_DEVICE_WACOM(0x3F) }, + { 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) }, - /* - * DTU-2231 has two interfaces on the same configuration, - * only one is used. - */ - { USB_DEVICE_DETAILED(0xCE, USB_CLASS_HID, - USB_INTERFACE_SUBCLASS_BOOT, - USB_INTERFACE_PROTOCOL_MOUSE) }, - { USB_DEVICE_WACOM(0x84) }, + { USB_DEVICE_WACOM(0xCC) }, + { USB_DEVICE_WACOM(0xCE) }, { USB_DEVICE_WACOM(0xD0) }, { USB_DEVICE_WACOM(0xD1) }, { USB_DEVICE_WACOM(0xD2) }, @@ -2438,13 +2681,6 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0xDD) }, { USB_DEVICE_WACOM(0xDE) }, { USB_DEVICE_WACOM(0xDF) }, - { USB_DEVICE_WACOM(0xF0) }, - { USB_DEVICE_WACOM(0xCC) }, - { USB_DEVICE_WACOM(0x90) }, - { USB_DEVICE_WACOM(0x93) }, - { USB_DEVICE_WACOM(0x97) }, - { USB_DEVICE_WACOM(0x9A) }, - { USB_DEVICE_WACOM(0x9F) }, { USB_DEVICE_WACOM(0xE2) }, { USB_DEVICE_WACOM(0xE3) }, { USB_DEVICE_WACOM(0xE5) }, @@ -2452,34 +2688,34 @@ const struct usb_device_id wacom_ids[] = { { 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_DETAILED(0x302, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_DETAILED(0x303, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_DETAILED(0x30E, USB_CLASS_HID, 0, 0) }, + { USB_DEVICE_WACOM(0x302) }, + { USB_DEVICE_WACOM(0x303) }, { USB_DEVICE_WACOM(0x304) }, - { USB_DEVICE_DETAILED(0x314, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_DETAILED(0x315, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_DETAILED(0x317, USB_CLASS_HID, 0, 0) }, + { USB_DEVICE_WACOM(0x307) }, + { USB_DEVICE_WACOM(0x309) }, + { 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(0x47) }, - { USB_DEVICE_WACOM(0xF4) }, - { USB_DEVICE_WACOM(0xF8) }, - { USB_DEVICE_DETAILED(0xF6, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_WACOM(0xFA) }, - { USB_DEVICE_WACOM(0xFB) }, - { USB_DEVICE_WACOM(0x0307) }, - { USB_DEVICE_DETAILED(0x0309, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_LENOVO(0x6004) }, { } }; -MODULE_DEVICE_TABLE(usb, wacom_ids); +MODULE_DEVICE_TABLE(hid, wacom_ids); diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/hid/wacom_wac.h index b2c9a9c..339ab5d 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -46,6 +46,7 @@ /* 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 @@ -68,10 +69,12 @@ #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, @@ -83,6 +86,7 @@ enum { INTUOS3L, INTUOS4S, INTUOS4, + INTUOS4WL, INTUOS4L, INTUOS5S, INTUOS5, @@ -114,7 +118,6 @@ enum { struct wacom_features { const char *name; - int pktlen; int x_max; int y_max; int pressure_max; @@ -127,8 +130,8 @@ struct wacom_features { int device_type; int x_phy; int y_phy; - unsigned char unit; - unsigned char unitExpo; + unsigned unit; + int unitExpo; int x_fuzz; int y_fuzz; int pressure_fuzz; @@ -137,6 +140,9 @@ struct wacom_features { unsigned touch_max; int oVid; int oPid; + int pktlen; + bool check_for_hid_type; + int hid_type; }; struct wacom_shared { @@ -150,16 +156,24 @@ struct wacom_shared { struct wacom_wac { char name[WACOM_NAME_MAX]; - unsigned char *data; + 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; int pid; int battery_capacity; int num_contacts_left; + int bat_charging; + int ps_connected; + u8 bt_features; + u8 bt_high_speed; }; #endif diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index f7e79b4..a3958c6 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -665,4 +665,14 @@ config KEYBOARD_CROS_EC To compile this driver as a module, choose M here: the module will be called cros_ec_keyb. +config KEYBOARD_CAP1106 + tristate "Microchip CAP1106 touch sensor" + depends on OF && I2C + select REGMAP_I2C + help + Say Y here to enable the CAP1106 touch sensor driver. + + To compile this driver as a module, choose M here: the + module will be called cap1106. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 7504ae1..0a33456 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o +obj-$(CONFIG_KEYBOARD_CAP1106) += cap1106.o obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o diff --git a/drivers/input/keyboard/cap1106.c b/drivers/input/keyboard/cap1106.c new file mode 100644 index 0000000..f7d7a0d --- /dev/null +++ b/drivers/input/keyboard/cap1106.c @@ -0,0 +1,335 @@ +/* + * Input driver for Microchip CAP1106, 6 channel capacitive touch sensor + * + * http://www.microchip.com/wwwproducts/Devices.aspx?product=CAP1106 + * + * (c) 2014 Daniel Mack <linux@zonque.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/of_irq.h> +#include <linux/regmap.h> +#include <linux/i2c.h> +#include <linux/gpio/consumer.h> + +#define CAP1106_REG_MAIN_CONTROL 0x00 +#define CAP1106_REG_MAIN_CONTROL_GAIN_SHIFT (6) +#define CAP1106_REG_MAIN_CONTROL_GAIN_MASK (0xc0) +#define CAP1106_REG_MAIN_CONTROL_DLSEEP BIT(4) +#define CAP1106_REG_GENERAL_STATUS 0x02 +#define CAP1106_REG_SENSOR_INPUT 0x03 +#define CAP1106_REG_NOISE_FLAG_STATUS 0x0a +#define CAP1106_REG_SENOR_DELTA(X) (0x10 + (X)) +#define CAP1106_REG_SENSITIVITY_CONTROL 0x1f +#define CAP1106_REG_CONFIG 0x20 +#define CAP1106_REG_SENSOR_ENABLE 0x21 +#define CAP1106_REG_SENSOR_CONFIG 0x22 +#define CAP1106_REG_SENSOR_CONFIG2 0x23 +#define CAP1106_REG_SAMPLING_CONFIG 0x24 +#define CAP1106_REG_CALIBRATION 0x25 +#define CAP1106_REG_INT_ENABLE 0x26 +#define CAP1106_REG_REPEAT_RATE 0x28 +#define CAP1106_REG_MT_CONFIG 0x2a +#define CAP1106_REG_MT_PATTERN_CONFIG 0x2b +#define CAP1106_REG_MT_PATTERN 0x2d +#define CAP1106_REG_RECALIB_CONFIG 0x2f +#define CAP1106_REG_SENSOR_THRESH(X) (0x30 + (X)) +#define CAP1106_REG_SENSOR_NOISE_THRESH 0x38 +#define CAP1106_REG_STANDBY_CHANNEL 0x40 +#define CAP1106_REG_STANDBY_CONFIG 0x41 +#define CAP1106_REG_STANDBY_SENSITIVITY 0x42 +#define CAP1106_REG_STANDBY_THRESH 0x43 +#define CAP1106_REG_CONFIG2 0x44 +#define CAP1106_REG_SENSOR_BASE_CNT(X) (0x50 + (X)) +#define CAP1106_REG_SENSOR_CALIB (0xb1 + (X)) +#define CAP1106_REG_SENSOR_CALIB_LSB1 0xb9 +#define CAP1106_REG_SENSOR_CALIB_LSB2 0xba +#define CAP1106_REG_PRODUCT_ID 0xfd +#define CAP1106_REG_MANUFACTURER_ID 0xfe +#define CAP1106_REG_REVISION 0xff + +#define CAP1106_NUM_CHN 6 +#define CAP1106_PRODUCT_ID 0x55 +#define CAP1106_MANUFACTURER_ID 0x5d + +struct cap1106_priv { + struct regmap *regmap; + struct input_dev *idev; + + /* config */ + unsigned int keycodes[CAP1106_NUM_CHN]; +}; + +static const struct reg_default cap1106_reg_defaults[] = { + { CAP1106_REG_MAIN_CONTROL, 0x00 }, + { CAP1106_REG_GENERAL_STATUS, 0x00 }, + { CAP1106_REG_SENSOR_INPUT, 0x00 }, + { CAP1106_REG_NOISE_FLAG_STATUS, 0x00 }, + { CAP1106_REG_SENSITIVITY_CONTROL, 0x2f }, + { CAP1106_REG_CONFIG, 0x20 }, + { CAP1106_REG_SENSOR_ENABLE, 0x3f }, + { CAP1106_REG_SENSOR_CONFIG, 0xa4 }, + { CAP1106_REG_SENSOR_CONFIG2, 0x07 }, + { CAP1106_REG_SAMPLING_CONFIG, 0x39 }, + { CAP1106_REG_CALIBRATION, 0x00 }, + { CAP1106_REG_INT_ENABLE, 0x3f }, + { CAP1106_REG_REPEAT_RATE, 0x3f }, + { CAP1106_REG_MT_CONFIG, 0x80 }, + { CAP1106_REG_MT_PATTERN_CONFIG, 0x00 }, + { CAP1106_REG_MT_PATTERN, 0x3f }, + { CAP1106_REG_RECALIB_CONFIG, 0x8a }, + { CAP1106_REG_SENSOR_THRESH(0), 0x40 }, + { CAP1106_REG_SENSOR_THRESH(1), 0x40 }, + { CAP1106_REG_SENSOR_THRESH(2), 0x40 }, + { CAP1106_REG_SENSOR_THRESH(3), 0x40 }, + { CAP1106_REG_SENSOR_THRESH(4), 0x40 }, + { CAP1106_REG_SENSOR_THRESH(5), 0x40 }, + { CAP1106_REG_SENSOR_NOISE_THRESH, 0x01 }, + { CAP1106_REG_STANDBY_CHANNEL, 0x00 }, + { CAP1106_REG_STANDBY_CONFIG, 0x39 }, + { CAP1106_REG_STANDBY_SENSITIVITY, 0x02 }, + { CAP1106_REG_STANDBY_THRESH, 0x40 }, + { CAP1106_REG_CONFIG2, 0x40 }, + { CAP1106_REG_SENSOR_CALIB_LSB1, 0x00 }, + { CAP1106_REG_SENSOR_CALIB_LSB2, 0x00 }, +}; + +static bool cap1106_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CAP1106_REG_MAIN_CONTROL: + case CAP1106_REG_SENSOR_INPUT: + case CAP1106_REG_SENOR_DELTA(0): + case CAP1106_REG_SENOR_DELTA(1): + case CAP1106_REG_SENOR_DELTA(2): + case CAP1106_REG_SENOR_DELTA(3): + case CAP1106_REG_SENOR_DELTA(4): + case CAP1106_REG_SENOR_DELTA(5): + case CAP1106_REG_PRODUCT_ID: + case CAP1106_REG_MANUFACTURER_ID: + case CAP1106_REG_REVISION: + return true; + } + + return false; +} + +static const struct regmap_config cap1106_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CAP1106_REG_REVISION, + .reg_defaults = cap1106_reg_defaults, + + .num_reg_defaults = ARRAY_SIZE(cap1106_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .volatile_reg = cap1106_volatile_reg, +}; + +static irqreturn_t cap1106_thread_func(int irq_num, void *data) +{ + struct cap1106_priv *priv = data; + unsigned int status; + int ret, i; + + /* + * Deassert interrupt. This needs to be done before reading the status + * registers, which will not carry valid values otherwise. + */ + ret = regmap_update_bits(priv->regmap, CAP1106_REG_MAIN_CONTROL, 1, 0); + if (ret < 0) + goto out; + + ret = regmap_read(priv->regmap, CAP1106_REG_SENSOR_INPUT, &status); + if (ret < 0) + goto out; + + for (i = 0; i < CAP1106_NUM_CHN; i++) + input_report_key(priv->idev, priv->keycodes[i], + status & (1 << i)); + + input_sync(priv->idev); + +out: + return IRQ_HANDLED; +} + +static int cap1106_set_sleep(struct cap1106_priv *priv, bool sleep) +{ + return regmap_update_bits(priv->regmap, CAP1106_REG_MAIN_CONTROL, + CAP1106_REG_MAIN_CONTROL_DLSEEP, + sleep ? CAP1106_REG_MAIN_CONTROL_DLSEEP : 0); +} + +static int cap1106_input_open(struct input_dev *idev) +{ + struct cap1106_priv *priv = input_get_drvdata(idev); + + return cap1106_set_sleep(priv, false); +} + +static void cap1106_input_close(struct input_dev *idev) +{ + struct cap1106_priv *priv = input_get_drvdata(idev); + + cap1106_set_sleep(priv, true); +} + +static int cap1106_i2c_probe(struct i2c_client *i2c_client, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c_client->dev; + struct cap1106_priv *priv; + struct device_node *node; + int i, error, irq, gain = 0; + unsigned int val, rev; + u32 gain32, keycodes[CAP1106_NUM_CHN]; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = devm_regmap_init_i2c(i2c_client, &cap1106_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + error = regmap_read(priv->regmap, CAP1106_REG_PRODUCT_ID, &val); + if (error) + return error; + + if (val != CAP1106_PRODUCT_ID) { + dev_err(dev, "Product ID: Got 0x%02x, expected 0x%02x\n", + val, CAP1106_PRODUCT_ID); + return -ENODEV; + } + + error = regmap_read(priv->regmap, CAP1106_REG_MANUFACTURER_ID, &val); + if (error) + return error; + + if (val != CAP1106_MANUFACTURER_ID) { + dev_err(dev, "Manufacturer ID: Got 0x%02x, expected 0x%02x\n", + val, CAP1106_MANUFACTURER_ID); + return -ENODEV; + } + + error = regmap_read(priv->regmap, CAP1106_REG_REVISION, &rev); + if (error < 0) + return error; + + dev_info(dev, "CAP1106 detected, revision 0x%02x\n", rev); + i2c_set_clientdata(i2c_client, priv); + node = dev->of_node; + + if (!of_property_read_u32(node, "microchip,sensor-gain", &gain32)) { + if (is_power_of_2(gain32) && gain32 <= 8) + gain = ilog2(gain32); + else + dev_err(dev, "Invalid sensor-gain value %d\n", gain32); + } + + BUILD_BUG_ON(ARRAY_SIZE(keycodes) != ARRAY_SIZE(priv->keycodes)); + + /* Provide some useful defaults */ + for (i = 0; i < ARRAY_SIZE(keycodes); i++) + keycodes[i] = KEY_A + i; + + of_property_read_u32_array(node, "linux,keycodes", + keycodes, ARRAY_SIZE(keycodes)); + + for (i = 0; i < ARRAY_SIZE(keycodes); i++) + priv->keycodes[i] = keycodes[i]; + + error = regmap_update_bits(priv->regmap, CAP1106_REG_MAIN_CONTROL, + CAP1106_REG_MAIN_CONTROL_GAIN_MASK, + gain << CAP1106_REG_MAIN_CONTROL_GAIN_SHIFT); + if (error) + return error; + + /* Disable autorepeat. The Linux input system has its own handling. */ + error = regmap_write(priv->regmap, CAP1106_REG_REPEAT_RATE, 0); + if (error) + return error; + + priv->idev = devm_input_allocate_device(dev); + if (!priv->idev) + return -ENOMEM; + + priv->idev->name = "CAP1106 capacitive touch sensor"; + priv->idev->id.bustype = BUS_I2C; + priv->idev->evbit[0] = BIT_MASK(EV_KEY); + + if (of_property_read_bool(node, "autorepeat")) + __set_bit(EV_REP, priv->idev->evbit); + + for (i = 0; i < CAP1106_NUM_CHN; i++) + __set_bit(priv->keycodes[i], priv->idev->keybit); + + priv->idev->id.vendor = CAP1106_MANUFACTURER_ID; + priv->idev->id.product = CAP1106_PRODUCT_ID; + priv->idev->id.version = rev; + + priv->idev->open = cap1106_input_open; + priv->idev->close = cap1106_input_close; + + input_set_drvdata(priv->idev, priv); + + /* + * Put the device in deep sleep mode for now. + * ->open() will bring it back once the it is actually needed. + */ + cap1106_set_sleep(priv, true); + + error = input_register_device(priv->idev); + if (error) + return error; + + irq = irq_of_parse_and_map(node, 0); + if (!irq) { + dev_err(dev, "Unable to parse or map IRQ\n"); + return -ENXIO; + } + + error = devm_request_threaded_irq(dev, irq, NULL, cap1106_thread_func, + IRQF_ONESHOT, dev_name(dev), priv); + if (error) + return error; + + return 0; +} + +static const struct of_device_id cap1106_dt_ids[] = { + { .compatible = "microchip,cap1106", }, + {} +}; +MODULE_DEVICE_TABLE(of, cap1106_dt_ids); + +static const struct i2c_device_id cap1106_i2c_ids[] = { + { "cap1106", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, cap1106_i2c_ids); + +static struct i2c_driver cap1106_i2c_driver = { + .driver = { + .name = "cap1106", + .owner = THIS_MODULE, + .of_match_table = cap1106_dt_ids, + }, + .id_table = cap1106_i2c_ids, + .probe = cap1106_i2c_probe, +}; + +module_i2c_driver(cap1106_i2c_driver); + +MODULE_ALIAS("platform:cap1106"); +MODULE_DESCRIPTION("Microchip CAP1106 driver"); +MODULE_AUTHOR("Daniel Mack <linux@zonque.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index 8280cb1..20a99c3 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -531,8 +531,7 @@ static int imx_keypad_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int imx_kbd_suspend(struct device *dev) +static int __maybe_unused imx_kbd_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct imx_keypad *kbd = platform_get_drvdata(pdev); @@ -552,7 +551,7 @@ static int imx_kbd_suspend(struct device *dev) return 0; } -static int imx_kbd_resume(struct device *dev) +static int __maybe_unused imx_kbd_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct imx_keypad *kbd = platform_get_drvdata(pdev); @@ -575,7 +574,6 @@ err_clk: return ret; } -#endif static SIMPLE_DEV_PM_OPS(imx_kbd_pm_ops, imx_kbd_suspend, imx_kbd_resume); diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c index 430b545..faa6da5 100644 --- a/drivers/input/keyboard/max7359_keypad.c +++ b/drivers/input/keyboard/max7359_keypad.c @@ -203,12 +203,17 @@ static int max7359_probe(struct i2c_client *client, dev_dbg(&client->dev, "keys FIFO is 0x%02x\n", ret); - keypad = kzalloc(sizeof(struct max7359_keypad), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!keypad || !input_dev) { + keypad = devm_kzalloc(&client->dev, sizeof(struct max7359_keypad), + GFP_KERNEL); + if (!keypad) { dev_err(&client->dev, "failed to allocate memory\n"); - error = -ENOMEM; - goto failed_free_mem; + return -ENOMEM; + } + + input_dev = devm_input_allocate_device(&client->dev); + if (!input_dev) { + dev_err(&client->dev, "failed to allocate input device\n"); + return -ENOMEM; } keypad->client = client; @@ -230,19 +235,20 @@ static int max7359_probe(struct i2c_client *client, max7359_build_keycode(keypad, keymap_data); - error = request_threaded_irq(client->irq, NULL, max7359_interrupt, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - client->name, keypad); + error = devm_request_threaded_irq(&client->dev, client->irq, NULL, + max7359_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + client->name, keypad); if (error) { dev_err(&client->dev, "failed to register interrupt\n"); - goto failed_free_mem; + return error; } /* Register the input device */ error = input_register_device(input_dev); if (error) { dev_err(&client->dev, "failed to register input device\n"); - goto failed_free_irq; + return error; } /* Initialize MAX7359 */ @@ -252,24 +258,6 @@ static int max7359_probe(struct i2c_client *client, device_init_wakeup(&client->dev, 1); return 0; - -failed_free_irq: - free_irq(client->irq, keypad); -failed_free_mem: - input_free_device(input_dev); - kfree(keypad); - return error; -} - -static int max7359_remove(struct i2c_client *client) -{ - struct max7359_keypad *keypad = i2c_get_clientdata(client); - - free_irq(client->irq, keypad); - input_unregister_device(keypad->input_dev); - kfree(keypad); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -313,7 +301,6 @@ static struct i2c_driver max7359_i2c_driver = { .pm = &max7359_pm, }, .probe = max7359_probe, - .remove = max7359_remove, .id_table = max7359_ids, }; diff --git a/drivers/input/misc/keyspan_remote.c b/drivers/input/misc/keyspan_remote.c index 01f3b5b..a3fe4a9 100644 --- a/drivers/input/misc/keyspan_remote.c +++ b/drivers/input/misc/keyspan_remote.c @@ -392,7 +392,6 @@ static void keyspan_irq_recv(struct urb *urb) default: goto resubmit; - break; } if (debug) diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index 5a6334b..e34dfc2 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -83,6 +83,9 @@ soc_button_device_create(struct pnp_dev *pdev, sizeof(*gpio_keys_pdata) + sizeof(*gpio_keys) * MAX_NBUTTONS, GFP_KERNEL); + if (!gpio_keys_pdata) + return ERR_PTR(-ENOMEM); + gpio_keys = (void *)(gpio_keys_pdata + 1); for (info = button_info; info->name; info++) { diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 8569362..421e29e 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -311,7 +311,14 @@ static int uinput_open(struct inode *inode, struct file *file) static int uinput_validate_absbits(struct input_dev *dev) { unsigned int cnt; - int retval = 0; + int nslot; + + if (!test_bit(EV_ABS, dev->evbit)) + return 0; + + /* + * Check if absmin/absmax/absfuzz/absflat are sane. + */ for (cnt = 0; cnt < ABS_CNT; cnt++) { int min, max; @@ -327,8 +334,7 @@ static int uinput_validate_absbits(struct input_dev *dev) UINPUT_NAME, cnt, input_abs_get_min(dev, cnt), input_abs_get_max(dev, cnt)); - retval = -EINVAL; - break; + return -EINVAL; } if (input_abs_get_flat(dev, cnt) > @@ -340,11 +346,18 @@ static int uinput_validate_absbits(struct input_dev *dev) input_abs_get_flat(dev, cnt), input_abs_get_min(dev, cnt), input_abs_get_max(dev, cnt)); - retval = -EINVAL; - break; + return -EINVAL; } } - return retval; + + if (test_bit(ABS_MT_SLOT, dev->absbit)) { + nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; + input_mt_init_slots(dev, nslot, 0); + } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { + input_set_events_per_packet(dev, 60); + } + + return 0; } static int uinput_allocate_device(struct uinput_device *udev) @@ -410,19 +423,9 @@ static int uinput_setup_device(struct uinput_device *udev, input_abs_set_flat(dev, i, user_dev->absflat[i]); } - /* check if absmin/absmax/absfuzz/absflat are filled as - * told in Documentation/input/input-programming.txt */ - if (test_bit(EV_ABS, dev->evbit)) { - retval = uinput_validate_absbits(dev); - if (retval < 0) - goto exit; - if (test_bit(ABS_MT_SLOT, dev->absbit)) { - int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; - input_mt_init_slots(dev, nslot, 0); - } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { - input_set_events_per_packet(dev, 60); - } - } + retval = uinput_validate_absbits(dev); + if (retval < 0) + goto exit; udev->state = UIST_SETUP_COMPLETE; retval = count; @@ -720,6 +723,12 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, } switch (cmd) { + case UI_GET_VERSION: + if (put_user(UINPUT_VERSION, + (unsigned int __user *)p)) + retval = -EFAULT; + goto out; + case UI_DEV_CREATE: retval = uinput_create_device(udev); goto out; diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index fb15c64..a59a1a6 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -99,6 +99,8 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = { #define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */ #define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with 6-byte ALPS packet */ +#define ALPS_IS_RUSHMORE 0x100 /* device is a rushmore */ +#define ALPS_BUTTONPAD 0x200 /* device is a clickpad */ static const struct alps_model_info alps_model_data[] = { { { 0x32, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ @@ -281,11 +283,10 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) * * The bitmaps don't have enough data to track fingers, so this function * only generates points representing a bounding box of at most two contacts. - * These two points are returned in x1, y1, x2, and y2. + * These two points are returned in fields->mt. */ static void alps_process_bitmap_dolphin(struct alps_data *priv, - struct alps_fields *fields, - int *x1, int *y1, int *x2, int *y2) + struct alps_fields *fields) { int box_middle_x, box_middle_y; unsigned int x_map, y_map; @@ -308,8 +309,6 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv, if (x_msb > priv->x_bits || y_msb > priv->y_bits) return; - *x1 = *y1 = *x2 = *y2 = 0; - if (fields->fingers > 1) { start_bit = priv->x_bits - x_msb; end_bit = priv->x_bits - x_lsb; @@ -320,10 +319,35 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv, end_bit = y_msb - 1; box_middle_y = (priv->y_max * (start_bit + end_bit)) / (2 * (priv->y_bits - 1)); - *x1 = fields->x; - *y1 = fields->y; - *x2 = 2 * box_middle_x - *x1; - *y2 = 2 * box_middle_y - *y1; + fields->mt[0] = fields->st; + fields->mt[1].x = 2 * box_middle_x - fields->mt[0].x; + fields->mt[1].y = 2 * box_middle_y - fields->mt[0].y; + } +} + +static void alps_get_bitmap_points(unsigned int map, + struct alps_bitmap_point *low, + struct alps_bitmap_point *high, + int *fingers) +{ + struct alps_bitmap_point *point; + int i, bit, prev_bit = 0; + + point = low; + for (i = 0; map != 0; i++, map >>= 1) { + bit = map & 1; + if (bit) { + if (!prev_bit) { + point->start_bit = i; + point->num_bits = 0; + (*fingers)++; + } + point->num_bits++; + } else { + if (prev_bit) + point = high; + } + prev_bit = bit; } } @@ -334,71 +358,21 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv, * * The bitmaps don't have enough data to track fingers, so this function * only generates points representing a bounding box of all contacts. - * These points are returned in x1, y1, x2, and y2 when the return value + * These points are returned in fields->mt when the return value * is greater than 0. */ static int alps_process_bitmap(struct alps_data *priv, - unsigned int x_map, unsigned int y_map, - int *x1, int *y1, int *x2, int *y2) + struct alps_fields *fields) { - struct alps_bitmap_point { - int start_bit; - int num_bits; - }; - - int fingers_x = 0, fingers_y = 0, fingers; - int i, bit, prev_bit; + int i, fingers_x = 0, fingers_y = 0, fingers; struct alps_bitmap_point x_low = {0,}, x_high = {0,}; struct alps_bitmap_point y_low = {0,}, y_high = {0,}; - struct alps_bitmap_point *point; - if (!x_map || !y_map) + if (!fields->x_map || !fields->y_map) return 0; - *x1 = *y1 = *x2 = *y2 = 0; - - prev_bit = 0; - point = &x_low; - for (i = 0; x_map != 0; i++, x_map >>= 1) { - bit = x_map & 1; - if (bit) { - if (!prev_bit) { - point->start_bit = i; - fingers_x++; - } - point->num_bits++; - } else { - if (prev_bit) - point = &x_high; - else - point->num_bits = 0; - } - prev_bit = bit; - } - - /* - * y bitmap is reversed for what we need (lower positions are in - * higher bits), so we process from the top end. - */ - y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - priv->y_bits); - prev_bit = 0; - point = &y_low; - for (i = 0; y_map != 0; i++, y_map <<= 1) { - bit = y_map & (1 << (sizeof(y_map) * BITS_PER_BYTE - 1)); - if (bit) { - if (!prev_bit) { - point->start_bit = i; - fingers_y++; - } - point->num_bits++; - } else { - if (prev_bit) - point = &y_high; - else - point->num_bits = 0; - } - prev_bit = bit; - } + alps_get_bitmap_points(fields->x_map, &x_low, &x_high, &fingers_x); + alps_get_bitmap_points(fields->y_map, &y_low, &y_high, &fingers_y); /* * Fingers can overlap, so we use the maximum count of fingers @@ -407,58 +381,91 @@ static int alps_process_bitmap(struct alps_data *priv, fingers = max(fingers_x, fingers_y); /* - * If total fingers is > 1 but either axis reports only a single - * contact, we have overlapping or adjacent fingers. For the - * purposes of creating a bounding box, divide the single contact - * (roughly) equally between the two points. + * If an axis reports only a single contact, we have overlapping or + * adjacent fingers. Divide the single contact between the two points. */ - if (fingers > 1) { - if (fingers_x == 1) { - i = x_low.num_bits / 2; - x_low.num_bits = x_low.num_bits - i; - x_high.start_bit = x_low.start_bit + i; - x_high.num_bits = max(i, 1); - } else if (fingers_y == 1) { - i = y_low.num_bits / 2; - y_low.num_bits = y_low.num_bits - i; - y_high.start_bit = y_low.start_bit + i; - y_high.num_bits = max(i, 1); - } + if (fingers_x == 1) { + i = (x_low.num_bits - 1) / 2; + x_low.num_bits = x_low.num_bits - i; + x_high.start_bit = x_low.start_bit + i; + x_high.num_bits = max(i, 1); + } + if (fingers_y == 1) { + i = (y_low.num_bits - 1) / 2; + y_low.num_bits = y_low.num_bits - i; + y_high.start_bit = y_low.start_bit + i; + y_high.num_bits = max(i, 1); } - *x1 = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) / - (2 * (priv->x_bits - 1)); - *y1 = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) / - (2 * (priv->y_bits - 1)); - - if (fingers > 1) { - *x2 = (priv->x_max * - (2 * x_high.start_bit + x_high.num_bits - 1)) / - (2 * (priv->x_bits - 1)); - *y2 = (priv->y_max * - (2 * y_high.start_bit + y_high.num_bits - 1)) / - (2 * (priv->y_bits - 1)); + fields->mt[0].x = + (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + fields->mt[0].y = + (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) / + (2 * (priv->y_bits - 1)); + + fields->mt[1].x = + (priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + fields->mt[1].y = + (priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) / + (2 * (priv->y_bits - 1)); + + /* y-bitmap order is reversed, except on rushmore */ + if (!(priv->flags & ALPS_IS_RUSHMORE)) { + fields->mt[0].y = priv->y_max - fields->mt[0].y; + fields->mt[1].y = priv->y_max - fields->mt[1].y; } return fingers; } -static void alps_set_slot(struct input_dev *dev, int slot, bool active, - int x, int y) +static void alps_set_slot(struct input_dev *dev, int slot, int x, int y) { input_mt_slot(dev, slot); - input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); - if (active) { - input_report_abs(dev, ABS_MT_POSITION_X, x); - input_report_abs(dev, ABS_MT_POSITION_Y, y); - } + input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); + input_report_abs(dev, ABS_MT_POSITION_X, x); + input_report_abs(dev, ABS_MT_POSITION_Y, y); } -static void alps_report_semi_mt_data(struct input_dev *dev, int num_fingers, - int x1, int y1, int x2, int y2) +static void alps_report_mt_data(struct psmouse *psmouse, int n) { - alps_set_slot(dev, 0, num_fingers != 0, x1, y1); - alps_set_slot(dev, 1, num_fingers == 2, x2, y2); + struct alps_data *priv = psmouse->private; + struct input_dev *dev = psmouse->dev; + struct alps_fields *f = &priv->f; + int i, slot[MAX_TOUCHES]; + + input_mt_assign_slots(dev, slot, f->mt, n); + for (i = 0; i < n; i++) + alps_set_slot(dev, slot[i], f->mt[i].x, f->mt[i].y); + + input_mt_sync_frame(dev); +} + +static void alps_report_semi_mt_data(struct psmouse *psmouse, int fingers) +{ + struct alps_data *priv = psmouse->private; + struct input_dev *dev = psmouse->dev; + struct alps_fields *f = &priv->f; + + /* Use st data when we don't have mt data */ + if (fingers < 2) { + f->mt[0].x = f->st.x; + f->mt[0].y = f->st.y; + fingers = f->pressure > 0 ? 1 : 0; + } + + alps_report_mt_data(psmouse, (fingers <= 2) ? fingers : 2); + + input_mt_report_finger_count(dev, fingers); + + input_report_key(dev, BTN_LEFT, f->left); + input_report_key(dev, BTN_RIGHT, f->right); + input_report_key(dev, BTN_MIDDLE, f->middle); + + input_report_abs(dev, ABS_PRESSURE, f->pressure); + + input_sync(dev); } static void alps_process_trackstick_packet_v3(struct psmouse *psmouse) @@ -532,7 +539,7 @@ static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p) f->ts_middle = !!(p[3] & 0x40); } -static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p, +static int alps_decode_pinnacle(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse) { f->first_mp = !!(p[4] & 0x40); @@ -546,24 +553,31 @@ static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p, ((p[2] & 0x7f) << 1) | (p[4] & 0x01); - f->x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | + f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | ((p[0] & 0x30) >> 4); - f->y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); - f->z = p[5] & 0x7f; + f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); + f->pressure = p[5] & 0x7f; alps_decode_buttons_v3(f, p); + + return 0; } -static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p, +static int alps_decode_rushmore(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse) { alps_decode_pinnacle(f, p, psmouse); + /* Rushmore's packet decode has a bit difference with Pinnacle's */ + f->is_mp = !!(p[5] & 0x40); + f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1; f->x_map |= (p[5] & 0x10) << 11; f->y_map |= (p[5] & 0x20) << 6; + + return 0; } -static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p, +static int alps_decode_dolphin(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse) { u64 palm_data = 0; @@ -573,9 +587,9 @@ static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p, f->is_mp = !!(p[0] & 0x20); if (!f->is_mp) { - f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7)); - f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3)); - f->z = (p[0] & 4) ? 0 : p[5] & 0x7f; + f->st.x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7)); + f->st.y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3)); + f->pressure = (p[0] & 4) ? 0 : p[5] & 0x7f; alps_decode_buttons_v3(f, p); } else { f->fingers = ((p[0] & 0x6) >> 1 | @@ -596,19 +610,21 @@ static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p, f->x_map = (palm_data >> priv->y_bits) & (BIT(priv->x_bits) - 1); } + + return 0; } static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; unsigned char *packet = psmouse->packet; - struct input_dev *dev = psmouse->dev; struct input_dev *dev2 = priv->dev2; - int x1 = 0, y1 = 0, x2 = 0, y2 = 0; - int fingers = 0, bmap_fn; - struct alps_fields f = {0}; + struct alps_fields *f = &priv->f; + int fingers = 0; - priv->decode_fields(&f, packet, psmouse); + memset(f, 0, sizeof(*f)); + + priv->decode_fields(f, packet, psmouse); /* * There's no single feature of touchpad position and bitmap packets @@ -623,22 +639,14 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) * packet. Check for this, and when it happens process the * position packet as usual. */ - if (f.is_mp) { - fingers = f.fingers; + if (f->is_mp) { + fingers = f->fingers; if (priv->proto_version == ALPS_PROTO_V3) { - bmap_fn = alps_process_bitmap(priv, f.x_map, - f.y_map, &x1, &y1, - &x2, &y2); - - /* - * We shouldn't report more than one finger if - * we don't have two coordinates. - */ - if (fingers > 1 && bmap_fn < 2) - fingers = bmap_fn; + if (alps_process_bitmap(priv, f) == 0) + fingers = 0; /* Use st data */ /* Now process position packet */ - priv->decode_fields(&f, priv->multi_data, + priv->decode_fields(f, priv->multi_data, psmouse); } else { /* @@ -647,15 +655,14 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) * calculate Pt2, so we need to do position * packet decode first. */ - priv->decode_fields(&f, priv->multi_data, + priv->decode_fields(f, priv->multi_data, psmouse); /* * Since Dolphin's finger number is reliable, * there is no need to compare with bmap_fn. */ - alps_process_bitmap_dolphin(priv, &f, &x1, &y1, - &x2, &y2); + alps_process_bitmap_dolphin(priv, f); } } else { priv->multi_packet = 0; @@ -670,10 +677,10 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) * out misidentified bitmap packets, we reject anything with this * bit set. */ - if (f.is_mp) + if (f->is_mp) return; - if (!priv->multi_packet && f.first_mp) { + if (!priv->multi_packet && f->first_mp) { priv->multi_packet = 1; memcpy(priv->multi_data, packet, sizeof(priv->multi_data)); return; @@ -687,44 +694,15 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) * with x, y, and z all zero, so these seem to be flukes. * Ignore them. */ - if (f.x && f.y && !f.z) + if (f->st.x && f->st.y && !f->pressure) return; - /* - * If we don't have MT data or the bitmaps were empty, we have - * to rely on ST data. - */ - if (!fingers) { - x1 = f.x; - y1 = f.y; - fingers = f.z > 0 ? 1 : 0; - } - - if (f.z >= 64) - input_report_key(dev, BTN_TOUCH, 1); - else - input_report_key(dev, BTN_TOUCH, 0); - - alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); - - input_mt_report_finger_count(dev, fingers); - - input_report_key(dev, BTN_LEFT, f.left); - input_report_key(dev, BTN_RIGHT, f.right); - input_report_key(dev, BTN_MIDDLE, f.middle); - - if (f.z > 0) { - input_report_abs(dev, ABS_X, f.x); - input_report_abs(dev, ABS_Y, f.y); - } - input_report_abs(dev, ABS_PRESSURE, f.z); - - input_sync(dev); + alps_report_semi_mt_data(psmouse, fingers); if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { - input_report_key(dev2, BTN_LEFT, f.ts_left); - input_report_key(dev2, BTN_RIGHT, f.ts_right); - input_report_key(dev2, BTN_MIDDLE, f.ts_middle); + input_report_key(dev2, BTN_LEFT, f->ts_left); + input_report_key(dev2, BTN_RIGHT, f->ts_right); + input_report_key(dev2, BTN_MIDDLE, f->ts_middle); input_sync(dev2); } } @@ -823,13 +801,8 @@ static void alps_process_packet_v4(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; unsigned char *packet = psmouse->packet; - struct input_dev *dev = psmouse->dev; + struct alps_fields *f = &priv->f; int offset; - int x, y, z; - int left, right; - int x1, y1, x2, y2; - int fingers = 0; - unsigned int x_bitmap, y_bitmap; /* * v4 has a 6-byte encoding for bitmap data, but this data is @@ -851,71 +824,207 @@ static void alps_process_packet_v4(struct psmouse *psmouse) if (++priv->multi_packet > 2) { priv->multi_packet = 0; - x_bitmap = ((priv->multi_data[2] & 0x1f) << 10) | + f->x_map = ((priv->multi_data[2] & 0x1f) << 10) | ((priv->multi_data[3] & 0x60) << 3) | ((priv->multi_data[0] & 0x3f) << 2) | ((priv->multi_data[1] & 0x60) >> 5); - y_bitmap = ((priv->multi_data[5] & 0x01) << 10) | + f->y_map = ((priv->multi_data[5] & 0x01) << 10) | ((priv->multi_data[3] & 0x1f) << 5) | (priv->multi_data[1] & 0x1f); - fingers = alps_process_bitmap(priv, x_bitmap, y_bitmap, - &x1, &y1, &x2, &y2); - - /* Store MT data.*/ - priv->fingers = fingers; - priv->x1 = x1; - priv->x2 = x2; - priv->y1 = y1; - priv->y2 = y2; + f->fingers = alps_process_bitmap(priv, f); } - left = packet[4] & 0x01; - right = packet[4] & 0x02; + f->left = packet[4] & 0x01; + f->right = packet[4] & 0x02; - x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) | - ((packet[0] & 0x30) >> 4); - y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); - z = packet[5] & 0x7f; + f->st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) | + ((packet[0] & 0x30) >> 4); + f->st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); + f->pressure = packet[5] & 0x7f; - /* - * If there were no contacts in the bitmap, use ST - * points in MT reports. - * If there were two contacts or more, report MT data. - */ - if (priv->fingers < 2) { - x1 = x; - y1 = y; - fingers = z > 0 ? 1 : 0; - } else { - fingers = priv->fingers; - x1 = priv->x1; - x2 = priv->x2; - y1 = priv->y1; - y2 = priv->y2; + alps_report_semi_mt_data(psmouse, f->fingers); +} + +static bool alps_is_valid_package_v7(struct psmouse *psmouse) +{ + switch (psmouse->pktcnt) { + case 3: + return (psmouse->packet[2] & 0x40) == 0x40; + case 4: + return (psmouse->packet[3] & 0x48) == 0x48; + case 6: + return (psmouse->packet[5] & 0x40) == 0x00; } + return true; +} - if (z >= 64) - input_report_key(dev, BTN_TOUCH, 1); +static unsigned char alps_get_packet_id_v7(char *byte) +{ + unsigned char packet_id; + + if (byte[4] & 0x40) + packet_id = V7_PACKET_ID_TWO; + else if (byte[4] & 0x01) + packet_id = V7_PACKET_ID_MULTI; + else if ((byte[0] & 0x10) && !(byte[4] & 0x43)) + packet_id = V7_PACKET_ID_NEW; + else if (byte[1] == 0x00 && byte[4] == 0x00) + packet_id = V7_PACKET_ID_IDLE; else - input_report_key(dev, BTN_TOUCH, 0); + packet_id = V7_PACKET_ID_UNKNOWN; - alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); + return packet_id; +} - input_mt_report_finger_count(dev, fingers); +static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt, + unsigned char *pkt, + unsigned char pkt_id) +{ + mt[0].x = ((pkt[2] & 0x80) << 4); + mt[0].x |= ((pkt[2] & 0x3F) << 5); + mt[0].x |= ((pkt[3] & 0x30) >> 1); + mt[0].x |= (pkt[3] & 0x07); + mt[0].y = (pkt[1] << 3) | (pkt[0] & 0x07); + + mt[1].x = ((pkt[3] & 0x80) << 4); + mt[1].x |= ((pkt[4] & 0x80) << 3); + mt[1].x |= ((pkt[4] & 0x3F) << 4); + mt[1].y = ((pkt[5] & 0x80) << 3); + mt[1].y |= ((pkt[5] & 0x3F) << 4); + + switch (pkt_id) { + case V7_PACKET_ID_TWO: + mt[1].x &= ~0x000F; + mt[1].y |= 0x000F; + break; - input_report_key(dev, BTN_LEFT, left); - input_report_key(dev, BTN_RIGHT, right); + case V7_PACKET_ID_MULTI: + mt[1].x &= ~0x003F; + mt[1].y &= ~0x0020; + mt[1].y |= ((pkt[4] & 0x02) << 4); + mt[1].y |= 0x001F; + break; - if (z > 0) { - input_report_abs(dev, ABS_X, x); - input_report_abs(dev, ABS_Y, y); + case V7_PACKET_ID_NEW: + mt[1].x &= ~0x003F; + mt[1].x |= (pkt[0] & 0x20); + mt[1].y |= 0x000F; + break; } - input_report_abs(dev, ABS_PRESSURE, z); + + mt[0].y = 0x7FF - mt[0].y; + mt[1].y = 0x7FF - mt[1].y; +} + +static int alps_get_mt_count(struct input_mt_pos *mt) +{ + int i; + + for (i = 0; i < MAX_TOUCHES && mt[i].x != 0 && mt[i].y != 0; i++) + /* empty */; + + return i; +} + +static int alps_decode_packet_v7(struct alps_fields *f, + unsigned char *p, + struct psmouse *psmouse) +{ + unsigned char pkt_id; + + pkt_id = alps_get_packet_id_v7(p); + if (pkt_id == V7_PACKET_ID_IDLE) + return 0; + if (pkt_id == V7_PACKET_ID_UNKNOWN) + return -1; + + alps_get_finger_coordinate_v7(f->mt, p, pkt_id); + + if (pkt_id == V7_PACKET_ID_TWO || pkt_id == V7_PACKET_ID_MULTI) { + f->left = (p[0] & 0x80) >> 7; + f->right = (p[0] & 0x20) >> 5; + f->middle = (p[0] & 0x10) >> 4; + } + + if (pkt_id == V7_PACKET_ID_TWO) + f->fingers = alps_get_mt_count(f->mt); + else if (pkt_id == V7_PACKET_ID_MULTI) + f->fingers = 3 + (p[5] & 0x03); + + return 0; +} + +static void alps_process_trackstick_packet_v7(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + unsigned char *packet = psmouse->packet; + struct input_dev *dev2 = priv->dev2; + int x, y, z, left, right, middle; + + /* + * b7 b6 b5 b4 b3 b2 b1 b0 + * Byte0 0 1 0 0 1 0 0 0 + * Byte1 1 1 * * 1 M R L + * Byte2 X7 1 X5 X4 X3 X2 X1 X0 + * Byte3 Z6 1 Y6 X6 1 Y2 Y1 Y0 + * Byte4 Y7 0 Y5 Y4 Y3 1 1 0 + * Byte5 T&P 0 Z5 Z4 Z3 Z2 Z1 Z0 + * M / R / L: Middle / Right / Left button + */ + + x = ((packet[2] & 0xbf)) | ((packet[3] & 0x10) << 2); + y = (packet[3] & 0x07) | (packet[4] & 0xb8) | + ((packet[3] & 0x20) << 1); + z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1); + + left = (packet[1] & 0x01); + right = (packet[1] & 0x02) >> 1; + middle = (packet[1] & 0x04) >> 2; + + /* Divide 2 since trackpoint's speed is too fast */ + input_report_rel(dev2, REL_X, (char)x / 2); + input_report_rel(dev2, REL_Y, -((char)y / 2)); + + input_report_key(dev2, BTN_LEFT, left); + input_report_key(dev2, BTN_RIGHT, right); + input_report_key(dev2, BTN_MIDDLE, middle); + + input_sync(dev2); +} + +static void alps_process_touchpad_packet_v7(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + struct input_dev *dev = psmouse->dev; + struct alps_fields *f = &priv->f; + + memset(f, 0, sizeof(*f)); + + if (priv->decode_fields(f, psmouse->packet, psmouse)) + return; + + alps_report_mt_data(psmouse, alps_get_mt_count(f->mt)); + + input_mt_report_finger_count(dev, f->fingers); + + input_report_key(dev, BTN_LEFT, f->left); + input_report_key(dev, BTN_RIGHT, f->right); + input_report_key(dev, BTN_MIDDLE, f->middle); input_sync(dev); } +static void alps_process_packet_v7(struct psmouse *psmouse) +{ + unsigned char *packet = psmouse->packet; + + if (packet[0] == 0x48 && (packet[4] & 0x47) == 0x06) + alps_process_trackstick_packet_v7(psmouse); + else + alps_process_touchpad_packet_v7(psmouse); +} + static void alps_report_bare_ps2_packet(struct psmouse *psmouse, unsigned char packet[], bool report_buttons) @@ -1080,6 +1189,14 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) return PSMOUSE_BAD_DATA; } + if (priv->proto_version == ALPS_PROTO_V7 && + !alps_is_valid_package_v7(psmouse)) { + psmouse_dbg(psmouse, "refusing packet[%i] = %x\n", + psmouse->pktcnt - 1, + psmouse->packet[psmouse->pktcnt - 1]); + return PSMOUSE_BAD_DATA; + } + if (psmouse->pktcnt == psmouse->pktsize) { priv->process_packet(psmouse); return PSMOUSE_FULL_PACKET; @@ -1192,6 +1309,22 @@ static int alps_rpt_cmd(struct psmouse *psmouse, int init_command, return 0; } +static bool alps_check_valid_firmware_id(unsigned char id[]) +{ + if (id[0] == 0x73) + return true; + + if (id[0] == 0x88 && + (id[1] == 0x07 || + id[1] == 0x08 || + (id[1] & 0xf0) == 0xb0 || + (id[1] & 0xf0) == 0xc0)) { + return true; + } + + return false; +} + static int alps_enter_command_mode(struct psmouse *psmouse) { unsigned char param[4]; @@ -1201,8 +1334,7 @@ static int alps_enter_command_mode(struct psmouse *psmouse) return -1; } - if ((param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) && - param[0] != 0x73) { + if (!alps_check_valid_firmware_id(param)) { psmouse_dbg(psmouse, "unknown response while entering command mode\n"); return -1; @@ -1660,6 +1792,45 @@ error: return -1; } +static int alps_get_v3_v7_resolution(struct psmouse *psmouse, int reg_pitch) +{ + int reg, x_pitch, y_pitch, x_electrode, y_electrode, x_phys, y_phys; + struct alps_data *priv = psmouse->private; + + reg = alps_command_mode_read_reg(psmouse, reg_pitch); + if (reg < 0) + return reg; + + x_pitch = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */ + x_pitch = 50 + 2 * x_pitch; /* In 0.1 mm units */ + + y_pitch = (char)reg >> 4; /* sign extend upper 4 bits */ + y_pitch = 36 + 2 * y_pitch; /* In 0.1 mm units */ + + reg = alps_command_mode_read_reg(psmouse, reg_pitch + 1); + if (reg < 0) + return reg; + + x_electrode = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */ + x_electrode = 17 + x_electrode; + + y_electrode = (char)reg >> 4; /* sign extend upper 4 bits */ + y_electrode = 13 + y_electrode; + + x_phys = x_pitch * (x_electrode - 1); /* In 0.1 mm units */ + y_phys = y_pitch * (y_electrode - 1); /* In 0.1 mm units */ + + priv->x_res = priv->x_max * 10 / x_phys; /* units / mm */ + priv->y_res = priv->y_max * 10 / y_phys; /* units / mm */ + + psmouse_dbg(psmouse, + "pitch %dx%d num-electrodes %dx%d physical size %dx%d mm res %dx%d\n", + x_pitch, y_pitch, x_electrode, y_electrode, + x_phys / 10, y_phys / 10, priv->x_res, priv->y_res); + + return 0; +} + static int alps_hw_init_rushmore_v3(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; @@ -1680,6 +1851,9 @@ static int alps_hw_init_rushmore_v3(struct psmouse *psmouse) alps_command_mode_write_reg(psmouse, 0xc2cb, 0x00)) goto error; + if (alps_get_v3_v7_resolution(psmouse, 0xc2da)) + goto error; + reg_val = alps_command_mode_read_reg(psmouse, 0xc2c6); if (reg_val == -1) goto error; @@ -1856,6 +2030,35 @@ static int alps_hw_init_dolphin_v1(struct psmouse *psmouse) return 0; } +static int alps_hw_init_v7(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + int reg_val, ret = -1; + + if (alps_enter_command_mode(psmouse) || + alps_command_mode_read_reg(psmouse, 0xc2d9) == -1) + goto error; + + if (alps_get_v3_v7_resolution(psmouse, 0xc397)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64)) + goto error; + + reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4); + if (reg_val == -1) + goto error; + if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02)) + goto error; + + alps_exit_command_mode(psmouse); + return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); + +error: + alps_exit_command_mode(psmouse); + return ret; +} + static void alps_set_defaults(struct alps_data *priv) { priv->byte0 = 0x8f; @@ -1914,6 +2117,21 @@ static void alps_set_defaults(struct alps_data *priv) priv->x_max = 2047; priv->y_max = 1535; break; + case ALPS_PROTO_V7: + priv->hw_init = alps_hw_init_v7; + priv->process_packet = alps_process_packet_v7; + priv->decode_fields = alps_decode_packet_v7; + priv->set_abs_params = alps_set_abs_params_mt; + priv->nibble_commands = alps_v3_nibble_commands; + priv->addr_command = PSMOUSE_CMD_RESET_WRAP; + priv->x_max = 0xfff; + priv->y_max = 0x7ff; + priv->byte0 = 0x48; + priv->mask0 = 0x48; + + if (priv->fw_ver[1] != 0xba) + priv->flags |= ALPS_BUTTONPAD; + break; } } @@ -1972,6 +2190,9 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) alps_exit_command_mode(psmouse)) return -EIO; + /* Save the Firmware version */ + memcpy(priv->fw_ver, ec, 3); + if (alps_match_table(psmouse, priv, e7, ec) == 0) { return 0; } else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 && @@ -1982,6 +2203,12 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) return -EIO; else return 0; + } else if (ec[0] == 0x88 && + ((ec[1] & 0xf0) == 0xb0 || (ec[1] & 0xf0) == 0xc0)) { + priv->proto_version = ALPS_PROTO_V7; + alps_set_defaults(priv); + + return 0; } else if (ec[0] == 0x88 && ec[1] == 0x08) { priv->proto_version = ALPS_PROTO_V3; alps_set_defaults(priv); @@ -1990,6 +2217,7 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) priv->decode_fields = alps_decode_rushmore; priv->x_bits = 16; priv->y_bits = 12; + priv->flags |= ALPS_IS_RUSHMORE; /* hack to make addr_command, nibble_command available */ psmouse->private = priv; @@ -2044,17 +2272,21 @@ static void alps_set_abs_params_st(struct alps_data *priv, static void alps_set_abs_params_mt(struct alps_data *priv, struct input_dev *dev1) { - set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); - input_mt_init_slots(dev1, 2, 0); input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0); input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0); - set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit); + input_abs_set_res(dev1, ABS_MT_POSITION_X, priv->x_res); + input_abs_set_res(dev1, ABS_MT_POSITION_Y, priv->y_res); + + input_mt_init_slots(dev1, MAX_TOUCHES, INPUT_MT_POINTER | + INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK | INPUT_MT_SEMI_MT); + set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit); set_bit(BTN_TOOL_QUADTAP, dev1->keybit); - input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0); - input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0); + /* V7 is real multi-touch */ + if (priv->proto_version == ALPS_PROTO_V7) + clear_bit(INPUT_PROP_SEMI_MT, dev1->propbit); } int alps_init(struct psmouse *psmouse) @@ -2100,7 +2332,9 @@ int alps_init(struct psmouse *psmouse) dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); priv->set_abs_params(priv, dev1); - input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); + /* No pressure on V7 */ + if (priv->proto_version != ALPS_PROTO_V7) + input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); if (priv->flags & ALPS_WHEEL) { dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL); @@ -2117,6 +2351,9 @@ int alps_init(struct psmouse *psmouse) dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1); dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3); + } else if (priv->flags & ALPS_BUTTONPAD) { + set_bit(INPUT_PROP_BUTTONPAD, dev1->propbit); + clear_bit(BTN_RIGHT, dev1->keybit); } else { dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE); } diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 03f88b6..66240b4 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -12,17 +12,39 @@ #ifndef _ALPS_H #define _ALPS_H +#include <linux/input/mt.h> + #define ALPS_PROTO_V1 1 #define ALPS_PROTO_V2 2 #define ALPS_PROTO_V3 3 #define ALPS_PROTO_V4 4 #define ALPS_PROTO_V5 5 #define ALPS_PROTO_V6 6 +#define ALPS_PROTO_V7 7 /* t3btl t4s */ + +#define MAX_TOUCHES 2 #define DOLPHIN_COUNT_PER_ELECTRODE 64 #define DOLPHIN_PROFILE_XOFFSET 8 /* x-electrode offset */ #define DOLPHIN_PROFILE_YOFFSET 1 /* y-electrode offset */ +/* + * enum V7_PACKET_ID - defines the packet type for V7 + * V7_PACKET_ID_IDLE: There's no finger and no button activity. + * V7_PACKET_ID_TWO: There's one or two non-resting fingers on touchpad + * or there's button activities. + * V7_PACKET_ID_MULTI: There are at least three non-resting fingers. + * V7_PACKET_ID_NEW: The finger position in slot is not continues from + * previous packet. +*/ +enum V7_PACKET_ID { + V7_PACKET_ID_IDLE, + V7_PACKET_ID_TWO, + V7_PACKET_ID_MULTI, + V7_PACKET_ID_NEW, + V7_PACKET_ID_UNKNOWN, +}; + /** * struct alps_model_info - touchpad ID table * @signature: E7 response string to match. @@ -46,7 +68,7 @@ struct alps_model_info { unsigned char command_mode_resp; unsigned char proto_version; unsigned char byte0, mask0; - unsigned char flags; + int flags; }; /** @@ -65,14 +87,19 @@ struct alps_nibble_commands { unsigned char data; }; +struct alps_bitmap_point { + int start_bit; + int num_bits; +}; + /** * struct alps_fields - decoded version of the report packet * @x_map: Bitmap of active X positions for MT. * @y_map: Bitmap of active Y positions for MT. * @fingers: Number of fingers for MT. - * @x: X position for ST. - * @y: Y position for ST. - * @z: Z position for ST. + * @pressure: Pressure. + * @st: position for ST. + * @mt: position for MT. * @first_mp: Packet is the first of a multi-packet report. * @is_mp: Packet is part of a multi-packet report. * @left: Left touchpad button is active. @@ -86,9 +113,11 @@ struct alps_fields { unsigned int x_map; unsigned int y_map; unsigned int fingers; - unsigned int x; - unsigned int y; - unsigned int z; + + int pressure; + struct input_mt_pos st; + struct input_mt_pos mt[MAX_TOUCHES]; + unsigned int first_mp:1; unsigned int is_mp:1; @@ -113,6 +142,7 @@ struct alps_fields { * known format for this model. The first byte of the report, ANDed with * mask0, should match byte0. * @mask0: The mask used to check the first byte of the report. + * @fw_ver: cached copy of firmware version (EC report) * @flags: Additional device capabilities (passthrough port, trackstick, etc.). * @x_max: Largest possible X position value. * @y_max: Largest possible Y position value. @@ -125,11 +155,7 @@ struct alps_fields { * @prev_fin: Finger bit from previous packet. * @multi_packet: Multi-packet data in progress. * @multi_data: Saved multi-packet data. - * @x1: First X coordinate from last MT report. - * @x2: Second X coordinate from last MT report. - * @y1: First Y coordinate from last MT report. - * @y2: Second Y coordinate from last MT report. - * @fingers: Number of fingers from last MT report. + * @f: Decoded packet data fields. * @quirks: Bitmap of ALPS_QUIRK_*. * @timer: Timer for flushing out the final report packet in the stream. */ @@ -142,23 +168,25 @@ struct alps_data { int addr_command; unsigned char proto_version; unsigned char byte0, mask0; - unsigned char flags; + unsigned char fw_ver[3]; + int flags; int x_max; int y_max; int x_bits; int y_bits; + unsigned int x_res; + unsigned int y_res; int (*hw_init)(struct psmouse *psmouse); void (*process_packet)(struct psmouse *psmouse); - void (*decode_fields)(struct alps_fields *f, unsigned char *p, + int (*decode_fields)(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse); void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1); int prev_fin; int multi_packet; unsigned char multi_data[6]; - int x1, x2, y1, y2; - int fingers; + struct alps_fields f; u8 quirks; struct timer_list timer; }; diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c index 6132619..e74e5d6 100644 --- a/drivers/input/serio/hyperv-keyboard.c +++ b/drivers/input/serio/hyperv-keyboard.c @@ -170,6 +170,15 @@ static void hv_kbd_on_receive(struct hv_device *hv_dev, serio_interrupt(kbd_dev->hv_serio, scan_code, 0); } spin_unlock_irqrestore(&kbd_dev->lock, flags); + + /* + * Only trigger a wakeup on key down, otherwise + * "echo freeze > /sys/power/state" can't really enter the + * state because the Enter-UP can trigger a wakeup at once. + */ + if (!(info & IS_BREAK)) + pm_wakeup_event(&hv_dev->device, 0); + break; default: @@ -376,6 +385,9 @@ static int hv_kbd_probe(struct hv_device *hv_dev, goto err_close_vmbus; serio_register_port(kbd_dev->hv_serio); + + device_init_wakeup(&hv_dev->device, true); + return 0; err_close_vmbus: @@ -390,6 +402,7 @@ static int hv_kbd_remove(struct hv_device *hv_dev) { struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); + device_init_wakeup(&hv_dev->device, false); serio_unregister_port(kbd_dev->hv_serio); vmbus_close(hv_dev->channel); kfree(kbd_dev); diff --git a/drivers/input/tablet/Kconfig b/drivers/input/tablet/Kconfig index bed7cbf..623bb9e 100644 --- a/drivers/input/tablet/Kconfig +++ b/drivers/input/tablet/Kconfig @@ -73,20 +73,14 @@ config TABLET_USB_KBTAB To compile this driver as a module, choose M here: the module will be called kbtab. -config TABLET_USB_WACOM - tristate "Wacom Intuos/Graphire tablet support (USB)" - depends on USB_ARCH_HAS_HCD - select POWER_SUPPLY - select USB - select NEW_LEDS - select LEDS_CLASS +config TABLET_SERIAL_WACOM4 + tristate "Wacom protocol 4 serial tablet support" + select SERIO help - Say Y here if you want to use the USB version of the Wacom Intuos - or Graphire tablet. Make sure to say Y to "Mouse support" - (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" - (CONFIG_INPUT_EVDEV) as well. + Say Y here if you want to use Wacom protocol 4 serial tablets. + E.g. serial versions of the Cintiq, Graphire or Penpartner. To compile this driver as a module, choose M here: the - module will be called wacom. + module will be called wacom_serial4. endif diff --git a/drivers/input/tablet/Makefile b/drivers/input/tablet/Makefile index 3f6c252..2e13010 100644 --- a/drivers/input/tablet/Makefile +++ b/drivers/input/tablet/Makefile @@ -2,12 +2,10 @@ # Makefile for the tablet drivers # -# Multipart objects. -wacom-objs := wacom_wac.o wacom_sys.o obj-$(CONFIG_TABLET_USB_ACECAD) += acecad.o obj-$(CONFIG_TABLET_USB_AIPTEK) += aiptek.o obj-$(CONFIG_TABLET_USB_GTCO) += gtco.o obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o -obj-$(CONFIG_TABLET_USB_WACOM) += wacom.o +obj-$(CONFIG_TABLET_SERIAL_WACOM4) += wacom_serial4.o diff --git a/drivers/input/tablet/wacom_serial4.c b/drivers/input/tablet/wacom_serial4.c new file mode 100644 index 0000000..20ab802 --- /dev/null +++ b/drivers/input/tablet/wacom_serial4.c @@ -0,0 +1,620 @@ +/* + * Wacom protocol 4 serial tablet driver + * + * Copyright 2014 Hans de Goede <hdegoede@redhat.com> + * Copyright 2011-2012 Julian Squires <julian@cipht.net> + * + * 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 of 2 of the License, or (at your + * option) any later version. See the file COPYING in the main directory of + * this archive for more details. + * + * Many thanks to Bill Seremetis, without whom PenPartner support + * would not have been possible. Thanks to Patrick Mahoney. + * + * This driver was developed with reference to much code written by others, + * particularly: + * - elo, gunze drivers by Vojtech Pavlik <vojtech@ucw.cz>; + * - wacom_w8001 driver by Jaya Kumar <jayakumar.lkml@gmail.com>; + * - the USB wacom input driver, credited to many people + * (see drivers/input/tablet/wacom.h); + * - new and old versions of linuxwacom / xf86-input-wacom credited to + * Frederic Lepied, France. <Lepied@XFree86.org> and + * Ping Cheng, Wacom. <pingc@wacom.com>; + * - and xf86wacom.c (a presumably ancient version of the linuxwacom code), + * by Frederic Lepied and Raph Levien <raph@gtk.org>. + * + * To do: + * - support pad buttons; (requires access to a model with pad buttons) + * - support (protocol 4-style) tilt (requires access to a > 1.4 rom model) + */ + +/* + * Wacom serial protocol 4 documentation taken from linuxwacom-0.9.9 code, + * protocol 4 uses 7 or 9 byte of data in the following format: + * + * Byte 1 + * bit 7 Sync bit always 1 + * bit 6 Pointing device detected + * bit 5 Cursor = 0 / Stylus = 1 + * bit 4 Reserved + * bit 3 1 if a button on the pointing device has been pressed + * bit 2 P0 (optional) + * bit 1 X15 + * bit 0 X14 + * + * Byte 2 + * bit 7 Always 0 + * bits 6-0 = X13 - X7 + * + * Byte 3 + * bit 7 Always 0 + * bits 6-0 = X6 - X0 + * + * Byte 4 + * bit 7 Always 0 + * bit 6 B3 + * bit 5 B2 + * bit 4 B1 + * bit 3 B0 + * bit 2 P1 (optional) + * bit 1 Y15 + * bit 0 Y14 + * + * Byte 5 + * bit 7 Always 0 + * bits 6-0 = Y13 - Y7 + * + * Byte 6 + * bit 7 Always 0 + * bits 6-0 = Y6 - Y0 + * + * Byte 7 + * bit 7 Always 0 + * bit 6 Sign of pressure data; or wheel-rel for cursor tool + * bit 5 P7; or REL1 for cursor tool + * bit 4 P6; or REL0 for cursor tool + * bit 3 P5 + * bit 2 P4 + * bit 1 P3 + * bit 0 P2 + * + * byte 8 and 9 are optional and present only + * in tilt mode. + * + * Byte 8 + * bit 7 Always 0 + * bit 6 Sign of tilt X + * bit 5 Xt6 + * bit 4 Xt5 + * bit 3 Xt4 + * bit 2 Xt3 + * bit 1 Xt2 + * bit 0 Xt1 + * + * Byte 9 + * bit 7 Always 0 + * bit 6 Sign of tilt Y + * bit 5 Yt6 + * bit 4 Yt5 + * bit 3 Yt4 + * bit 2 Yt3 + * bit 1 Yt2 + * bit 0 Yt1 + */ + +#include <linux/completion.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/serio.h> +#include <linux/slab.h> +#include <linux/string.h> + +MODULE_AUTHOR("Julian Squires <julian@cipht.net>, Hans de Goede <hdegoede@redhat.com>"); +MODULE_DESCRIPTION("Wacom protocol 4 serial tablet driver"); +MODULE_LICENSE("GPL"); + +#define REQUEST_MODEL_AND_ROM_VERSION "~#" +#define REQUEST_MAX_COORDINATES "~C\r" +#define REQUEST_CONFIGURATION_STRING "~R\r" +#define REQUEST_RESET_TO_PROTOCOL_IV "\r#" +/* + * Note: sending "\r$\r" causes at least the Digitizer II to send + * packets in ASCII instead of binary. "\r#" seems to undo that. + */ + +#define COMMAND_START_SENDING_PACKETS "ST\r" +#define COMMAND_STOP_SENDING_PACKETS "SP\r" +#define COMMAND_MULTI_MODE_INPUT "MU1\r" +#define COMMAND_ORIGIN_IN_UPPER_LEFT "OC1\r" +#define COMMAND_ENABLE_ALL_MACRO_BUTTONS "~M0\r" +#define COMMAND_DISABLE_GROUP_1_MACRO_BUTTONS "~M1\r" +#define COMMAND_TRANSMIT_AT_MAX_RATE "IT0\r" +#define COMMAND_DISABLE_INCREMENTAL_MODE "IN0\r" +#define COMMAND_ENABLE_CONTINUOUS_MODE "SR\r" +#define COMMAND_ENABLE_PRESSURE_MODE "PH1\r" +#define COMMAND_Z_FILTER "ZF1\r" + +/* Note that this is a protocol 4 packet without tilt information. */ +#define PACKET_LENGTH 7 +#define DATA_SIZE 32 + +/* flags */ +#define F_COVERS_SCREEN 0x01 +#define F_HAS_STYLUS2 0x02 +#define F_HAS_SCROLLWHEEL 0x04 + +/* device IDs */ +#define STYLUS_DEVICE_ID 0x02 +#define CURSOR_DEVICE_ID 0x06 +#define ERASER_DEVICE_ID 0x0A + +enum { STYLUS = 1, ERASER, CURSOR }; + +static const struct { + int device_id; + int input_id; +} tools[] = { + { 0, 0 }, + { STYLUS_DEVICE_ID, BTN_TOOL_PEN }, + { ERASER_DEVICE_ID, BTN_TOOL_RUBBER }, + { CURSOR_DEVICE_ID, BTN_TOOL_MOUSE }, +}; + +struct wacom { + struct input_dev *dev; + struct completion cmd_done; + int result; + u8 expect; + u8 eraser_mask; + unsigned int extra_z_bits; + unsigned int flags; + unsigned int res_x, res_y; + unsigned int max_x, max_y; + unsigned int tool; + unsigned int idx; + u8 data[DATA_SIZE]; + char phys[32]; +}; + +enum { + MODEL_CINTIQ = 0x504C, /* PL */ + MODEL_CINTIQ2 = 0x4454, /* DT */ + MODEL_DIGITIZER_II = 0x5544, /* UD */ + MODEL_GRAPHIRE = 0x4554, /* ET */ + MODEL_PENPARTNER = 0x4354, /* CT */ +}; + +static void wacom_handle_model_response(struct wacom *wacom) +{ + int major_v, minor_v, r = 0; + char *p; + + p = strrchr(wacom->data, 'V'); + if (p) + r = sscanf(p + 1, "%u.%u", &major_v, &minor_v); + if (r != 2) + major_v = minor_v = 0; + + switch (wacom->data[2] << 8 | wacom->data[3]) { + case MODEL_CINTIQ: /* UNTESTED */ + case MODEL_CINTIQ2: + if ((wacom->data[2] << 8 | wacom->data[3]) == MODEL_CINTIQ) { + wacom->dev->name = "Wacom Cintiq"; + wacom->dev->id.version = MODEL_CINTIQ; + } else { + wacom->dev->name = "Wacom Cintiq II"; + wacom->dev->id.version = MODEL_CINTIQ2; + } + wacom->res_x = 508; + wacom->res_y = 508; + + switch (wacom->data[5] << 8 | wacom->data[6]) { + case 0x3731: /* PL-710 */ + wacom->res_x = 2540; + wacom->res_y = 2540; + /* fall through */ + case 0x3535: /* PL-550 */ + case 0x3830: /* PL-800 */ + wacom->extra_z_bits = 2; + } + + wacom->flags = F_COVERS_SCREEN; + break; + + case MODEL_PENPARTNER: + wacom->dev->name = "Wacom Penpartner"; + wacom->dev->id.version = MODEL_PENPARTNER; + wacom->res_x = 1000; + wacom->res_y = 1000; + break; + + case MODEL_GRAPHIRE: + wacom->dev->name = "Wacom Graphire"; + wacom->dev->id.version = MODEL_GRAPHIRE; + wacom->res_x = 1016; + wacom->res_y = 1016; + wacom->max_x = 5103; + wacom->max_y = 3711; + wacom->extra_z_bits = 2; + wacom->eraser_mask = 0x08; + wacom->flags = F_HAS_STYLUS2 | F_HAS_SCROLLWHEEL; + break; + + case MODEL_DIGITIZER_II: + wacom->dev->name = "Wacom Digitizer II"; + wacom->dev->id.version = MODEL_DIGITIZER_II; + if (major_v == 1 && minor_v <= 2) + wacom->extra_z_bits = 0; /* UNTESTED */ + break; + + default: + dev_err(&wacom->dev->dev, "Unsupported Wacom model %s\n", + wacom->data); + wacom->result = -ENODEV; + return; + } + + dev_info(&wacom->dev->dev, "%s tablet, version %u.%u\n", + wacom->dev->name, major_v, minor_v); +} + +static void wacom_handle_configuration_response(struct wacom *wacom) +{ + int r, skip; + + dev_dbg(&wacom->dev->dev, "Configuration string: %s\n", wacom->data); + r = sscanf(wacom->data, "~R%x,%u,%u,%u,%u", &skip, &skip, &skip, + &wacom->res_x, &wacom->res_y); + if (r != 5) + dev_warn(&wacom->dev->dev, "could not get resolution\n"); +} + +static void wacom_handle_coordinates_response(struct wacom *wacom) +{ + int r; + + dev_dbg(&wacom->dev->dev, "Coordinates string: %s\n", wacom->data); + r = sscanf(wacom->data, "~C%u,%u", &wacom->max_x, &wacom->max_y); + if (r != 2) + dev_warn(&wacom->dev->dev, "could not get max coordinates\n"); +} + +static void wacom_handle_response(struct wacom *wacom) +{ + if (wacom->data[0] != '~' || wacom->data[1] != wacom->expect) { + dev_err(&wacom->dev->dev, + "Wacom got an unexpected response: %s\n", wacom->data); + wacom->result = -EIO; + } else { + wacom->result = 0; + + switch (wacom->data[1]) { + case '#': + wacom_handle_model_response(wacom); + break; + case 'R': + wacom_handle_configuration_response(wacom); + break; + case 'C': + wacom_handle_coordinates_response(wacom); + break; + } + } + + complete(&wacom->cmd_done); +} + +static void wacom_handle_packet(struct wacom *wacom) +{ + u8 in_proximity_p, stylus_p, button; + unsigned int tool; + int x, y, z; + + in_proximity_p = wacom->data[0] & 0x40; + stylus_p = wacom->data[0] & 0x20; + button = (wacom->data[3] & 0x78) >> 3; + x = (wacom->data[0] & 3) << 14 | wacom->data[1]<<7 | wacom->data[2]; + y = (wacom->data[3] & 3) << 14 | wacom->data[4]<<7 | wacom->data[5]; + + if (in_proximity_p && stylus_p) { + z = wacom->data[6] & 0x7f; + if (wacom->extra_z_bits >= 1) + z = z << 1 | (wacom->data[3] & 0x4) >> 2; + if (wacom->extra_z_bits > 1) + z = z << 1 | (wacom->data[0] & 0x4) >> 2; + z = z ^ (0x40 << wacom->extra_z_bits); + } else { + z = -1; + } + + if (stylus_p) + tool = (button & wacom->eraser_mask) ? ERASER : STYLUS; + else + tool = CURSOR; + + if (tool != wacom->tool && wacom->tool != 0) { + input_report_key(wacom->dev, tools[wacom->tool].input_id, 0); + input_sync(wacom->dev); + } + wacom->tool = tool; + + input_report_key(wacom->dev, tools[tool].input_id, in_proximity_p); + input_report_abs(wacom->dev, ABS_MISC, + in_proximity_p ? tools[tool].device_id : 0); + input_report_abs(wacom->dev, ABS_X, x); + input_report_abs(wacom->dev, ABS_Y, y); + input_report_abs(wacom->dev, ABS_PRESSURE, z); + if (stylus_p) { + input_report_key(wacom->dev, BTN_TOUCH, button & 1); + input_report_key(wacom->dev, BTN_STYLUS, button & 2); + input_report_key(wacom->dev, BTN_STYLUS2, button & 4); + } else { + input_report_key(wacom->dev, BTN_LEFT, button & 1); + input_report_key(wacom->dev, BTN_RIGHT, button & 2); + input_report_key(wacom->dev, BTN_MIDDLE, button & 4); + /* handle relative wheel for non-stylus device */ + z = (wacom->data[6] & 0x30) >> 4; + if (wacom->data[6] & 0x40) + z = -z; + input_report_rel(wacom->dev, REL_WHEEL, z); + } + input_sync(wacom->dev); +} + +static void wacom_clear_data_buf(struct wacom *wacom) +{ + memset(wacom->data, 0, DATA_SIZE); + wacom->idx = 0; +} + +static irqreturn_t wacom_interrupt(struct serio *serio, unsigned char data, + unsigned int flags) +{ + struct wacom *wacom = serio_get_drvdata(serio); + + if (data & 0x80) + wacom->idx = 0; + + /* + * We're either expecting a carriage return-terminated ASCII + * response string, or a seven-byte packet with the MSB set on + * the first byte. + * + * Note however that some tablets (the PenPartner, for + * example) don't send a carriage return at the end of a + * command. We handle these by waiting for timeout. + */ + if (data == '\r' && !(wacom->data[0] & 0x80)) { + wacom_handle_response(wacom); + wacom_clear_data_buf(wacom); + return IRQ_HANDLED; + } + + /* Leave place for 0 termination */ + if (wacom->idx > (DATA_SIZE - 2)) { + dev_dbg(&wacom->dev->dev, + "throwing away %d bytes of garbage\n", wacom->idx); + wacom_clear_data_buf(wacom); + } + wacom->data[wacom->idx++] = data; + + if (wacom->idx == PACKET_LENGTH && (wacom->data[0] & 0x80)) { + wacom_handle_packet(wacom); + wacom_clear_data_buf(wacom); + } + + return IRQ_HANDLED; +} + +static void wacom_disconnect(struct serio *serio) +{ + struct wacom *wacom = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(wacom->dev); + kfree(wacom); +} + +static int wacom_send(struct serio *serio, const u8 *command) +{ + int err = 0; + + for (; !err && *command; command++) + err = serio_write(serio, *command); + + return err; +} + +static int wacom_send_setup_string(struct wacom *wacom, struct serio *serio) +{ + const u8 *cmd; + + switch (wacom->dev->id.version) { + case MODEL_CINTIQ: /* UNTESTED */ + cmd = COMMAND_ORIGIN_IN_UPPER_LEFT + COMMAND_TRANSMIT_AT_MAX_RATE + COMMAND_ENABLE_CONTINUOUS_MODE + COMMAND_START_SENDING_PACKETS; + break; + + case MODEL_PENPARTNER: + cmd = COMMAND_ENABLE_PRESSURE_MODE + COMMAND_START_SENDING_PACKETS; + break; + + default: + cmd = COMMAND_MULTI_MODE_INPUT + COMMAND_ORIGIN_IN_UPPER_LEFT + COMMAND_ENABLE_ALL_MACRO_BUTTONS + COMMAND_DISABLE_GROUP_1_MACRO_BUTTONS + COMMAND_TRANSMIT_AT_MAX_RATE + COMMAND_DISABLE_INCREMENTAL_MODE + COMMAND_ENABLE_CONTINUOUS_MODE + COMMAND_Z_FILTER + COMMAND_START_SENDING_PACKETS; + break; + } + + return wacom_send(serio, cmd); +} + +static int wacom_send_and_wait(struct wacom *wacom, struct serio *serio, + const u8 *cmd, const char *desc) +{ + int err; + unsigned long u; + + wacom->expect = cmd[1]; + init_completion(&wacom->cmd_done); + + err = wacom_send(serio, cmd); + if (err) + return err; + + u = wait_for_completion_timeout(&wacom->cmd_done, HZ); + if (u == 0) { + /* Timeout, process what we've received. */ + wacom_handle_response(wacom); + } + + wacom->expect = 0; + return wacom->result; +} + +static int wacom_setup(struct wacom *wacom, struct serio *serio) +{ + int err; + + /* Note that setting the link speed is the job of inputattach. + * We assume that reset negotiation has already happened, + * here. */ + err = wacom_send_and_wait(wacom, serio, REQUEST_MODEL_AND_ROM_VERSION, + "model and version"); + if (err) + return err; + + if (!(wacom->res_x && wacom->res_y)) { + err = wacom_send_and_wait(wacom, serio, + REQUEST_CONFIGURATION_STRING, + "configuration string"); + if (err) + return err; + } + + if (!(wacom->max_x && wacom->max_y)) { + err = wacom_send_and_wait(wacom, serio, + REQUEST_MAX_COORDINATES, + "coordinates string"); + if (err) + return err; + } + + return wacom_send_setup_string(wacom, serio); +} + +static int wacom_connect(struct serio *serio, struct serio_driver *drv) +{ + struct wacom *wacom; + struct input_dev *input_dev; + int err = -ENOMEM; + + wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!wacom || !input_dev) + goto free_device; + + wacom->dev = input_dev; + wacom->extra_z_bits = 1; + wacom->eraser_mask = 0x04; + wacom->tool = wacom->idx = 0; + snprintf(wacom->phys, sizeof(wacom->phys), "%s/input0", serio->phys); + input_dev->phys = wacom->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_WACOM_IV; + input_dev->id.product = serio->id.extra; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = + BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | BIT_MASK(EV_REL); + set_bit(ABS_MISC, input_dev->absbit); + set_bit(BTN_TOOL_PEN, input_dev->keybit); + set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + set_bit(BTN_TOOL_MOUSE, input_dev->keybit); + set_bit(BTN_TOUCH, input_dev->keybit); + set_bit(BTN_STYLUS, input_dev->keybit); + set_bit(BTN_LEFT, input_dev->keybit); + set_bit(BTN_RIGHT, input_dev->keybit); + set_bit(BTN_MIDDLE, input_dev->keybit); + + serio_set_drvdata(serio, wacom); + + err = serio_open(serio, drv); + if (err) + goto free_device; + + err = wacom_setup(wacom, serio); + if (err) + goto close_serio; + + set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + if (!(wacom->flags & F_COVERS_SCREEN)) + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + + if (wacom->flags & F_HAS_STYLUS2) + __set_bit(BTN_STYLUS2, input_dev->keybit); + + if (wacom->flags & F_HAS_SCROLLWHEEL) + __set_bit(REL_WHEEL, input_dev->relbit); + + input_abs_set_res(wacom->dev, ABS_X, wacom->res_x); + input_abs_set_res(wacom->dev, ABS_Y, wacom->res_y); + input_set_abs_params(wacom->dev, ABS_X, 0, wacom->max_x, 0, 0); + input_set_abs_params(wacom->dev, ABS_Y, 0, wacom->max_y, 0, 0); + input_set_abs_params(wacom->dev, ABS_PRESSURE, -1, + (1 << (7 + wacom->extra_z_bits)) - 1, 0, 0); + + err = input_register_device(wacom->dev); + if (err) + goto close_serio; + + return 0; + +close_serio: + serio_close(serio); +free_device: + serio_set_drvdata(serio, NULL); + input_free_device(input_dev); + kfree(wacom); + return err; +} + +static struct serio_device_id wacom_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_WACOM_IV, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, wacom_serio_ids); + +static struct serio_driver wacom_drv = { + .driver = { + .name = "wacom_serial4", + }, + .description = "Wacom protocol 4 serial tablet driver", + .id_table = wacom_serio_ids, + .interrupt = wacom_interrupt, + .connect = wacom_connect, + .disconnect = wacom_disconnect, +}; + +module_serio_driver(wacom_drv); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index a23a94b..6bb9a7d 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -471,6 +471,18 @@ config TOUCHSCREEN_HP7XX To compile this driver as a module, choose M here: the module will be called jornada720_ts. +config TOUCHSCREEN_IPAQ_MICRO + tristate "HP iPAQ Atmel Micro ASIC touchscreen" + depends on MFD_IPAQ_MICRO + help + Say Y here to enable support for the touchscreen attached to + the Atmel Micro peripheral controller on iPAQ h3100/h3600/h3700 + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ipaq-micro-ts. + config TOUCHSCREEN_HTCPEN tristate "HTC Shift X9500 touchscreen" depends on ISA diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 126479d..4be94fc 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o +obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index da201b8..e57ba52 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1302,8 +1302,10 @@ static int ads7846_probe(struct spi_device *spi) pdata = dev_get_platdata(&spi->dev); if (!pdata) { pdata = ads7846_probe_dt(&spi->dev); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); + if (IS_ERR(pdata)) { + err = PTR_ERR(pdata); + goto err_free_mem; + } } ts->model = pdata->model ? : 7846; diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 6e0b4a2..03b8571 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2,6 +2,7 @@ * Atmel maXTouch Touchscreen driver * * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Copyright (C) 2011-2014 Atmel Corporation * Copyright (C) 2012 Google, Inc. * * Author: Joonyoung Shim <jy0922.shim@samsung.com> @@ -22,6 +23,7 @@ #include <linux/i2c/atmel_mxt_ts.h> #include <linux/input/mt.h> #include <linux/interrupt.h> +#include <linux/of.h> #include <linux/slab.h> /* Version */ @@ -29,8 +31,10 @@ #define MXT_VER_21 21 #define MXT_VER_22 22 -/* Firmware */ +/* Firmware files */ #define MXT_FW_NAME "maxtouch.fw" +#define MXT_CFG_NAME "maxtouch.cfg" +#define MXT_CFG_MAGIC "OBP_RAW V1" /* Registers */ #define MXT_INFO 0x00 @@ -44,6 +48,8 @@ #define MXT_OBJECT_START 0x07 #define MXT_OBJECT_SIZE 6 +#define MXT_INFO_CHECKSUM_SIZE 3 +#define MXT_MAX_BLOCK_WRITE 256 /* Object types */ #define MXT_DEBUG_DIAGNOSTIC_T37 37 @@ -74,6 +80,9 @@ #define MXT_SPT_MESSAGECOUNT_T44 44 #define MXT_SPT_CTECONFIG_T46 46 +/* MXT_GEN_MESSAGE_T5 object */ +#define MXT_RPTID_NOMSG 0xff + /* MXT_GEN_COMMAND_T6 field */ #define MXT_COMMAND_RESET 0 #define MXT_COMMAND_BACKUPNV 1 @@ -83,11 +92,20 @@ /* Define for T6 status byte */ #define MXT_T6_STATUS_RESET (1 << 7) +#define MXT_T6_STATUS_OFL (1 << 6) +#define MXT_T6_STATUS_SIGERR (1 << 5) +#define MXT_T6_STATUS_CAL (1 << 4) +#define MXT_T6_STATUS_CFGERR (1 << 3) +#define MXT_T6_STATUS_COMSERR (1 << 2) /* MXT_GEN_POWER_T7 field */ -#define MXT_POWER_IDLEACQINT 0 -#define MXT_POWER_ACTVACQINT 1 -#define MXT_POWER_ACTV2IDLETO 2 +struct t7_config { + u8 idle; + u8 active; +} __packed; + +#define MXT_POWER_CFG_RUN 0 +#define MXT_POWER_CFG_DEEPSLEEP 1 /* MXT_GEN_ACQUIRE_T8 field */ #define MXT_ACQUIRE_CHRGTIME 0 @@ -99,7 +117,6 @@ #define MXT_ACQUIRE_ATCHCALSTHR 7 /* MXT_TOUCH_MULTI_T9 field */ -#define MXT_TOUCH_CTRL 0 #define MXT_T9_ORIENT 9 #define MXT_T9_RANGE 18 @@ -217,11 +234,6 @@ struct mxt_object { u8 num_report_ids; } __packed; -struct mxt_message { - u8 reportid; - u8 message[7]; -}; - /* Each client has this additional data */ struct mxt_data { struct i2c_client *client; @@ -234,15 +246,28 @@ struct mxt_data { unsigned int max_x; unsigned int max_y; bool in_bootloader; + u16 mem_size; + u8 max_reportid; u32 config_crc; + u32 info_crc; u8 bootloader_addr; + u8 *msg_buf; + u8 t6_status; + bool update_input; + u8 last_message_count; + u8 num_touchids; + struct t7_config t7_cfg; /* Cached parameters from object table */ + u16 T5_address; + u8 T5_msg_size; u8 T6_reportid; u16 T6_address; + u16 T7_address; u8 T9_reportid_min; u8 T9_reportid_max; u8 T19_reportid; + u16 T44_address; /* for fw update in bootloader */ struct completion bl_completion; @@ -297,42 +322,10 @@ static bool mxt_object_readable(unsigned int type) } } -static bool mxt_object_writable(unsigned int type) -{ - switch (type) { - case MXT_GEN_COMMAND_T6: - case MXT_GEN_POWER_T7: - case MXT_GEN_ACQUIRE_T8: - case MXT_TOUCH_MULTI_T9: - case MXT_TOUCH_KEYARRAY_T15: - case MXT_TOUCH_PROXIMITY_T23: - case MXT_TOUCH_PROXKEY_T52: - case MXT_PROCI_GRIPFACE_T20: - case MXT_PROCG_NOISE_T22: - case MXT_PROCI_ONETOUCH_T24: - case MXT_PROCI_TWOTOUCH_T27: - case MXT_PROCI_GRIP_T40: - case MXT_PROCI_PALM_T41: - case MXT_PROCI_TOUCHSUPPRESSION_T42: - case MXT_PROCI_STYLUS_T47: - case MXT_PROCG_NOISESUPPRESSION_T48: - case MXT_SPT_COMMSCONFIG_T18: - case MXT_SPT_GPIOPWM_T19: - case MXT_SPT_SELFTEST_T25: - case MXT_SPT_CTECONFIG_T28: - case MXT_SPT_DIGITIZER_T43: - case MXT_SPT_CTECONFIG_T46: - return true; - default: - return false; - } -} - -static void mxt_dump_message(struct device *dev, - struct mxt_message *message) +static void mxt_dump_message(struct mxt_data *data, u8 *message) { - dev_dbg(dev, "reportid: %u\tmessage: %*ph\n", - message->reportid, 7, message->message); + dev_dbg(&data->client->dev, "message: %*ph\n", + data->T5_msg_size, message); } static int mxt_wait_for_completion(struct mxt_data *data, @@ -401,7 +394,7 @@ static int mxt_bootloader_write(struct mxt_data *data, return ret; } -static int mxt_lookup_bootloader_address(struct mxt_data *data) +static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry) { u8 appmode = data->client->addr; u8 bootloader; @@ -409,6 +402,12 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data) switch (appmode) { case 0x4a: case 0x4b: + /* Chips after 1664S use different scheme */ + if (retry || data->info.family_id >= 0xa2) { + bootloader = appmode - 0x24; + break; + } + /* Fall through for normal case */ case 0x4c: case 0x4d: case 0x5a: @@ -426,6 +425,30 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data) return 0; } +static int mxt_probe_bootloader(struct mxt_data *data, bool retry) +{ + struct device *dev = &data->client->dev; + int ret; + u8 val; + bool crc_failure; + + ret = mxt_lookup_bootloader_address(data, retry); + if (ret) + return ret; + + ret = mxt_bootloader_read(data, &val, 1); + if (ret) + return ret; + + /* Check app crc fail mode */ + crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL; + + dev_err(dev, "Detected bootloader, status:%02X%s\n", + val, crc_failure ? ", APP_CRC_FAIL" : ""); + + return 0; +} + static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) { struct device *dev = &data->client->dev; @@ -447,14 +470,15 @@ static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) } } -static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) +static int mxt_check_bootloader(struct mxt_data *data, unsigned int state, + bool wait) { struct device *dev = &data->client->dev; u8 val; int ret; recheck: - if (state != MXT_WAITING_BOOTLOAD_CMD) { + if (wait) { /* * In application update mode, the interrupt * line signals state transitions. We must wait for the @@ -485,6 +509,7 @@ recheck: switch (state) { case MXT_WAITING_BOOTLOAD_CMD: case MXT_WAITING_FRAME_DATA: + case MXT_APP_CRC_FAIL: val &= ~MXT_BOOT_STATUS_MASK; break; case MXT_FRAME_CRC_PASS: @@ -508,13 +533,18 @@ recheck: return 0; } -static int mxt_unlock_bootloader(struct mxt_data *data) +static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock) { int ret; u8 buf[2]; - buf[0] = MXT_UNLOCK_CMD_LSB; - buf[1] = MXT_UNLOCK_CMD_MSB; + if (unlock) { + buf[0] = MXT_UNLOCK_CMD_LSB; + buf[1] = MXT_UNLOCK_CMD_MSB; + } else { + buf[0] = 0x01; + buf[1] = 0x01; + } ret = mxt_bootloader_write(data, buf, 2); if (ret) @@ -605,40 +635,44 @@ mxt_get_object(struct mxt_data *data, u8 type) return object; } - dev_err(&data->client->dev, "Invalid object type T%u\n", type); + dev_warn(&data->client->dev, "Invalid object type T%u\n", type); return NULL; } -static int mxt_read_message(struct mxt_data *data, - struct mxt_message *message) +static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg) { - struct mxt_object *object; - u16 reg; - - object = mxt_get_object(data, MXT_GEN_MESSAGE_T5); - if (!object) - return -EINVAL; - - reg = object->start_address; - return __mxt_read_reg(data->client, reg, - sizeof(struct mxt_message), message); -} + struct device *dev = &data->client->dev; + u8 status = msg[1]; + u32 crc = msg[2] | (msg[3] << 8) | (msg[4] << 16); -static int mxt_write_object(struct mxt_data *data, - u8 type, u8 offset, u8 val) -{ - struct mxt_object *object; - u16 reg; + complete(&data->crc_completion); - object = mxt_get_object(data, type); - if (!object || offset >= mxt_obj_size(object)) - return -EINVAL; + if (crc != data->config_crc) { + data->config_crc = crc; + dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc); + } - reg = object->start_address; - return mxt_write_reg(data->client, reg + offset, val); + /* Detect reset */ + if (status & MXT_T6_STATUS_RESET) + complete(&data->reset_completion); + + /* Output debug if status has changed */ + if (status != data->t6_status) + dev_dbg(dev, "T6 Status 0x%02X%s%s%s%s%s%s%s\n", + status, + status == 0 ? " OK" : "", + status & MXT_T6_STATUS_RESET ? " RESET" : "", + status & MXT_T6_STATUS_OFL ? " OFL" : "", + status & MXT_T6_STATUS_SIGERR ? " SIGERR" : "", + status & MXT_T6_STATUS_CAL ? " CAL" : "", + status & MXT_T6_STATUS_CFGERR ? " CFGERR" : "", + status & MXT_T6_STATUS_COMSERR ? " COMSERR" : ""); + + /* Save current status */ + data->t6_status = status; } -static void mxt_input_button(struct mxt_data *data, struct mxt_message *message) +static void mxt_input_button(struct mxt_data *data, u8 *message) { struct input_dev *input = data->input_dev; const struct mxt_platform_data *pdata = data->pdata; @@ -649,30 +683,33 @@ static void mxt_input_button(struct mxt_data *data, struct mxt_message *message) for (i = 0; i < pdata->t19_num_keys; i++) { if (pdata->t19_keymap[i] == KEY_RESERVED) continue; - button = !(message->message[0] & (1 << i)); + button = !(message[1] & (1 << i)); input_report_key(input, pdata->t19_keymap[i], button); } } -static void mxt_input_sync(struct input_dev *input_dev) +static void mxt_input_sync(struct mxt_data *data) { - input_mt_report_pointer_emulation(input_dev, false); - input_sync(input_dev); + input_mt_report_pointer_emulation(data->input_dev, + data->pdata->t19_num_keys); + input_sync(data->input_dev); } -static void mxt_input_touchevent(struct mxt_data *data, - struct mxt_message *message, int id) +static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) { struct device *dev = &data->client->dev; - u8 status = message->message[0]; struct input_dev *input_dev = data->input_dev; + int id; + u8 status; int x; int y; int area; int amplitude; - x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf); - y = (message->message[2] << 4) | ((message->message[3] & 0xf)); + id = message[0] - data->T9_reportid_min; + status = message[1]; + x = (message[2] << 4) | ((message[4] >> 4) & 0xf); + y = (message[3] << 4) | ((message[4] & 0xf)); /* Handle 10/12 bit switching */ if (data->max_x < 1024) @@ -680,8 +717,8 @@ static void mxt_input_touchevent(struct mxt_data *data, if (data->max_y < 1024) y >>= 2; - area = message->message[4]; - amplitude = message->message[5]; + area = message[5]; + amplitude = message[6]; dev_dbg(dev, "[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n", @@ -707,7 +744,7 @@ static void mxt_input_touchevent(struct mxt_data *data, if (status & MXT_T9_RELEASE) { input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); - mxt_input_sync(input_dev); + mxt_input_sync(data); } /* Touch active */ @@ -720,64 +757,179 @@ static void mxt_input_touchevent(struct mxt_data *data, /* Touch no longer active, close out slot */ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); } + + data->update_input = true; +} + +static int mxt_proc_message(struct mxt_data *data, u8 *message) +{ + u8 report_id = message[0]; + + if (report_id == MXT_RPTID_NOMSG) + return 0; + + if (report_id == data->T6_reportid) { + mxt_proc_t6_messages(data, message); + } else if (!data->input_dev) { + /* + * Do not report events if input device + * is not yet registered. + */ + mxt_dump_message(data, message); + } else if (report_id >= data->T9_reportid_min + && report_id <= data->T9_reportid_max) { + mxt_proc_t9_message(data, message); + } else if (report_id == data->T19_reportid) { + mxt_input_button(data, message); + data->update_input = true; + } else { + mxt_dump_message(data, message); + } + + return 1; } -static u16 mxt_extract_T6_csum(const u8 *csum) +static int mxt_read_and_process_messages(struct mxt_data *data, u8 count) { - return csum[0] | (csum[1] << 8) | (csum[2] << 16); + struct device *dev = &data->client->dev; + int ret; + int i; + u8 num_valid = 0; + + /* Safety check for msg_buf */ + if (count > data->max_reportid) + return -EINVAL; + + /* Process remaining messages if necessary */ + ret = __mxt_read_reg(data->client, data->T5_address, + data->T5_msg_size * count, data->msg_buf); + if (ret) { + dev_err(dev, "Failed to read %u messages (%d)\n", count, ret); + return ret; + } + + for (i = 0; i < count; i++) { + ret = mxt_proc_message(data, + data->msg_buf + data->T5_msg_size * i); + + if (ret == 1) + num_valid++; + } + + /* return number of messages read */ + return num_valid; } -static bool mxt_is_T9_message(struct mxt_data *data, struct mxt_message *msg) +static irqreturn_t mxt_process_messages_t44(struct mxt_data *data) { - u8 id = msg->reportid; - return (id >= data->T9_reportid_min && id <= data->T9_reportid_max); + struct device *dev = &data->client->dev; + int ret; + u8 count, num_left; + + /* Read T44 and T5 together */ + ret = __mxt_read_reg(data->client, data->T44_address, + data->T5_msg_size + 1, data->msg_buf); + if (ret) { + dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret); + return IRQ_NONE; + } + + count = data->msg_buf[0]; + + if (count == 0) { + dev_warn(dev, "Interrupt triggered but zero messages\n"); + return IRQ_NONE; + } else if (count > data->max_reportid) { + dev_err(dev, "T44 count %d exceeded max report id\n", count); + count = data->max_reportid; + } + + /* Process first message */ + ret = mxt_proc_message(data, data->msg_buf + 1); + if (ret < 0) { + dev_warn(dev, "Unexpected invalid message\n"); + return IRQ_NONE; + } + + num_left = count - 1; + + /* Process remaining messages if necessary */ + if (num_left) { + ret = mxt_read_and_process_messages(data, num_left); + if (ret < 0) + goto end; + else if (ret != num_left) + dev_warn(dev, "Unexpected invalid message\n"); + } + +end: + if (data->update_input) { + mxt_input_sync(data); + data->update_input = false; + } + + return IRQ_HANDLED; } -static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) +static int mxt_process_messages_until_invalid(struct mxt_data *data) { - struct mxt_message message; - const u8 *payload = &message.message[0]; struct device *dev = &data->client->dev; - u8 reportid; - bool update_input = false; - u32 crc; + int count, read; + u8 tries = 2; + count = data->max_reportid; + + /* Read messages until we force an invalid */ do { - if (mxt_read_message(data, &message)) { - dev_err(dev, "Failed to read message\n"); - return IRQ_NONE; - } + read = mxt_read_and_process_messages(data, count); + if (read < count) + return 0; + } while (--tries); - reportid = message.reportid; + if (data->update_input) { + mxt_input_sync(data); + data->update_input = false; + } - if (reportid == data->T6_reportid) { - u8 status = payload[0]; + dev_err(dev, "CHG pin isn't cleared\n"); + return -EBUSY; +} - crc = mxt_extract_T6_csum(&payload[1]); - if (crc != data->config_crc) { - data->config_crc = crc; - complete(&data->crc_completion); - } +static irqreturn_t mxt_process_messages(struct mxt_data *data) +{ + int total_handled, num_handled; + u8 count = data->last_message_count; - dev_dbg(dev, "Status: %02x Config Checksum: %06x\n", - status, data->config_crc); - - if (status & MXT_T6_STATUS_RESET) - complete(&data->reset_completion); - } else if (mxt_is_T9_message(data, &message)) { - int id = reportid - data->T9_reportid_min; - mxt_input_touchevent(data, &message, id); - update_input = true; - } else if (message.reportid == data->T19_reportid) { - mxt_input_button(data, &message); - update_input = true; - } else { - mxt_dump_message(dev, &message); - } - } while (reportid != 0xff); + if (count < 1 || count > data->max_reportid) + count = 1; - if (update_input) - mxt_input_sync(data->input_dev); + /* include final invalid message */ + total_handled = mxt_read_and_process_messages(data, count + 1); + if (total_handled < 0) + return IRQ_NONE; + /* if there were invalid messages, then we are done */ + else if (total_handled <= count) + goto update_count; + + /* keep reading two msgs until one is invalid or reportid limit */ + do { + num_handled = mxt_read_and_process_messages(data, 2); + if (num_handled < 0) + return IRQ_NONE; + + total_handled += num_handled; + + if (num_handled < 2) + break; + } while (total_handled < data->num_touchids); + +update_count: + data->last_message_count = total_handled; + + if (data->update_input) { + mxt_input_sync(data); + data->update_input = false; + } return IRQ_HANDLED; } @@ -792,7 +944,14 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } - return mxt_process_messages_until_invalid(data); + if (!data->object_table) + return IRQ_HANDLED; + + if (data->T44_address) { + return mxt_process_messages_t44(data); + } else { + return mxt_process_messages(data); + } } static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset, @@ -866,78 +1025,314 @@ static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value) mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT); } -static int mxt_check_reg_init(struct mxt_data *data) +static void mxt_calc_crc24(u32 *crc, u8 firstbyte, u8 secondbyte) +{ + static const unsigned int crcpoly = 0x80001B; + u32 result; + u32 data_word; + + data_word = (secondbyte << 8) | firstbyte; + result = ((*crc << 1) ^ data_word); + + if (result & 0x1000000) + result ^= crcpoly; + + *crc = result; +} + +static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off) +{ + u32 crc = 0; + u8 *ptr = base + start_off; + u8 *last_val = base + end_off - 1; + + if (end_off < start_off) + return -EINVAL; + + while (ptr < last_val) { + mxt_calc_crc24(&crc, *ptr, *(ptr + 1)); + ptr += 2; + } + + /* if len is odd, fill the last byte with 0 */ + if (ptr == last_val) + mxt_calc_crc24(&crc, *ptr, 0); + + /* Mask to 24-bit */ + crc &= 0x00FFFFFF; + + return crc; +} + +/* + * mxt_update_cfg - download configuration to chip + * + * Atmel Raw Config File Format + * + * The first four lines of the raw config file contain: + * 1) Version + * 2) Chip ID Information (first 7 bytes of device memory) + * 3) Chip Information Block 24-bit CRC Checksum + * 4) Chip Configuration 24-bit CRC Checksum + * + * The rest of the file consists of one line per object instance: + * <TYPE> <INSTANCE> <SIZE> <CONTENTS> + * + * <TYPE> - 2-byte object type as hex + * <INSTANCE> - 2-byte object instance number as hex + * <SIZE> - 2-byte object size as hex + * <CONTENTS> - array of <SIZE> 1-byte hex values + */ +static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) { - const struct mxt_platform_data *pdata = data->pdata; - struct mxt_object *object; struct device *dev = &data->client->dev; - int index = 0; - int i, size; + struct mxt_info cfg_info; + struct mxt_object *object; int ret; + int offset; + int data_pos; + int byte_offset; + int i; + int cfg_start_ofs; + u32 info_crc, config_crc, calculated_crc; + u8 *config_mem; + size_t config_mem_size; + unsigned int type, instance, size; + u8 val; + u16 reg; - if (!pdata->config) { - dev_dbg(dev, "No cfg data defined, skipping reg init\n"); - return 0; + mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); + + if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { + dev_err(dev, "Unrecognised config file\n"); + ret = -EINVAL; + goto release; } - mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); + data_pos = strlen(MXT_CFG_MAGIC); + + /* Load information block and check */ + for (i = 0; i < sizeof(struct mxt_info); i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + (unsigned char *)&cfg_info + i, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + goto release; + } - if (data->config_crc == pdata->config_crc) { - dev_info(dev, "Config CRC 0x%06X: OK\n", data->config_crc); - return 0; + data_pos += offset; } - dev_info(dev, "Config CRC 0x%06X: does not match 0x%06X\n", - data->config_crc, pdata->config_crc); + if (cfg_info.family_id != data->info.family_id) { + dev_err(dev, "Family ID mismatch!\n"); + ret = -EINVAL; + goto release; + } - for (i = 0; i < data->info.object_num; i++) { - object = data->object_table + i; + if (cfg_info.variant_id != data->info.variant_id) { + dev_err(dev, "Variant ID mismatch!\n"); + ret = -EINVAL; + goto release; + } + + /* Read CRCs */ + ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format: failed to parse Info CRC\n"); + ret = -EINVAL; + goto release; + } + data_pos += offset; - if (!mxt_object_writable(object->type)) + ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format: failed to parse Config CRC\n"); + ret = -EINVAL; + goto release; + } + data_pos += offset; + + /* + * The Info Block CRC is calculated over mxt_info and the object + * table. If it does not match then we are trying to load the + * configuration from a different chip or firmware version, so + * the configuration CRC is invalid anyway. + */ + if (info_crc == data->info_crc) { + if (config_crc == 0 || data->config_crc == 0) { + dev_info(dev, "CRC zero, attempting to apply config\n"); + } else if (config_crc == data->config_crc) { + dev_dbg(dev, "Config CRC 0x%06X: OK\n", + data->config_crc); + ret = 0; + goto release; + } else { + dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", + data->config_crc, config_crc); + } + } else { + dev_warn(dev, + "Warning: Info CRC error - device=0x%06X file=0x%06X\n", + data->info_crc, info_crc); + } + + /* Malloc memory to store configuration */ + cfg_start_ofs = MXT_OBJECT_START + + data->info.object_num * sizeof(struct mxt_object) + + MXT_INFO_CHECKSUM_SIZE; + config_mem_size = data->mem_size - cfg_start_ofs; + config_mem = kzalloc(config_mem_size, GFP_KERNEL); + if (!config_mem) { + dev_err(dev, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto release; + } + + while (data_pos < cfg->size) { + /* Read type, instance, length */ + ret = sscanf(cfg->data + data_pos, "%x %x %x%n", + &type, &instance, &size, &offset); + if (ret == 0) { + /* EOF */ + break; + } else if (ret != 3) { + dev_err(dev, "Bad format: failed to parse object\n"); + ret = -EINVAL; + goto release_mem; + } + data_pos += offset; + + object = mxt_get_object(data, type); + if (!object) { + /* Skip object */ + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + &val, + &offset); + data_pos += offset; + } continue; + } - size = mxt_obj_size(object) * mxt_obj_instances(object); - if (index + size > pdata->config_length) { - dev_err(dev, "Not enough config data!\n"); - return -EINVAL; + if (size > mxt_obj_size(object)) { + /* + * Either we are in fallback mode due to wrong + * config or config from a later fw version, + * or the file is corrupt or hand-edited. + */ + dev_warn(dev, "Discarding %zu byte(s) in T%u\n", + size - mxt_obj_size(object), type); + } else if (mxt_obj_size(object) > size) { + /* + * If firmware is upgraded, new bytes may be added to + * end of objects. It is generally forward compatible + * to zero these bytes - previous behaviour will be + * retained. However this does invalidate the CRC and + * will force fallback mode until the configuration is + * updated. We warn here but do nothing else - the + * malloc has zeroed the entire configuration. + */ + dev_warn(dev, "Zeroing %zu byte(s) in T%d\n", + mxt_obj_size(object) - size, type); } - ret = __mxt_write_reg(data->client, object->start_address, - size, &pdata->config[index]); - if (ret) - return ret; - index += size; + if (instance >= mxt_obj_instances(object)) { + dev_err(dev, "Object instances exceeded!\n"); + ret = -EINVAL; + goto release_mem; + } + + reg = object->start_address + mxt_obj_size(object) * instance; + + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + &val, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format in T%d\n", type); + ret = -EINVAL; + goto release_mem; + } + data_pos += offset; + + if (i > mxt_obj_size(object)) + continue; + + byte_offset = reg + i - cfg_start_ofs; + + if ((byte_offset >= 0) + && (byte_offset <= config_mem_size)) { + *(config_mem + byte_offset) = val; + } else { + dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n", + reg, object->type, byte_offset); + ret = -EINVAL; + goto release_mem; + } + } + } + + /* Calculate crc of the received configs (not the raw config file) */ + if (data->T7_address < cfg_start_ofs) { + dev_err(dev, "Bad T7 address, T7addr = %x, config offset %x\n", + data->T7_address, cfg_start_ofs); + ret = 0; + goto release_mem; + } + + calculated_crc = mxt_calculate_crc(config_mem, + data->T7_address - cfg_start_ofs, + config_mem_size); + + if (config_crc > 0 && (config_crc != calculated_crc)) + dev_warn(dev, "Config CRC error, calculated=%06X, file=%06X\n", + calculated_crc, config_crc); + + /* Write configuration as blocks */ + byte_offset = 0; + while (byte_offset < config_mem_size) { + size = config_mem_size - byte_offset; + + if (size > MXT_MAX_BLOCK_WRITE) + size = MXT_MAX_BLOCK_WRITE; + + ret = __mxt_write_reg(data->client, + cfg_start_ofs + byte_offset, + size, config_mem + byte_offset); + if (ret != 0) { + dev_err(dev, "Config write error, ret=%d\n", ret); + goto release_mem; + } + + byte_offset += size; } mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); ret = mxt_soft_reset(data); if (ret) - return ret; + goto release_mem; dev_info(dev, "Config successfully updated\n"); - return 0; +release_mem: + kfree(config_mem); +release: + release_firmware(cfg); + return ret; } -static int mxt_make_highchg(struct mxt_data *data) +static int mxt_acquire_irq(struct mxt_data *data) { - struct device *dev = &data->client->dev; - struct mxt_message message; - int count = 10; int error; - /* Read dummy message to make high CHG pin */ - do { - error = mxt_read_message(data, &message); - if (error) - return error; - } while (message.reportid != 0xff && --count); + enable_irq(data->irq); - if (!count) { - dev_err(dev, "CHG pin isn't cleared\n"); - return -EBUSY; - } + error = mxt_process_messages_until_invalid(data); + if (error) + return error; return 0; } @@ -956,24 +1351,55 @@ static int mxt_get_info(struct mxt_data *data) return 0; } +static void mxt_free_object_table(struct mxt_data *data) +{ + input_unregister_device(data->input_dev); + data->input_dev = NULL; + + kfree(data->object_table); + data->object_table = NULL; + kfree(data->msg_buf); + data->msg_buf = NULL; + data->T5_address = 0; + data->T5_msg_size = 0; + data->T6_reportid = 0; + data->T7_address = 0; + data->T9_reportid_min = 0; + data->T9_reportid_max = 0; + data->T19_reportid = 0; + data->T44_address = 0; + data->max_reportid = 0; +} + static int mxt_get_object_table(struct mxt_data *data) { struct i2c_client *client = data->client; size_t table_size; + struct mxt_object *object_table; int error; int i; u8 reportid; + u16 end_address; table_size = data->info.object_num * sizeof(struct mxt_object); + object_table = kzalloc(table_size, GFP_KERNEL); + if (!object_table) { + dev_err(&data->client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + error = __mxt_read_reg(client, MXT_OBJECT_START, table_size, - data->object_table); - if (error) + object_table); + if (error) { + kfree(object_table); return error; + } /* Valid Report IDs start counting from 1 */ reportid = 1; + data->mem_size = 0; for (i = 0; i < data->info.object_num; i++) { - struct mxt_object *object = data->object_table + i; + struct mxt_object *object = object_table + i; u8 min_id, max_id; le16_to_cpus(&object->start_address); @@ -995,31 +1421,71 @@ static int mxt_get_object_table(struct mxt_data *data) min_id, max_id); switch (object->type) { + case MXT_GEN_MESSAGE_T5: + if (data->info.family_id == 0x80) { + /* + * On mXT224 read and discard unused CRC byte + * otherwise DMA reads are misaligned + */ + data->T5_msg_size = mxt_obj_size(object); + } else { + /* CRC not enabled, so skip last byte */ + data->T5_msg_size = mxt_obj_size(object) - 1; + } + data->T5_address = object->start_address; case MXT_GEN_COMMAND_T6: data->T6_reportid = min_id; data->T6_address = object->start_address; break; + case MXT_GEN_POWER_T7: + data->T7_address = object->start_address; + break; case MXT_TOUCH_MULTI_T9: data->T9_reportid_min = min_id; data->T9_reportid_max = max_id; + data->num_touchids = object->num_report_ids + * mxt_obj_instances(object); + break; + case MXT_SPT_MESSAGECOUNT_T44: + data->T44_address = object->start_address; break; case MXT_SPT_GPIOPWM_T19: data->T19_reportid = min_id; break; } + + end_address = object->start_address + + mxt_obj_size(object) * mxt_obj_instances(object) - 1; + + if (end_address >= data->mem_size) + data->mem_size = end_address + 1; + } + + /* Store maximum reportid */ + data->max_reportid = reportid; + + /* If T44 exists, T5 position has to be directly after */ + if (data->T44_address && (data->T5_address != data->T44_address + 1)) { + dev_err(&client->dev, "Invalid T44 position\n"); + error = -EINVAL; + goto free_object_table; + } + + data->msg_buf = kcalloc(data->max_reportid, + data->T5_msg_size, GFP_KERNEL); + if (!data->msg_buf) { + dev_err(&client->dev, "Failed to allocate message buffer\n"); + error = -ENOMEM; + goto free_object_table; } + data->object_table = object_table; + return 0; -} -static void mxt_free_object_table(struct mxt_data *data) -{ - kfree(data->object_table); - data->object_table = NULL; - data->T6_reportid = 0; - data->T9_reportid_min = 0; - data->T9_reportid_max = 0; - data->T19_reportid = 0; +free_object_table: + mxt_free_object_table(data); + return error; } static int mxt_read_t9_resolution(struct mxt_data *data) @@ -1070,55 +1536,255 @@ static int mxt_read_t9_resolution(struct mxt_data *data) return 0; } +static int mxt_input_open(struct input_dev *dev); +static void mxt_input_close(struct input_dev *dev); + +static int mxt_initialize_t9_input_device(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + const struct mxt_platform_data *pdata = data->pdata; + struct input_dev *input_dev; + int error; + unsigned int num_mt_slots; + unsigned int mt_flags = 0; + int i; + + error = mxt_read_t9_resolution(data); + if (error) + dev_warn(dev, "Failed to initialize T9 resolution\n"); + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + input_dev->name = "Atmel maXTouch Touchscreen"; + input_dev->phys = data->phys; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = dev; + input_dev->open = mxt_input_open; + input_dev->close = mxt_input_close; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + + if (pdata->t19_num_keys) { + __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + + for (i = 0; i < pdata->t19_num_keys; i++) + if (pdata->t19_keymap[i] != KEY_RESERVED) + input_set_capability(input_dev, EV_KEY, + pdata->t19_keymap[i]); + + mt_flags |= INPUT_MT_POINTER; + + input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_X, + MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_Y, + MXT_PIXELS_PER_MM); + + input_dev->name = "Atmel maXTouch Touchpad"; + } + + /* For single touch */ + input_set_abs_params(input_dev, ABS_X, + 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, data->max_y, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + 0, 255, 0, 0); + + /* For multi touch */ + num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; + error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); + if (error) { + dev_err(dev, "Error %d initialising slots\n", error); + goto err_free_mem; + } + + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, MXT_MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, data->max_y, 0, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, + 0, 255, 0, 0); + + input_set_drvdata(input_dev, data); + + error = input_register_device(input_dev); + if (error) { + dev_err(dev, "Error %d registering input device\n", error); + goto err_free_mem; + } + + data->input_dev = input_dev; + + return 0; + +err_free_mem: + input_free_device(input_dev); + return error; +} + +static int mxt_configure_objects(struct mxt_data *data, + const struct firmware *cfg); + +static void mxt_config_cb(const struct firmware *cfg, void *ctx) +{ + mxt_configure_objects(ctx, cfg); +} + static int mxt_initialize(struct mxt_data *data) { struct i2c_client *client = data->client; - struct mxt_info *info = &data->info; int error; + bool alt_bootloader_addr = false; + bool retry = false; +retry_info: error = mxt_get_info(data); - if (error) - return error; + if (error) { +retry_bootloader: + error = mxt_probe_bootloader(data, alt_bootloader_addr); + if (error) { + if (alt_bootloader_addr) { + /* Chip is not in appmode or bootloader mode */ + return error; + } - data->object_table = kcalloc(info->object_num, - sizeof(struct mxt_object), - GFP_KERNEL); - if (!data->object_table) { - dev_err(&client->dev, "Failed to allocate memory\n"); - return -ENOMEM; + dev_info(&client->dev, "Trying alternate bootloader address\n"); + alt_bootloader_addr = true; + goto retry_bootloader; + } else { + if (retry) { + dev_err(&client->dev, "Could not recover from bootloader mode\n"); + /* + * We can reflash from this state, so do not + * abort init + */ + data->in_bootloader = true; + return 0; + } + + /* Attempt to exit bootloader into app mode */ + mxt_send_bootloader_cmd(data, false); + msleep(MXT_FW_RESET_TIME); + retry = true; + goto retry_info; + } } /* Get object table information */ error = mxt_get_object_table(data); if (error) { dev_err(&client->dev, "Error %d reading object table\n", error); - goto err_free_object_table; + return error; } - /* Check register init values */ - error = mxt_check_reg_init(data); - if (error) { - dev_err(&client->dev, "Error %d initializing configuration\n", - error); + mxt_acquire_irq(data); + if (error) goto err_free_object_table; + + request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME, + &data->client->dev, GFP_KERNEL, data, + mxt_config_cb); + + return 0; + +err_free_object_table: + mxt_free_object_table(data); + return error; +} + +static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep) +{ + struct device *dev = &data->client->dev; + int error; + struct t7_config *new_config; + struct t7_config deepsleep = { .active = 0, .idle = 0 }; + + if (sleep == MXT_POWER_CFG_DEEPSLEEP) + new_config = &deepsleep; + else + new_config = &data->t7_cfg; + + error = __mxt_write_reg(data->client, data->T7_address, + sizeof(data->t7_cfg), new_config); + if (error) + return error; + + dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n", + new_config->active, new_config->idle); + + return 0; +} + +static int mxt_init_t7_power_cfg(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int error; + bool retry = false; + +recheck: + error = __mxt_read_reg(data->client, data->T7_address, + sizeof(data->t7_cfg), &data->t7_cfg); + if (error) + return error; + + if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) { + if (!retry) { + dev_dbg(dev, "T7 cfg zero, resetting\n"); + mxt_soft_reset(data); + retry = true; + goto recheck; + } else { + dev_dbg(dev, "T7 cfg zero after reset, overriding\n"); + data->t7_cfg.active = 20; + data->t7_cfg.idle = 100; + return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + } } - error = mxt_read_t9_resolution(data); + dev_dbg(dev, "Initialized power cfg: ACTV %d, IDLE %d\n", + data->t7_cfg.active, data->t7_cfg.idle); + return 0; +} + +static int mxt_configure_objects(struct mxt_data *data, + const struct firmware *cfg) +{ + struct device *dev = &data->client->dev; + struct mxt_info *info = &data->info; + int error; + + if (cfg) { + error = mxt_update_cfg(data, cfg); + if (error) + dev_warn(dev, "Error %d updating config\n", error); + } + + error = mxt_init_t7_power_cfg(data); if (error) { - dev_err(&client->dev, "Failed to initialize T9 resolution\n"); - goto err_free_object_table; + dev_err(dev, "Failed to initialize power cfg\n"); + return error; } - dev_info(&client->dev, + error = mxt_initialize_t9_input_device(data); + if (error) + return error; + + dev_info(dev, "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", info->family_id, info->variant_id, info->version >> 4, info->version & 0xf, info->build, info->object_num); return 0; - -err_free_object_table: - mxt_free_object_table(data); - return error; } /* Firmware Version is returned as Major.Minor.Build */ @@ -1246,30 +1912,45 @@ static int mxt_load_fw(struct device *dev, const char *fn) if (ret) goto release_firmware; - ret = mxt_lookup_bootloader_address(data); - if (ret) - goto release_firmware; + if (!data->in_bootloader) { + /* Change to the bootloader mode */ + data->in_bootloader = true; - /* Change to the bootloader mode */ - data->in_bootloader = true; + ret = mxt_t6_command(data, MXT_COMMAND_RESET, + MXT_BOOT_VALUE, false); + if (ret) + goto release_firmware; - ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_BOOT_VALUE, false); - if (ret) - goto release_firmware; + msleep(MXT_RESET_TIME); - msleep(MXT_RESET_TIME); + /* Do not need to scan since we know family ID */ + ret = mxt_lookup_bootloader_address(data, 0); + if (ret) + goto release_firmware; + } else { + enable_irq(data->irq); + } + mxt_free_object_table(data); reinit_completion(&data->bl_completion); - ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD); - if (ret) - goto disable_irq; + ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); + if (ret) { + /* Bootloader may still be unlocked from previous attempt */ + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false); + if (ret) + goto disable_irq; + } else { + dev_info(dev, "Unlocking bootloader\n"); - /* Unlock bootloader */ - mxt_unlock_bootloader(data); + /* Unlock bootloader */ + ret = mxt_send_bootloader_cmd(data, true); + if (ret) + goto disable_irq; + } while (pos < fw->size) { - ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA); + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true); if (ret) goto disable_irq; @@ -1283,7 +1964,7 @@ static int mxt_load_fw(struct device *dev, const char *fn) if (ret) goto disable_irq; - ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS); + ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true); if (ret) { retry++; @@ -1343,13 +2024,7 @@ static ssize_t mxt_update_fw_store(struct device *dev, } else { dev_info(dev, "The firmware update succeeded\n"); - mxt_free_object_table(data); - - mxt_initialize(data); - - enable_irq(data->irq); - - error = mxt_make_highchg(data); + error = mxt_initialize(data); if (error) return error; } @@ -1376,16 +2051,15 @@ static const struct attribute_group mxt_attr_group = { static void mxt_start(struct mxt_data *data) { - /* Touch enable */ - mxt_write_object(data, - MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83); + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + + /* Recalibrate since chip has been in deep sleep */ + mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false); } static void mxt_stop(struct mxt_data *data) { - /* Touch disable */ - mxt_write_object(data, - MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0); + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); } static int mxt_input_open(struct input_dev *dev) @@ -1404,138 +2078,112 @@ static void mxt_input_close(struct input_dev *dev) mxt_stop(data); } -static int mxt_probe(struct i2c_client *client, - const struct i2c_device_id *id) +#ifdef CONFIG_OF +static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) +{ + struct mxt_platform_data *pdata; + u32 *keymap; + u32 keycode; + int proplen, i, ret; + + if (!client->dev.of_node) + return ERR_PTR(-ENODEV); + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + if (of_find_property(client->dev.of_node, "linux,gpio-keymap", + &proplen)) { + pdata->t19_num_keys = proplen / sizeof(u32); + + keymap = devm_kzalloc(&client->dev, + pdata->t19_num_keys * sizeof(keymap[0]), + GFP_KERNEL); + if (!keymap) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < pdata->t19_num_keys; i++) { + ret = of_property_read_u32_index(client->dev.of_node, + "linux,gpio-keymap", i, &keycode); + if (ret) + keycode = KEY_RESERVED; + + keymap[i] = keycode; + } + + pdata->t19_keymap = keymap; + } + + return pdata; +} +#else +static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) +{ + dev_dbg(&client->dev, "No platform data specified\n"); + return ERR_PTR(-EINVAL); +} +#endif + +static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct mxt_platform_data *pdata = dev_get_platdata(&client->dev); struct mxt_data *data; - struct input_dev *input_dev; + const struct mxt_platform_data *pdata; int error; - unsigned int num_mt_slots; - unsigned int mt_flags = 0; - int i; - if (!pdata) - return -EINVAL; + pdata = dev_get_platdata(&client->dev); + if (!pdata) { + pdata = mxt_parse_dt(client); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!data || !input_dev) { + if (!data) { dev_err(&client->dev, "Failed to allocate memory\n"); - error = -ENOMEM; - goto err_free_mem; + return -ENOMEM; } - input_dev->name = "Atmel maXTouch Touchscreen"; snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", client->adapter->nr, client->addr); - input_dev->phys = data->phys; - - input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = &client->dev; - input_dev->open = mxt_input_open; - input_dev->close = mxt_input_close; - data->client = client; - data->input_dev = input_dev; data->pdata = pdata; data->irq = client->irq; + i2c_set_clientdata(client, data); init_completion(&data->bl_completion); init_completion(&data->reset_completion); init_completion(&data->crc_completion); - error = mxt_initialize(data); - if (error) - goto err_free_mem; - - __set_bit(EV_ABS, input_dev->evbit); - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(BTN_TOUCH, input_dev->keybit); - - if (pdata->t19_num_keys) { - __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); - - for (i = 0; i < pdata->t19_num_keys; i++) - if (pdata->t19_keymap[i] != KEY_RESERVED) - input_set_capability(input_dev, EV_KEY, - pdata->t19_keymap[i]); - - mt_flags |= INPUT_MT_POINTER; - - input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_MT_POSITION_X, - MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_MT_POSITION_Y, - MXT_PIXELS_PER_MM); - - input_dev->name = "Atmel maXTouch Touchpad"; - } - - /* For single touch */ - input_set_abs_params(input_dev, ABS_X, - 0, data->max_x, 0, 0); - input_set_abs_params(input_dev, ABS_Y, - 0, data->max_y, 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, - 0, 255, 0, 0); - - /* For multi touch */ - num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; - error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); - if (error) - goto err_free_object; - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, - 0, MXT_MAX_AREA, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_X, - 0, data->max_x, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, - 0, data->max_y, 0, 0); - input_set_abs_params(input_dev, ABS_MT_PRESSURE, - 0, 255, 0, 0); - - input_set_drvdata(input_dev, data); - i2c_set_clientdata(client, data); - error = request_threaded_irq(client->irq, NULL, mxt_interrupt, pdata->irqflags | IRQF_ONESHOT, client->name, data); if (error) { dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_free_object; + goto err_free_mem; } - error = mxt_make_highchg(data); - if (error) - goto err_free_irq; + disable_irq(client->irq); - error = input_register_device(input_dev); - if (error) { - dev_err(&client->dev, "Error %d registering input device\n", - error); + error = mxt_initialize(data); + if (error) goto err_free_irq; - } error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); if (error) { dev_err(&client->dev, "Failure %d creating sysfs group\n", error); - goto err_unregister_device; + goto err_free_object; } return 0; -err_unregister_device: - input_unregister_device(input_dev); - input_dev = NULL; +err_free_object: + mxt_free_object_table(data); err_free_irq: free_irq(client->irq, data); -err_free_object: - kfree(data->object_table); err_free_mem: - input_free_device(input_dev); kfree(data); return error; } @@ -1547,7 +2195,7 @@ static int mxt_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); free_irq(data->irq, data); input_unregister_device(data->input_dev); - kfree(data->object_table); + mxt_free_object_table(data); kfree(data); return 0; @@ -1576,8 +2224,6 @@ static int mxt_resume(struct device *dev) struct mxt_data *data = i2c_get_clientdata(client); struct input_dev *input_dev = data->input_dev; - mxt_soft_reset(data); - mutex_lock(&input_dev->mutex); if (input_dev->users) @@ -1591,6 +2237,12 @@ static int mxt_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume); +static const struct of_device_id mxt_of_match[] = { + { .compatible = "atmel,maxtouch", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mxt_of_match); + static const struct i2c_device_id mxt_id[] = { { "qt602240_ts", 0 }, { "atmel_mxt_ts", 0 }, @@ -1604,6 +2256,7 @@ static struct i2c_driver mxt_driver = { .driver = { .name = "atmel_mxt_ts", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(mxt_of_match), .pm = &mxt_pm_ops, }, .probe = mxt_probe, diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index d4f3399..5a6d50c 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -733,8 +733,7 @@ edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) { - if (tsdata->debug_dir) - debugfs_remove_recursive(tsdata->debug_dir); + debugfs_remove_recursive(tsdata->debug_dir); kfree(tsdata->raw_buffer); } diff --git a/drivers/input/touchscreen/ipaq-micro-ts.c b/drivers/input/touchscreen/ipaq-micro-ts.c new file mode 100644 index 0000000..62c8976 --- /dev/null +++ b/drivers/input/touchscreen/ipaq-micro-ts.c @@ -0,0 +1,166 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * h3600 atmel micro companion support, touchscreen subdevice + * Author : Alessandro Gardich <gremlin@gremlin.it> + * Author : Dmitry Artamonow <mad_soft@inbox.ru> + * Author : Linus Walleij <linus.walleij@linaro.org> + * + */ + +#include <asm/byteorder.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/mfd/ipaq-micro.h> + +struct touchscreen_data { + struct input_dev *input; + struct ipaq_micro *micro; +}; + +static void micro_ts_receive(void *data, int len, unsigned char *msg) +{ + struct touchscreen_data *ts = data; + + if (len == 4) { + input_report_abs(ts->input, ABS_X, + be16_to_cpup((__be16 *) &msg[2])); + input_report_abs(ts->input, ABS_Y, + be16_to_cpup((__be16 *) &msg[0])); + input_report_key(ts->input, BTN_TOUCH, 1); + input_sync(ts->input); + } else if (len == 0) { + input_report_abs(ts->input, ABS_X, 0); + input_report_abs(ts->input, ABS_Y, 0); + input_report_key(ts->input, BTN_TOUCH, 0); + input_sync(ts->input); + } +} + +static void micro_ts_toggle_receive(struct touchscreen_data *ts, bool enable) +{ + struct ipaq_micro *micro = ts->micro; + + spin_lock_irq(µ->lock); + + if (enable) { + micro->ts = micro_ts_receive; + micro->ts_data = ts; + } else { + micro->ts = NULL; + micro->ts_data = NULL; + } + + spin_unlock_irq(&ts->micro->lock); +} + +static int micro_ts_open(struct input_dev *input) +{ + struct touchscreen_data *ts = input_get_drvdata(input); + + micro_ts_toggle_receive(ts, true); + + return 0; +} + +static void micro_ts_close(struct input_dev *input) +{ + struct touchscreen_data *ts = input_get_drvdata(input); + + micro_ts_toggle_receive(ts, false); +} + +static int micro_ts_probe(struct platform_device *pdev) +{ + struct ipaq_micro *micro = dev_get_drvdata(pdev->dev.parent); + struct touchscreen_data *ts; + int error; + + ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + ts->micro = micro; + + ts->input = devm_input_allocate_device(&pdev->dev); + if (!ts->input) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + ts->input->name = "ipaq micro ts"; + ts->input->open = micro_ts_open; + ts->input->close = micro_ts_close; + + input_set_drvdata(ts->input, ts); + + input_set_capability(ts->input, EV_KEY, BTN_TOUCH); + input_set_capability(ts->input, EV_ABS, ABS_X); + input_set_capability(ts->input, EV_ABS, ABS_Y); + input_set_abs_params(ts->input, ABS_X, 0, 1023, 0, 0); + input_set_abs_params(ts->input, ABS_Y, 0, 1023, 0, 0); + + error = input_register_device(ts->input); + if (error) { + dev_err(&pdev->dev, "error registering touch input\n"); + return error; + } + + platform_set_drvdata(pdev, ts); + + dev_info(&pdev->dev, "iPAQ micro touchscreen\n"); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int micro_ts_suspend(struct device *dev) +{ + struct touchscreen_data *ts = dev_get_drvdata(dev); + + micro_ts_toggle_receive(ts, false); + + return 0; +} + +static int micro_ts_resume(struct device *dev) +{ + struct touchscreen_data *ts = dev_get_drvdata(dev); + struct input_dev *input = ts->input; + + mutex_lock(&input->mutex); + + if (input->users) + micro_ts_toggle_receive(ts, true); + + mutex_unlock(&input->mutex); + + return 0; +} +#endif + +static const struct dev_pm_ops micro_ts_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(micro_ts_suspend, micro_ts_resume) +}; + +static struct platform_driver micro_ts_device_driver = { + .driver = { + .name = "ipaq-micro-ts", + .pm = µ_ts_dev_pm_ops, + }, + .probe = micro_ts_probe, +}; +module_platform_driver(micro_ts_device_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("driver for iPAQ Atmel micro touchscreen"); +MODULE_ALIAS("platform:ipaq-micro-ts"); diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c index 7324c5c..651ec71 100644 --- a/drivers/input/touchscreen/jornada720_ts.c +++ b/drivers/input/touchscreen/jornada720_ts.c @@ -36,22 +36,21 @@ struct jornada_ts { static void jornada720_ts_collect_data(struct jornada_ts *jornada_ts) { + /* 3 low word X samples */ + jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY); + jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY); + jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY); - /* 3 low word X samples */ - jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY); - jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY); - jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY); + /* 3 low word Y samples */ + jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY); + jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY); + jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY); - /* 3 low word Y samples */ - jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY); - jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY); - jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY); + /* combined x samples bits */ + jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY); - /* combined x samples bits */ - jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY); - - /* combined y samples bits */ - jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY); + /* combined y samples bits */ + jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY); } static int jornada720_ts_average(int coords[4]) @@ -104,13 +103,13 @@ static int jornada720_ts_probe(struct platform_device *pdev) struct input_dev *input_dev; int error; - jornada_ts = kzalloc(sizeof(struct jornada_ts), GFP_KERNEL); - input_dev = input_allocate_device(); + jornada_ts = devm_kzalloc(&pdev->dev, sizeof(*jornada_ts), GFP_KERNEL); + if (!jornada_ts) + return -ENOMEM; - if (!jornada_ts || !input_dev) { - error = -ENOMEM; - goto fail1; - } + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) + return -ENOMEM; platform_set_drvdata(pdev, jornada_ts); @@ -126,36 +125,18 @@ static int jornada720_ts_probe(struct platform_device *pdev) input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0); input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0); - error = request_irq(IRQ_GPIO9, - jornada720_ts_interrupt, - IRQF_TRIGGER_RISING, - "HP7XX Touchscreen driver", pdev); + error = devm_request_irq(&pdev->dev, IRQ_GPIO9, + jornada720_ts_interrupt, + IRQF_TRIGGER_RISING, + "HP7XX Touchscreen driver", pdev); if (error) { - printk(KERN_INFO "HP7XX TS : Unable to acquire irq!\n"); - goto fail1; + dev_err(&pdev->dev, "HP7XX TS : Unable to acquire irq!\n"); + return error; } error = input_register_device(jornada_ts->dev); if (error) - goto fail2; - - return 0; - - fail2: - free_irq(IRQ_GPIO9, pdev); - fail1: - input_free_device(input_dev); - kfree(jornada_ts); - return error; -} - -static int jornada720_ts_remove(struct platform_device *pdev) -{ - struct jornada_ts *jornada_ts = platform_get_drvdata(pdev); - - free_irq(IRQ_GPIO9, pdev); - input_unregister_device(jornada_ts->dev); - kfree(jornada_ts); + return error; return 0; } @@ -165,7 +146,6 @@ MODULE_ALIAS("platform:jornada_ts"); static struct platform_driver jornada720_ts_driver = { .probe = jornada720_ts_probe, - .remove = jornada720_ts_remove, .driver = { .name = "jornada_ts", .owner = THIS_MODULE, diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c index 00510a9..8b47e1f 100644 --- a/drivers/input/touchscreen/mcs5000_ts.c +++ b/drivers/input/touchscreen/mcs5000_ts.c @@ -248,8 +248,7 @@ static int mcs5000_ts_probe(struct i2c_client *client, return 0; } -#ifdef CONFIG_PM -static int mcs5000_ts_suspend(struct device *dev) +static int __maybe_unused mcs5000_ts_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -259,7 +258,7 @@ static int mcs5000_ts_suspend(struct device *dev) return 0; } -static int mcs5000_ts_resume(struct device *dev) +static int __maybe_unused mcs5000_ts_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct mcs5000_ts_data *data = i2c_get_clientdata(client); @@ -269,7 +268,6 @@ static int mcs5000_ts_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume); diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 19c6c0f..fc49c753 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -23,22 +23,51 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/input.h> +#include <linux/input/mt.h> #include <linux/input/pixcir_ts.h> #include <linux/gpio.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_device.h> + +#define PIXCIR_MAX_SLOTS 5 /* Max fingers supported by driver */ struct pixcir_i2c_ts_data { struct i2c_client *client; struct input_dev *input; - const struct pixcir_ts_platform_data *chip; + const struct pixcir_ts_platform_data *pdata; bool running; + int max_fingers; /* Max fingers supported in this instance */ +}; + +struct pixcir_touch { + int x; + int y; + int id; +}; + +struct pixcir_report_data { + int num_touches; + struct pixcir_touch touches[PIXCIR_MAX_SLOTS]; }; -static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data) +static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata, + struct pixcir_report_data *report) { - struct pixcir_i2c_ts_data *tsdata = data; - u8 rdbuf[10], wrbuf[1] = { 0 }; + u8 rdbuf[2 + PIXCIR_MAX_SLOTS * 5]; + u8 wrbuf[1] = { 0 }; + u8 *bufptr; u8 touch; - int ret; + int ret, i; + int readsize; + const struct pixcir_i2c_chip_data *chip = &tsdata->pdata->chip; + + memset(report, 0, sizeof(struct pixcir_report_data)); + + i = chip->has_hw_ids ? 1 : 0; + readsize = 2 + tsdata->max_fingers * (4 + i); + if (readsize > sizeof(rdbuf)) + readsize = sizeof(rdbuf); ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf)); if (ret != sizeof(wrbuf)) { @@ -48,7 +77,7 @@ static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data) return; } - ret = i2c_master_recv(tsdata->client, rdbuf, sizeof(rdbuf)); + ret = i2c_master_recv(tsdata->client, rdbuf, readsize); if (ret != sizeof(rdbuf)) { dev_err(&tsdata->client->dev, "%s: i2c_master_recv failed(), ret=%d\n", @@ -56,45 +85,103 @@ static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data) return; } - touch = rdbuf[0]; - if (touch) { - u16 posx1 = (rdbuf[3] << 8) | rdbuf[2]; - u16 posy1 = (rdbuf[5] << 8) | rdbuf[4]; - u16 posx2 = (rdbuf[7] << 8) | rdbuf[6]; - u16 posy2 = (rdbuf[9] << 8) | rdbuf[8]; - - input_report_key(tsdata->input, BTN_TOUCH, 1); - input_report_abs(tsdata->input, ABS_X, posx1); - input_report_abs(tsdata->input, ABS_Y, posy1); - - input_report_abs(tsdata->input, ABS_MT_POSITION_X, posx1); - input_report_abs(tsdata->input, ABS_MT_POSITION_Y, posy1); - input_mt_sync(tsdata->input); - - if (touch == 2) { - input_report_abs(tsdata->input, - ABS_MT_POSITION_X, posx2); - input_report_abs(tsdata->input, - ABS_MT_POSITION_Y, posy2); - input_mt_sync(tsdata->input); + touch = rdbuf[0] & 0x7; + if (touch > tsdata->max_fingers) + touch = tsdata->max_fingers; + + report->num_touches = touch; + bufptr = &rdbuf[2]; + + for (i = 0; i < touch; i++) { + report->touches[i].x = (bufptr[1] << 8) | bufptr[0]; + report->touches[i].y = (bufptr[3] << 8) | bufptr[2]; + + if (chip->has_hw_ids) { + report->touches[i].id = bufptr[4]; + bufptr = bufptr + 5; + } else { + bufptr = bufptr + 4; + } + } +} + +static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts, + struct pixcir_report_data *report) +{ + struct input_mt_pos pos[PIXCIR_MAX_SLOTS]; + int slots[PIXCIR_MAX_SLOTS]; + struct pixcir_touch *touch; + int n, i, slot; + struct device *dev = &ts->client->dev; + const struct pixcir_i2c_chip_data *chip = &ts->pdata->chip; + + n = report->num_touches; + if (n > PIXCIR_MAX_SLOTS) + n = PIXCIR_MAX_SLOTS; + + if (!chip->has_hw_ids) { + for (i = 0; i < n; i++) { + touch = &report->touches[i]; + pos[i].x = touch->x; + pos[i].y = touch->y; + } + + input_mt_assign_slots(ts->input, slots, pos, n); + } + + for (i = 0; i < n; i++) { + touch = &report->touches[i]; + + if (chip->has_hw_ids) { + slot = input_mt_get_slot_by_key(ts->input, touch->id); + if (slot < 0) { + dev_dbg(dev, "no free slot for id 0x%x\n", + touch->id); + continue; + } + } else { + slot = slots[i]; } - } else { - input_report_key(tsdata->input, BTN_TOUCH, 0); + + input_mt_slot(ts->input, slot); + input_mt_report_slot_state(ts->input, + MT_TOOL_FINGER, true); + + input_event(ts->input, EV_ABS, ABS_MT_POSITION_X, touch->x); + input_event(ts->input, EV_ABS, ABS_MT_POSITION_Y, touch->y); + + dev_dbg(dev, "%d: slot %d, x %d, y %d\n", + i, slot, touch->x, touch->y); } - input_sync(tsdata->input); + input_mt_sync_frame(ts->input); + input_sync(ts->input); } static irqreturn_t pixcir_ts_isr(int irq, void *dev_id) { struct pixcir_i2c_ts_data *tsdata = dev_id; - const struct pixcir_ts_platform_data *pdata = tsdata->chip; + const struct pixcir_ts_platform_data *pdata = tsdata->pdata; + struct pixcir_report_data report; while (tsdata->running) { - pixcir_ts_poscheck(tsdata); - - if (gpio_get_value(pdata->gpio_attb)) + /* parse packet */ + pixcir_ts_parse(tsdata, &report); + + /* report it */ + pixcir_ts_report(tsdata, &report); + + if (gpio_get_value(pdata->gpio_attb)) { + if (report.num_touches) { + /* + * Last report with no finger up? + * Do it now then. + */ + input_mt_sync_frame(tsdata->input); + input_sync(tsdata->input); + } break; + } msleep(20); } @@ -323,16 +410,69 @@ unlock: static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops, pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume); +#ifdef CONFIG_OF +static const struct of_device_id pixcir_of_match[]; + +static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev) +{ + struct pixcir_ts_platform_data *pdata; + struct device_node *np = dev->of_node; + const struct of_device_id *match; + + match = of_match_device(of_match_ptr(pixcir_of_match), dev); + if (!match) + return ERR_PTR(-EINVAL); + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + pdata->chip = *(const struct pixcir_i2c_chip_data *)match->data; + + pdata->gpio_attb = of_get_named_gpio(np, "attb-gpio", 0); + /* gpio_attb validity is checked in probe */ + + if (of_property_read_u32(np, "touchscreen-size-x", &pdata->x_max)) { + dev_err(dev, "Failed to get touchscreen-size-x property\n"); + return ERR_PTR(-EINVAL); + } + pdata->x_max -= 1; + + if (of_property_read_u32(np, "touchscreen-size-y", &pdata->y_max)) { + dev_err(dev, "Failed to get touchscreen-size-y property\n"); + return ERR_PTR(-EINVAL); + } + pdata->y_max -= 1; + + dev_dbg(dev, "%s: x %d, y %d, gpio %d\n", __func__, + pdata->x_max + 1, pdata->y_max + 1, pdata->gpio_attb); + + return pdata; +} +#else +static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev) +{ + return ERR_PTR(-EINVAL); +} +#endif + static int pixcir_i2c_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { const struct pixcir_ts_platform_data *pdata = dev_get_platdata(&client->dev); struct device *dev = &client->dev; + struct device_node *np = dev->of_node; struct pixcir_i2c_ts_data *tsdata; struct input_dev *input; int error; + if (np && !pdata) { + pdata = pixcir_parse_dt(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + if (!pdata) { dev_err(&client->dev, "platform data not defined\n"); return -EINVAL; @@ -343,6 +483,11 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, return -EINVAL; } + if (!pdata->chip.max_fingers) { + dev_err(dev, "Invalid max_fingers in pdata\n"); + return -EINVAL; + } + tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL); if (!tsdata) return -ENOMEM; @@ -355,7 +500,7 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, tsdata->client = client; tsdata->input = input; - tsdata->chip = pdata; + tsdata->pdata = pdata; input->name = client->name; input->id.bustype = BUS_I2C; @@ -371,6 +516,20 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0); + tsdata->max_fingers = tsdata->pdata->chip.max_fingers; + if (tsdata->max_fingers > PIXCIR_MAX_SLOTS) { + tsdata->max_fingers = PIXCIR_MAX_SLOTS; + dev_info(dev, "Limiting maximum fingers to %d\n", + tsdata->max_fingers); + } + + error = input_mt_init_slots(input, tsdata->max_fingers, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) { + dev_err(dev, "Error initializing Multi-Touch slots\n"); + return error; + } + input_set_drvdata(input, tsdata); error = devm_gpio_request_one(dev, pdata->gpio_attb, @@ -419,15 +578,36 @@ static int pixcir_i2c_ts_remove(struct i2c_client *client) static const struct i2c_device_id pixcir_i2c_ts_id[] = { { "pixcir_ts", 0 }, + { "pixcir_tangoc", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id); +#ifdef CONFIG_OF +static const struct pixcir_i2c_chip_data pixcir_ts_data = { + .max_fingers = 2, + /* no hw id support */ +}; + +static const struct pixcir_i2c_chip_data pixcir_tangoc_data = { + .max_fingers = 5, + .has_hw_ids = true, +}; + +static const struct of_device_id pixcir_of_match[] = { + { .compatible = "pixcir,pixcir_ts", .data = &pixcir_ts_data }, + { .compatible = "pixcir,pixcir_tangoc", .data = &pixcir_tangoc_data }, + { } +}; +MODULE_DEVICE_TABLE(of, pixcir_of_match); +#endif + static struct i2c_driver pixcir_i2c_ts_driver = { .driver = { .owner = THIS_MODULE, .name = "pixcir_ts", .pm = &pixcir_dev_pm_ops, + .of_match_table = of_match_ptr(pixcir_of_match), }, .probe = pixcir_i2c_ts_probe, .remove = pixcir_i2c_ts_remove, diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index 19cb247..5a69ded 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -264,7 +264,7 @@ static int s3c2410ts_probe(struct platform_device *pdev) return -ENOENT; } - clk_enable(ts.clock); + clk_prepare_enable(ts.clock); dev_dbg(dev, "got and enabled clocks\n"); ts.irq_tc = ret = platform_get_irq(pdev, 0); @@ -369,7 +369,7 @@ static int s3c2410ts_remove(struct platform_device *pdev) free_irq(ts.irq_tc, ts.input); del_timer_sync(&touch_timer); - clk_disable(ts.clock); + clk_disable_unprepare(ts.clock); clk_put(ts.clock); input_unregister_device(ts.input); diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index feea85b..8ba48f5 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -29,6 +29,8 @@ #include <linux/sysfs.h> #include <linux/input/mt.h> #include <linux/platform_data/zforce_ts.h> +#include <linux/regulator/consumer.h> +#include <linux/delay.h> #include <linux/of.h> #include <linux/of_gpio.h> @@ -117,6 +119,8 @@ struct zforce_ts { const struct zforce_ts_platdata *pdata; char phys[32]; + struct regulator *reg_vdd; + bool suspending; bool suspended; bool boot_complete; @@ -690,6 +694,11 @@ static void zforce_reset(void *data) struct zforce_ts *ts = data; gpio_set_value(ts->pdata->gpio_rst, 0); + + udelay(10); + + if (!IS_ERR(ts->reg_vdd)) + regulator_disable(ts->reg_vdd); } static struct zforce_ts_platdata *zforce_parse_dt(struct device *dev) @@ -765,10 +774,32 @@ static int zforce_probe(struct i2c_client *client, return ret; } + ts->reg_vdd = devm_regulator_get_optional(&client->dev, "vdd"); + if (IS_ERR(ts->reg_vdd)) { + ret = PTR_ERR(ts->reg_vdd); + if (ret == -EPROBE_DEFER) + return ret; + } else { + ret = regulator_enable(ts->reg_vdd); + if (ret) + return ret; + + /* + * according to datasheet add 100us grace time after regular + * regulator enable delay. + */ + udelay(100); + } + ret = devm_add_action(&client->dev, zforce_reset, ts); if (ret) { dev_err(&client->dev, "failed to register reset action, %d\n", ret); + + /* hereafter the regulator will be disabled by the action */ + if (!IS_ERR(ts->reg_vdd)) + regulator_disable(ts->reg_vdd); + return ret; } diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 7f1a2e2..67b316b 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -97,8 +97,6 @@ static struct mxt_platform_data atmel_224s_tp_platform_data = { .irqflags = IRQF_TRIGGER_FALLING, .t19_num_keys = ARRAY_SIZE(mxt_t19_keys), .t19_keymap = mxt_t19_keys, - .config = NULL, - .config_length = 0, }; static struct i2c_board_info atmel_224s_tp_device = { @@ -109,8 +107,6 @@ static struct i2c_board_info atmel_224s_tp_device = { static struct mxt_platform_data atmel_1664s_platform_data = { .irqflags = IRQF_TRIGGER_FALLING, - .config = NULL, - .config_length = 0, }; static struct i2c_board_info atmel_1664s_device = { diff --git a/include/linux/hid.h b/include/linux/hid.h index fca74f1..f53c4a9 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -312,6 +312,11 @@ struct hid_item { #define HID_GROUP_RMI 0x0100 /* + * Vendor specific HID device groups + */ +#define HID_GROUP_WACOM 0x0101 + +/* * This is the global environment of the parser. This information is * persistent for main-items. The global environment can be saved and * restored with PUSH/POP statements. diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index 3891dc1..02bf6ea 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -17,9 +17,6 @@ /* The platform data for the Atmel maXTouch touchscreen driver */ struct mxt_platform_data { - const u8 *config; - size_t config_length; - u32 config_crc; unsigned long irqflags; u8 t19_num_keys; const unsigned int *t19_keymap; diff --git a/include/linux/input/pixcir_ts.h b/include/linux/input/pixcir_ts.h index 160cf35..7bae83b 100644 --- a/include/linux/input/pixcir_ts.h +++ b/include/linux/input/pixcir_ts.h @@ -43,10 +43,22 @@ enum pixcir_int_mode { #define PIXCIR_INT_ENABLE (1UL << 3) #define PIXCIR_INT_POL_HIGH (1UL << 2) +/** + * struct pixcir_irc_chip_data - chip related data + * @max_fingers: Max number of fingers reported simultaneously by h/w + * @has_hw_ids: Hardware supports finger tracking IDs + * + */ +struct pixcir_i2c_chip_data { + u8 max_fingers; + bool has_hw_ids; +}; + struct pixcir_ts_platform_data { int x_max; int y_max; int gpio_attb; /* GPIO connected to ATTB line */ + struct pixcir_i2c_chip_data chip; }; #endif diff --git a/include/uapi/linux/serio.h b/include/uapi/linux/serio.h index 9f53fa7..becdd78 100644 --- a/include/uapi/linux/serio.h +++ b/include/uapi/linux/serio.h @@ -76,5 +76,6 @@ #define SERIO_HAMPSHIRE 0x3b #define SERIO_PS2MULT 0x3c #define SERIO_TSC40 0x3d +#define SERIO_WACOM_IV 0x3e #endif /* _UAPI_SERIO_H */ diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h index 0389b48..baeab83 100644 --- a/include/uapi/linux/uinput.h +++ b/include/uapi/linux/uinput.h @@ -84,6 +84,15 @@ struct uinput_ff_erase { */ #define UI_GET_SYSNAME(len) _IOC(_IOC_READ, UINPUT_IOCTL_BASE, 300, len) +/** + * UI_GET_VERSION - Return version of uinput protocol + * + * This writes uinput protocol version implemented by the kernel into + * the integer pointed to by the ioctl argument. The protocol version + * is hard-coded in the kernel and is independent of the uinput device. + */ +#define UI_GET_VERSION _IOR(UINPUT_IOCTL_BASE, 301, unsigned int) + /* * To write a force-feedback-capable driver, the upload_effect * and erase_effect callbacks in input_dev must be implemented. |