1
0
Fork 0

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input

Pull input updates from Dmitry Torokhov:
 - big update to Wacom driver by Benjamin Tissoires, converting it to
   HID infrastructure and unifying USB and Bluetooth models
 - large update to ALPS driver by Hans de Goede, which adds support for
   newer touchpad models as well as cleans up and restructures the code
 - more changes to Atmel MXT driver, including device tree support
 - new driver for iPaq x3xxx touchscreen
 - driver for serial Wacom tablets
 - driver for Microchip's CAP1106
 - assorted cleanups and improvements to existing drover and input core

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (93 commits)
  Input: wacom - update the ABI doc according to latest changes
  Input: wacom - only register once the MODULE_* macros
  Input: HID - remove hid-wacom Bluetooth driver
  Input: wacom - add copyright note and bump version to 2.0
  Input: wacom - remove passing id for wacom_set_report
  Input: wacom - check for bluetooth protocol while setting OLEDs
  Input: wacom - handle Intuos 4 BT in wacom.ko
  Input: wacom - handle Graphire BT tablets in wacom.ko
  Input: wacom - prepare the driver to include BT devices
  Input: hyperv-keyboard - register as a wakeup source
  Input: imx_keypad - remove ifdef round PM methods
  Input: jornada720_ts - get rid of space indentation and use tab
  Input: jornada720_ts - switch to using managed resources
  Input: alps - Rushmore and v7 resolution support
  Input: mcs5000_ts - remove ifdef around power management methods
  Input: mcs5000_ts - protect PM functions with CONFIG_PM_SLEEP
  Input: ads7846 - release resources on failure for clean exit
  Input: wacom - add support for 0x12C ISDv4 sensor
  Input: atmel_mxt_ts - use deep sleep mode when stopped
  ARM: dts: am437x-gp-evm: Update binding for touchscreen size
  ...
hifive-unleashed-5.1
Linus Torvalds 2014-08-08 17:39:48 -07:00
commit 664fb23070
48 changed files with 4543 additions and 2913 deletions

View File

@ -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

View File

@ -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>;
};

View File

@ -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 */
};
}

View File

@ -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>;
};
/* ... */
};

View File

@ -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 = <&reg_zforce_vdd>;
gpios = <&gpio5 6 0>, /* INT */
<&gpio5 9 0>; /* RST */

View File

@ -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

View File

@ -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

View File

@ -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>;
};
};

View File

@ -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>;
};
};

View File

@ -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"

View File

@ -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

View File

@ -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) },

View File

@ -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");

View File

@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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);

View File

@ -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 */
@ -251,24 +257,6 @@ static int max7359_probe(struct i2c_client *client,
i2c_set_clientdata(client, keypad);
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;
}
@ -313,7 +301,6 @@ static struct i2c_driver max7359_i2c_driver = {
.pm = &max7359_pm,
},
.probe = max7359_probe,
.remove = max7359_remove,
.id_table = max7359_ids,
};

View File

@ -392,7 +392,6 @@ static void keyspan_irq_recv(struct urb *urb)
default:
goto resubmit;
break;
}
if (debug)

View File

@ -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++) {

View File

@ -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;

View File

@ -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));
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));
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[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;
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;
}
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
packet_id = V7_PACKET_ID_UNKNOWN;
return packet_id;
}
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;
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;
case V7_PACKET_ID_NEW:
mt[1].x &= ~0x003F;
mt[1].x |= (pkt[0] & 0x20);
mt[1].y |= 0x000F;
break;
}
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;
/*
* If there were no contacts in the bitmap, use ST
* points in MT reports.
* If there were two contacts or more, report MT data.
* 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
*/
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;
}
if (z >= 64)
input_report_key(dev, BTN_TOUCH, 1);
else
input_report_key(dev, BTN_TOUCH, 0);
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);
alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
left = (packet[1] & 0x01);
right = (packet[1] & 0x02) >> 1;
middle = (packet[1] & 0x04) >> 2;
input_mt_report_finger_count(dev, fingers);
/* 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(dev, BTN_LEFT, left);
input_report_key(dev, BTN_RIGHT, right);
input_report_key(dev2, BTN_LEFT, left);
input_report_key(dev2, BTN_RIGHT, right);
input_report_key(dev2, BTN_MIDDLE, middle);
if (z > 0) {
input_report_abs(dev, ABS_X, x);
input_report_abs(dev, ABS_Y, y);
}
input_report_abs(dev, ABS_PRESSURE, z);
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);
}

View File

@ -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;
};

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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(&micro->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 = &micro_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");

View File

@ -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,

View File

@ -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);

View File

@ -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 */
};
static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data)
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_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];
touch = rdbuf[0] & 0x7;
if (touch > tsdata->max_fingers)
touch = tsdata->max_fingers;
input_report_key(tsdata->input, BTN_TOUCH, 1);
input_report_abs(tsdata->input, ABS_X, posx1);
input_report_abs(tsdata->input, ABS_Y, posy1);
report->num_touches = touch;
bufptr = &rdbuf[2];
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);
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 (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);
if (chip->has_hw_ids) {
report->touches[i].id = bufptr[4];
bufptr = bufptr + 5;
} else {
bufptr = bufptr + 4;
}
} else {
input_report_key(tsdata->input, BTN_TOUCH, 0);
}
}
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);
}
input_sync(tsdata->input);
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];
}
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_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);
/* parse packet */
pixcir_ts_parse(tsdata, &report);
if (gpio_get_value(pdata->gpio_attb))
/* 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,

View File

@ -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);

View File

@ -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;
}

View File

@ -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 = {

View File

@ -311,6 +311,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

View File

@ -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;

View File

@ -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

View File

@ -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 */

View File

@ -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.