diff options
Diffstat (limited to 'drivers/input')
33 files changed, 3491 insertions, 1010 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index a829338..d8fa5d7 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -73,7 +73,7 @@ config KEYBOARD_ATKBD default y select SERIO select SERIO_LIBPS2 - select SERIO_I8042 if X86 + select SERIO_I8042 if X86 && !X86_MRST select SERIO_GSCPS2 if GSC help Say Y here if you want to use a standard AT or PS/2 keyboard. Usually @@ -179,6 +179,22 @@ config KEYBOARD_GPIO To compile this driver as a module, choose M here: the module will be called gpio_keys. +config KEYBOARD_TCA6416 + tristate "TCA6416 Keypad Support" + depends on I2C + help + This driver implements basic keypad functionality + for keys connected through TCA6416 IO expander + + Say Y here if your device has keys connected to + TCA6416 IO expander. Your board-specific setup logic + must also provide pin-mask details(of which TCA6416 pins + are used for keypad). + + If enabled the complete TCA6416 device will be managed through + this driver. + + config KEYBOARD_MATRIX tristate "GPIO driven matrix keypad support" depends on GENERIC_GPIO diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 9a74127e..4596d0c 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o +obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c index 60ac468..bc69693 100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@ -670,8 +670,6 @@ static int __devinit lm8323_probe(struct i2c_client *client, goto fail1; } - i2c_set_clientdata(client, lm); - lm->client = client; lm->idev = idev; mutex_init(&lm->lock); @@ -753,6 +751,8 @@ static int __devinit lm8323_probe(struct i2c_client *client, goto fail4; } + i2c_set_clientdata(client, lm); + device_init_wakeup(&client->dev, 1); enable_irq_wake(client->irq); @@ -778,6 +778,8 @@ static int __devexit lm8323_remove(struct i2c_client *client) struct lm8323_chip *lm = i2c_get_clientdata(client); int i; + i2c_set_clientdata(client, NULL); + disable_irq_wake(client->irq); free_irq(client->irq, lm); cancel_work_sync(&lm->work); diff --git a/drivers/input/keyboard/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c new file mode 100644 index 0000000..493c93f --- /dev/null +++ b/drivers/input/keyboard/tca6416-keypad.c @@ -0,0 +1,349 @@ +/* + * Driver for keys on TCA6416 I2C IO expander + * + * Copyright (C) 2010 Texas Instruments + * + * Author : Sriramakrishnan.A.G. <srk@ti.com> + * + * 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/types.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/tca6416_keypad.h> + +#define TCA6416_INPUT 0 +#define TCA6416_OUTPUT 1 +#define TCA6416_INVERT 2 +#define TCA6416_DIRECTION 3 + +static const struct i2c_device_id tca6416_id[] = { + { "tca6416-keys", 16, }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tca6416_id); + +struct tca6416_drv_data { + struct input_dev *input; + struct tca6416_button data[0]; +}; + +struct tca6416_keypad_chip { + uint16_t reg_output; + uint16_t reg_direction; + uint16_t reg_input; + + struct i2c_client *client; + struct input_dev *input; + struct delayed_work dwork; + u16 pinmask; + int irqnum; + bool use_polling; + struct tca6416_button buttons[0]; +}; + +static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, u16 val) +{ + int error; + + error = i2c_smbus_write_word_data(chip->client, reg << 1, val); + if (error < 0) { + dev_err(&chip->client->dev, + "%s failed, reg: %d, val: %d, error: %d\n", + __func__, reg, val, error); + return error; + } + + return 0; +} + +static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val) +{ + int retval; + + retval = i2c_smbus_read_word_data(chip->client, reg << 1); + if (retval < 0) { + dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n", + __func__, reg, retval); + return retval; + } + + *val = (u16)retval; + return 0; +} + +static void tca6416_keys_scan(struct tca6416_keypad_chip *chip) +{ + struct input_dev *input = chip->input; + u16 reg_val, val; + int error, i, pin_index; + + error = tca6416_read_reg(chip, TCA6416_INPUT, ®_val); + if (error) + return; + + reg_val &= chip->pinmask; + + /* Figure out which lines have changed */ + val = reg_val ^ chip->reg_input; + chip->reg_input = reg_val; + + for (i = 0, pin_index = 0; i < 16; i++) { + if (val & (1 << i)) { + struct tca6416_button *button = &chip->buttons[pin_index]; + unsigned int type = button->type ?: EV_KEY; + int state = ((reg_val & (1 << i)) ? 1 : 0) + ^ button->active_low; + + input_event(input, type, button->code, !!state); + input_sync(input); + } + + if (chip->pinmask & (1 << i)) + pin_index++; + } +} + +/* + * This is threaded IRQ handler and this can (and will) sleep. + */ +static irqreturn_t tca6416_keys_isr(int irq, void *dev_id) +{ + struct tca6416_keypad_chip *chip = dev_id; + + tca6416_keys_scan(chip); + + return IRQ_HANDLED; +} + +static void tca6416_keys_work_func(struct work_struct *work) +{ + struct tca6416_keypad_chip *chip = + container_of(work, struct tca6416_keypad_chip, dwork.work); + + tca6416_keys_scan(chip); + schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100)); +} + +static int tca6416_keys_open(struct input_dev *dev) +{ + struct tca6416_keypad_chip *chip = input_get_drvdata(dev); + + /* Get initial device state in case it has switches */ + tca6416_keys_scan(chip); + + if (chip->use_polling) + schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100)); + else + enable_irq(chip->irqnum); + + return 0; +} + +static void tca6416_keys_close(struct input_dev *dev) +{ + struct tca6416_keypad_chip *chip = input_get_drvdata(dev); + + if (chip->use_polling) + cancel_delayed_work_sync(&chip->dwork); + else + disable_irq(chip->irqnum); +} + +static int __devinit tca6416_setup_registers(struct tca6416_keypad_chip *chip) +{ + int error; + + error = tca6416_read_reg(chip, TCA6416_OUTPUT, &chip->reg_output); + if (error) + return error; + + error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); + if (error) + return error; + + /* ensure that keypad pins are set to input */ + error = tca6416_write_reg(chip, TCA6416_DIRECTION, + chip->reg_direction | chip->pinmask); + if (error) + return error; + + error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); + if (error) + return error; + + error = tca6416_read_reg(chip, TCA6416_INPUT, &chip->reg_input); + if (error) + return error; + + chip->reg_input &= chip->pinmask; + + return 0; +} + +static int __devinit tca6416_keypad_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tca6416_keys_platform_data *pdata; + struct tca6416_keypad_chip *chip; + struct input_dev *input; + int error; + int i; + + /* Check functionality */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { + dev_err(&client->dev, "%s adapter not supported\n", + dev_driver_string(&client->adapter->dev)); + return -ENODEV; + } + + pdata = client->dev.platform_data; + if (!pdata) { + dev_dbg(&client->dev, "no platform data\n"); + return -EINVAL; + } + + chip = kzalloc(sizeof(struct tca6416_keypad_chip) + + pdata->nbuttons * sizeof(struct tca6416_button), + GFP_KERNEL); + input = input_allocate_device(); + if (!chip || !input) { + error = -ENOMEM; + goto fail1; + } + + chip->client = client; + chip->input = input; + chip->pinmask = pdata->pinmask; + chip->use_polling = pdata->use_polling; + + INIT_DELAYED_WORK(&chip->dwork, tca6416_keys_work_func); + + input->phys = "tca6416-keys/input0"; + input->name = client->name; + input->dev.parent = &client->dev; + + input->open = tca6416_keys_open; + input->close = tca6416_keys_close; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + /* Enable auto repeat feature of Linux input subsystem */ + if (pdata->rep) + __set_bit(EV_REP, input->evbit); + + for (i = 0; i < pdata->nbuttons; i++) { + unsigned int type; + + chip->buttons[i] = pdata->buttons[i]; + type = (pdata->buttons[i].type) ?: EV_KEY; + input_set_capability(input, type, pdata->buttons[i].code); + } + + input_set_drvdata(input, chip); + + /* + * Initialize cached registers from their original values. + * we can't share this chip with another i2c master. + */ + error = tca6416_setup_registers(chip); + if (error) + goto fail1; + + if (!chip->use_polling) { + if (pdata->irq_is_gpio) + chip->irqnum = gpio_to_irq(client->irq); + else + chip->irqnum = client->irq; + + error = request_threaded_irq(chip->irqnum, NULL, + tca6416_keys_isr, + IRQF_TRIGGER_FALLING, + "tca6416-keypad", chip); + if (error) { + dev_dbg(&client->dev, + "Unable to claim irq %d; error %d\n", + chip->irqnum, error); + goto fail1; + } + disable_irq(chip->irqnum); + } + + error = input_register_device(input); + if (error) { + dev_dbg(&client->dev, + "Unable to register input device, error: %d\n", error); + goto fail2; + } + + i2c_set_clientdata(client, chip); + + return 0; + +fail2: + if (!chip->use_polling) { + free_irq(chip->irqnum, chip); + enable_irq(chip->irqnum); + } +fail1: + input_free_device(input); + kfree(chip); + return error; +} + +static int __devexit tca6416_keypad_remove(struct i2c_client *client) +{ + struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); + + if (!chip->use_polling) { + free_irq(chip->irqnum, chip); + enable_irq(chip->irqnum); + } + + input_unregister_device(chip->input); + kfree(chip); + + i2c_set_clientdata(client, NULL); + + return 0; +} + + +static struct i2c_driver tca6416_keypad_driver = { + .driver = { + .name = "tca6416-keypad", + }, + .probe = tca6416_keypad_probe, + .remove = __devexit_p(tca6416_keypad_remove), + .id_table = tca6416_id, +}; + +static int __init tca6416_keypad_init(void) +{ + return i2c_add_driver(&tca6416_keypad_driver); +} + +subsys_initcall(tca6416_keypad_init); + +static void __exit tca6416_keypad_exit(void) +{ + i2c_del_driver(&tca6416_keypad_driver); +} +module_exit(tca6416_keypad_exit); + +MODULE_AUTHOR("Sriramakrishnan <srk@ti.com>"); +MODULE_DESCRIPTION("Keypad driver over tca6146 IO expander"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/88pm860x_onkey.c b/drivers/input/misc/88pm860x_onkey.c index 40dabd8..4cc8282 100644 --- a/drivers/input/misc/88pm860x_onkey.c +++ b/drivers/input/misc/88pm860x_onkey.c @@ -87,7 +87,6 @@ static int __devinit pm860x_onkey_probe(struct platform_device *pdev) info->idev->phys = "88pm860x_on/input0"; info->idev->id.bustype = BUS_I2C; info->idev->dev.parent = &pdev->dev; - info->irq = irq; info->idev->evbit[0] = BIT_MASK(EV_KEY); info->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 23140a3..48cdabe 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -22,6 +22,36 @@ config INPUT_88PM860X_ONKEY To compile this driver as a module, choose M here: the module will be called 88pm860x_onkey. +config INPUT_AD714X + tristate "Analog Devices AD714x Capacitance Touch Sensor" + help + Say Y here if you want to support an AD7142/3/7/8/7A touch sensor. + + You should select a bus connection too. + + To compile this driver as a module, choose M here: the + module will be called ad714x. + +config INPUT_AD714X_I2C + tristate "support I2C bus connection" + depends on INPUT_AD714X && I2C + default y + help + Say Y here if you have AD7142/AD7147 hooked to an I2C bus. + + To compile this driver as a module, choose M here: the + module will be called ad714x-i2c. + +config INPUT_AD714X_SPI + tristate "support SPI bus connection" + depends on INPUT_AD714X && SPI + default y + help + Say Y here if you have AD7142/AD7147 hooked to a SPI bus. + + To compile this driver as a module, choose M here: the + module will be called ad714x-spi. + config INPUT_PCSPKR tristate "PC Speaker support" depends on PCSPKR_PLATFORM @@ -277,6 +307,16 @@ config INPUT_PCF50633_PMU Say Y to include support for delivering PMU events via input layer on NXP PCF50633. +config INPUT_PCF8574 + tristate "PCF8574 Keypad input device" + depends on I2C && EXPERIMENTAL + help + Say Y here if you want to support a keypad connetced via I2C + with a PCF8574. + + To compile this driver as a module, choose M here: the + module will be called pcf8574_keypad. + config INPUT_GPIO_ROTARY_ENCODER tristate "Rotary encoders connected to GPIO pins" depends on GPIOLIB && GENERIC_GPIO diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 7e95a5d..f9f5770 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -5,6 +5,9 @@ # Each configuration option enables a list of files. obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o +obj-$(CONFIG_INPUT_AD714X) += ad714x.o +obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o +obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o obj-$(CONFIG_INPUT_APANEL) += apanel.o obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o @@ -19,6 +22,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o +obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o diff --git a/drivers/input/misc/ad714x-i2c.c b/drivers/input/misc/ad714x-i2c.c new file mode 100644 index 0000000..e9adbe4 --- /dev/null +++ b/drivers/input/misc/ad714x-i2c.c @@ -0,0 +1,140 @@ +/* + * AD714X CapTouch Programmable Controller driver (I2C bus) + * + * Copyright 2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/input.h> /* BUS_I2C */ +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/types.h> +#include "ad714x.h" + +#ifdef CONFIG_PM +static int ad714x_i2c_suspend(struct i2c_client *client, pm_message_t message) +{ + return ad714x_disable(i2c_get_clientdata(client)); +} + +static int ad714x_i2c_resume(struct i2c_client *client) +{ + return ad714x_enable(i2c_get_clientdata(client)); +} +#else +# define ad714x_i2c_suspend NULL +# define ad714x_i2c_resume NULL +#endif + +static int ad714x_i2c_write(struct device *dev, unsigned short reg, + unsigned short data) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret = 0; + u8 *_reg = (u8 *)® + u8 *_data = (u8 *)&data; + + u8 tx[4] = { + _reg[1], + _reg[0], + _data[1], + _data[0] + }; + + ret = i2c_master_send(client, tx, 4); + if (ret < 0) + dev_err(&client->dev, "I2C write error\n"); + + return ret; +} + +static int ad714x_i2c_read(struct device *dev, unsigned short reg, + unsigned short *data) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret = 0; + u8 *_reg = (u8 *)® + u8 *_data = (u8 *)data; + + u8 tx[2] = { + _reg[1], + _reg[0] + }; + u8 rx[2]; + + ret = i2c_master_send(client, tx, 2); + if (ret >= 0) + ret = i2c_master_recv(client, rx, 2); + + if (unlikely(ret < 0)) { + dev_err(&client->dev, "I2C read error\n"); + } else { + _data[0] = rx[1]; + _data[1] = rx[0]; + } + + return ret; +} + +static int __devinit ad714x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ad714x_chip *chip; + + chip = ad714x_probe(&client->dev, BUS_I2C, client->irq, + ad714x_i2c_read, ad714x_i2c_write); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + i2c_set_clientdata(client, chip); + + return 0; +} + +static int __devexit ad714x_i2c_remove(struct i2c_client *client) +{ + struct ad714x_chip *chip = i2c_get_clientdata(client); + + ad714x_remove(chip); + i2c_set_clientdata(client, NULL); + + return 0; +} + +static const struct i2c_device_id ad714x_id[] = { + { "ad7142_captouch", 0 }, + { "ad7143_captouch", 0 }, + { "ad7147_captouch", 0 }, + { "ad7147a_captouch", 0 }, + { "ad7148_captouch", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ad714x_id); + +static struct i2c_driver ad714x_i2c_driver = { + .driver = { + .name = "ad714x_captouch", + }, + .probe = ad714x_i2c_probe, + .remove = __devexit_p(ad714x_i2c_remove), + .suspend = ad714x_i2c_suspend, + .resume = ad714x_i2c_resume, + .id_table = ad714x_id, +}; + +static __init int ad714x_i2c_init(void) +{ + return i2c_add_driver(&ad714x_i2c_driver); +} +module_init(ad714x_i2c_init); + +static __exit void ad714x_i2c_exit(void) +{ + i2c_del_driver(&ad714x_i2c_driver); +} +module_exit(ad714x_i2c_exit); + +MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor I2C Bus Driver"); +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/ad714x-spi.c b/drivers/input/misc/ad714x-spi.c new file mode 100644 index 0000000..7f8dedf --- /dev/null +++ b/drivers/input/misc/ad714x-spi.c @@ -0,0 +1,103 @@ +/* + * AD714X CapTouch Programmable Controller driver (SPI bus) + * + * Copyright 2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/input.h> /* BUS_I2C */ +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/types.h> +#include "ad714x.h" + +#define AD714x_SPI_CMD_PREFIX 0xE000 /* bits 15:11 */ +#define AD714x_SPI_READ BIT(10) + +#ifdef CONFIG_PM +static int ad714x_spi_suspend(struct spi_device *spi, pm_message_t message) +{ + return ad714x_disable(spi_get_drvdata(spi)); +} + +static int ad714x_spi_resume(struct spi_device *spi) +{ + return ad714x_enable(spi_get_drvdata(spi)); +} +#else +# define ad714x_spi_suspend NULL +# define ad714x_spi_resume NULL +#endif + +static int ad714x_spi_read(struct device *dev, unsigned short reg, + unsigned short *data) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned short tx = AD714x_SPI_CMD_PREFIX | AD714x_SPI_READ | reg; + + return spi_write_then_read(spi, (u8 *)&tx, 2, (u8 *)data, 2); +} + +static int ad714x_spi_write(struct device *dev, unsigned short reg, + unsigned short data) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned short tx[2] = { + AD714x_SPI_CMD_PREFIX | reg, + data + }; + + return spi_write(spi, (u8 *)tx, 4); +} + +static int __devinit ad714x_spi_probe(struct spi_device *spi) +{ + struct ad714x_chip *chip; + + chip = ad714x_probe(&spi->dev, BUS_SPI, spi->irq, + ad714x_spi_read, ad714x_spi_write); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + spi_set_drvdata(spi, chip); + + return 0; +} + +static int __devexit ad714x_spi_remove(struct spi_device *spi) +{ + struct ad714x_chip *chip = spi_get_drvdata(spi); + + ad714x_remove(chip); + spi_set_drvdata(spi, NULL); + + return 0; +} + +static struct spi_driver ad714x_spi_driver = { + .driver = { + .name = "ad714x_captouch", + .owner = THIS_MODULE, + }, + .probe = ad714x_spi_probe, + .remove = __devexit_p(ad714x_spi_remove), + .suspend = ad714x_spi_suspend, + .resume = ad714x_spi_resume, +}; + +static __init int ad714x_spi_init(void) +{ + return spi_register_driver(&ad714x_spi_driver); +} +module_init(ad714x_spi_init); + +static __exit void ad714x_spi_exit(void) +{ + spi_unregister_driver(&ad714x_spi_driver); +} +module_exit(ad714x_spi_exit); + +MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor SPI Bus Driver"); +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/ad714x.c b/drivers/input/misc/ad714x.c new file mode 100644 index 0000000..0fe27ba --- /dev/null +++ b/drivers/input/misc/ad714x.c @@ -0,0 +1,1347 @@ +/* + * AD714X CapTouch Programmable Controller driver supporting AD7142/3/7/8/7A + * + * Copyright 2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/input/ad714x.h> +#include "ad714x.h" + +#define AD714X_PWR_CTRL 0x0 +#define AD714X_STG_CAL_EN_REG 0x1 +#define AD714X_AMB_COMP_CTRL0_REG 0x2 +#define AD714X_PARTID_REG 0x17 +#define AD7142_PARTID 0xE620 +#define AD7143_PARTID 0xE630 +#define AD7147_PARTID 0x1470 +#define AD7148_PARTID 0x1480 +#define AD714X_STAGECFG_REG 0x80 +#define AD714X_SYSCFG_REG 0x0 + +#define STG_LOW_INT_EN_REG 0x5 +#define STG_HIGH_INT_EN_REG 0x6 +#define STG_COM_INT_EN_REG 0x7 +#define STG_LOW_INT_STA_REG 0x8 +#define STG_HIGH_INT_STA_REG 0x9 +#define STG_COM_INT_STA_REG 0xA + +#define CDC_RESULT_S0 0xB +#define CDC_RESULT_S1 0xC +#define CDC_RESULT_S2 0xD +#define CDC_RESULT_S3 0xE +#define CDC_RESULT_S4 0xF +#define CDC_RESULT_S5 0x10 +#define CDC_RESULT_S6 0x11 +#define CDC_RESULT_S7 0x12 +#define CDC_RESULT_S8 0x13 +#define CDC_RESULT_S9 0x14 +#define CDC_RESULT_S10 0x15 +#define CDC_RESULT_S11 0x16 + +#define STAGE0_AMBIENT 0xF1 +#define STAGE1_AMBIENT 0x115 +#define STAGE2_AMBIENT 0x139 +#define STAGE3_AMBIENT 0x15D +#define STAGE4_AMBIENT 0x181 +#define STAGE5_AMBIENT 0x1A5 +#define STAGE6_AMBIENT 0x1C9 +#define STAGE7_AMBIENT 0x1ED +#define STAGE8_AMBIENT 0x211 +#define STAGE9_AMBIENT 0x234 +#define STAGE10_AMBIENT 0x259 +#define STAGE11_AMBIENT 0x27D + +#define PER_STAGE_REG_NUM 36 +#define STAGE_NUM 12 +#define STAGE_CFGREG_NUM 8 +#define SYS_CFGREG_NUM 8 + +/* + * driver information which will be used to maintain the software flow + */ +enum ad714x_device_state { IDLE, JITTER, ACTIVE, SPACE }; + +struct ad714x_slider_drv { + int highest_stage; + int abs_pos; + int flt_pos; + enum ad714x_device_state state; + struct input_dev *input; +}; + +struct ad714x_wheel_drv { + int abs_pos; + int flt_pos; + int pre_mean_value; + int pre_highest_stage; + int pre_mean_value_no_offset; + int mean_value; + int mean_value_no_offset; + int pos_offset; + int pos_ratio; + int highest_stage; + enum ad714x_device_state state; + struct input_dev *input; +}; + +struct ad714x_touchpad_drv { + int x_highest_stage; + int x_flt_pos; + int x_abs_pos; + int y_highest_stage; + int y_flt_pos; + int y_abs_pos; + int left_ep; + int left_ep_val; + int right_ep; + int right_ep_val; + int top_ep; + int top_ep_val; + int bottom_ep; + int bottom_ep_val; + enum ad714x_device_state state; + struct input_dev *input; +}; + +struct ad714x_button_drv { + enum ad714x_device_state state; + /* + * Unlike slider/wheel/touchpad, all buttons point to + * same input_dev instance + */ + struct input_dev *input; +}; + +struct ad714x_driver_data { + struct ad714x_slider_drv *slider; + struct ad714x_wheel_drv *wheel; + struct ad714x_touchpad_drv *touchpad; + struct ad714x_button_drv *button; +}; + +/* + * information to integrate all things which will be private data + * of spi/i2c device + */ +struct ad714x_chip { + unsigned short h_state; + unsigned short l_state; + unsigned short c_state; + unsigned short adc_reg[STAGE_NUM]; + unsigned short amb_reg[STAGE_NUM]; + unsigned short sensor_val[STAGE_NUM]; + + struct ad714x_platform_data *hw; + struct ad714x_driver_data *sw; + + int irq; + struct device *dev; + ad714x_read_t read; + ad714x_write_t write; + + struct mutex mutex; + + unsigned product; + unsigned version; +}; + +static void ad714x_use_com_int(struct ad714x_chip *ad714x, + int start_stage, int end_stage) +{ + unsigned short data; + unsigned short mask; + + mask = ((1 << (end_stage + 1)) - 1) - (1 << start_stage); + + ad714x->read(ad714x->dev, STG_COM_INT_EN_REG, &data); + data |= 1 << start_stage; + ad714x->write(ad714x->dev, STG_COM_INT_EN_REG, data); + + ad714x->read(ad714x->dev, STG_HIGH_INT_EN_REG, &data); + data &= ~mask; + ad714x->write(ad714x->dev, STG_HIGH_INT_EN_REG, data); +} + +static void ad714x_use_thr_int(struct ad714x_chip *ad714x, + int start_stage, int end_stage) +{ + unsigned short data; + unsigned short mask; + + mask = ((1 << (end_stage + 1)) - 1) - (1 << start_stage); + + ad714x->read(ad714x->dev, STG_COM_INT_EN_REG, &data); + data &= ~(1 << start_stage); + ad714x->write(ad714x->dev, STG_COM_INT_EN_REG, data); + + ad714x->read(ad714x->dev, STG_HIGH_INT_EN_REG, &data); + data |= mask; + ad714x->write(ad714x->dev, STG_HIGH_INT_EN_REG, data); +} + +static int ad714x_cal_highest_stage(struct ad714x_chip *ad714x, + int start_stage, int end_stage) +{ + int max_res = 0; + int max_idx = 0; + int i; + + for (i = start_stage; i <= end_stage; i++) { + if (ad714x->sensor_val[i] > max_res) { + max_res = ad714x->sensor_val[i]; + max_idx = i; + } + } + + return max_idx; +} + +static int ad714x_cal_abs_pos(struct ad714x_chip *ad714x, + int start_stage, int end_stage, + int highest_stage, int max_coord) +{ + int a_param, b_param; + + if (highest_stage == start_stage) { + a_param = ad714x->sensor_val[start_stage + 1]; + b_param = ad714x->sensor_val[start_stage] + + ad714x->sensor_val[start_stage + 1]; + } else if (highest_stage == end_stage) { + a_param = ad714x->sensor_val[end_stage] * + (end_stage - start_stage) + + ad714x->sensor_val[end_stage - 1] * + (end_stage - start_stage - 1); + b_param = ad714x->sensor_val[end_stage] + + ad714x->sensor_val[end_stage - 1]; + } else { + a_param = ad714x->sensor_val[highest_stage] * + (highest_stage - start_stage) + + ad714x->sensor_val[highest_stage - 1] * + (highest_stage - start_stage - 1) + + ad714x->sensor_val[highest_stage + 1] * + (highest_stage - start_stage + 1); + b_param = ad714x->sensor_val[highest_stage] + + ad714x->sensor_val[highest_stage - 1] + + ad714x->sensor_val[highest_stage + 1]; + } + + return (max_coord / (end_stage - start_stage)) * a_param / b_param; +} + +/* + * One button can connect to multi positive and negative of CDCs + * Multi-buttons can connect to same positive/negative of one CDC + */ +static void ad714x_button_state_machine(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_button_plat *hw = &ad714x->hw->button[idx]; + struct ad714x_button_drv *sw = &ad714x->sw->button[idx]; + + switch (sw->state) { + case IDLE: + if (((ad714x->h_state & hw->h_mask) == hw->h_mask) && + ((ad714x->l_state & hw->l_mask) == hw->l_mask)) { + dev_dbg(ad714x->dev, "button %d touched\n", idx); + input_report_key(sw->input, hw->keycode, 1); + input_sync(sw->input); + sw->state = ACTIVE; + } + break; + + case ACTIVE: + if (((ad714x->h_state & hw->h_mask) != hw->h_mask) || + ((ad714x->l_state & hw->l_mask) != hw->l_mask)) { + dev_dbg(ad714x->dev, "button %d released\n", idx); + input_report_key(sw->input, hw->keycode, 0); + input_sync(sw->input); + sw->state = IDLE; + } + break; + + default: + break; + } +} + +/* + * The response of a sensor is defined by the absolute number of codes + * between the current CDC value and the ambient value. + */ +static void ad714x_slider_cal_sensor_val(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; + int i; + + for (i = hw->start_stage; i <= hw->end_stage; i++) { + ad714x->read(ad714x->dev, CDC_RESULT_S0 + i, + &ad714x->adc_reg[i]); + ad714x->read(ad714x->dev, + STAGE0_AMBIENT + i * PER_STAGE_REG_NUM, + &ad714x->amb_reg[i]); + + ad714x->sensor_val[i] = abs(ad714x->adc_reg[i] - + ad714x->amb_reg[i]); + } +} + +static void ad714x_slider_cal_highest_stage(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; + struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx]; + + sw->highest_stage = ad714x_cal_highest_stage(ad714x, hw->start_stage, + hw->end_stage); + + dev_dbg(ad714x->dev, "slider %d highest_stage:%d\n", idx, + sw->highest_stage); +} + +/* + * The formulae are very straight forward. It uses the sensor with the + * highest response and the 2 adjacent ones. + * When Sensor 0 has the highest response, only sensor 0 and sensor 1 + * are used in the calculations. Similarly when the last sensor has the + * highest response, only the last sensor and the second last sensors + * are used in the calculations. + * + * For i= idx_of_peak_Sensor-1 to i= idx_of_peak_Sensor+1 + * v += Sensor response(i)*i + * w += Sensor response(i) + * POS=(Number_of_Positions_Wanted/(Number_of_Sensors_Used-1)) *(v/w) + */ +static void ad714x_slider_cal_abs_pos(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; + struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx]; + + sw->abs_pos = ad714x_cal_abs_pos(ad714x, hw->start_stage, hw->end_stage, + sw->highest_stage, hw->max_coord); + + dev_dbg(ad714x->dev, "slider %d absolute position:%d\n", idx, + sw->abs_pos); +} + +/* + * To minimise the Impact of the noise on the algorithm, ADI developed a + * routine that filters the CDC results after they have been read by the + * host processor. + * The filter used is an Infinite Input Response(IIR) filter implemented + * in firmware and attenuates the noise on the CDC results after they've + * been read by the host processor. + * Filtered_CDC_result = (Filtered_CDC_result * (10 - Coefficient) + + * Latest_CDC_result * Coefficient)/10 + */ +static void ad714x_slider_cal_flt_pos(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx]; + + sw->flt_pos = (sw->flt_pos * (10 - 4) + + sw->abs_pos * 4)/10; + + dev_dbg(ad714x->dev, "slider %d filter position:%d\n", idx, + sw->flt_pos); +} + +static void ad714x_slider_use_com_int(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; + + ad714x_use_com_int(ad714x, hw->start_stage, hw->end_stage); +} + +static void ad714x_slider_use_thr_int(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; + + ad714x_use_thr_int(ad714x, hw->start_stage, hw->end_stage); +} + +static void ad714x_slider_state_machine(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; + struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx]; + unsigned short h_state, c_state; + unsigned short mask; + + mask = ((1 << (hw->end_stage + 1)) - 1) - ((1 << hw->start_stage) - 1); + + h_state = ad714x->h_state & mask; + c_state = ad714x->c_state & mask; + + switch (sw->state) { + case IDLE: + if (h_state) { + sw->state = JITTER; + /* In End of Conversion interrupt mode, the AD714X + * continuously generates hardware interrupts. + */ + ad714x_slider_use_com_int(ad714x, idx); + dev_dbg(ad714x->dev, "slider %d touched\n", idx); + } + break; + + case JITTER: + if (c_state == mask) { + ad714x_slider_cal_sensor_val(ad714x, idx); + ad714x_slider_cal_highest_stage(ad714x, idx); + ad714x_slider_cal_abs_pos(ad714x, idx); + sw->flt_pos = sw->abs_pos; + sw->state = ACTIVE; + } + break; + + case ACTIVE: + if (c_state == mask) { + if (h_state) { + ad714x_slider_cal_sensor_val(ad714x, idx); + ad714x_slider_cal_highest_stage(ad714x, idx); + ad714x_slider_cal_abs_pos(ad714x, idx); + ad714x_slider_cal_flt_pos(ad714x, idx); + + input_report_abs(sw->input, ABS_X, sw->flt_pos); + input_report_key(sw->input, BTN_TOUCH, 1); + } else { + /* When the user lifts off the sensor, configure + * the AD714X back to threshold interrupt mode. + */ + ad714x_slider_use_thr_int(ad714x, idx); + sw->state = IDLE; + input_report_key(sw->input, BTN_TOUCH, 0); + dev_dbg(ad714x->dev, "slider %d released\n", + idx); + } + input_sync(sw->input); + } + break; + + default: + break; + } +} + +/* + * When the scroll wheel is activated, we compute the absolute position based + * on the sensor values. To calculate the position, we first determine the + * sensor that has the greatest response among the 8 sensors that constitutes + * the scrollwheel. Then we determined the 2 sensors on either sides of the + * sensor with the highest response and we apply weights to these sensors. + */ +static void ad714x_wheel_cal_highest_stage(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; + struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx]; + + sw->pre_highest_stage = sw->highest_stage; + sw->highest_stage = ad714x_cal_highest_stage(ad714x, hw->start_stage, + hw->end_stage); + + dev_dbg(ad714x->dev, "wheel %d highest_stage:%d\n", idx, + sw->highest_stage); +} + +static void ad714x_wheel_cal_sensor_val(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; + int i; + + for (i = hw->start_stage; i <= hw->end_stage; i++) { + ad714x->read(ad714x->dev, CDC_RESULT_S0 + i, + &ad714x->adc_reg[i]); + ad714x->read(ad714x->dev, + STAGE0_AMBIENT + i * PER_STAGE_REG_NUM, + &ad714x->amb_reg[i]); + if (ad714x->adc_reg[i] > ad714x->amb_reg[i]) + ad714x->sensor_val[i] = ad714x->adc_reg[i] - + ad714x->amb_reg[i]; + else + ad714x->sensor_val[i] = 0; + } +} + +/* + * When the scroll wheel is activated, we compute the absolute position based + * on the sensor values. To calculate the position, we first determine the + * sensor that has the greatest response among the 8 sensors that constitutes + * the scrollwheel. Then we determined the 2 sensors on either sides of the + * sensor with the highest response and we apply weights to these sensors. The + * result of this computation gives us the mean value which defined by the + * following formula: + * For i= second_before_highest_stage to i= second_after_highest_stage + * v += Sensor response(i)*WEIGHT*(i+3) + * w += Sensor response(i) + * Mean_Value=v/w + * pos_on_scrollwheel = (Mean_Value - position_offset) / position_ratio + */ + +#define WEIGHT_FACTOR 30 +/* This constant prevents the "PositionOffset" from reaching a big value */ +#define OFFSET_POSITION_CLAMP 120 +static void ad714x_wheel_cal_abs_pos(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; + struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx]; + int stage_num = hw->end_stage - hw->start_stage + 1; + int second_before, first_before, highest, first_after, second_after; + int a_param, b_param; + + /* Calculate Mean value */ + + second_before = (sw->highest_stage + stage_num - 2) % stage_num; + first_before = (sw->highest_stage + stage_num - 1) % stage_num; + highest = sw->highest_stage; + first_after = (sw->highest_stage + stage_num + 1) % stage_num; + second_after = (sw->highest_stage + stage_num + 2) % stage_num; + + if (((sw->highest_stage - hw->start_stage) > 1) && + ((hw->end_stage - sw->highest_stage) > 1)) { + a_param = ad714x->sensor_val[second_before] * + (second_before - hw->start_stage + 3) + + ad714x->sensor_val[first_before] * + (second_before - hw->start_stage + 3) + + ad714x->sensor_val[highest] * + (second_before - hw->start_stage + 3) + + ad714x->sensor_val[first_after] * + (first_after - hw->start_stage + 3) + + ad714x->sensor_val[second_after] * + (second_after - hw->start_stage + 3); + } else { + a_param = ad714x->sensor_val[second_before] * + (second_before - hw->start_stage + 1) + + ad714x->sensor_val[first_before] * + (second_before - hw->start_stage + 2) + + ad714x->sensor_val[highest] * + (second_before - hw->start_stage + 3) + + ad714x->sensor_val[first_after] * + (first_after - hw->start_stage + 4) + + ad714x->sensor_val[second_after] * + (second_after - hw->start_stage + 5); + } + a_param *= WEIGHT_FACTOR; + + b_param = ad714x->sensor_val[second_before] + + ad714x->sensor_val[first_before] + + ad714x->sensor_val[highest] + + ad714x->sensor_val[first_after] + + ad714x->sensor_val[second_after]; + + sw->pre_mean_value = sw->mean_value; + sw->mean_value = a_param / b_param; + + /* Calculate the offset */ + + if ((sw->pre_highest_stage == hw->end_stage) && + (sw->highest_stage == hw->start_stage)) + sw->pos_offset = sw->mean_value; + else if ((sw->pre_highest_stage == hw->start_stage) && + (sw->highest_stage == hw->end_stage)) + sw->pos_offset = sw->pre_mean_value; + + if (sw->pos_offset > OFFSET_POSITION_CLAMP) + sw->pos_offset = OFFSET_POSITION_CLAMP; + + /* Calculate the mean value without the offset */ + + sw->pre_mean_value_no_offset = sw->mean_value_no_offset; + sw->mean_value_no_offset = sw->mean_value - sw->pos_offset; + if (sw->mean_value_no_offset < 0) + sw->mean_value_no_offset = 0; + + /* Calculate ratio to scale down to NUMBER_OF_WANTED_POSITIONS */ + + if ((sw->pre_highest_stage == hw->end_stage) && + (sw->highest_stage == hw->start_stage)) + sw->pos_ratio = (sw->pre_mean_value_no_offset * 100) / + hw->max_coord; + else if ((sw->pre_highest_stage == hw->start_stage) && + (sw->highest_stage == hw->end_stage)) + sw->pos_ratio = (sw->mean_value_no_offset * 100) / + hw->max_coord; + sw->abs_pos = (sw->mean_value_no_offset * 100) / sw->pos_ratio; + if (sw->abs_pos > hw->max_coord) + sw->abs_pos = hw->max_coord; +} + +static void ad714x_wheel_cal_flt_pos(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; + struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx]; + if (((sw->pre_highest_stage == hw->end_stage) && + (sw->highest_stage == hw->start_stage)) || + ((sw->pre_highest_stage == hw->start_stage) && + (sw->highest_stage == hw->end_stage))) + sw->flt_pos = sw->abs_pos; + else + sw->flt_pos = ((sw->flt_pos * 30) + (sw->abs_pos * 71)) / 100; + + if (sw->flt_pos > hw->max_coord) + sw->flt_pos = hw->max_coord; +} + +static void ad714x_wheel_use_com_int(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; + + ad714x_use_com_int(ad714x, hw->start_stage, hw->end_stage); +} + +static void ad714x_wheel_use_thr_int(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; + + ad714x_use_thr_int(ad714x, hw->start_stage, hw->end_stage); +} + +static void ad714x_wheel_state_machine(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; + struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx]; + unsigned short h_state, c_state; + unsigned short mask; + + mask = ((1 << (hw->end_stage + 1)) - 1) - ((1 << hw->start_stage) - 1); + + h_state = ad714x->h_state & mask; + c_state = ad714x->c_state & mask; + + switch (sw->state) { + case IDLE: + if (h_state) { + sw->state = JITTER; + /* In End of Conversion interrupt mode, the AD714X + * continuously generates hardware interrupts. + */ + ad714x_wheel_use_com_int(ad714x, idx); + dev_dbg(ad714x->dev, "wheel %d touched\n", idx); + } + break; + + case JITTER: + if (c_state == mask) { + ad714x_wheel_cal_sensor_val(ad714x, idx); + ad714x_wheel_cal_highest_stage(ad714x, idx); + ad714x_wheel_cal_abs_pos(ad714x, idx); + sw->flt_pos = sw->abs_pos; + sw->state = ACTIVE; + } + break; + + case ACTIVE: + if (c_state == mask) { + if (h_state) { + ad714x_wheel_cal_sensor_val(ad714x, idx); + ad714x_wheel_cal_highest_stage(ad714x, idx); + ad714x_wheel_cal_abs_pos(ad714x, idx); + ad714x_wheel_cal_flt_pos(ad714x, idx); + + input_report_abs(sw->input, ABS_WHEEL, + sw->abs_pos); + input_report_key(sw->input, BTN_TOUCH, 1); + } else { + /* When the user lifts off the sensor, configure + * the AD714X back to threshold interrupt mode. + */ + ad714x_wheel_use_thr_int(ad714x, idx); + sw->state = IDLE; + input_report_key(sw->input, BTN_TOUCH, 0); + + dev_dbg(ad714x->dev, "wheel %d released\n", + idx); + } + input_sync(sw->input); + } + break; + + default: + break; + } +} + +static void touchpad_cal_sensor_val(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; + int i; + + for (i = hw->x_start_stage; i <= hw->x_end_stage; i++) { + ad714x->read(ad714x->dev, CDC_RESULT_S0 + i, + &ad714x->adc_reg[i]); + ad714x->read(ad714x->dev, + STAGE0_AMBIENT + i * PER_STAGE_REG_NUM, + &ad714x->amb_reg[i]); + if (ad714x->adc_reg[i] > ad714x->amb_reg[i]) + ad714x->sensor_val[i] = ad714x->adc_reg[i] - + ad714x->amb_reg[i]; + else + ad714x->sensor_val[i] = 0; + } +} + +static void touchpad_cal_highest_stage(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; + struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; + + sw->x_highest_stage = ad714x_cal_highest_stage(ad714x, + hw->x_start_stage, hw->x_end_stage); + sw->y_highest_stage = ad714x_cal_highest_stage(ad714x, + hw->y_start_stage, hw->y_end_stage); + + dev_dbg(ad714x->dev, + "touchpad %d x_highest_stage:%d, y_highest_stage:%d\n", + idx, sw->x_highest_stage, sw->y_highest_stage); +} + +/* + * If 2 fingers are touching the sensor then 2 peaks can be observed in the + * distribution. + * The arithmetic doesn't support to get absolute coordinates for multi-touch + * yet. + */ +static int touchpad_check_second_peak(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; + struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; + int i; + + for (i = hw->x_start_stage; i < sw->x_highest_stage; i++) { + if ((ad714x->sensor_val[i] - ad714x->sensor_val[i + 1]) + > (ad714x->sensor_val[i + 1] / 10)) + return 1; + } + + for (i = sw->x_highest_stage; i < hw->x_end_stage; i++) { + if ((ad714x->sensor_val[i + 1] - ad714x->sensor_val[i]) + > (ad714x->sensor_val[i] / 10)) + return 1; + } + + for (i = hw->y_start_stage; i < sw->y_highest_stage; i++) { + if ((ad714x->sensor_val[i] - ad714x->sensor_val[i + 1]) + > (ad714x->sensor_val[i + 1] / 10)) + return 1; + } + + for (i = sw->y_highest_stage; i < hw->y_end_stage; i++) { + if ((ad714x->sensor_val[i + 1] - ad714x->sensor_val[i]) + > (ad714x->sensor_val[i] / 10)) + return 1; + } + + return 0; +} + +/* + * If only one finger is used to activate the touch pad then only 1 peak will be + * registered in the distribution. This peak and the 2 adjacent sensors will be + * used in the calculation of the absolute position. This will prevent hand + * shadows to affect the absolute position calculation. + */ +static void touchpad_cal_abs_pos(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; + struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; + + sw->x_abs_pos = ad714x_cal_abs_pos(ad714x, hw->x_start_stage, + hw->x_end_stage, sw->x_highest_stage, hw->x_max_coord); + sw->y_abs_pos = ad714x_cal_abs_pos(ad714x, hw->y_start_stage, + hw->y_end_stage, sw->y_highest_stage, hw->y_max_coord); + + dev_dbg(ad714x->dev, "touchpad %d absolute position:(%d, %d)\n", idx, + sw->x_abs_pos, sw->y_abs_pos); +} + +static void touchpad_cal_flt_pos(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; + + sw->x_flt_pos = (sw->x_flt_pos * (10 - 4) + + sw->x_abs_pos * 4)/10; + sw->y_flt_pos = (sw->y_flt_pos * (10 - 4) + + sw->y_abs_pos * 4)/10; + + dev_dbg(ad714x->dev, "touchpad %d filter position:(%d, %d)\n", + idx, sw->x_flt_pos, sw->y_flt_pos); +} + +/* + * To prevent distortion from showing in the absolute position, it is + * necessary to detect the end points. When endpoints are detected, the + * driver stops updating the status variables with absolute positions. + * End points are detected on the 4 edges of the touchpad sensor. The + * method to detect them is the same for all 4. + * To detect the end points, the firmware computes the difference in + * percent between the sensor on the edge and the adjacent one. The + * difference is calculated in percent in order to make the end point + * detection independent of the pressure. + */ + +#define LEFT_END_POINT_DETECTION_LEVEL 550 +#define RIGHT_END_POINT_DETECTION_LEVEL 750 +#define LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL 850 +#define TOP_END_POINT_DETECTION_LEVEL 550 +#define BOTTOM_END_POINT_DETECTION_LEVEL 950 +#define TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL 700 +static int touchpad_check_endpoint(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; + struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; + int percent_sensor_diff; + + /* left endpoint detect */ + percent_sensor_diff = (ad714x->sensor_val[hw->x_start_stage] - + ad714x->sensor_val[hw->x_start_stage + 1]) * 100 / + ad714x->sensor_val[hw->x_start_stage + 1]; + if (!sw->left_ep) { + if (percent_sensor_diff >= LEFT_END_POINT_DETECTION_LEVEL) { + sw->left_ep = 1; + sw->left_ep_val = + ad714x->sensor_val[hw->x_start_stage + 1]; + } + } else { + if ((percent_sensor_diff < LEFT_END_POINT_DETECTION_LEVEL) && + (ad714x->sensor_val[hw->x_start_stage + 1] > + LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL + sw->left_ep_val)) + sw->left_ep = 0; + } + + /* right endpoint detect */ + percent_sensor_diff = (ad714x->sensor_val[hw->x_end_stage] - + ad714x->sensor_val[hw->x_end_stage - 1]) * 100 / + ad714x->sensor_val[hw->x_end_stage - 1]; + if (!sw->right_ep) { + if (percent_sensor_diff >= RIGHT_END_POINT_DETECTION_LEVEL) { + sw->right_ep = 1; + sw->right_ep_val = + ad714x->sensor_val[hw->x_end_stage - 1]; + } + } else { + if ((percent_sensor_diff < RIGHT_END_POINT_DETECTION_LEVEL) && + (ad714x->sensor_val[hw->x_end_stage - 1] > + LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL + sw->right_ep_val)) + sw->right_ep = 0; + } + + /* top endpoint detect */ + percent_sensor_diff = (ad714x->sensor_val[hw->y_start_stage] - + ad714x->sensor_val[hw->y_start_stage + 1]) * 100 / + ad714x->sensor_val[hw->y_start_stage + 1]; + if (!sw->top_ep) { + if (percent_sensor_diff >= TOP_END_POINT_DETECTION_LEVEL) { + sw->top_ep = 1; + sw->top_ep_val = + ad714x->sensor_val[hw->y_start_stage + 1]; + } + } else { + if ((percent_sensor_diff < TOP_END_POINT_DETECTION_LEVEL) && + (ad714x->sensor_val[hw->y_start_stage + 1] > + TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL + sw->top_ep_val)) + sw->top_ep = 0; + } + + /* bottom endpoint detect */ + percent_sensor_diff = (ad714x->sensor_val[hw->y_end_stage] - + ad714x->sensor_val[hw->y_end_stage - 1]) * 100 / + ad714x->sensor_val[hw->y_end_stage - 1]; + if (!sw->bottom_ep) { + if (percent_sensor_diff >= BOTTOM_END_POINT_DETECTION_LEVEL) { + sw->bottom_ep = 1; + sw->bottom_ep_val = + ad714x->sensor_val[hw->y_end_stage - 1]; + } + } else { + if ((percent_sensor_diff < BOTTOM_END_POINT_DETECTION_LEVEL) && + (ad714x->sensor_val[hw->y_end_stage - 1] > + TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL + sw->bottom_ep_val)) + sw->bottom_ep = 0; + } + + return sw->left_ep || sw->right_ep || sw->top_ep || sw->bottom_ep; +} + +static void touchpad_use_com_int(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; + + ad714x_use_com_int(ad714x, hw->x_start_stage, hw->x_end_stage); +} + +static void touchpad_use_thr_int(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; + + ad714x_use_thr_int(ad714x, hw->x_start_stage, hw->x_end_stage); + ad714x_use_thr_int(ad714x, hw->y_start_stage, hw->y_end_stage); +} + +static void ad714x_touchpad_state_machine(struct ad714x_chip *ad714x, int idx) +{ + struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; + struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; + unsigned short h_state, c_state; + unsigned short mask; + + mask = (((1 << (hw->x_end_stage + 1)) - 1) - + ((1 << hw->x_start_stage) - 1)) + + (((1 << (hw->y_end_stage + 1)) - 1) - + ((1 << hw->y_start_stage) - 1)); + + h_state = ad714x->h_state & mask; + c_state = ad714x->c_state & mask; + + switch (sw->state) { + case IDLE: + if (h_state) { + sw->state = JITTER; + /* In End of Conversion interrupt mode, the AD714X + * continuously generates hardware interrupts. + */ + touchpad_use_com_int(ad714x, idx); + dev_dbg(ad714x->dev, "touchpad %d touched\n", idx); + } + break; + + case JITTER: + if (c_state == mask) { + touchpad_cal_sensor_val(ad714x, idx); + touchpad_cal_highest_stage(ad714x, idx); + if ((!touchpad_check_second_peak(ad714x, idx)) && + (!touchpad_check_endpoint(ad714x, idx))) { + dev_dbg(ad714x->dev, + "touchpad%d, 2 fingers or endpoint\n", + idx); + touchpad_cal_abs_pos(ad714x, idx); + sw->x_flt_pos = sw->x_abs_pos; + sw->y_flt_pos = sw->y_abs_pos; + sw->state = ACTIVE; + } + } + break; + + case ACTIVE: + if (c_state == mask) { + if (h_state) { + touchpad_cal_sensor_val(ad714x, idx); + touchpad_cal_highest_stage(ad714x, idx); + if ((!touchpad_check_second_peak(ad714x, idx)) + && (!touchpad_check_endpoint(ad714x, idx))) { + touchpad_cal_abs_pos(ad714x, idx); + touchpad_cal_flt_pos(ad714x, idx); + input_report_abs(sw->input, ABS_X, + sw->x_flt_pos); + input_report_abs(sw->input, ABS_Y, + sw->y_flt_pos); + input_report_key(sw->input, BTN_TOUCH, + 1); + } + } else { + /* When the user lifts off the sensor, configure + * the AD714X back to threshold interrupt mode. + */ + touchpad_use_thr_int(ad714x, idx); + sw->state = IDLE; + input_report_key(sw->input, BTN_TOUCH, 0); + dev_dbg(ad714x->dev, "touchpad %d released\n", + idx); + } + input_sync(sw->input); + } + break; + + default: + break; + } +} + +static int ad714x_hw_detect(struct ad714x_chip *ad714x) +{ + unsigned short data; + + ad714x->read(ad714x->dev, AD714X_PARTID_REG, &data); + switch (data & 0xFFF0) { + case AD7142_PARTID: + ad714x->product = 0x7142; + ad714x->version = data & 0xF; + dev_info(ad714x->dev, "found AD7142 captouch, rev:%d\n", + ad714x->version); + return 0; + + case AD7143_PARTID: + ad714x->product = 0x7143; + ad714x->version = data & 0xF; + dev_info(ad714x->dev, "found AD7143 captouch, rev:%d\n", + ad714x->version); + return 0; + + case AD7147_PARTID: + ad714x->product = 0x7147; + ad714x->version = data & 0xF; + dev_info(ad714x->dev, "found AD7147(A) captouch, rev:%d\n", + ad714x->version); + return 0; + + case AD7148_PARTID: + ad714x->product = 0x7148; + ad714x->version = data & 0xF; + dev_info(ad714x->dev, "found AD7148 captouch, rev:%d\n", + ad714x->version); + return 0; + + default: + dev_err(ad714x->dev, + "fail to detect AD714X captouch, read ID is %04x\n", + data); + return -ENODEV; + } +} + +static void ad714x_hw_init(struct ad714x_chip *ad714x) +{ + int i, j; + unsigned short reg_base; + unsigned short data; + + /* configuration CDC and interrupts */ + + for (i = 0; i < STAGE_NUM; i++) { + reg_base = AD714X_STAGECFG_REG + i * STAGE_CFGREG_NUM; + for (j = 0; j < STAGE_CFGREG_NUM; j++) + ad714x->write(ad714x->dev, reg_base + j, + ad714x->hw->stage_cfg_reg[i][j]); + } + + for (i = 0; i < SYS_CFGREG_NUM; i++) + ad714x->write(ad714x->dev, AD714X_SYSCFG_REG + i, + ad714x->hw->sys_cfg_reg[i]); + for (i = 0; i < SYS_CFGREG_NUM; i++) + ad714x->read(ad714x->dev, AD714X_SYSCFG_REG + i, + &data); + + ad714x->write(ad714x->dev, AD714X_STG_CAL_EN_REG, 0xFFF); + + /* clear all interrupts */ + ad714x->read(ad714x->dev, STG_LOW_INT_STA_REG, &data); + ad714x->read(ad714x->dev, STG_HIGH_INT_STA_REG, &data); + ad714x->read(ad714x->dev, STG_COM_INT_STA_REG, &data); +} + +static irqreturn_t ad714x_interrupt_thread(int irq, void *data) +{ + struct ad714x_chip *ad714x = data; + int i; + + mutex_lock(&ad714x->mutex); + + ad714x->read(ad714x->dev, STG_LOW_INT_STA_REG, &ad714x->l_state); + ad714x->read(ad714x->dev, STG_HIGH_INT_STA_REG, &ad714x->h_state); + ad714x->read(ad714x->dev, STG_COM_INT_STA_REG, &ad714x->c_state); + + for (i = 0; i < ad714x->hw->button_num; i++) + ad714x_button_state_machine(ad714x, i); + for (i = 0; i < ad714x->hw->slider_num; i++) + ad714x_slider_state_machine(ad714x, i); + for (i = 0; i < ad714x->hw->wheel_num; i++) + ad714x_wheel_state_machine(ad714x, i); + for (i = 0; i < ad714x->hw->touchpad_num; i++) + ad714x_touchpad_state_machine(ad714x, i); + + mutex_unlock(&ad714x->mutex); + + return IRQ_HANDLED; +} + +#define MAX_DEVICE_NUM 8 +struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq, + ad714x_read_t read, ad714x_write_t write) +{ + int i, alloc_idx; + int error; + struct input_dev *input[MAX_DEVICE_NUM]; + + struct ad714x_platform_data *plat_data = dev->platform_data; + struct ad714x_chip *ad714x; + void *drv_mem; + + struct ad714x_button_drv *bt_drv; + struct ad714x_slider_drv *sd_drv; + struct ad714x_wheel_drv *wl_drv; + struct ad714x_touchpad_drv *tp_drv; + + + if (irq <= 0) { + dev_err(dev, "IRQ not configured!\n"); + error = -EINVAL; + goto err_out; + } + + if (dev->platform_data == NULL) { + dev_err(dev, "platform data for ad714x doesn't exist\n"); + error = -EINVAL; + goto err_out; + } + + ad714x = kzalloc(sizeof(*ad714x) + sizeof(*ad714x->sw) + + sizeof(*sd_drv) * plat_data->slider_num + + sizeof(*wl_drv) * plat_data->wheel_num + + sizeof(*tp_drv) * plat_data->touchpad_num + + sizeof(*bt_drv) * plat_data->button_num, GFP_KERNEL); + if (!ad714x) { + error = -ENOMEM; + goto err_out; + } + + ad714x->hw = plat_data; + + drv_mem = ad714x + 1; + ad714x->sw = drv_mem; + drv_mem += sizeof(*ad714x->sw); + ad714x->sw->slider = sd_drv = drv_mem; + drv_mem += sizeof(*sd_drv) * ad714x->hw->slider_num; + ad714x->sw->wheel = wl_drv = drv_mem; + drv_mem += sizeof(*wl_drv) * ad714x->hw->wheel_num; + ad714x->sw->touchpad = tp_drv = drv_mem; + drv_mem += sizeof(*tp_drv) * ad714x->hw->touchpad_num; + ad714x->sw->button = bt_drv = drv_mem; + drv_mem += sizeof(*bt_drv) * ad714x->hw->button_num; + + ad714x->read = read; + ad714x->write = write; + ad714x->irq = irq; + ad714x->dev = dev; + + error = ad714x_hw_detect(ad714x); + if (error) + goto err_free_mem; + + /* initilize and request sw/hw resources */ + + ad714x_hw_init(ad714x); + mutex_init(&ad714x->mutex); + + /* + * Allocate and register AD714X input device + */ + alloc_idx = 0; + + /* a slider uses one input_dev instance */ + if (ad714x->hw->slider_num > 0) { + struct ad714x_slider_plat *sd_plat = ad714x->hw->slider; + + for (i = 0; i < ad714x->hw->slider_num; i++) { + sd_drv[i].input = input[alloc_idx] = input_allocate_device(); + if (!input[alloc_idx]) { + error = -ENOMEM; + goto err_free_dev; + } + + __set_bit(EV_ABS, input[alloc_idx]->evbit); + __set_bit(EV_KEY, input[alloc_idx]->evbit); + __set_bit(ABS_X, input[alloc_idx]->absbit); + __set_bit(BTN_TOUCH, input[alloc_idx]->keybit); + input_set_abs_params(input[alloc_idx], + ABS_X, 0, sd_plat->max_coord, 0, 0); + + input[alloc_idx]->id.bustype = bus_type; + input[alloc_idx]->id.product = ad714x->product; + input[alloc_idx]->id.version = ad714x->version; + + error = input_register_device(input[alloc_idx]); + if (error) + goto err_free_dev; + + alloc_idx++; + } + } + + /* a wheel uses one input_dev instance */ + if (ad714x->hw->wheel_num > 0) { + struct ad714x_wheel_plat *wl_plat = ad714x->hw->wheel; + + for (i = 0; i < ad714x->hw->wheel_num; i++) { + wl_drv[i].input = input[alloc_idx] = input_allocate_device(); + if (!input[alloc_idx]) { + error = -ENOMEM; + goto err_free_dev; + } + + __set_bit(EV_KEY, input[alloc_idx]->evbit); + __set_bit(EV_ABS, input[alloc_idx]->evbit); + __set_bit(ABS_WHEEL, input[alloc_idx]->absbit); + __set_bit(BTN_TOUCH, input[alloc_idx]->keybit); + input_set_abs_params(input[alloc_idx], + ABS_WHEEL, 0, wl_plat->max_coord, 0, 0); + + input[alloc_idx]->id.bustype = bus_type; + input[alloc_idx]->id.product = ad714x->product; + input[alloc_idx]->id.version = ad714x->version; + + error = input_register_device(input[alloc_idx]); + if (error) + goto err_free_dev; + + alloc_idx++; + } + } + + /* a touchpad uses one input_dev instance */ + if (ad714x->hw->touchpad_num > 0) { + struct ad714x_touchpad_plat *tp_plat = ad714x->hw->touchpad; + + for (i = 0; i < ad714x->hw->touchpad_num; i++) { + tp_drv[i].input = input[alloc_idx] = input_allocate_device(); + if (!input[alloc_idx]) { + error = -ENOMEM; + goto err_free_dev; + } + + __set_bit(EV_ABS, input[alloc_idx]->evbit); + __set_bit(EV_KEY, input[alloc_idx]->evbit); + __set_bit(ABS_X, input[alloc_idx]->absbit); + __set_bit(ABS_Y, input[alloc_idx]->absbit); + __set_bit(BTN_TOUCH, input[alloc_idx]->keybit); + input_set_abs_params(input[alloc_idx], + ABS_X, 0, tp_plat->x_max_coord, 0, 0); + input_set_abs_params(input[alloc_idx], + ABS_Y, 0, tp_plat->y_max_coord, 0, 0); + + input[alloc_idx]->id.bustype = bus_type; + input[alloc_idx]->id.product = ad714x->product; + input[alloc_idx]->id.version = ad714x->version; + + error = input_register_device(input[alloc_idx]); + if (error) + goto err_free_dev; + + alloc_idx++; + } + } + + /* all buttons use one input node */ + if (ad714x->hw->button_num > 0) { + struct ad714x_button_plat *bt_plat = ad714x->hw->button; + + input[alloc_idx] = input_allocate_device(); + if (!input[alloc_idx]) { + error = -ENOMEM; + goto err_free_dev; + } + + __set_bit(EV_KEY, input[alloc_idx]->evbit); + for (i = 0; i < ad714x->hw->button_num; i++) { + bt_drv[i].input = input[alloc_idx]; + __set_bit(bt_plat[i].keycode, input[alloc_idx]->keybit); + } + + input[alloc_idx]->id.bustype = bus_type; + input[alloc_idx]->id.product = ad714x->product; + input[alloc_idx]->id.version = ad714x->version; + + error = input_register_device(input[alloc_idx]); + if (error) + goto err_free_dev; + + alloc_idx++; + } + + error = request_threaded_irq(ad714x->irq, NULL, ad714x_interrupt_thread, + IRQF_TRIGGER_FALLING, "ad714x_captouch", ad714x); + if (error) { + dev_err(dev, "can't allocate irq %d\n", ad714x->irq); + goto err_unreg_dev; + } + + return ad714x; + + err_free_dev: + dev_err(dev, "failed to setup AD714x input device %i\n", alloc_idx); + input_free_device(input[alloc_idx]); + err_unreg_dev: + while (--alloc_idx >= 0) + input_unregister_device(input[alloc_idx]); + err_free_mem: + kfree(ad714x); + err_out: + return ERR_PTR(error); +} +EXPORT_SYMBOL(ad714x_probe); + +void ad714x_remove(struct ad714x_chip *ad714x) +{ + struct ad714x_platform_data *hw = ad714x->hw; + struct ad714x_driver_data *sw = ad714x->sw; + int i; + + free_irq(ad714x->irq, ad714x); + + /* unregister and free all input devices */ + + for (i = 0; i < hw->slider_num; i++) + input_unregister_device(sw->slider[i].input); + + for (i = 0; i < hw->wheel_num; i++) + input_unregister_device(sw->wheel[i].input); + + for (i = 0; i < hw->touchpad_num; i++) + input_unregister_device(sw->touchpad[i].input); + + if (hw->button_num) + input_unregister_device(sw->button[0].input); + + kfree(ad714x); +} +EXPORT_SYMBOL(ad714x_remove); + +#ifdef CONFIG_PM +int ad714x_disable(struct ad714x_chip *ad714x) +{ + unsigned short data; + + dev_dbg(ad714x->dev, "%s enter\n", __func__); + + mutex_lock(&ad714x->mutex); + + data = ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL] | 0x3; + ad714x->write(ad714x->dev, AD714X_PWR_CTRL, data); + + mutex_unlock(&ad714x->mutex); + + return 0; +} +EXPORT_SYMBOL(ad714x_disable); + +int ad714x_enable(struct ad714x_chip *ad714x) +{ + unsigned short data; + + dev_dbg(ad714x->dev, "%s enter\n", __func__); + + mutex_lock(&ad714x->mutex); + + /* resume to non-shutdown mode */ + + ad714x->write(ad714x->dev, AD714X_PWR_CTRL, + ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL]); + + /* make sure the interrupt output line is not low level after resume, + * otherwise we will get no chance to enter falling-edge irq again + */ + + ad714x->read(ad714x->dev, STG_LOW_INT_STA_REG, &data); + ad714x->read(ad714x->dev, STG_HIGH_INT_STA_REG, &data); + ad714x->read(ad714x->dev, STG_COM_INT_STA_REG, &data); + + mutex_unlock(&ad714x->mutex); + + return 0; +} +EXPORT_SYMBOL(ad714x_enable); +#endif + +MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor Driver"); +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/ad714x.h b/drivers/input/misc/ad714x.h new file mode 100644 index 0000000..45c54fb --- /dev/null +++ b/drivers/input/misc/ad714x.h @@ -0,0 +1,26 @@ +/* + * AD714X CapTouch Programmable Controller driver (bus interfaces) + * + * Copyright 2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef _AD714X_H_ +#define _AD714X_H_ + +#include <linux/types.h> + +struct device; +struct ad714x_chip; + +typedef int (*ad714x_read_t)(struct device *, unsigned short, unsigned short *); +typedef int (*ad714x_write_t)(struct device *, unsigned short, unsigned short); + +int ad714x_disable(struct ad714x_chip *ad714x); +int ad714x_enable(struct ad714x_chip *ad714x); +struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq, + ad714x_read_t read, ad714x_write_t write); +void ad714x_remove(struct ad714x_chip *ad714x); + +#endif diff --git a/drivers/input/misc/pcf8574_keypad.c b/drivers/input/misc/pcf8574_keypad.c new file mode 100644 index 0000000..5c3ac4e --- /dev/null +++ b/drivers/input/misc/pcf8574_keypad.c @@ -0,0 +1,227 @@ +/* + * Driver for a keypad w/16 buttons connected to a PCF8574 I2C I/O expander + * + * Copyright 2005-2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/workqueue.h> + +#define DRV_NAME "pcf8574_keypad" + +static const unsigned char pcf8574_kp_btncode[] = { + [0] = KEY_RESERVED, + [1] = KEY_ENTER, + [2] = KEY_BACKSLASH, + [3] = KEY_0, + [4] = KEY_RIGHTBRACE, + [5] = KEY_C, + [6] = KEY_9, + [7] = KEY_8, + [8] = KEY_7, + [9] = KEY_B, + [10] = KEY_6, + [11] = KEY_5, + [12] = KEY_4, + [13] = KEY_A, + [14] = KEY_3, + [15] = KEY_2, + [16] = KEY_1 +}; + +struct kp_data { + unsigned short btncode[ARRAY_SIZE(pcf8574_kp_btncode)]; + struct input_dev *idev; + struct i2c_client *client; + char name[64]; + char phys[32]; + unsigned char laststate; +}; + +static short read_state(struct kp_data *lp) +{ + unsigned char x, y, a, b; + + i2c_smbus_write_byte(lp->client, 240); + x = 0xF & (~(i2c_smbus_read_byte(lp->client) >> 4)); + + i2c_smbus_write_byte(lp->client, 15); + y = 0xF & (~i2c_smbus_read_byte(lp->client)); + + for (a = 0; x > 0; a++) + x = x >> 1; + for (b = 0; y > 0; b++) + y = y >> 1; + + return ((a - 1) * 4) + b; +} + +static irqreturn_t pcf8574_kp_irq_handler(int irq, void *dev_id) +{ + struct kp_data *lp = dev_id; + unsigned char nextstate = read_state(lp); + + if (lp->laststate != nextstate) { + int key_down = nextstate <= ARRAY_SIZE(lp->btncode); + unsigned short keycode = key_down ? + lp->btncode[nextstate] : lp->btncode[lp->laststate]; + + input_report_key(lp->idev, keycode, key_down); + input_sync(lp->idev); + + lp->laststate = nextstate; + } + + return IRQ_HANDLED; +} + +static int __devinit pcf8574_kp_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int i, ret; + struct input_dev *idev; + struct kp_data *lp; + + if (i2c_smbus_write_byte(client, 240) < 0) { + dev_err(&client->dev, "probe: write fail\n"); + return -ENODEV; + } + + lp = kzalloc(sizeof(*lp), GFP_KERNEL); + if (!lp) + return -ENOMEM; + + idev = input_allocate_device(); + if (!idev) { + dev_err(&client->dev, "Can't allocate input device\n"); + ret = -ENOMEM; + goto fail_allocate; + } + + lp->idev = idev; + lp->client = client; + + idev->evbit[0] = BIT_MASK(EV_KEY); + idev->keycode = lp->btncode; + idev->keycodesize = sizeof(lp->btncode[0]); + idev->keycodemax = ARRAY_SIZE(lp->btncode); + + for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) { + lp->btncode[i] = pcf8574_kp_btncode[i]; + __set_bit(lp->btncode[i] & KEY_MAX, idev->keybit); + } + + sprintf(lp->name, DRV_NAME); + sprintf(lp->phys, "kp_data/input0"); + + idev->name = lp->name; + idev->phys = lp->phys; + idev->id.bustype = BUS_I2C; + idev->id.vendor = 0x0001; + idev->id.product = 0x0001; + idev->id.version = 0x0100; + + input_set_drvdata(idev, lp); + + ret = input_register_device(idev); + if (ret) { + dev_err(&client->dev, "input_register_device() failed\n"); + goto fail_register; + } + + lp->laststate = read_state(lp); + + ret = request_threaded_irq(client->irq, NULL, pcf8574_kp_irq_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + DRV_NAME, lp); + if (ret) { + dev_err(&client->dev, "IRQ %d is not free\n", client->irq); + goto fail_irq; + } + + i2c_set_clientdata(client, lp); + return 0; + + fail_irq: + input_unregister_device(idev); + fail_register: + input_set_drvdata(idev, NULL); + input_free_device(idev); + fail_allocate: + kfree(lp); + + return ret; +} + +static int __devexit pcf8574_kp_remove(struct i2c_client *client) +{ + struct kp_data *lp = i2c_get_clientdata(client); + + free_irq(client->irq, lp); + + input_unregister_device(lp->idev); + kfree(lp); + + i2c_set_clientdata(client, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int pcf8574_kp_resume(struct i2c_client *client) +{ + enable_irq(client->irq); + + return 0; +} + +static int pcf8574_kp_suspend(struct i2c_client *client, pm_message_t mesg) +{ + disable_irq(client->irq); + + return 0; +} +#else +# define pcf8574_kp_resume NULL +# define pcf8574_kp_suspend NULL +#endif + +static const struct i2c_device_id pcf8574_kp_id[] = { + { DRV_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id); + +static struct i2c_driver pcf8574_kp_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = pcf8574_kp_probe, + .remove = __devexit_p(pcf8574_kp_remove), + .suspend = pcf8574_kp_suspend, + .resume = pcf8574_kp_resume, + .id_table = pcf8574_kp_id, +}; + +static int __init pcf8574_kp_init(void) +{ + return i2c_add_driver(&pcf8574_kp_driver); +} +module_init(pcf8574_kp_init); + +static void __exit pcf8574_kp_exit(void) +{ + i2c_del_driver(&pcf8574_kp_driver); +} +module_exit(pcf8574_kp_exit); + +MODULE_AUTHOR("Michael Hennerich"); +MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index 04d5a4a..4dac8b7 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -983,11 +983,11 @@ static int __init copy_keymap(void) for (key = keymap; key->type != KE_END; key++) length++; - new_keymap = kmalloc(length * sizeof(struct key_entry), GFP_KERNEL); + new_keymap = kmemdup(keymap, length * sizeof(struct key_entry), + GFP_KERNEL); if (!new_keymap) return -ENOMEM; - memcpy(new_keymap, keymap, length * sizeof(struct key_entry)); keymap = new_keymap; return 0; diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index c714ca2..eeb58c1c 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -17,7 +17,7 @@ config MOUSE_PS2 default y select SERIO select SERIO_LIBPS2 - select SERIO_I8042 if X86 + select SERIO_I8042 if X86 && !X86_MRST select SERIO_GSCPS2 if GSC help Say Y here if you have a PS/2 mouse connected to your system. This diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 112b4ee..b18862b 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -10,6 +10,8 @@ * Trademarks are the property of their respective owners. */ +#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt + #include <linux/delay.h> #include <linux/slab.h> #include <linux/module.h> @@ -19,10 +21,10 @@ #include "psmouse.h" #include "elantech.h" -#define elantech_debug(format, arg...) \ - do { \ - if (etd->debug) \ - printk(KERN_DEBUG format, ##arg); \ +#define elantech_debug(fmt, ...) \ + do { \ + if (etd->debug) \ + printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \ } while (0) static bool force_elantech; @@ -37,7 +39,7 @@ static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, { if (psmouse_sliced_command(psmouse, c) || ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { - pr_err("elantech.c: synaptics_send_cmd query 0x%02x failed.\n", c); + pr_err("synaptics_send_cmd query 0x%02x failed.\n", c); return -1; } @@ -60,13 +62,13 @@ static int elantech_ps2_command(struct psmouse *psmouse, if (rc == 0) break; tries--; - elantech_debug("elantech.c: retrying ps2 command 0x%02x (%d).\n", - command, tries); + elantech_debug("retrying ps2 command 0x%02x (%d).\n", + command, tries); msleep(ETP_PS2_COMMAND_DELAY); } while (tries > 0); if (rc) - pr_err("elantech.c: ps2 command 0x%02x failed.\n", command); + pr_err("ps2 command 0x%02x failed.\n", command); return rc; } @@ -108,7 +110,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, } if (rc) - pr_err("elantech.c: failed to read register 0x%02x.\n", reg); + pr_err("failed to read register 0x%02x.\n", reg); else *val = param[0]; @@ -154,7 +156,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, } if (rc) - pr_err("elantech.c: failed to write register 0x%02x with value 0x%02x.\n", + pr_err("failed to write register 0x%02x with value 0x%02x.\n", reg, val); return rc; @@ -167,7 +169,7 @@ static void elantech_packet_dump(unsigned char *packet, int size) { int i; - printk(KERN_DEBUG "elantech.c: PS/2 packet ["); + printk(KERN_DEBUG pr_fmt("PS/2 packet [")); for (i = 0; i < size; i++) printk("%s0x%02x ", (i) ? ", " : " ", packet[i]); printk("]\n"); @@ -203,7 +205,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse) if (etd->jumpy_cursor) { /* Discard packets that are likely to have bogus coordinates */ if (fingers > old_fingers) { - elantech_debug("elantech.c: discarding packet\n"); + elantech_debug("discarding packet\n"); goto discard_packet_v1; } } @@ -413,23 +415,21 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse) if (rc == 0) break; tries--; - elantech_debug("elantech.c: retrying read (%d).\n", - tries); + elantech_debug("retrying read (%d).\n", tries); msleep(ETP_READ_BACK_DELAY); } while (tries > 0); if (rc) { - pr_err("elantech.c: failed to read back register 0x10.\n"); + pr_err("failed to read back register 0x10.\n"); } else if (etd->hw_version == 1 && !(val & ETP_R10_ABSOLUTE_MODE)) { - pr_err("elantech.c: touchpad refuses " - "to switch to absolute mode.\n"); + pr_err("touchpad refuses to switch to absolute mode.\n"); rc = -1; } } if (rc) - pr_err("elantech.c: failed to initialise registers.\n"); + pr_err("failed to initialise registers.\n"); return rc; } @@ -575,6 +575,24 @@ static struct attribute_group elantech_attr_group = { .attrs = elantech_attrs, }; +static bool elantech_is_signature_valid(const unsigned char *param) +{ + static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10 }; + int i; + + if (param[0] == 0) + return false; + + if (param[1] == 0) + return true; + + for (i = 0; i < ARRAY_SIZE(rates); i++) + if (param[2] == rates[i]) + return false; + + return true; +} + /* * Use magic knock to detect Elantech touchpad */ @@ -590,7 +608,7 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties) ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { - pr_debug("elantech.c: sending Elantech magic knock failed.\n"); + pr_debug("sending Elantech magic knock failed.\n"); return -1; } @@ -599,8 +617,7 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties) * set of magic numbers */ if (param[0] != 0x3c || param[1] != 0x03 || param[2] != 0xc8) { - pr_debug("elantech.c: " - "unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n", + pr_debug("unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n", param[0], param[1], param[2]); return -1; } @@ -611,20 +628,20 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties) * to Elantech magic knock and there might be more. */ if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) { - pr_debug("elantech.c: failed to query firmware version.\n"); + pr_debug("failed to query firmware version.\n"); return -1; } - pr_debug("elantech.c: Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n", + pr_debug("Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n", param[0], param[1], param[2]); - if (param[0] == 0 || param[1] != 0) { + if (!elantech_is_signature_valid(param)) { if (!force_elantech) { - pr_debug("elantech.c: Probably not a real Elantech touchpad. Aborting.\n"); + pr_debug("Probably not a real Elantech touchpad. Aborting.\n"); return -1; } - pr_debug("elantech.c: Probably not a real Elantech touchpad. Enabling anyway due to force_elantech.\n"); + pr_debug("Probably not a real Elantech touchpad. Enabling anyway due to force_elantech.\n"); } if (set_properties) { @@ -655,7 +672,7 @@ static int elantech_reconnect(struct psmouse *psmouse) return -1; if (elantech_set_absolute_mode(psmouse)) { - pr_err("elantech.c: failed to put touchpad back into absolute mode.\n"); + pr_err("failed to put touchpad back into absolute mode.\n"); return -1; } @@ -683,7 +700,7 @@ int elantech_init(struct psmouse *psmouse) * Do the version query again so we can store the result */ if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) { - pr_err("elantech.c: failed to query firmware version.\n"); + pr_err("failed to query firmware version.\n"); goto init_fail; } @@ -704,14 +721,14 @@ int elantech_init(struct psmouse *psmouse) etd->paritycheck = 1; } - pr_info("elantech.c: assuming hardware version %d, firmware version %d.%d.%d\n", + pr_info("assuming hardware version %d, firmware version %d.%d.%d\n", etd->hw_version, param[0], param[1], param[2]); if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY, param)) { - pr_err("elantech.c: failed to query capabilities.\n"); + pr_err("failed to query capabilities.\n"); goto init_fail; } - pr_info("elantech.c: Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n", + pr_info("Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n", param[0], param[1], param[2]); etd->capabilities = param[0]; @@ -721,13 +738,12 @@ int elantech_init(struct psmouse *psmouse) * to jump. Enable a workaround. */ if (etd->fw_version == 0x020022) { - pr_info("elantech.c: firmware version 2.0.34 detected, " - "enabling jumpy cursor workaround\n"); + pr_info("firmware version 2.0.34 detected, enabling jumpy cursor workaround\n"); etd->jumpy_cursor = 1; } if (elantech_set_absolute_mode(psmouse)) { - pr_err("elantech.c: failed to put touchpad into absolute mode.\n"); + pr_err("failed to put touchpad into absolute mode.\n"); goto init_fail; } @@ -736,8 +752,7 @@ int elantech_init(struct psmouse *psmouse) error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj, &elantech_attr_group); if (error) { - pr_err("elantech.c: failed to create sysfs attributes, error: %d.\n", - error); + pr_err("failed to create sysfs attributes, error: %d.\n", error); goto init_fail; } diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index 08d66d8..1d2205b 100644 --- a/drivers/input/mouse/hgpk.c +++ b/drivers/input/mouse/hgpk.c @@ -40,8 +40,8 @@ #include "psmouse.h" #include "hgpk.h" -static int tpdebug; -module_param(tpdebug, int, 0644); +static bool tpdebug; +module_param(tpdebug, bool, 0644); MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG."); static int recalib_delta = 100; diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index 543c240..c9983ae 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -56,36 +56,36 @@ static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse) /* Logitech extended packet */ switch ((packet[1] >> 4) | (packet[0] & 0x30)) { - case 0x0d: /* Mouse extra info */ + case 0x0d: /* Mouse extra info */ - input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL, - (int) (packet[2] & 8) - (int) (packet[2] & 7)); - input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1); - input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1); + input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL, + (int) (packet[2] & 8) - (int) (packet[2] & 7)); + input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1); + input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1); - break; + break; - case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */ + case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */ - input_report_key(dev, BTN_SIDE, (packet[2]) & 1); - input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1); - input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1); - input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1); - input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1); + input_report_key(dev, BTN_SIDE, (packet[2]) & 1); + input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1); + input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1); + input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1); + input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1); - break; + break; - case 0x0f: /* TouchPad extra info */ + case 0x0f: /* TouchPad extra info */ - input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL, - (int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7)); - packet[0] = packet[2] | 0x08; - break; + input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL, + (int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7)); + packet[0] = packet[2] | 0x08; + break; #ifdef DEBUG - default: - printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n", - (packet[1] >> 4) | (packet[0] & 0x30)); + default: + printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n", + (packet[1] >> 4) | (packet[0] & 0x30)); #endif } } else { @@ -250,7 +250,6 @@ static const struct ps2pp_info *get_model_info(unsigned char model) if (model == ps2pp_list[i].model) return &ps2pp_list[i]; - printk(KERN_WARNING "logips2pp: Detected unknown logitech mouse model %d\n", model); return NULL; } @@ -285,31 +284,32 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse, __set_bit(REL_HWHEEL, input_dev->relbit); switch (model_info->kind) { - case PS2PP_KIND_WHEEL: - psmouse->name = "Wheel Mouse"; - break; - - case PS2PP_KIND_MX: - psmouse->name = "MX Mouse"; - break; - case PS2PP_KIND_TP3: - psmouse->name = "TouchPad 3"; - break; - - case PS2PP_KIND_TRACKMAN: - psmouse->name = "TrackMan"; - break; - - default: - /* - * Set name to "Mouse" only when using PS2++, - * otherwise let other protocols define suitable - * name - */ - if (using_ps2pp) - psmouse->name = "Mouse"; - break; + case PS2PP_KIND_WHEEL: + psmouse->name = "Wheel Mouse"; + break; + + case PS2PP_KIND_MX: + psmouse->name = "MX Mouse"; + break; + + case PS2PP_KIND_TP3: + psmouse->name = "TouchPad 3"; + break; + + case PS2PP_KIND_TRACKMAN: + psmouse->name = "TrackMan"; + break; + + default: + /* + * Set name to "Mouse" only when using PS2++, + * otherwise let other protocols define suitable + * name + */ + if (using_ps2pp) + psmouse->name = "Mouse"; + break; } } @@ -343,7 +343,8 @@ int ps2pp_init(struct psmouse *psmouse, bool set_properties) if (!model || !buttons) return -1; - if ((model_info = get_model_info(model)) != NULL) { + model_info = get_model_info(model); + if (model_info) { /* * Do Logitech PS2++ / PS2T++ magic init. @@ -379,6 +380,9 @@ int ps2pp_init(struct psmouse *psmouse, bool set_properties) use_ps2pp = true; } } + + } else { + printk(KERN_WARNING "logips2pp: Detected unknown logitech mouse model %d\n", model); } if (set_properties) { diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index a3c9731..979c502 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -147,18 +147,18 @@ static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) if (psmouse->type == PSMOUSE_IMEX) { switch (packet[3] & 0xC0) { - case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */ - input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); - break; - case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */ - input_report_rel(dev, REL_HWHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); - break; - case 0x00: - case 0xC0: - input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7)); - input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1); - input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1); - break; + case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */ + input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); + break; + case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */ + input_report_rel(dev, REL_HWHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); + break; + case 0x00: + case 0xC0: + input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7)); + input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1); + input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1); + break; } } @@ -247,31 +247,31 @@ static int psmouse_handle_byte(struct psmouse *psmouse) psmouse_ret_t rc = psmouse->protocol_handler(psmouse); switch (rc) { - case PSMOUSE_BAD_DATA: - if (psmouse->state == PSMOUSE_ACTIVATED) { - printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n", - psmouse->name, psmouse->phys, psmouse->pktcnt); - if (++psmouse->out_of_sync_cnt == psmouse->resetafter) { - __psmouse_set_state(psmouse, PSMOUSE_IGNORE); - printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n"); - serio_reconnect(psmouse->ps2dev.serio); - return -1; - } - } - psmouse->pktcnt = 0; - break; - - case PSMOUSE_FULL_PACKET: - psmouse->pktcnt = 0; - if (psmouse->out_of_sync_cnt) { - psmouse->out_of_sync_cnt = 0; - printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n", - psmouse->name, psmouse->phys); + case PSMOUSE_BAD_DATA: + if (psmouse->state == PSMOUSE_ACTIVATED) { + printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n", + psmouse->name, psmouse->phys, psmouse->pktcnt); + if (++psmouse->out_of_sync_cnt == psmouse->resetafter) { + __psmouse_set_state(psmouse, PSMOUSE_IGNORE); + printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n"); + serio_reconnect(psmouse->ps2dev.serio); + return -1; } - break; + } + psmouse->pktcnt = 0; + break; + + case PSMOUSE_FULL_PACKET: + psmouse->pktcnt = 0; + if (psmouse->out_of_sync_cnt) { + psmouse->out_of_sync_cnt = 0; + printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n", + psmouse->name, psmouse->phys); + } + break; - case PSMOUSE_GOOD_DATA: - break; + case PSMOUSE_GOOD_DATA: + break; } return 0; } @@ -1245,7 +1245,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, psmouse->pktsize = 3; if (proto && (proto->detect || proto->init)) { - if (proto->detect && proto->detect(psmouse, 1) < 0) + if (proto->detect && proto->detect(psmouse, true) < 0) return -1; if (proto->init && proto->init(psmouse) < 0) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index ebd7a99..40cea33 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -36,6 +36,8 @@ * The x/y limits are taken from the Synaptics TouchPad interfacing Guide, * section 2.3.2, which says that they should be valid regardless of the * actual size of the sensor. + * Note that newer firmware allows querying device for maximum useable + * coordinates. */ #define XMIN_NOMINAL 1472 #define XMAX_NOMINAL 5472 @@ -194,23 +196,33 @@ static int synaptics_identify(struct psmouse *psmouse) } /* - * Read touchpad resolution + * Read touchpad resolution and maximum reported coordinates * Resolution is left zero if touchpad does not support the query */ static int synaptics_resolution(struct psmouse *psmouse) { struct synaptics_data *priv = psmouse->private; unsigned char res[3]; + unsigned char max[3]; if (SYN_ID_MAJOR(priv->identity) < 4) - return 0; - if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, res)) - return 0; + if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, res) == 0) { + if (res[0] != 0 && (res[1] & 0x80) && res[2] != 0) { + priv->x_res = res[0]; /* x resolution in units/mm */ + priv->y_res = res[2]; /* y resolution in units/mm */ + } + } - if ((res[0] != 0) && (res[1] & 0x80) && (res[2] != 0)) { - priv->x_res = res[0]; /* x resolution in units/mm */ - priv->y_res = res[2]; /* y resolution in units/mm */ + if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 && + SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) { + if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_DIMENSIONS, max)) { + printk(KERN_ERR "Synaptics claims to have dimensions query," + " but I'm not able to read it.\n"); + } else { + priv->x_max = (max[0] << 5) | ((max[1] & 0x0f) << 1); + priv->y_max = (max[2] << 5) | ((max[1] & 0xf0) >> 3); + } } return 0; @@ -520,19 +532,20 @@ static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned cha return 0; switch (pkt_type) { - case SYN_NEWABS: - case SYN_NEWABS_RELAXED: - return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx]; - case SYN_NEWABS_STRICT: - return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx]; + case SYN_NEWABS: + case SYN_NEWABS_RELAXED: + return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx]; - case SYN_OLDABS: - return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx]; + case SYN_NEWABS_STRICT: + return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx]; - default: - printk(KERN_ERR "synaptics: unknown packet type %d\n", pkt_type); - return 0; + case SYN_OLDABS: + return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx]; + + default: + printk(KERN_ERR "synaptics: unknown packet type %d\n", pkt_type); + return 0; } } @@ -578,8 +591,10 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) int i; __set_bit(EV_ABS, dev->evbit); - input_set_abs_params(dev, ABS_X, XMIN_NOMINAL, XMAX_NOMINAL, 0, 0); - input_set_abs_params(dev, ABS_Y, YMIN_NOMINAL, YMAX_NOMINAL, 0, 0); + input_set_abs_params(dev, ABS_X, + XMIN_NOMINAL, priv->x_max ?: XMAX_NOMINAL, 0, 0); + input_set_abs_params(dev, ABS_Y, + YMIN_NOMINAL, priv->y_max ?: YMAX_NOMINAL, 0, 0); input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); __set_bit(ABS_TOOL_WIDTH, dev->absbit); diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index ae37c5d..7d4d5e1 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -19,6 +19,7 @@ #define SYN_QUE_RESOLUTION 0x08 #define SYN_QUE_EXT_CAPAB 0x09 #define SYN_QUE_EXT_CAPAB_0C 0x0c +#define SYN_QUE_EXT_DIMENSIONS 0x0d /* synatics modes */ #define SYN_BIT_ABSOLUTE_MODE (1 << 7) @@ -51,6 +52,7 @@ #define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12) #define SYN_CAP_PRODUCT_ID(ec) (((ec) & 0xff0000) >> 16) #define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100100) +#define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & 0x020000) /* synaptics modes query bits */ #define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7)) @@ -101,8 +103,8 @@ struct synaptics_data { unsigned long int ext_cap; /* Extended Capabilities */ unsigned long int ext_cap_0c; /* Ext Caps from 0x0c query */ unsigned long int identity; /* Identification */ - int x_res; /* X resolution in units/mm */ - int y_res; /* Y resolution in units/mm */ + unsigned int x_res, y_res; /* X/Y resolution in units/mm */ + unsigned int x_max, y_max; /* Max dimensions (from FW) */ unsigned char pkt_type; /* packet type - old, new, etc */ unsigned char mode; /* current mode byte */ diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index ead0494..6168469 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -660,8 +660,21 @@ static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id * } static struct pnp_device_id pnp_kbd_devids[] = { + { .id = "PNP0300", .driver_data = 0 }, + { .id = "PNP0301", .driver_data = 0 }, + { .id = "PNP0302", .driver_data = 0 }, { .id = "PNP0303", .driver_data = 0 }, + { .id = "PNP0304", .driver_data = 0 }, + { .id = "PNP0305", .driver_data = 0 }, + { .id = "PNP0306", .driver_data = 0 }, + { .id = "PNP0309", .driver_data = 0 }, + { .id = "PNP030a", .driver_data = 0 }, { .id = "PNP030b", .driver_data = 0 }, + { .id = "PNP0320", .driver_data = 0 }, + { .id = "PNP0343", .driver_data = 0 }, + { .id = "PNP0344", .driver_data = 0 }, + { .id = "PNP0345", .driver_data = 0 }, + { .id = "CPQA0D7", .driver_data = 0 }, { .id = "", }, }; @@ -672,6 +685,7 @@ static struct pnp_driver i8042_pnp_kbd_driver = { }; static struct pnp_device_id pnp_aux_devids[] = { + { .id = "AUI0200", .driver_data = 0 }, { .id = "FJC6000", .driver_data = 0 }, { .id = "FJC6001", .driver_data = 0 }, { .id = "PNP0f03", .driver_data = 0 }, diff --git a/drivers/input/tablet/acecad.c b/drivers/input/tablet/acecad.c index 670c61c..37d0539 100644 --- a/drivers/input/tablet/acecad.c +++ b/drivers/input/tablet/acecad.c @@ -66,18 +66,18 @@ static void usb_acecad_irq(struct urb *urb) int prox, status; switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __func__, urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", __func__, urb->status); - goto resubmit; + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __func__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __func__, urb->status); + goto resubmit; } prox = (data[0] & 0x04) >> 2; @@ -135,7 +135,7 @@ static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_ struct usb_acecad *acecad; struct input_dev *input_dev; int pipe, maxp; - int err = -ENOMEM; + int err; if (interface->desc.bNumEndpoints != 1) return -ENODEV; @@ -193,40 +193,34 @@ static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_ input_dev->close = usb_acecad_close; input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input_dev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | - BIT_MASK(ABS_PRESSURE); - input_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); input_dev->keybit[BIT_WORD(BTN_DIGI)] = BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_TOUCH) | BIT_MASK(BTN_STYLUS) | BIT_MASK(BTN_STYLUS2); switch (id->driver_info) { - case 0: - input_dev->absmax[ABS_X] = 5000; - input_dev->absmax[ABS_Y] = 3750; - input_dev->absmax[ABS_PRESSURE] = 512; - if (!strlen(acecad->name)) - snprintf(acecad->name, sizeof(acecad->name), - "USB Acecad Flair Tablet %04x:%04x", - le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - break; - case 1: - input_dev->absmax[ABS_X] = 3000; - input_dev->absmax[ABS_Y] = 2250; - input_dev->absmax[ABS_PRESSURE] = 1024; - if (!strlen(acecad->name)) - snprintf(acecad->name, sizeof(acecad->name), - "USB Acecad 302 Tablet %04x:%04x", - le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - break; + case 0: + input_set_abs_params(input_dev, ABS_X, 0, 5000, 4, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 3750, 4, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 512, 0, 0); + if (!strlen(acecad->name)) + snprintf(acecad->name, sizeof(acecad->name), + "USB Acecad Flair Tablet %04x:%04x", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + break; + + case 1: + input_set_abs_params(input_dev, ABS_X, 0, 53000, 4, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 2250, 4, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1024, 0, 0); + if (!strlen(acecad->name)) + snprintf(acecad->name, sizeof(acecad->name), + "USB Acecad 302 Tablet %04x:%04x", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + break; } - input_dev->absfuzz[ABS_X] = 4; - input_dev->absfuzz[ABS_Y] = 4; - usb_fill_int_urb(acecad->irq, dev, pipe, acecad->data, maxp > 8 ? 8 : maxp, usb_acecad_irq, acecad, endpoint->bInterval); @@ -252,13 +246,11 @@ static void usb_acecad_disconnect(struct usb_interface *intf) struct usb_acecad *acecad = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); - if (acecad) { - usb_kill_urb(acecad->irq); - input_unregister_device(acecad->input); - usb_free_urb(acecad->irq); - usb_buffer_free(interface_to_usbdev(intf), 10, acecad->data, acecad->data_dma); - kfree(acecad); - } + + input_unregister_device(acecad->input); + usb_free_urb(acecad->irq); + usb_buffer_free(acecad->usbdev, 8, acecad->data, acecad->data_dma); + kfree(acecad); } static struct usb_device_id usb_acecad_id_table [] = { diff --git a/drivers/input/tablet/kbtab.c b/drivers/input/tablet/kbtab.c index 6682b17..b9969f1 100644 --- a/drivers/input/tablet/kbtab.c +++ b/drivers/input/tablet/kbtab.c @@ -34,10 +34,6 @@ struct kbtab { struct input_dev *dev; struct usb_device *usbdev; struct urb *irq; - int x, y; - int button; - int pressure; - __u32 serial[2]; char phys[32]; }; @@ -46,6 +42,7 @@ static void kbtab_irq(struct urb *urb) struct kbtab *kbtab = urb->context; unsigned char *data = kbtab->data; struct input_dev *dev = kbtab->dev; + int pressure; int retval; switch (urb->status) { @@ -63,31 +60,27 @@ static void kbtab_irq(struct urb *urb) goto exit; } - kbtab->x = get_unaligned_le16(&data[1]); - kbtab->y = get_unaligned_le16(&data[3]); - - kbtab->pressure = (data[5]); input_report_key(dev, BTN_TOOL_PEN, 1); - input_report_abs(dev, ABS_X, kbtab->x); - input_report_abs(dev, ABS_Y, kbtab->y); + input_report_abs(dev, ABS_X, get_unaligned_le16(&data[1])); + input_report_abs(dev, ABS_Y, get_unaligned_le16(&data[3])); /*input_report_key(dev, BTN_TOUCH , data[0] & 0x01);*/ input_report_key(dev, BTN_RIGHT, data[0] & 0x02); - if (-1 == kb_pressure_click) { - input_report_abs(dev, ABS_PRESSURE, kbtab->pressure); - } else { - input_report_key(dev, BTN_LEFT, (kbtab->pressure > kb_pressure_click) ? 1 : 0); - }; + pressure = data[5]; + if (kb_pressure_click == -1) + input_report_abs(dev, ABS_PRESSURE, pressure); + else + input_report_key(dev, BTN_LEFT, pressure > kb_pressure_click ? 1 : 0); input_sync(dev); exit: - retval = usb_submit_urb (urb, GFP_ATOMIC); + retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) - err ("%s - usb_submit_urb failed with result %d", + err("%s - usb_submit_urb failed with result %d", __func__, retval); } @@ -153,13 +146,11 @@ static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *i input_dev->open = kbtab_open; input_dev->close = kbtab_close; - input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | - BIT_MASK(EV_MSC); - input_dev->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); - input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_PEN) | - BIT_MASK(BTN_TOUCH); - input_dev->mscbit[0] |= BIT_MASK(MSC_SERIAL); + input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_LEFT)] |= + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); + input_dev->keybit[BIT_WORD(BTN_DIGI)] |= + BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_TOUCH); input_set_abs_params(input_dev, ABS_X, 0, 0x2000, 4, 0); input_set_abs_params(input_dev, ABS_Y, 0, 0x1750, 4, 0); input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xff, 0, 0); @@ -182,7 +173,7 @@ static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *i return 0; fail3: usb_free_urb(kbtab->irq); - fail2: usb_buffer_free(dev, 10, kbtab->data, kbtab->data_dma); + fail2: usb_buffer_free(dev, 8, kbtab->data, kbtab->data_dma); fail1: input_free_device(input_dev); kfree(kbtab); return error; @@ -193,13 +184,11 @@ static void kbtab_disconnect(struct usb_interface *intf) struct kbtab *kbtab = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); - if (kbtab) { - usb_kill_urb(kbtab->irq); - input_unregister_device(kbtab->dev); - usb_free_urb(kbtab->irq); - usb_buffer_free(interface_to_usbdev(intf), 10, kbtab->data, kbtab->data_dma); - kfree(kbtab); - } + + input_unregister_device(kbtab->dev); + usb_free_urb(kbtab->irq); + usb_buffer_free(kbtab->usbdev, 8, kbtab->data, kbtab->data_dma); + kfree(kbtab); } static struct usb_driver kbtab_driver = { diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h index 8fef1b6..284dfaa 100644 --- a/drivers/input/tablet/wacom.h +++ b/drivers/input/tablet/wacom.h @@ -106,44 +106,18 @@ MODULE_LICENSE(DRIVER_LICENSE); struct wacom { dma_addr_t data_dma; - struct input_dev *dev; struct usb_device *usbdev; struct usb_interface *intf; struct urb *irq; - struct wacom_wac *wacom_wac; + struct wacom_wac wacom_wac; struct mutex lock; - unsigned int open:1; + bool open; char phys[32]; }; -struct wacom_combo { - struct wacom *wacom; - struct urb *urb; -}; - extern const struct usb_device_id wacom_ids[]; -extern int wacom_wac_irq(struct wacom_wac * wacom_wac, void * wcombo); -extern void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data); -extern void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data); -extern void wacom_report_key(void *wcombo, unsigned int key_type, int key_data); -extern void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value); -extern void wacom_input_sync(void *wcombo); -extern void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac); -extern void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac); -extern void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac); -extern void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac); -extern void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac); -extern void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac); -extern void input_dev_i4s(struct input_dev *input_dev, struct wacom_wac *wacom_wac); -extern void input_dev_i4(struct input_dev *input_dev, struct wacom_wac *wacom_wac); -extern void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac); -extern void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac); -extern void input_dev_tpc(struct input_dev *input_dev, struct wacom_wac *wacom_wac); -extern void input_dev_tpc2fg(struct input_dev *input_dev, struct wacom_wac *wacom_wac); -extern void input_dev_mo(struct input_dev *input_dev, struct wacom_wac *wacom_wac); -extern void input_dev_bee(struct input_dev *input_dev, struct wacom_wac *wacom_wac); -extern __u16 wacom_le16_to_cpu(unsigned char *data); -extern __u16 wacom_be16_to_cpu(unsigned char *data); - +void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len); +void wacom_setup_input_capabilities(struct input_dev *input_dev, + struct wacom_wac *wacom_wac); #endif diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index f465025..d90f4e0 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -11,8 +11,8 @@ * (at your option) any later version. */ -#include "wacom.h" #include "wacom_wac.h" +#include "wacom.h" /* defines to get HID report descriptor */ #define HID_DEVICET_HID (USB_TYPE_CLASS | 0x01) @@ -70,15 +70,9 @@ static int usb_set_report(struct usb_interface *intf, unsigned char type, buf, size, 1000); } -static struct input_dev * get_input_dev(struct wacom_combo *wcombo) -{ - return wcombo->wacom->dev; -} - static void wacom_sys_irq(struct urb *urb) { struct wacom *wacom = urb->context; - struct wacom_combo wcombo; int retval; switch (urb->status) { @@ -96,59 +90,16 @@ static void wacom_sys_irq(struct urb *urb) goto exit; } - wcombo.wacom = wacom; - wcombo.urb = urb; - - if (wacom_wac_irq(wacom->wacom_wac, (void *)&wcombo)) - input_sync(get_input_dev(&wcombo)); + wacom_wac_irq(&wacom->wacom_wac, urb->actual_length); exit: usb_mark_last_busy(wacom->usbdev); - retval = usb_submit_urb (urb, GFP_ATOMIC); + retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) err ("%s - usb_submit_urb failed with result %d", __func__, retval); } -void wacom_report_key(void *wcombo, unsigned int key_type, int key_data) -{ - input_report_key(get_input_dev((struct wacom_combo *)wcombo), key_type, key_data); -} - -void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data) -{ - input_report_abs(get_input_dev((struct wacom_combo *)wcombo), abs_type, abs_data); -} - -void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data) -{ - input_report_rel(get_input_dev((struct wacom_combo *)wcombo), rel_type, rel_data); -} - -void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value) -{ - input_event(get_input_dev((struct wacom_combo *)wcombo), type, code, value); -} - -__u16 wacom_be16_to_cpu(unsigned char *data) -{ - __u16 value; - value = be16_to_cpu(*(__be16 *) data); - return value; -} - -__u16 wacom_le16_to_cpu(unsigned char *data) -{ - __u16 value; - value = le16_to_cpu(*(__le16 *) data); - return value; -} - -void wacom_input_sync(void *wcombo) -{ - input_sync(get_input_dev((struct wacom_combo *)wcombo)); -} - static int wacom_open(struct input_dev *dev) { struct wacom *wacom = input_get_drvdata(dev); @@ -168,7 +119,7 @@ static int wacom_open(struct input_dev *dev) return -EIO; } - wacom->open = 1; + wacom->open = true; wacom->intf->needs_remote_wakeup = 1; mutex_unlock(&wacom->lock); @@ -181,128 +132,11 @@ static void wacom_close(struct input_dev *dev) mutex_lock(&wacom->lock); usb_kill_urb(wacom->irq); - wacom->open = 0; + wacom->open = false; wacom->intf->needs_remote_wakeup = 0; mutex_unlock(&wacom->lock); } -void input_dev_mo(struct input_dev *input_dev, struct wacom_wac *wacom_wac) -{ - input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_1) | - BIT_MASK(BTN_5); - input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); -} - -void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac) -{ - input_dev->evbit[0] |= BIT_MASK(EV_MSC); - input_dev->mscbit[0] |= BIT_MASK(MSC_SERIAL); - input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_FINGER); - input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_0) | - BIT_MASK(BTN_4); -} - -void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac) -{ - input_dev->evbit[0] |= BIT_MASK(EV_REL); - input_dev->relbit[0] |= BIT_MASK(REL_WHEEL); - input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); - input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_RUBBER) | - BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_STYLUS) | - BIT_MASK(BTN_TOOL_MOUSE) | BIT_MASK(BTN_STYLUS2); - input_set_abs_params(input_dev, ABS_DISTANCE, - 0, wacom_wac->features.distance_max, 0, 0); -} - -void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac) -{ - input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_FINGER); - input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_0) | - BIT_MASK(BTN_1) | BIT_MASK(BTN_2) | BIT_MASK(BTN_3); - input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0); - input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); -} - -void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac) -{ - input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_4) | - BIT_MASK(BTN_5) | BIT_MASK(BTN_6) | BIT_MASK(BTN_7); - input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0); -} - -void input_dev_i4s(struct input_dev *input_dev, struct wacom_wac *wacom_wac) -{ - input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_FINGER); - input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_0) | BIT_MASK(BTN_1) | BIT_MASK(BTN_2) | BIT_MASK(BTN_3); - input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_4) | BIT_MASK(BTN_5) | BIT_MASK(BTN_6); - input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); -} - -void input_dev_i4(struct input_dev *input_dev, struct wacom_wac *wacom_wac) -{ - input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_7) | BIT_MASK(BTN_8); -} - -void input_dev_bee(struct input_dev *input_dev, struct wacom_wac *wacom_wac) -{ - input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_8) | BIT_MASK(BTN_9); -} - -void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac) -{ - input_dev->evbit[0] |= BIT_MASK(EV_MSC) | BIT_MASK(EV_REL); - input_dev->mscbit[0] |= BIT_MASK(MSC_SERIAL); - input_dev->relbit[0] |= BIT_MASK(REL_WHEEL); - input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE) | - BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA); - input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_RUBBER) | - BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_STYLUS) | - BIT_MASK(BTN_TOOL_MOUSE) | BIT_MASK(BTN_TOOL_BRUSH) | - BIT_MASK(BTN_TOOL_PENCIL) | BIT_MASK(BTN_TOOL_AIRBRUSH) | - BIT_MASK(BTN_TOOL_LENS) | BIT_MASK(BTN_STYLUS2); - input_set_abs_params(input_dev, ABS_DISTANCE, - 0, wacom_wac->features.distance_max, 0, 0); - input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0); - input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0); - input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0); - input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0); - input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0); -} - -void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac) -{ - input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_PEN) | - BIT_MASK(BTN_STYLUS) | BIT_MASK(BTN_STYLUS2); -} - -void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac) -{ - input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_RUBBER); -} - -void input_dev_tpc(struct input_dev *input_dev, struct wacom_wac *wacom_wac) -{ - struct wacom_features *features = &wacom_wac->features; - - if (features->device_type == BTN_TOOL_DOUBLETAP || - features->device_type == BTN_TOOL_TRIPLETAP) { - input_set_abs_params(input_dev, ABS_RX, 0, features->x_phy, 0, 0); - input_set_abs_params(input_dev, ABS_RY, 0, features->y_phy, 0, 0); - __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); - } -} - -void input_dev_tpc2fg(struct input_dev *input_dev, struct wacom_wac *wacom_wac) -{ - if (wacom_wac->features.device_type == BTN_TOOL_TRIPLETAP) { - input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_TRIPLETAP); - input_dev->evbit[0] |= BIT_MASK(EV_MSC); - input_dev->mscbit[0] |= BIT_MASK(MSC_SERIAL); - } -} - static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc, struct wacom_features *features) { @@ -362,9 +196,9 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi features->device_type = BTN_TOOL_TRIPLETAP; } features->x_max = - wacom_le16_to_cpu(&report[i + 3]); + get_unaligned_le16(&report[i + 3]); features->x_phy = - wacom_le16_to_cpu(&report[i + 6]); + get_unaligned_le16(&report[i + 6]); features->unit = report[i + 9]; features->unitExpo = report[i + 11]; i += 12; @@ -374,7 +208,7 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi features->pktlen = WACOM_PKGLEN_GRAPHIRE; features->device_type = BTN_TOOL_PEN; features->x_max = - wacom_le16_to_cpu(&report[i + 3]); + get_unaligned_le16(&report[i + 3]); i += 4; } } else if (usage == WCM_DIGITIZER) { @@ -396,15 +230,15 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi features->pktlen = WACOM_PKGLEN_TPC2FG; features->device_type = BTN_TOOL_TRIPLETAP; features->y_max = - wacom_le16_to_cpu(&report[i + 3]); + get_unaligned_le16(&report[i + 3]); features->y_phy = - wacom_le16_to_cpu(&report[i + 6]); + get_unaligned_le16(&report[i + 6]); i += 7; } else { features->y_max = features->x_max; features->y_phy = - wacom_le16_to_cpu(&report[i + 3]); + get_unaligned_le16(&report[i + 3]); i += 4; } } else if (pen) { @@ -413,7 +247,7 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi features->pktlen = WACOM_PKGLEN_GRAPHIRE; features->device_type = BTN_TOOL_PEN; features->y_max = - wacom_le16_to_cpu(&report[i + 3]); + get_unaligned_le16(&report[i + 3]); i += 4; } } @@ -432,7 +266,7 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi case HID_USAGE_UNDEFINED: if (usage == WCM_DESKTOP && finger) /* capacity */ features->pressure_max = - wacom_le16_to_cpu(&report[i + 3]); + get_unaligned_le16(&report[i + 3]); i += 4; break; } @@ -528,6 +362,81 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, return error; } +struct wacom_usbdev_data { + struct list_head list; + struct kref kref; + struct usb_device *dev; + struct wacom_shared shared; +}; + +static LIST_HEAD(wacom_udev_list); +static DEFINE_MUTEX(wacom_udev_list_lock); + +static struct wacom_usbdev_data *wacom_get_usbdev_data(struct usb_device *dev) +{ + struct wacom_usbdev_data *data; + + list_for_each_entry(data, &wacom_udev_list, list) { + if (data->dev == dev) { + kref_get(&data->kref); + return data; + } + } + + return NULL; +} + +static int wacom_add_shared_data(struct wacom_wac *wacom, + struct usb_device *dev) +{ + struct wacom_usbdev_data *data; + int retval = 0; + + mutex_lock(&wacom_udev_list_lock); + + data = wacom_get_usbdev_data(dev); + if (!data) { + data = kzalloc(sizeof(struct wacom_usbdev_data), GFP_KERNEL); + if (!data) { + retval = -ENOMEM; + goto out; + } + + kref_init(&data->kref); + data->dev = dev; + list_add_tail(&data->list, &wacom_udev_list); + } + + wacom->shared = &data->shared; + +out: + mutex_unlock(&wacom_udev_list_lock); + return retval; +} + +static void wacom_release_shared_data(struct kref *kref) +{ + struct wacom_usbdev_data *data = + container_of(kref, struct wacom_usbdev_data, kref); + + mutex_lock(&wacom_udev_list_lock); + list_del(&data->list); + mutex_unlock(&wacom_udev_list_lock); + + kfree(data); +} + +static void wacom_remove_shared_data(struct wacom_wac *wacom) +{ + struct wacom_usbdev_data *data; + + if (wacom->shared) { + data = container_of(wacom->shared, struct wacom_usbdev_data, shared); + kref_put(&data->kref, wacom_release_shared_data); + wacom->shared = NULL; + } +} + static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf); @@ -542,13 +451,13 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i return -EINVAL; wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); - wacom_wac = kzalloc(sizeof(struct wacom_wac), GFP_KERNEL); input_dev = input_allocate_device(); - if (!wacom || !input_dev || !wacom_wac) { + if (!wacom || !input_dev) { error = -ENOMEM; goto fail1; } + wacom_wac = &wacom->wacom_wac; wacom_wac->features = *((struct wacom_features *)id->driver_info); features = &wacom_wac->features; if (features->pktlen > WACOM_PKGLEN_MAX) { @@ -570,20 +479,12 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i } wacom->usbdev = dev; - wacom->dev = input_dev; wacom->intf = intf; mutex_init(&wacom->lock); usb_make_path(dev, wacom->phys, sizeof(wacom->phys)); strlcat(wacom->phys, "/input0", sizeof(wacom->phys)); - usb_to_input_id(dev, &input_dev->id); - - input_dev->dev.parent = &intf->dev; - - input_set_drvdata(input_dev, wacom); - - input_dev->open = wacom_open; - input_dev->close = wacom_close; + wacom_wac->input = input_dev; endpoint = &intf->cur_altsetting->endpoint[0].desc; @@ -600,20 +501,21 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i features->device_type == BTN_TOOL_PEN ? " Pen" : " Finger", sizeof(wacom_wac->name)); + + error = wacom_add_shared_data(wacom_wac, dev); + if (error) + goto fail3; } input_dev->name = wacom_wac->name; - wacom->wacom_wac = wacom_wac; - - input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOUCH); - - input_set_abs_params(input_dev, ABS_X, 0, features->x_max, 4, 0); - input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, 4, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, 0, 0); - input_dev->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC); + input_dev->name = wacom_wac->name; + input_dev->dev.parent = &intf->dev; + input_dev->open = wacom_open; + input_dev->close = wacom_close; + usb_to_input_id(dev, &input_dev->id); + input_set_drvdata(input_dev, wacom); - wacom_init_input_dev(input_dev, wacom_wac); + wacom_setup_input_capabilities(input_dev, wacom_wac); usb_fill_int_urb(wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), @@ -622,9 +524,9 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i wacom->irq->transfer_dma = wacom->data_dma; wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - error = input_register_device(wacom->dev); + error = input_register_device(input_dev); if (error) - goto fail3; + goto fail4; /* Note that if query fails it is not a hard failure */ wacom_query_tablet_data(intf, features); @@ -632,11 +534,11 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i usb_set_intfdata(intf, wacom); return 0; + fail4: wacom_remove_shared_data(wacom_wac); fail3: usb_free_urb(wacom->irq); fail2: usb_buffer_free(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma); fail1: input_free_device(input_dev); kfree(wacom); - kfree(wacom_wac); return error; } @@ -647,11 +549,11 @@ static void wacom_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); usb_kill_urb(wacom->irq); - input_unregister_device(wacom->dev); + input_unregister_device(wacom->wacom_wac.input); usb_free_urb(wacom->irq); usb_buffer_free(interface_to_usbdev(intf), WACOM_PKGLEN_MAX, - wacom->wacom_wac->data, wacom->data_dma); - kfree(wacom->wacom_wac); + wacom->wacom_wac.data, wacom->data_dma); + wacom_remove_shared_data(&wacom->wacom_wac); kfree(wacom); } @@ -669,7 +571,7 @@ static int wacom_suspend(struct usb_interface *intf, pm_message_t message) static int wacom_resume(struct usb_interface *intf) { struct wacom *wacom = usb_get_intfdata(intf); - struct wacom_features *features = &wacom->wacom_wac->features; + struct wacom_features *features = &wacom->wacom_wac.features; int rv; mutex_lock(&wacom->lock); diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 4a852d8..847fd01 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -11,52 +11,58 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ -#include "wacom.h" + #include "wacom_wac.h" +#include "wacom.h" -static int wacom_penpartner_irq(struct wacom_wac *wacom, void *wcombo) +static int wacom_penpartner_irq(struct wacom_wac *wacom) { unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; switch (data[0]) { - case 1: - if (data[5] & 0x80) { - wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; - wacom->id[0] = (data[5] & 0x20) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID; - wacom_report_key(wcombo, wacom->tool[0], 1); - wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); /* report tool id */ - wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1])); - wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3])); - wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127); - wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -127)); - wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40)); - } else { - wacom_report_key(wcombo, wacom->tool[0], 0); - wacom_report_abs(wcombo, ABS_MISC, 0); /* report tool id */ - wacom_report_abs(wcombo, ABS_PRESSURE, -1); - wacom_report_key(wcombo, BTN_TOUCH, 0); - } - break; - case 2: - wacom_report_key(wcombo, BTN_TOOL_PEN, 1); - wacom_report_abs(wcombo, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */ - wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1])); - wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3])); - wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127); - wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20)); - wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40)); - break; - default: - printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]); - return 0; + case 1: + if (data[5] & 0x80) { + wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + wacom->id[0] = (data[5] & 0x20) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID; + input_report_key(input, wacom->tool[0], 1); + input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */ + input_report_abs(input, ABS_X, get_unaligned_le16(&data[1])); + input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3])); + input_report_abs(input, ABS_PRESSURE, (signed char)data[6] + 127); + input_report_key(input, BTN_TOUCH, ((signed char)data[6] > -127)); + input_report_key(input, BTN_STYLUS, (data[5] & 0x40)); + } else { + input_report_key(input, wacom->tool[0], 0); + input_report_abs(input, ABS_MISC, 0); /* report tool id */ + input_report_abs(input, ABS_PRESSURE, -1); + input_report_key(input, BTN_TOUCH, 0); + } + break; + + case 2: + input_report_key(input, BTN_TOOL_PEN, 1); + input_report_abs(input, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */ + input_report_abs(input, ABS_X, get_unaligned_le16(&data[1])); + input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3])); + input_report_abs(input, ABS_PRESSURE, (signed char)data[6] + 127); + input_report_key(input, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20)); + input_report_key(input, BTN_STYLUS, (data[5] & 0x40)); + break; + + default: + printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]); + return 0; } + return 1; } -static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo) +static int wacom_pl_irq(struct wacom_wac *wacom) { struct wacom_features *features = &wacom->features; unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; int prox, pressure; if (data[0] != WACOM_REPORT_PENABLED) { @@ -90,8 +96,8 @@ static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo) /* was entered with stylus2 pressed */ if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) { /* report out proximity for previous tool */ - wacom_report_key(wcombo, wacom->tool[1], 0); - wacom_input_sync(wcombo); + input_report_key(input, wacom->tool[1], 0); + input_sync(input); wacom->tool[1] = BTN_TOOL_PEN; return 0; } @@ -101,32 +107,33 @@ static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo) wacom->tool[1] = BTN_TOOL_PEN; wacom->id[0] = STYLUS_DEVICE_ID; } - wacom_report_key(wcombo, wacom->tool[1], prox); /* report in proximity for tool */ - wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); /* report tool id */ - wacom_report_abs(wcombo, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14)); - wacom_report_abs(wcombo, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14)); - wacom_report_abs(wcombo, ABS_PRESSURE, pressure); - - wacom_report_key(wcombo, BTN_TOUCH, data[4] & 0x08); - wacom_report_key(wcombo, BTN_STYLUS, data[4] & 0x10); + input_report_key(input, wacom->tool[1], prox); /* report in proximity for tool */ + input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */ + input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14)); + input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14)); + input_report_abs(input, ABS_PRESSURE, pressure); + + input_report_key(input, BTN_TOUCH, data[4] & 0x08); + input_report_key(input, BTN_STYLUS, data[4] & 0x10); /* Only allow the stylus2 button to be reported for the pen tool. */ - wacom_report_key(wcombo, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20)); + input_report_key(input, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20)); } else { /* report proximity-out of a (valid) tool */ if (wacom->tool[1] != BTN_TOOL_RUBBER) { /* Unknown tool selected default to pen tool */ wacom->tool[1] = BTN_TOOL_PEN; } - wacom_report_key(wcombo, wacom->tool[1], prox); + input_report_key(input, wacom->tool[1], prox); } wacom->tool[0] = prox; /* Save proximity state */ return 1; } -static int wacom_ptu_irq(struct wacom_wac *wacom, void *wcombo) +static int wacom_ptu_irq(struct wacom_wac *wacom) { unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; if (data[0] != WACOM_REPORT_PENABLED) { printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]); @@ -134,40 +141,41 @@ static int wacom_ptu_irq(struct wacom_wac *wacom, void *wcombo) } if (data[1] & 0x04) { - wacom_report_key(wcombo, BTN_TOOL_RUBBER, data[1] & 0x20); - wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x08); + input_report_key(input, BTN_TOOL_RUBBER, data[1] & 0x20); + input_report_key(input, BTN_TOUCH, data[1] & 0x08); wacom->id[0] = ERASER_DEVICE_ID; } else { - wacom_report_key(wcombo, BTN_TOOL_PEN, data[1] & 0x20); - wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01); + input_report_key(input, BTN_TOOL_PEN, data[1] & 0x20); + input_report_key(input, BTN_TOUCH, data[1] & 0x01); wacom->id[0] = STYLUS_DEVICE_ID; } - wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); /* report tool id */ - wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2])); - wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4])); - wacom_report_abs(wcombo, ABS_PRESSURE, wacom_le16_to_cpu(&data[6])); - wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02); - wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x10); + input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */ + input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); + input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); + input_report_abs(input, ABS_PRESSURE, le16_to_cpup((__le16 *)&data[6])); + input_report_key(input, BTN_STYLUS, data[1] & 0x02); + input_report_key(input, BTN_STYLUS2, data[1] & 0x10); return 1; } -static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) +static int wacom_graphire_irq(struct wacom_wac *wacom) { struct wacom_features *features = &wacom->features; unsigned char *data = wacom->data; - int x, y, rw; - static int penData = 0; + struct input_dev *input = wacom->input; + int prox; + int rw = 0; + int retval = 0; if (data[0] != WACOM_REPORT_PENABLED) { dbg("wacom_graphire_irq: received unknown report #%d", data[0]); - return 0; + goto exit; } - if (data[1] & 0x80) { - /* in prox and not a pad data */ - penData = 1; - - switch ((data[1] >> 5) & 3) { + prox = data[1] & 0x80; + if (prox || wacom->id[0]) { + if (prox) { + switch ((data[1] >> 5) & 3) { case 0: /* Pen */ wacom->tool[0] = BTN_TOOL_PEN; @@ -180,128 +188,89 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) break; case 2: /* Mouse with wheel */ - wacom_report_key(wcombo, BTN_MIDDLE, data[1] & 0x04); - if (features->type == WACOM_G4 || features->type == WACOM_MO) { - rw = data[7] & 0x04 ? (data[7] & 0x03)-4 : (data[7] & 0x03); - wacom_report_rel(wcombo, REL_WHEEL, -rw); - } else - wacom_report_rel(wcombo, REL_WHEEL, -(signed char) data[6]); + input_report_key(input, BTN_MIDDLE, data[1] & 0x04); /* fall through */ case 3: /* Mouse without wheel */ wacom->tool[0] = BTN_TOOL_MOUSE; wacom->id[0] = CURSOR_DEVICE_ID; - wacom_report_key(wcombo, BTN_LEFT, data[1] & 0x01); - wacom_report_key(wcombo, BTN_RIGHT, data[1] & 0x02); - if (features->type == WACOM_G4 || features->type == WACOM_MO) - wacom_report_abs(wcombo, ABS_DISTANCE, data[6] & 0x3f); - else - wacom_report_abs(wcombo, ABS_DISTANCE, data[7] & 0x3f); break; + } } - x = wacom_le16_to_cpu(&data[2]); - y = wacom_le16_to_cpu(&data[4]); - wacom_report_abs(wcombo, ABS_X, x); - wacom_report_abs(wcombo, ABS_Y, y); + input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); + input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); if (wacom->tool[0] != BTN_TOOL_MOUSE) { - wacom_report_abs(wcombo, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8)); - wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01); - wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02); - wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x04); - } - wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); /* report tool id */ - wacom_report_key(wcombo, wacom->tool[0], 1); - } else if (wacom->id[0]) { - wacom_report_abs(wcombo, ABS_X, 0); - wacom_report_abs(wcombo, ABS_Y, 0); - if (wacom->tool[0] == BTN_TOOL_MOUSE) { - wacom_report_key(wcombo, BTN_LEFT, 0); - wacom_report_key(wcombo, BTN_RIGHT, 0); - wacom_report_abs(wcombo, ABS_DISTANCE, 0); + input_report_abs(input, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8)); + input_report_key(input, BTN_TOUCH, data[1] & 0x01); + input_report_key(input, BTN_STYLUS, data[1] & 0x02); + input_report_key(input, BTN_STYLUS2, data[1] & 0x04); } else { - wacom_report_abs(wcombo, ABS_PRESSURE, 0); - wacom_report_key(wcombo, BTN_TOUCH, 0); - wacom_report_key(wcombo, BTN_STYLUS, 0); - wacom_report_key(wcombo, BTN_STYLUS2, 0); + input_report_key(input, BTN_LEFT, data[1] & 0x01); + input_report_key(input, BTN_RIGHT, data[1] & 0x02); + if (features->type == WACOM_G4 || + features->type == WACOM_MO) { + input_report_abs(input, ABS_DISTANCE, data[6] & 0x3f); + rw = (signed)(data[7] & 0x04) - (data[7] & 0x03); + } else { + input_report_abs(input, ABS_DISTANCE, data[7] & 0x3f); + rw = -(signed)data[6]; + } + input_report_rel(input, REL_WHEEL, rw); } - wacom->id[0] = 0; - wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */ - wacom_report_key(wcombo, wacom->tool[0], 0); + + if (!prox) + wacom->id[0] = 0; + input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */ + input_report_key(input, wacom->tool[0], prox); + input_sync(input); /* sync last event */ } /* send pad data */ switch (features->type) { - case WACOM_G4: - if (data[7] & 0xf8) { - if (penData) { - wacom_input_sync(wcombo); /* sync last event */ - if (!wacom->id[0]) - penData = 0; - } + case WACOM_G4: + prox = data[7] & 0xf8; + if (prox || wacom->id[1]) { wacom->id[1] = PAD_DEVICE_ID; - wacom_report_key(wcombo, BTN_0, (data[7] & 0x40)); - wacom_report_key(wcombo, BTN_4, (data[7] & 0x80)); + input_report_key(input, BTN_0, (data[7] & 0x40)); + input_report_key(input, BTN_4, (data[7] & 0x80)); rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3); - wacom_report_rel(wcombo, REL_WHEEL, rw); - wacom_report_key(wcombo, BTN_TOOL_FINGER, 0xf0); - wacom_report_abs(wcombo, ABS_MISC, wacom->id[1]); - wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); - } else if (wacom->id[1]) { - if (penData) { - wacom_input_sync(wcombo); /* sync last event */ - if (!wacom->id[0]) - penData = 0; - } - wacom->id[1] = 0; - wacom_report_key(wcombo, BTN_0, (data[7] & 0x40)); - wacom_report_key(wcombo, BTN_4, (data[7] & 0x80)); - wacom_report_rel(wcombo, REL_WHEEL, 0); - wacom_report_key(wcombo, BTN_TOOL_FINGER, 0); - wacom_report_abs(wcombo, ABS_MISC, 0); - wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); + input_report_rel(input, REL_WHEEL, rw); + input_report_key(input, BTN_TOOL_FINGER, 0xf0); + if (!prox) + wacom->id[1] = 0; + input_report_abs(input, ABS_MISC, wacom->id[1]); + input_event(input, EV_MSC, MSC_SERIAL, 0xf0); + retval = 1; } break; - case WACOM_MO: - if ((data[7] & 0xf8) || (data[8] & 0xff)) { - if (penData) { - wacom_input_sync(wcombo); /* sync last event */ - if (!wacom->id[0]) - penData = 0; - } + + case WACOM_MO: + prox = (data[7] & 0xf8) || data[8]; + if (prox || wacom->id[1]) { wacom->id[1] = PAD_DEVICE_ID; - wacom_report_key(wcombo, BTN_0, (data[7] & 0x08)); - wacom_report_key(wcombo, BTN_1, (data[7] & 0x20)); - wacom_report_key(wcombo, BTN_4, (data[7] & 0x10)); - wacom_report_key(wcombo, BTN_5, (data[7] & 0x40)); - wacom_report_abs(wcombo, ABS_WHEEL, (data[8] & 0x7f)); - wacom_report_key(wcombo, BTN_TOOL_FINGER, 0xf0); - wacom_report_abs(wcombo, ABS_MISC, wacom->id[1]); - wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); - } else if (wacom->id[1]) { - if (penData) { - wacom_input_sync(wcombo); /* sync last event */ - if (!wacom->id[0]) - penData = 0; - } - wacom->id[1] = 0; - wacom_report_key(wcombo, BTN_0, (data[7] & 0x08)); - wacom_report_key(wcombo, BTN_1, (data[7] & 0x20)); - wacom_report_key(wcombo, BTN_4, (data[7] & 0x10)); - wacom_report_key(wcombo, BTN_5, (data[7] & 0x40)); - wacom_report_abs(wcombo, ABS_WHEEL, (data[8] & 0x7f)); - wacom_report_key(wcombo, BTN_TOOL_FINGER, 0); - wacom_report_abs(wcombo, ABS_MISC, 0); - wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); + input_report_key(input, BTN_0, (data[7] & 0x08)); + input_report_key(input, BTN_1, (data[7] & 0x20)); + input_report_key(input, BTN_4, (data[7] & 0x10)); + input_report_key(input, BTN_5, (data[7] & 0x40)); + input_report_abs(input, ABS_WHEEL, (data[8] & 0x7f)); + input_report_key(input, BTN_TOOL_FINGER, 0xf0); + if (!prox) + wacom->id[1] = 0; + input_report_abs(input, ABS_MISC, wacom->id[1]); + input_event(input, EV_MSC, MSC_SERIAL, 0xf0); } + retval = 1; break; } - return 1; +exit: + return retval; } -static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo) +static int wacom_intuos_inout(struct wacom_wac *wacom) { struct wacom_features *features = &wacom->features; unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; int idx = 0; /* tool number */ @@ -316,64 +285,73 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo) (data[6] << 4) + (data[7] >> 4); wacom->id[idx] = (data[2] << 4) | (data[3] >> 4); + switch (wacom->id[idx]) { - case 0x812: /* Inking pen */ - case 0x801: /* Intuos3 Inking pen */ - case 0x20802: /* Intuos4 Classic Pen */ - case 0x012: - wacom->tool[idx] = BTN_TOOL_PENCIL; - break; - case 0x822: /* Pen */ - case 0x842: - case 0x852: - case 0x823: /* Intuos3 Grip Pen */ - case 0x813: /* Intuos3 Classic Pen */ - case 0x885: /* Intuos3 Marker Pen */ - case 0x802: /* Intuos4 Grip Pen Eraser */ - case 0x804: /* Intuos4 Marker Pen */ - case 0x40802: /* Intuos4 Classic Pen */ - case 0x022: - wacom->tool[idx] = BTN_TOOL_PEN; - break; - case 0x832: /* Stroke pen */ - case 0x032: - wacom->tool[idx] = BTN_TOOL_BRUSH; - break; - case 0x007: /* Mouse 4D and 2D */ - case 0x09c: - case 0x094: - case 0x017: /* Intuos3 2D Mouse */ - case 0x806: /* Intuos4 Mouse */ - wacom->tool[idx] = BTN_TOOL_MOUSE; - break; - case 0x096: /* Lens cursor */ - case 0x097: /* Intuos3 Lens cursor */ - case 0x006: /* Intuos4 Lens cursor */ - wacom->tool[idx] = BTN_TOOL_LENS; - break; - case 0x82a: /* Eraser */ - case 0x85a: - case 0x91a: - case 0xd1a: - case 0x0fa: - case 0x82b: /* Intuos3 Grip Pen Eraser */ - case 0x81b: /* Intuos3 Classic Pen Eraser */ - case 0x91b: /* Intuos3 Airbrush Eraser */ - case 0x80c: /* Intuos4 Marker Pen Eraser */ - case 0x80a: /* Intuos4 Grip Pen Eraser */ - case 0x4080a: /* Intuos4 Classic Pen Eraser */ - case 0x90a: /* Intuos4 Airbrush Eraser */ - wacom->tool[idx] = BTN_TOOL_RUBBER; - break; - case 0xd12: - case 0x912: - case 0x112: - case 0x913: /* Intuos3 Airbrush */ - case 0x902: /* Intuos4 Airbrush */ - wacom->tool[idx] = BTN_TOOL_AIRBRUSH; - break; - default: /* Unknown tool */ - wacom->tool[idx] = BTN_TOOL_PEN; + case 0x812: /* Inking pen */ + case 0x801: /* Intuos3 Inking pen */ + case 0x20802: /* Intuos4 Classic Pen */ + case 0x012: + wacom->tool[idx] = BTN_TOOL_PENCIL; + break; + + case 0x822: /* Pen */ + case 0x842: + case 0x852: + case 0x823: /* Intuos3 Grip Pen */ + case 0x813: /* Intuos3 Classic Pen */ + case 0x885: /* Intuos3 Marker Pen */ + case 0x802: /* Intuos4 Grip Pen Eraser */ + case 0x804: /* Intuos4 Marker Pen */ + case 0x40802: /* Intuos4 Classic Pen */ + case 0x022: + wacom->tool[idx] = BTN_TOOL_PEN; + break; + + case 0x832: /* Stroke pen */ + case 0x032: + wacom->tool[idx] = BTN_TOOL_BRUSH; + break; + + case 0x007: /* Mouse 4D and 2D */ + case 0x09c: + case 0x094: + case 0x017: /* Intuos3 2D Mouse */ + case 0x806: /* Intuos4 Mouse */ + wacom->tool[idx] = BTN_TOOL_MOUSE; + break; + + case 0x096: /* Lens cursor */ + case 0x097: /* Intuos3 Lens cursor */ + case 0x006: /* Intuos4 Lens cursor */ + wacom->tool[idx] = BTN_TOOL_LENS; + break; + + case 0x82a: /* Eraser */ + case 0x85a: + case 0x91a: + case 0xd1a: + case 0x0fa: + case 0x82b: /* Intuos3 Grip Pen Eraser */ + case 0x81b: /* Intuos3 Classic Pen Eraser */ + case 0x91b: /* Intuos3 Airbrush Eraser */ + case 0x80c: /* Intuos4 Marker Pen Eraser */ + case 0x80a: /* Intuos4 Grip Pen Eraser */ + case 0x4080a: /* Intuos4 Classic Pen Eraser */ + case 0x90a: /* Intuos4 Airbrush Eraser */ + wacom->tool[idx] = BTN_TOOL_RUBBER; + break; + + case 0xd12: + case 0x912: + case 0x112: + case 0x913: /* Intuos3 Airbrush */ + case 0x902: /* Intuos4 Airbrush */ + wacom->tool[idx] = BTN_TOOL_AIRBRUSH; + break; + + default: /* Unknown tool */ + wacom->tool[idx] = BTN_TOOL_PEN; + break; } return 1; } @@ -384,41 +362,42 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo) * Reset all states otherwise we lose the initial states * when in-prox next time */ - wacom_report_abs(wcombo, ABS_X, 0); - wacom_report_abs(wcombo, ABS_Y, 0); - wacom_report_abs(wcombo, ABS_DISTANCE, 0); - wacom_report_abs(wcombo, ABS_TILT_X, 0); - wacom_report_abs(wcombo, ABS_TILT_Y, 0); + input_report_abs(input, ABS_X, 0); + input_report_abs(input, ABS_Y, 0); + input_report_abs(input, ABS_DISTANCE, 0); + input_report_abs(input, ABS_TILT_X, 0); + input_report_abs(input, ABS_TILT_Y, 0); if (wacom->tool[idx] >= BTN_TOOL_MOUSE) { - wacom_report_key(wcombo, BTN_LEFT, 0); - wacom_report_key(wcombo, BTN_MIDDLE, 0); - wacom_report_key(wcombo, BTN_RIGHT, 0); - wacom_report_key(wcombo, BTN_SIDE, 0); - wacom_report_key(wcombo, BTN_EXTRA, 0); - wacom_report_abs(wcombo, ABS_THROTTLE, 0); - wacom_report_abs(wcombo, ABS_RZ, 0); + input_report_key(input, BTN_LEFT, 0); + input_report_key(input, BTN_MIDDLE, 0); + input_report_key(input, BTN_RIGHT, 0); + input_report_key(input, BTN_SIDE, 0); + input_report_key(input, BTN_EXTRA, 0); + input_report_abs(input, ABS_THROTTLE, 0); + input_report_abs(input, ABS_RZ, 0); } else { - wacom_report_abs(wcombo, ABS_PRESSURE, 0); - wacom_report_key(wcombo, BTN_STYLUS, 0); - wacom_report_key(wcombo, BTN_STYLUS2, 0); - wacom_report_key(wcombo, BTN_TOUCH, 0); - wacom_report_abs(wcombo, ABS_WHEEL, 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, BTN_TOUCH, 0); + input_report_abs(input, ABS_WHEEL, 0); if (features->type >= INTUOS3S) - wacom_report_abs(wcombo, ABS_Z, 0); + input_report_abs(input, ABS_Z, 0); } - wacom_report_key(wcombo, wacom->tool[idx], 0); - wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */ - wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]); + input_report_key(input, wacom->tool[idx], 0); + input_report_abs(input, ABS_MISC, 0); /* reset tool id */ + input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]); wacom->id[idx] = 0; return 2; } return 0; } -static void wacom_intuos_general(struct wacom_wac *wacom, void *wcombo) +static void wacom_intuos_general(struct wacom_wac *wacom) { struct wacom_features *features = &wacom->features; unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; unsigned int t; /* general pen packet */ @@ -426,30 +405,30 @@ static void wacom_intuos_general(struct wacom_wac *wacom, void *wcombo) t = (data[6] << 2) | ((data[7] >> 6) & 3); if (features->type >= INTUOS4S && features->type <= INTUOS4L) t = (t << 1) | (data[1] & 1); - wacom_report_abs(wcombo, ABS_PRESSURE, t); - wacom_report_abs(wcombo, ABS_TILT_X, + input_report_abs(input, ABS_PRESSURE, t); + input_report_abs(input, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7)); - wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f); - wacom_report_key(wcombo, BTN_STYLUS, data[1] & 2); - wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 4); - wacom_report_key(wcombo, BTN_TOUCH, t > 10); + input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f); + input_report_key(input, BTN_STYLUS, data[1] & 2); + input_report_key(input, BTN_STYLUS2, data[1] & 4); + input_report_key(input, BTN_TOUCH, t > 10); } /* airbrush second packet */ if ((data[1] & 0xbc) == 0xb4) { - wacom_report_abs(wcombo, ABS_WHEEL, + input_report_abs(input, ABS_WHEEL, (data[6] << 2) | ((data[7] >> 6) & 3)); - wacom_report_abs(wcombo, ABS_TILT_X, + input_report_abs(input, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7)); - wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f); + input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f); } - return; } -static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo) +static int wacom_intuos_irq(struct wacom_wac *wacom) { struct wacom_features *features = &wacom->features; unsigned char *data = wacom->data; + struct input_dev *input = wacom->input; unsigned int t; int idx = 0, result; @@ -470,61 +449,61 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo) wacom->tool[1] = BTN_TOOL_FINGER; if (features->type >= INTUOS4S && features->type <= INTUOS4L) { - wacom_report_key(wcombo, BTN_0, (data[2] & 0x01)); - wacom_report_key(wcombo, BTN_1, (data[3] & 0x01)); - wacom_report_key(wcombo, BTN_2, (data[3] & 0x02)); - wacom_report_key(wcombo, BTN_3, (data[3] & 0x04)); - wacom_report_key(wcombo, BTN_4, (data[3] & 0x08)); - wacom_report_key(wcombo, BTN_5, (data[3] & 0x10)); - wacom_report_key(wcombo, BTN_6, (data[3] & 0x20)); + input_report_key(input, BTN_0, (data[2] & 0x01)); + input_report_key(input, BTN_1, (data[3] & 0x01)); + input_report_key(input, BTN_2, (data[3] & 0x02)); + input_report_key(input, BTN_3, (data[3] & 0x04)); + input_report_key(input, BTN_4, (data[3] & 0x08)); + input_report_key(input, BTN_5, (data[3] & 0x10)); + input_report_key(input, BTN_6, (data[3] & 0x20)); if (data[1] & 0x80) { - wacom_report_abs(wcombo, ABS_WHEEL, (data[1] & 0x7f)); + input_report_abs(input, ABS_WHEEL, (data[1] & 0x7f)); } else { /* Out of proximity, clear wheel value. */ - wacom_report_abs(wcombo, ABS_WHEEL, 0); + input_report_abs(input, ABS_WHEEL, 0); } if (features->type != INTUOS4S) { - wacom_report_key(wcombo, BTN_7, (data[3] & 0x40)); - wacom_report_key(wcombo, BTN_8, (data[3] & 0x80)); + input_report_key(input, BTN_7, (data[3] & 0x40)); + input_report_key(input, BTN_8, (data[3] & 0x80)); } if (data[1] | (data[2] & 0x01) | data[3]) { - wacom_report_key(wcombo, wacom->tool[1], 1); - wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID); + input_report_key(input, wacom->tool[1], 1); + input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); } else { - wacom_report_key(wcombo, wacom->tool[1], 0); - wacom_report_abs(wcombo, ABS_MISC, 0); + input_report_key(input, wacom->tool[1], 0); + input_report_abs(input, ABS_MISC, 0); } } else { - wacom_report_key(wcombo, BTN_0, (data[5] & 0x01)); - wacom_report_key(wcombo, BTN_1, (data[5] & 0x02)); - wacom_report_key(wcombo, BTN_2, (data[5] & 0x04)); - wacom_report_key(wcombo, BTN_3, (data[5] & 0x08)); - wacom_report_key(wcombo, BTN_4, (data[6] & 0x01)); - wacom_report_key(wcombo, BTN_5, (data[6] & 0x02)); - wacom_report_key(wcombo, BTN_6, (data[6] & 0x04)); - wacom_report_key(wcombo, BTN_7, (data[6] & 0x08)); - wacom_report_key(wcombo, BTN_8, (data[5] & 0x10)); - wacom_report_key(wcombo, BTN_9, (data[6] & 0x10)); - wacom_report_abs(wcombo, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]); - wacom_report_abs(wcombo, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]); + input_report_key(input, BTN_0, (data[5] & 0x01)); + input_report_key(input, BTN_1, (data[5] & 0x02)); + input_report_key(input, BTN_2, (data[5] & 0x04)); + input_report_key(input, BTN_3, (data[5] & 0x08)); + input_report_key(input, BTN_4, (data[6] & 0x01)); + input_report_key(input, BTN_5, (data[6] & 0x02)); + input_report_key(input, BTN_6, (data[6] & 0x04)); + input_report_key(input, BTN_7, (data[6] & 0x08)); + input_report_key(input, BTN_8, (data[5] & 0x10)); + input_report_key(input, BTN_9, (data[6] & 0x10)); + input_report_abs(input, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]); + input_report_abs(input, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]); if ((data[5] & 0x1f) | (data[6] & 0x1f) | (data[1] & 0x1f) | data[2] | (data[3] & 0x1f) | data[4]) { - wacom_report_key(wcombo, wacom->tool[1], 1); - wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID); + input_report_key(input, wacom->tool[1], 1); + input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); } else { - wacom_report_key(wcombo, wacom->tool[1], 0); - wacom_report_abs(wcombo, ABS_MISC, 0); + input_report_key(input, wacom->tool[1], 0); + input_report_abs(input, ABS_MISC, 0); } } - wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xffffffff); + input_event(input, EV_MSC, MSC_SERIAL, 0xffffffff); return 1; } /* process in/out prox events */ - result = wacom_intuos_inout(wacom, wcombo); + result = wacom_intuos_inout(wacom); if (result) - return result-1; + return result - 1; /* don't proceed if we don't know the ID */ if (!wacom->id[idx]) @@ -545,17 +524,17 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo) return 0; if (features->type >= INTUOS3S) { - wacom_report_abs(wcombo, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1)); - wacom_report_abs(wcombo, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1)); - wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 2) & 0x3f)); + input_report_abs(input, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1)); + input_report_abs(input, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1)); + input_report_abs(input, ABS_DISTANCE, ((data[9] >> 2) & 0x3f)); } else { - wacom_report_abs(wcombo, ABS_X, wacom_be16_to_cpu(&data[2])); - wacom_report_abs(wcombo, ABS_Y, wacom_be16_to_cpu(&data[4])); - wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 3) & 0x1f)); + input_report_abs(input, ABS_X, be16_to_cpup((__be16 *)&data[2])); + input_report_abs(input, ABS_Y, be16_to_cpup((__be16 *)&data[4])); + input_report_abs(input, ABS_DISTANCE, ((data[9] >> 3) & 0x1f)); } /* process general packets */ - wacom_intuos_general(wacom, wcombo); + wacom_intuos_general(wacom); /* 4D mouse, 2D mouse, marker pen rotation, tilt mouse, or Lens cursor packets */ if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0 || (data[1] & 0xbc) == 0xac) { @@ -567,174 +546,191 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo) t = (data[6] << 3) | ((data[7] >> 5) & 7); t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) : ((t-1) / 2 + 450)) : (450 - t / 2) ; - wacom_report_abs(wcombo, ABS_Z, t); + input_report_abs(input, ABS_Z, t); } else { /* 4D mouse rotation packet */ t = (data[6] << 3) | ((data[7] >> 5) & 7); - wacom_report_abs(wcombo, ABS_RZ, (data[7] & 0x20) ? + input_report_abs(input, ABS_RZ, (data[7] & 0x20) ? ((t - 1) / 2) : -t / 2); } } else if (!(data[1] & 0x10) && features->type < INTUOS3S) { /* 4D mouse packet */ - wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01); - wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02); - wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x04); + input_report_key(input, BTN_LEFT, data[8] & 0x01); + input_report_key(input, BTN_MIDDLE, data[8] & 0x02); + input_report_key(input, BTN_RIGHT, data[8] & 0x04); - wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x20); - wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x10); + input_report_key(input, BTN_SIDE, data[8] & 0x20); + input_report_key(input, BTN_EXTRA, data[8] & 0x10); t = (data[6] << 2) | ((data[7] >> 6) & 3); - wacom_report_abs(wcombo, ABS_THROTTLE, (data[8] & 0x08) ? -t : t); + input_report_abs(input, ABS_THROTTLE, (data[8] & 0x08) ? -t : t); } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) { /* I4 mouse */ if (features->type >= INTUOS4S && features->type <= INTUOS4L) { - wacom_report_key(wcombo, BTN_LEFT, data[6] & 0x01); - wacom_report_key(wcombo, BTN_MIDDLE, data[6] & 0x02); - wacom_report_key(wcombo, BTN_RIGHT, data[6] & 0x04); - wacom_report_rel(wcombo, REL_WHEEL, ((data[7] & 0x80) >> 7) + input_report_key(input, BTN_LEFT, data[6] & 0x01); + input_report_key(input, BTN_MIDDLE, data[6] & 0x02); + input_report_key(input, BTN_RIGHT, data[6] & 0x04); + input_report_rel(input, REL_WHEEL, ((data[7] & 0x80) >> 7) - ((data[7] & 0x40) >> 6)); - wacom_report_key(wcombo, BTN_SIDE, data[6] & 0x08); - wacom_report_key(wcombo, BTN_EXTRA, data[6] & 0x10); + input_report_key(input, BTN_SIDE, data[6] & 0x08); + input_report_key(input, BTN_EXTRA, data[6] & 0x10); - wacom_report_abs(wcombo, ABS_TILT_X, + input_report_abs(input, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7)); - wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f); + input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f); } else { /* 2D mouse packet */ - wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x04); - wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x08); - wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x10); - wacom_report_rel(wcombo, REL_WHEEL, (data[8] & 0x01) + input_report_key(input, BTN_LEFT, data[8] & 0x04); + input_report_key(input, BTN_MIDDLE, data[8] & 0x08); + input_report_key(input, BTN_RIGHT, data[8] & 0x10); + input_report_rel(input, REL_WHEEL, (data[8] & 0x01) - ((data[8] & 0x02) >> 1)); /* I3 2D mouse side buttons */ if (features->type >= INTUOS3S && features->type <= INTUOS3L) { - wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x40); - wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x20); + input_report_key(input, BTN_SIDE, data[8] & 0x40); + input_report_key(input, BTN_EXTRA, data[8] & 0x20); } } } else if ((features->type < INTUOS3S || features->type == INTUOS3L || features->type == INTUOS4L) && wacom->tool[idx] == BTN_TOOL_LENS) { /* Lens cursor packets */ - wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01); - wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02); - wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x04); - wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x10); - wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x08); + input_report_key(input, BTN_LEFT, data[8] & 0x01); + input_report_key(input, BTN_MIDDLE, data[8] & 0x02); + input_report_key(input, BTN_RIGHT, data[8] & 0x04); + input_report_key(input, BTN_SIDE, data[8] & 0x10); + input_report_key(input, BTN_EXTRA, data[8] & 0x08); } } - wacom_report_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */ - wacom_report_key(wcombo, wacom->tool[idx], 1); - wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]); + input_report_abs(input, ABS_MISC, wacom->id[idx]); /* report tool id */ + input_report_key(input, wacom->tool[idx], 1); + input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]); return 1; } -static void wacom_tpc_finger_in(struct wacom_wac *wacom, void *wcombo, char *data, int idx) +static void wacom_tpc_finger_in(struct wacom_wac *wacom, char *data, int idx) { - wacom_report_abs(wcombo, ABS_X, - (data[2 + idx * 2] & 0xff) | ((data[3 + idx * 2] & 0x7f) << 8)); - wacom_report_abs(wcombo, ABS_Y, - (data[6 + idx * 2] & 0xff) | ((data[7 + idx * 2] & 0x7f) << 8)); - wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); - wacom_report_key(wcombo, wacom->tool[idx], 1); - if (idx) - wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); - else - wacom_report_key(wcombo, BTN_TOUCH, 1); + struct input_dev *input = wacom->input; + int finger = idx + 1; + int x = le16_to_cpup((__le16 *)&data[finger * 2]) & 0x7fff; + int y = le16_to_cpup((__le16 *)&data[4 + finger * 2]) & 0x7fff; + + /* + * Work around input core suppressing "duplicate" events since + * we are abusing ABS_X/ABS_Y to transmit multi-finger data. + * This should go away once we switch to true multitouch + * protocol. + */ + if (wacom->last_finger != finger) { + if (x == input->abs[ABS_X]) + x++; + + if (y == input->abs[ABS_Y]) + y++; + } + + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_MISC, wacom->id[0]); + input_report_key(input, wacom->tool[finger], 1); + if (!idx) + input_report_key(input, BTN_TOUCH, 1); + input_event(input, EV_MSC, MSC_SERIAL, finger); + input_sync(wacom->input); + + wacom->last_finger = finger; } -static void wacom_tpc_touch_out(struct wacom_wac *wacom, void *wcombo, int idx) +static void wacom_tpc_touch_out(struct wacom_wac *wacom, int idx) { - wacom_report_abs(wcombo, ABS_X, 0); - wacom_report_abs(wcombo, ABS_Y, 0); - wacom_report_abs(wcombo, ABS_MISC, 0); - wacom_report_key(wcombo, wacom->tool[idx], 0); - if (idx) - wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); - else - wacom_report_key(wcombo, BTN_TOUCH, 0); - return; + struct input_dev *input = wacom->input; + int finger = idx + 1; + + input_report_abs(input, ABS_X, 0); + input_report_abs(input, ABS_Y, 0); + input_report_abs(input, ABS_MISC, 0); + input_report_key(input, wacom->tool[finger], 0); + if (!idx) + input_report_key(input, BTN_TOUCH, 0); + input_event(input, EV_MSC, MSC_SERIAL, finger); + input_sync(input); } -static void wacom_tpc_touch_in(struct wacom_wac *wacom, void *wcombo) +static void wacom_tpc_touch_in(struct wacom_wac *wacom, size_t len) { char *data = wacom->data; - struct urb *urb = ((struct wacom_combo *)wcombo)->urb; - static int firstFinger = 0; - static int secondFinger = 0; + struct input_dev *input = wacom->input; - wacom->tool[0] = BTN_TOOL_DOUBLETAP; + wacom->tool[1] = BTN_TOOL_DOUBLETAP; wacom->id[0] = TOUCH_DEVICE_ID; - wacom->tool[1] = BTN_TOOL_TRIPLETAP; + wacom->tool[2] = BTN_TOOL_TRIPLETAP; + + if (len != WACOM_PKGLEN_TPC1FG) { - if (urb->actual_length != WACOM_PKGLEN_TPC1FG) { switch (data[0]) { - case WACOM_REPORT_TPC1FG: - wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2])); - wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4])); - wacom_report_abs(wcombo, ABS_PRESSURE, wacom_le16_to_cpu(&data[6])); - wacom_report_key(wcombo, BTN_TOUCH, wacom_le16_to_cpu(&data[6])); - wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); - wacom_report_key(wcombo, wacom->tool[0], 1); - break; - case WACOM_REPORT_TPC2FG: - /* keep this byte to send proper out-prox event */ - wacom->id[1] = data[1] & 0x03; - - if (data[1] & 0x01) { - wacom_tpc_finger_in(wacom, wcombo, data, 0); - firstFinger = 1; - } else if (firstFinger) { - wacom_tpc_touch_out(wacom, wcombo, 0); - } - if (data[1] & 0x02) { - /* sync first finger data */ - if (firstFinger) - wacom_input_sync(wcombo); + case WACOM_REPORT_TPC1FG: + input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); + input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); + input_report_abs(input, ABS_PRESSURE, le16_to_cpup((__le16 *)&data[6])); + input_report_key(input, BTN_TOUCH, le16_to_cpup((__le16 *)&data[6])); + input_report_abs(input, ABS_MISC, wacom->id[0]); + input_report_key(input, wacom->tool[1], 1); + input_sync(input); + break; - wacom_tpc_finger_in(wacom, wcombo, data, 1); - secondFinger = 1; - } else if (secondFinger) { - /* sync first finger data */ - if (firstFinger) - wacom_input_sync(wcombo); + case WACOM_REPORT_TPC2FG: + if (data[1] & 0x01) + wacom_tpc_finger_in(wacom, data, 0); + else if (wacom->id[1] & 0x01) + wacom_tpc_touch_out(wacom, 0); - wacom_tpc_touch_out(wacom, wcombo, 1); - secondFinger = 0; - } - if (!(data[1] & 0x01)) - firstFinger = 0; - break; + if (data[1] & 0x02) + wacom_tpc_finger_in(wacom, data, 1); + else if (wacom->id[1] & 0x02) + wacom_tpc_touch_out(wacom, 1); + break; } } else { - wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1])); - wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3])); - wacom_report_key(wcombo, BTN_TOUCH, 1); - wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); - wacom_report_key(wcombo, wacom->tool[0], 1); + input_report_abs(input, ABS_X, get_unaligned_le16(&data[1])); + input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3])); + input_report_key(input, BTN_TOUCH, 1); + input_report_abs(input, ABS_MISC, wacom->id[1]); + input_report_key(input, wacom->tool[1], 1); + input_sync(input); } - return; } -static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo) +static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) { struct wacom_features *features = &wacom->features; char *data = wacom->data; - int prox = 0, pressure, idx = -1; - static int stylusInProx, touchInProx = 1, touchOut; - struct urb *urb = ((struct wacom_combo *)wcombo)->urb; + struct input_dev *input = wacom->input; + int prox = 0, pressure; + int retval = 0; dbg("wacom_tpc_irq: received report #%d", data[0]); - if (urb->actual_length == WACOM_PKGLEN_TPC1FG || /* single touch */ + if (len == WACOM_PKGLEN_TPC1FG || /* single touch */ data[0] == WACOM_REPORT_TPC1FG || /* single touch */ data[0] == WACOM_REPORT_TPC2FG) { /* 2FG touch */ - if (urb->actual_length == WACOM_PKGLEN_TPC1FG) { /* with touch */ + + if (wacom->shared->stylus_in_proximity) { + if (wacom->id[1] & 0x01) + wacom_tpc_touch_out(wacom, 0); + + if (wacom->id[1] & 0x02) + wacom_tpc_touch_out(wacom, 1); + + wacom->id[1] = 0; + return 0; + } + + if (len == WACOM_PKGLEN_TPC1FG) { /* with touch */ prox = data[0] & 0x01; } else { /* with capacity */ if (data[0] == WACOM_REPORT_TPC1FG) @@ -745,168 +741,264 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo) prox = data[1] & 0x03; } - if (!stylusInProx) { /* stylus not in prox */ - if (prox) { - if (touchInProx) { - wacom_tpc_touch_in(wacom, wcombo); - touchOut = 1; - return 1; - } - } else { + if (prox) { + if (!wacom->id[1]) + wacom->last_finger = 1; + wacom_tpc_touch_in(wacom, len); + } else { + if (data[0] == WACOM_REPORT_TPC2FG) { /* 2FGT out-prox */ - if (data[0] == WACOM_REPORT_TPC2FG) { - idx = (wacom->id[1] & 0x01) - 1; - if (idx == 0) { - wacom_tpc_touch_out(wacom, wcombo, idx); - /* sync first finger event */ - if (wacom->id[1] & 0x02) - wacom_input_sync(wcombo); - } - idx = (wacom->id[1] & 0x02) - 1; - if (idx == 1) - wacom_tpc_touch_out(wacom, wcombo, idx); - } else /* one finger touch */ - wacom_tpc_touch_out(wacom, wcombo, 0); - touchOut = 0; - touchInProx = 1; - return 1; - } - } else if (touchOut || !prox) { /* force touch out-prox */ - wacom_tpc_touch_out(wacom, wcombo, 0); - touchOut = 0; - touchInProx = 1; - return 1; + if (wacom->id[1] & 0x01) + wacom_tpc_touch_out(wacom, 0); + + if (wacom->id[1] & 0x02) + wacom_tpc_touch_out(wacom, 1); + } else + /* one finger touch */ + wacom_tpc_touch_out(wacom, 0); + + wacom->id[0] = 0; } + /* keep prox bit to send proper out-prox event */ + wacom->id[1] = prox; } else if (data[0] == WACOM_REPORT_PENABLED) { /* Penabled */ prox = data[1] & 0x20; - touchInProx = 0; + if (!wacom->shared->stylus_in_proximity) { /* first in prox */ + /* Going into proximity select tool */ + wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + if (wacom->tool[0] == BTN_TOOL_PEN) + wacom->id[0] = STYLUS_DEVICE_ID; + else + wacom->id[0] = ERASER_DEVICE_ID; - if (prox) { /* in prox */ - if (!wacom->id[0]) { - /* Going into proximity select tool */ - wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; - if (wacom->tool[0] == BTN_TOOL_PEN) - wacom->id[0] = STYLUS_DEVICE_ID; - else - wacom->id[0] = ERASER_DEVICE_ID; - } - wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02); - wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x10); - wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2])); - wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4])); - pressure = ((data[7] & 0x01) << 8) | data[6]; - if (pressure < 0) - pressure = features->pressure_max + pressure + 1; - wacom_report_abs(wcombo, ABS_PRESSURE, pressure); - wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x05); - } else { - wacom_report_abs(wcombo, ABS_X, 0); - wacom_report_abs(wcombo, ABS_Y, 0); - wacom_report_abs(wcombo, ABS_PRESSURE, 0); - wacom_report_key(wcombo, BTN_STYLUS, 0); - wacom_report_key(wcombo, BTN_STYLUS2, 0); - wacom_report_key(wcombo, BTN_TOUCH, 0); + wacom->shared->stylus_in_proximity = true; + } + input_report_key(input, BTN_STYLUS, data[1] & 0x02); + input_report_key(input, BTN_STYLUS2, data[1] & 0x10); + input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); + input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); + pressure = ((data[7] & 0x01) << 8) | data[6]; + if (pressure < 0) + pressure = features->pressure_max + pressure + 1; + input_report_abs(input, ABS_PRESSURE, pressure); + input_report_key(input, BTN_TOUCH, data[1] & 0x05); + if (!prox) { /* out-prox */ wacom->id[0] = 0; - /* pen is out so touch can be enabled now */ - touchInProx = 1; + wacom->shared->stylus_in_proximity = false; } - wacom_report_key(wcombo, wacom->tool[0], prox); - wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); - stylusInProx = prox; - return 1; + input_report_key(input, wacom->tool[0], prox); + input_report_abs(input, ABS_MISC, wacom->id[0]); + retval = 1; } - return 0; + return retval; } -int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo) +void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) { + bool sync; + switch (wacom_wac->features.type) { - case PENPARTNER: - return wacom_penpartner_irq(wacom_wac, wcombo); - - case PL: - return wacom_pl_irq(wacom_wac, wcombo); - - case WACOM_G4: - case GRAPHIRE: - case WACOM_MO: - return wacom_graphire_irq(wacom_wac, wcombo); - - case PTU: - return wacom_ptu_irq(wacom_wac, wcombo); - - case INTUOS: - case INTUOS3S: - case INTUOS3: - case INTUOS3L: - case INTUOS4S: - case INTUOS4: - case INTUOS4L: - case CINTIQ: - case WACOM_BEE: - return wacom_intuos_irq(wacom_wac, wcombo); - - case TABLETPC: - case TABLETPC2FG: - return wacom_tpc_irq(wacom_wac, wcombo); - - default: - return 0; + case PENPARTNER: + sync = wacom_penpartner_irq(wacom_wac); + break; + + case PL: + sync = wacom_pl_irq(wacom_wac); + break; + + case WACOM_G4: + case GRAPHIRE: + case WACOM_MO: + sync = wacom_graphire_irq(wacom_wac); + break; + + case PTU: + sync = wacom_ptu_irq(wacom_wac); + break; + + case INTUOS: + case INTUOS3S: + case INTUOS3: + case INTUOS3L: + case INTUOS4S: + case INTUOS4: + case INTUOS4L: + case CINTIQ: + case WACOM_BEE: + sync = wacom_intuos_irq(wacom_wac); + break; + + case TABLETPC: + case TABLETPC2FG: + sync = wacom_tpc_irq(wacom_wac, len); + break; + + default: + sync = false; + break; } - return 0; + + if (sync) + input_sync(wacom_wac->input); +} + +static void wacom_setup_intuos(struct wacom_wac *wacom_wac) +{ + struct input_dev *input_dev = wacom_wac->input; + + input_set_capability(input_dev, EV_MSC, MSC_SERIAL); + input_set_capability(input_dev, EV_REL, REL_WHEEL); + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(BTN_MIDDLE, input_dev->keybit); + __set_bit(BTN_SIDE, input_dev->keybit); + __set_bit(BTN_EXTRA, input_dev->keybit); + + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); + __set_bit(BTN_TOOL_BRUSH, input_dev->keybit); + __set_bit(BTN_TOOL_PENCIL, input_dev->keybit); + __set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit); + __set_bit(BTN_TOOL_LENS, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + __set_bit(BTN_STYLUS2, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_DISTANCE, + 0, wacom_wac->features.distance_max, 0, 0); + input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0); + input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0); + input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0); + input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0); + input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0); } -void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac) +void wacom_setup_input_capabilities(struct input_dev *input_dev, + struct wacom_wac *wacom_wac) { + struct wacom_features *features = &wacom_wac->features; + int i; + + input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + __set_bit(BTN_TOUCH, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_X, 0, features->x_max, 4, 0); + input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, 4, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, 0, 0); + + __set_bit(ABS_MISC, input_dev->absbit); + switch (wacom_wac->features.type) { - case WACOM_MO: - input_dev_mo(input_dev, wacom_wac); - case WACOM_G4: - input_dev_g4(input_dev, wacom_wac); - /* fall through */ - case GRAPHIRE: - input_dev_g(input_dev, wacom_wac); - break; - case WACOM_BEE: - input_dev_bee(input_dev, wacom_wac); - case INTUOS3: - case INTUOS3L: - case CINTIQ: - input_dev_i3(input_dev, wacom_wac); - /* fall through */ - case INTUOS3S: - input_dev_i3s(input_dev, wacom_wac); - /* fall through */ - case INTUOS: - input_dev_i(input_dev, wacom_wac); - break; - case INTUOS4: - case INTUOS4L: - input_dev_i4(input_dev, wacom_wac); - /* fall through */ - case INTUOS4S: - input_dev_i4s(input_dev, wacom_wac); - input_dev_i(input_dev, wacom_wac); - break; - case TABLETPC2FG: - input_dev_tpc2fg(input_dev, wacom_wac); - /* fall through */ - case TABLETPC: - input_dev_tpc(input_dev, wacom_wac); - if (wacom_wac->features.device_type != BTN_TOOL_PEN) - break; /* no need to process stylus stuff */ - - /* fall through */ - case PL: - case PTU: - input_dev_pl(input_dev, wacom_wac); - /* fall through */ - case PENPARTNER: - input_dev_pt(input_dev, wacom_wac); - break; + case WACOM_MO: + __set_bit(BTN_1, input_dev->keybit); + __set_bit(BTN_5, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); + /* fall through */ + + case WACOM_G4: + input_set_capability(input_dev, EV_MSC, MSC_SERIAL); + + __set_bit(BTN_TOOL_FINGER, input_dev->keybit); + __set_bit(BTN_0, input_dev->keybit); + __set_bit(BTN_4, input_dev->keybit); + /* fall through */ + + case GRAPHIRE: + input_set_capability(input_dev, EV_REL, REL_WHEEL); + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(BTN_MIDDLE, input_dev->keybit); + + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + __set_bit(BTN_STYLUS2, input_dev->keybit); + break; + + case WACOM_BEE: + __set_bit(BTN_8, input_dev->keybit); + __set_bit(BTN_9, input_dev->keybit); + /* fall through */ + + case INTUOS3: + case INTUOS3L: + case CINTIQ: + __set_bit(BTN_4, input_dev->keybit); + __set_bit(BTN_5, input_dev->keybit); + __set_bit(BTN_6, input_dev->keybit); + __set_bit(BTN_7, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0); + /* fall through */ + + case INTUOS3S: + __set_bit(BTN_0, input_dev->keybit); + __set_bit(BTN_1, input_dev->keybit); + __set_bit(BTN_2, input_dev->keybit); + __set_bit(BTN_3, input_dev->keybit); + + __set_bit(BTN_TOOL_FINGER, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0); + input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + /* fall through */ + + case INTUOS: + wacom_setup_intuos(wacom_wac); + break; + + case INTUOS4: + case INTUOS4L: + __set_bit(BTN_7, input_dev->keybit); + __set_bit(BTN_8, input_dev->keybit); + /* fall through */ + + case INTUOS4S: + for (i = 0; i < 7; i++) + __set_bit(BTN_0 + i, input_dev->keybit); + __set_bit(BTN_TOOL_FINGER, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + wacom_setup_intuos(wacom_wac); + break; + + case TABLETPC2FG: + if (features->device_type == BTN_TOOL_TRIPLETAP) { + __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); + input_set_capability(input_dev, EV_MSC, MSC_SERIAL); + } + /* fall through */ + + case TABLETPC: + if (features->device_type == BTN_TOOL_DOUBLETAP || + features->device_type == BTN_TOOL_TRIPLETAP) { + input_set_abs_params(input_dev, ABS_RX, 0, features->x_phy, 0, 0); + input_set_abs_params(input_dev, ABS_RY, 0, features->y_phy, 0, 0); + __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); + } + + if (features->device_type != BTN_TOOL_PEN) + break; /* no need to process stylus stuff */ + + /* fall through */ + + case PL: + case PTU: + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + __set_bit(BTN_STYLUS2, input_dev->keybit); + /* fall through */ + + case PENPARTNER: + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + break; } - return; } static const struct wacom_features wacom_features_0x00 = diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index b50cf04..063f1af 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -9,6 +9,8 @@ #ifndef WACOM_WAC_H #define WACOM_WAC_H +#include <linux/types.h> + /* maximum packet length for USB devices */ #define WACOM_PKGLEN_MAX 32 @@ -71,13 +73,20 @@ struct wacom_features { unsigned char unitExpo; }; +struct wacom_shared { + bool stylus_in_proximity; +}; + struct wacom_wac { char name[64]; unsigned char *data; - int tool[2]; - int id[2]; + int tool[3]; + int id[3]; __u32 serial[2]; + int last_finger; struct wacom_features features; + struct wacom_shared *shared; + struct input_dev *input; }; #endif diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 6c0f171..b9f58ca 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -119,6 +119,18 @@ config TOUCHSCREEN_DYNAPRO To compile this driver as a module, choose M here: the module will be called dynapro. +config TOUCHSCREEN_HAMPSHIRE + tristate "Hampshire serial touchscreen" + select SERIO + help + Say Y here if you have a Hampshire serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called hampshire. + config TOUCHSCREEN_EETI tristate "EETI touchscreen panel support" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 41145d0..8ad36ee 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o +obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o diff --git a/drivers/input/touchscreen/hampshire.c b/drivers/input/touchscreen/hampshire.c new file mode 100644 index 0000000..2da6cc3 --- /dev/null +++ b/drivers/input/touchscreen/hampshire.c @@ -0,0 +1,205 @@ +/* + * Hampshire serial touchscreen driver + * + * Copyright (c) 2010 Adam Bennett + * Based on the dynapro driver (c) Tias Guns + * + */ + +/* + * 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. + */ + +/* + * 2010/04/08 Adam Bennett <abennett72@gmail.com> + * Copied dynapro.c and edited for Hampshire 4-byte protocol + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/init.h> + +#define DRIVER_DESC "Hampshire serial touchscreen driver" + +MODULE_AUTHOR("Adam Bennett <abennett72@gmail.com>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define HAMPSHIRE_FORMAT_TOUCH_BIT 0x40 +#define HAMPSHIRE_FORMAT_LENGTH 4 +#define HAMPSHIRE_RESPONSE_BEGIN_BYTE 0x80 + +#define HAMPSHIRE_MIN_XC 0 +#define HAMPSHIRE_MAX_XC 0x1000 +#define HAMPSHIRE_MIN_YC 0 +#define HAMPSHIRE_MAX_YC 0x1000 + +#define HAMPSHIRE_GET_XC(data) (((data[3] & 0x0c) >> 2) | (data[1] << 2) | ((data[0] & 0x38) << 6)) +#define HAMPSHIRE_GET_YC(data) ((data[3] & 0x03) | (data[2] << 2) | ((data[0] & 0x07) << 9)) +#define HAMPSHIRE_GET_TOUCHED(data) (HAMPSHIRE_FORMAT_TOUCH_BIT & data[0]) + +/* + * Per-touchscreen data. + */ + +struct hampshire { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[HAMPSHIRE_FORMAT_LENGTH]; + char phys[32]; +}; + +static void hampshire_process_data(struct hampshire *phampshire) +{ + struct input_dev *dev = phampshire->dev; + + if (HAMPSHIRE_FORMAT_LENGTH == ++phampshire->idx) { + input_report_abs(dev, ABS_X, HAMPSHIRE_GET_XC(phampshire->data)); + input_report_abs(dev, ABS_Y, HAMPSHIRE_GET_YC(phampshire->data)); + input_report_key(dev, BTN_TOUCH, + HAMPSHIRE_GET_TOUCHED(phampshire->data)); + input_sync(dev); + + phampshire->idx = 0; + } +} + +static irqreturn_t hampshire_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct hampshire *phampshire = serio_get_drvdata(serio); + + phampshire->data[phampshire->idx] = data; + + if (HAMPSHIRE_RESPONSE_BEGIN_BYTE & phampshire->data[0]) + hampshire_process_data(phampshire); + else + dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n", + phampshire->data[0]); + + return IRQ_HANDLED; +} + +static void hampshire_disconnect(struct serio *serio) +{ + struct hampshire *phampshire = serio_get_drvdata(serio); + + input_get_device(phampshire->dev); + input_unregister_device(phampshire->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(phampshire->dev); + kfree(phampshire); +} + +/* + * hampshire_connect() is the routine that is called when someone adds a + * new serio device that supports hampshire protocol and registers it as + * an input device. This is usually accomplished using inputattach. + */ + +static int hampshire_connect(struct serio *serio, struct serio_driver *drv) +{ + struct hampshire *phampshire; + struct input_dev *input_dev; + int err; + + phampshire = kzalloc(sizeof(struct hampshire), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!phampshire || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + phampshire->serio = serio; + phampshire->dev = input_dev; + snprintf(phampshire->phys, sizeof(phampshire->phys), + "%s/input0", serio->phys); + + input_dev->name = "Hampshire Serial TouchScreen"; + input_dev->phys = phampshire->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_HAMPSHIRE; + input_dev->id.product = 0; + input_dev->id.version = 0x0001; + input_dev->dev.parent = &serio->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(phampshire->dev, ABS_X, + HAMPSHIRE_MIN_XC, HAMPSHIRE_MAX_XC, 0, 0); + input_set_abs_params(phampshire->dev, ABS_Y, + HAMPSHIRE_MIN_YC, HAMPSHIRE_MAX_YC, 0, 0); + + serio_set_drvdata(serio, phampshire); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(phampshire->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(phampshire); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id hampshire_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_HAMPSHIRE, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, hampshire_serio_ids); + +static struct serio_driver hampshire_drv = { + .driver = { + .name = "hampshire", + }, + .description = DRIVER_DESC, + .id_table = hampshire_serio_ids, + .interrupt = hampshire_interrupt, + .connect = hampshire_connect, + .disconnect = hampshire_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init hampshire_init(void) +{ + return serio_register_driver(&hampshire_drv); +} + +static void __exit hampshire_exit(void) +{ + serio_unregister_driver(&hampshire_drv); +} + +module_init(hampshire_init); +module_exit(hampshire_exit); diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c index be23780..769b479 100644 --- a/drivers/input/touchscreen/tsc2007.c +++ b/drivers/input/touchscreen/tsc2007.c @@ -347,6 +347,8 @@ static int __devexit tsc2007_remove(struct i2c_client *client) struct tsc2007 *ts = i2c_get_clientdata(client); struct tsc2007_platform_data *pdata = client->dev.platform_data; + i2c_set_clientdata(client, NULL); + tsc2007_free_irq(ts); if (pdata->exit_platform_hw) diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index 99330bb..0b0ae2e 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -811,12 +811,11 @@ static int nexio_init(struct usbtouch_usb *usbtouch) priv = usbtouch->priv; - priv->ack_buf = kmalloc(sizeof(nexio_ack_pkt), GFP_KERNEL); + priv->ack_buf = kmemdup(nexio_ack_pkt, sizeof(nexio_ack_pkt), + GFP_KERNEL); if (!priv->ack_buf) goto err_priv; - memcpy(priv->ack_buf, nexio_ack_pkt, sizeof(nexio_ack_pkt)); - priv->ack = usb_alloc_urb(0, GFP_KERNEL); if (!priv->ack) { dbg("%s - usb_alloc_urb failed: usbtouch->ack", __func__); diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index 5109bf3..cbfef1e 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -200,7 +200,7 @@ void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, mutex_lock(&wm->codec_mutex); reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS); - if (status & WM97XX_GPIO_HIGH) + if (status == WM97XX_GPIO_HIGH) reg |= gpio; else reg &= ~gpio; |